Cultivation_9+dfsg1_UnixSource/0000750000175000017500000000000011401021143015405 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/0000750000175000017500000000000011401021140016375 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/Makefile.GnuLinux0000640000175000017500000000155611377047544021651 0ustar pabspabs# # Modification History # # 2006-June-27 Jason Rohrer # Created. Condensed from X86 and PPC makefiles in Transcend project. # # 2007-April-23 Jason Rohrer # Removed unneeded libraries. # ## # The common GnuLinux portion of Makefiles. # Should not be made manually---used by configure to build Makefiles. ## PLATFORM_COMPILE_FLAGS = -DLINUX # various GL and X windows librariesneeded for linux # also need portaudio library (which in turn needs pthreads) PLATFORM_LINK_FLAGS = -L/usr/X11R6/lib -lGL -lglut -lGLU -lX11 ${PORT_AUDIO_PATH}/lib/libportaudio.a -lpthread -lpng # All platforms but OSX support g++ and need no linker hacks GXX = g++ LINK_FLAGS = ## # Platform-specific minorGems file path prefixes ## PLATFORM = Linux PLATFORM_PATH = linux TIME_PLATFORM = Unix TIME_PLATFORM_PATH = unix DIRECTORY_PLATFORM = Unix DIRECTORY_PLATFORM_PATH = unix Cultivation_9+dfsg1_UnixSource/game2/gameSource/0000750000175000017500000000000011401021171020473 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/gameSource/testPortal.h0000640000175000017500000000002310653414134023016 0ustar pabspabsvoid testPortal(); Cultivation_9+dfsg1_UnixSource/game2/gameSource/game.cpp0000640000175000017500000026121311377047545022147 0ustar pabspabs/* * Modification History * * 2006-June-27 Jason Rohrer * Created. * * 2006-September-12 Jason Rohrer * Fixed the machine-gun mating bug. * * 2006-October-4 Jason Rohrer * Fixed music loudness bug. * * 2006-October-9 Jason Rohrer * Fixed crash on exit. * Reduced music sample rate. * * 2006-October-10 Jason Rohrer * Added a limit on maximum plot size. * * 2006-October-13 Jason Rohrer * Fixed a bug that can result in a crash at startup. * * 2006-October-30 Jason Rohrer * Started work on follow button. * * 2006-November-2 Jason Rohrer * Fixed a button selected object bug when user-controlled gardener dies. * * 2006-November-26 Jason Rohrer * Fixed bug reading language and font files on mac. * * 2006-December-14 Jason Rohrer * Fixed missing tooltips (and crash) after restart. * Added a pause button. * Switched to dragging for plot selection. * * 2007-November-17 Jason Rohrer * Fixed behavior when second corner of plot dropped on a GUI panel. * Disabled button clicks while paused (except to unpause or quit). * * 2007-December-11 Jason Rohrer * Locked max frame rate down to 80fps. */ #include #include #include #include #include #include "minorGems/graphics/openGL/ScreenGL.h" #include "minorGems/graphics/openGL/SceneHandlerGL.h" #include "minorGems/graphics/Color.h" // GL GUI includes #include "minorGems/graphics/openGL/gui/GUIPanelGL.h" #include "minorGems/graphics/openGL/gui/GUIContainerGL.h" #include "minorGems/graphics/openGL/gui/GUITranslatorGL.h" #include "minorGems/graphics/openGL/gui/ButtonGL.h" #include "minorGems/graphics/openGL/gui/LabelGL.h" // for tool tips #include "minorGems/graphics/openGL/gui/TextGL.h" #include "minorGems/io/file/FileOutputStream.h" #include "minorGems/graphics/converters/TGAImageConverter.h" // for video grabbing // #include "minorGems/graphics/converters/PNGImageConverter.h" // #include "minorGems/graphics/converters/JPEGImageConverter.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include "minorGems/util/TranslationManager.h" #include "minorGems/ui/event/ActionListener.h" #include "minorGems/system/Time.h" #include "minorGems/system/Thread.h" // needed to synchronize between GL thread and sound thread #include "minorGems/system/MutexLock.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/random/StdRandomSource.h" #include "minorGems/math/geometry/Vector3D.h" #include "features.h" #include "gameFunctions.h" #include "World.h" #include "Plant.h" #include "Gardener.h" #include "GardenerAI2.h" #include "glCommon.h" #include "userInterface/TexturedPanel.h" #include "userInterface/PlantButton.h" #include "userInterface/PlotButton.h" #include "userInterface/WaterButton.h" #include "userInterface/HarvestButton.h" #include "userInterface/EmotionButton.h" #include "userInterface/MateButton.h" #include "userInterface/FollowButton.h" #include "userInterface/PoisonButton.h" #include "userInterface/PauseButton.h" #include "userInterface/RestartButton.h" #include "userInterface/QuitButton.h" #include "userInterface/NextTutorialButton.h" #include "userInterface/TextBlockGL.h" #include "userInterface/BorderPanel.h" #include "userInterface/ObjectSelector.h" #include "userInterface/EatButton.h" #include "userInterface/GiftButton.h" #include "userInterface/DiscardButton.h" #include "sound/SoundPlayer.h" #include "sound/SoundEffectsBank.h" #include "sound/MusicPlayer.h" #include "sound/MusicNoteWaveTable.h" // seed with time for same behavior each time int timeSeed = time( NULL ); //int timeSeed = 1156711006; //int timeSeed = 1156767128; //int timeSeed = 1156774629; //int timeSeed = 1156775513; StdRandomSource globalRandomSource( timeSeed ); //StdRandomSource globalRandomSource( 1156711006 ); // global pointer to current world World *globalWorld; Vector3D globalPlayerCurrentPosition; // AI gardeners start out with 20x20 plots // but player's screen limits player's plot to about 40x40 // to be fair, don't allow AI gardeners to make larger plots. double globalMaxPlotDimension = 40; int globalSoundSampleRate = 11025; double globalMusicSongLength = 12; int globalNumNotesPerMelody = 4; double globalShortestNoteLength = 0.25; double globalLongestNoteLength = 0.5; int globalMaxSimultaneousSounds = 2; // maximum number of gardeners we've seen so far int globalMaxNumGardeners = 0; SoundPlayer *globalSoundPlayer; SoundEffectsBank *globalSoundEffectsBank; MusicNoteWaveTable globalWaveTable( globalSoundSampleRate ); MusicPlayer globalMusicPlayer( globalSoundSampleRate, &globalWaveTable, globalMusicSongLength ); // synchronize between GL thread and sound thread MutexLock globalLock; // set to true to gradually shift to mUserControlledGardener position char smoothPositionTransition = false; double smoothTransitionVelocity = 20; // user action states enum UserActionState{ none = 1, plotFirstCorner, plotSecondCorner, // used to ignore button presses that may result when player // drops second corner of plot on top of a button doneDrawingPlot }; class GameSceneHandler : public SceneHandlerGL, public MouseHandlerGL, public KeyboardHandlerGL, public RedrawListenerGL, public ActionListener { friend void addGardenerToGame( Gardener *inGardener, Vector3D *inPosition, Angle3D *inRotation ); public: /** * Constructs a sceen handler. * * @param inScreen the screen to interact with. * Must be destroyed by caller after this class is destroyed. */ GameSceneHandler( ScreenGL *inScreen ); virtual ~GameSceneHandler(); /** * Executes necessary init code that reads from files. * * Must be called before using a newly-constructed GameSceneHandler. * * This call assumes that the needed files are in the current working * directory. */ void initFromFiles(); ScreenGL *mScreen; /** * Projects a point from the screen out onto the scene plane. * * @param inX, in Y, the x and y screen coordinates * @param outSceneX, outSceneY, pointers to where the sceen * coordinates should be returned. */ void projectScreenPointIntoScene( int inX, int inY, double *outSceneX, double *outSceneY ); // implements the SceneHandlerGL interface virtual void drawScene(); // implements the MouseHandlerGL interface virtual void mouseMoved( int inX, int inY ); virtual void mouseDragged( int inX, int inY ); virtual void mousePressed( int inX, int inY ); virtual void mouseReleased( int inX, int inY ); // implements the KeyboardHandlerGL interface virtual void keyPressed( unsigned char inKey, int inX, int inY ); virtual void specialKeyPressed( int inKey, int inX, int inY ); virtual void keyReleased( unsigned char inKey, int inX, int inY ); virtual void specialKeyReleased( int inKey, int inX, int inY ); // implements the RedrawListener interface virtual void fireRedraw(); // implements the ActionListener interface virtual void actionPerformed( GUIComponent *inTarget ); protected: // the time that the last frame was drawn unsigned long mLastFrameSeconds; unsigned long mLastFrameMilliseconds; // our current frame rate unsigned long mFrameMillisecondDelta; int mStartTimeSeconds; char mPaused; double mMaxFrameRate; char mPrintFrameRate; unsigned long mNumFrames; unsigned long mFrameBatchSize; unsigned long mFrameBatchStartTimeSeconds; unsigned long mFrameBatchStartTimeMilliseconds; Color mBackgroundColor; World mWorld; SoundPlayer mSoundPlayer; SoundEffectsBank mSoundEffectsBank; Gardener *mUserControlledGardener; SimpleVector mOtherGardeners; SimpleVector mOtherGardenerAI; // GUI GUITranslatorGL *mMainPanelGuiTranslator; GUIPanelGL *mMainPanel; GUIPanelGL *mSidePanel; BorderPanel *mTutorialPanel; TextBlockGL *mTutorialTextBlock; // the key of the current tutorial on display // must point to NULL or a const string char *mCurrentTutorialKey; char mTutorialEnabled; // track which tutorials have already been show SimpleVector mSpentTutorialKeys; // these do not need to be destroyed (destroyed by main panel) // they are only saved for use by the action listener function ButtonBase *mPlantButton; ButtonBase *mPlotButton; WaterButton *mWaterButton; ButtonBase *mHarvestButton; MateButton *mMateButton; FollowButton *mFollowButton; PoisonButton *mPoisonButton; ButtonBase *mPauseButton; ButtonBase *mRestartButton; ButtonBase *mQuitButton; ButtonBase *mEatButton; ButtonBase *mGiftButton; DiscardButton *mDiscardButton; ObjectSelector *mSelector; // tutorial buttons ButtonBase *mStopTutorialButton; ButtonBase *mNextTutorialButton; UserActionState mActionState; // for tool tips TextGL *mTextGL; LabelGL *mToolTipLabel; GUIComponentGL *mCurrentTipGUIComponent; int mCurrentTipStoredItem; SimpleVector mToolTipButtons; SimpleVector mToolTipKeys; SimpleVector mToolTipTimers; double mSelectorToolTipTimer; double mToolTipDisplayTime; // true if we're in video capture mode char mVideoCaptureMode; int mNumFramesToCapture; int mFramesCaptured; char *mFrameCaptureFileNamePrefix; int mVideoNumber; int mVideoFramesPerSecond; int mVideoFrameWidth; int mVideoFrameHeight; /** * Sets the second corner of user's plot. * * @param inPosition the world position of the second corner. * Destroyed by caller. */ void setSecondPlotCorner( Vector3D *inPosition ); void updateStorageUI(); /** * Shows a tutorial message. * * Each message is shown at most once (a second call with * the same key displays nothing). * * @param inTutorialKey the key of the tutorial to show. */ void showTutorial( const char *inTutorialKey ); /** * Called by event handlers when a given tutorial message is dismissed. * * @param inTutorialKey the key of the tutorial. */ void tutorialDone( const char *inTutorialKey ); }; // a restart is triggered by a button press, but since it involves destroying // and recreating the scene handler (which contains the buttons), we can // only handle the reset in our fireRedraw callback (called by // screen, which we don't destroy) char restartFlag = false; GameSceneHandler *sceneHandler; ScreenGL *screen; double baseViewZ = -23; // function that destroys object when exit is called. // exit is the only way to stop the GLUT-based ScreenGL void cleanUpAtExit() { printf( "exiting\n" ); destroyCommonTextures(); delete sceneHandler; delete screen; } int main( int inNumArgs, char **inArgs ) { printf( "Game seed = %d\n", timeSeed ); // must pass args to GLUT before constructing the screen glutInit( &inNumArgs, inArgs ); screen = new ScreenGL( 300, 300, false, "Cultivation", NULL, NULL, NULL ); // lock while sceneHandler constructed so that // sound player does not try to access world before it is constructed globalLock.lock(); sceneHandler = new GameSceneHandler( screen ); // safe for sound player to touch world now globalLock.unlock(); Vector3D move( 0, 0, baseViewZ ); screen->moveView( &move ); // do this mac check after constructing scene handler and screen, // since these cause various Mac frameworks to be loaded (which can // change the current working directory out from under us) #ifdef __mac__ // make sure working directory is the same as the directory // that the app resides in // this is especially important on the mac platform, which // doesn't set a proper working directory for double-clicked // app bundles // arg 0 is the path to the app executable char *appDirectoryPath = stringDuplicate( inArgs[0] ); char *appNamePointer = strstr( appDirectoryPath, "Cultivation.app" ); if( appNamePointer != NULL ) { // terminate full app path to get parent directory appNamePointer[0] = '\0'; chdir( appDirectoryPath ); } delete [] appDirectoryPath; #endif // read features from file here, after initializing mac working directory initializeFeatures(); // also do file-dependent part of init for GameSceneHandler here sceneHandler->initFromFiles(); if( Features::largeWindow ) { screen->changeWindowSize( 600, 600 ); } // register cleanup function, since screen->start() will never return atexit( cleanUpAtExit ); screen->start(); return 0; } int getMaxDistanceForTransactions() { return 20; } double getGardenerZPosition() { return -2.5; } void addGardenerToGame( Gardener *inGardener, Vector3D *inPosition, Angle3D *inRotation ) { inGardener->setPlotHidden( false ); World *world = &( sceneHandler->mWorld ); world->addGardener( inGardener, inPosition, inRotation ); GardenerAI *ai = new GardenerAI( inGardener, world ); sceneHandler->mOtherGardeners.push_back( inGardener ); sceneHandler->mOtherGardenerAI.push_back( ai ); } void setNumGardeners( int inCount ) { int numGardeners = inCount; // avoid divide by zero if( numGardeners < 1 ) { numGardeners = 1; } if( globalMaxNumGardeners < numGardeners ) { // an increase globalMaxNumGardeners = numGardeners; } // set volume based on maximum number of gardeners seen so far // Thus, as population grows, per-song volume declines // but as population shrinks, volume stays constant. // Otherwise, if we increase volume as gardeners die off, music // gets louder and louder (expecially when player's gardener is // last one left---it can be annoyingly loud). // avoid clipping double musicLoudness = ( 1 - 0.1 * globalMaxSimultaneousSounds ) / globalMaxNumGardeners; // make sure we don't access player after it has been destroyed if( globalSoundPlayer != NULL ) { globalSoundPlayer->setMusicLoudness( musicLoudness ); } } double mousePositionX=0, mousePositionY=0; Vector3D worldCornerA( -60, -60, 0 ); Vector3D worldCornerB( 60, 60, 0 ); GameSceneHandler::GameSceneHandler( ScreenGL *inScreen ) : mScreen( inScreen ), mFrameMillisecondDelta( 0 ), mStartTimeSeconds( time( NULL ) ), mPaused( false ), mMaxFrameRate( 80 ), // limit frame rate mPrintFrameRate( true ), mNumFrames( 0 ), mFrameBatchSize( 100 ), mFrameBatchStartTimeSeconds( time( NULL ) ), mFrameBatchStartTimeMilliseconds( 0 ), mBackgroundColor( 0, 0, 1, 1 ), mWorld( &worldCornerA, &worldCornerB ), mSoundPlayer( globalSoundSampleRate, globalMaxSimultaneousSounds, &globalMusicPlayer, // default loudness // set later when gardeners added 1, // start global loudness at zero, fade in later 0 ), mSoundEffectsBank( &mSoundPlayer ), mActionState( none ) { glClearColor( mBackgroundColor.r, mBackgroundColor.g, mBackgroundColor.b, mBackgroundColor.a ); // set external pointer so it can be used in calls below sceneHandler = this; globalWorld = &mWorld; globalSoundPlayer = &mSoundPlayer; globalSoundEffectsBank = &mSoundEffectsBank; mScreen->addMouseHandler( this ); mScreen->addKeyboardHandler( this ); mScreen->addSceneHandler( this ); mScreen->addRedrawListener( this ); Time::getCurrentTime( &mLastFrameSeconds, &mLastFrameMilliseconds ); // start user's gardner in random land spot Vector3D startPosition( -100, -100, 0 ); while( mWorld.isInWater( &startPosition ) ) { double x = globalRandomSource.getRandomBoundedDouble( worldCornerA.mX, worldCornerB.mX ); double y = globalRandomSource.getRandomBoundedDouble( worldCornerA.mY, worldCornerB.mY ); startPosition.setCoordinates( x, y, 0 ); } mUserControlledGardener = new Gardener( &startPosition ); mUserControlledGardener->setPlotHidden( false ); // start off with 3random seeds mUserControlledGardener->storeItem( new Seeds() ); mUserControlledGardener->storeItem( new Seeds() ); mUserControlledGardener->storeItem( new Seeds() ); mWorld.addGardener( mUserControlledGardener, &startPosition ); mUserControlledGardener->mUserCanControl = true; mUserControlledGardener->mUserControlling = true; int numOtherGardeners = 3; for( int i=0; istoreItem( new Seeds() ); gardener->storeItem( new Seeds() ); gardener->storeItem( new Seeds() ); addGardenerToGame( gardener, &gardenerPosition ); // FIXME: watch this gardener instead // mUserControlledGardener = gardener; } mMainPanel = new TexturedPanel( 0, 0, 2, 0.1, new Color( 1, 1, 1, 0.5 ), false ); mMainPanelGuiTranslator = new GUITranslatorGL( mMainPanel, screen ); mScreen->addSceneHandler( mMainPanelGuiTranslator ); mScreen->addMouseHandler( mMainPanelGuiTranslator ); mScreen->addKeyboardHandler( mMainPanelGuiTranslator ); mPlantButton = new PlantButton( 0.1, 0.01, 0.08, 0.08 ); mMainPanel->add( mPlantButton ); mPlantButton->addActionListener( this ); mPlotButton = new PlotButton( 0.2, 0.01, 0.08, 0.08 ); mMainPanel->add( mPlotButton ); mPlotButton->addActionListener( this ); mWaterButton = new WaterButton( 0.3, 0.01, 0.08, 0.08 ); mMainPanel->add( mWaterButton ); mWaterButton->addActionListener( this ); mHarvestButton = new HarvestButton( 0.4, 0.01, 0.08, 0.08 ); mMainPanel->add( mHarvestButton ); mHarvestButton->addActionListener( this ); mMateButton = new MateButton( 0.5, 0.01, 0.08, 0.08 ); mMainPanel->add( mMateButton ); mMateButton->addActionListener( this ); mFollowButton = new FollowButton( 0.6, 0.01, 0.08, 0.08 ); mMainPanel->add( mFollowButton ); mFollowButton->addActionListener( this ); mPoisonButton = new PoisonButton( 0.7, 0.01, 0.08, 0.08 ); mMainPanel->add( mPoisonButton ); mPoisonButton->addActionListener( this ); // only show one (restart or pause) mPauseButton = new PauseButton( 0.8, 0.01, 0.08, 0.08 ); mRestartButton = new RestartButton( 0.8, 0.01, 0.08, 0.08 ); mMainPanel->add( mPauseButton ); //mMainPanel->add( mRestartButton ); mPauseButton->addActionListener( this ); mRestartButton->addActionListener( this ); mQuitButton = new QuitButton( 0.9, 0.01, 0.08, 0.08 ); mMainPanel->add( mQuitButton ); mQuitButton->addActionListener( this ); // layout side panel mSidePanel = new TexturedPanel( 0, 0.1, 0.1, 2, new Color( 1, 1, 1, 0.5 ), true ); mMainPanel->add( mSidePanel ); mDiscardButton = new DiscardButton( 0.01, 0.1, 0.08, 0.08 ); mSidePanel->add( mDiscardButton ); mDiscardButton->addActionListener( this ); mSelector = new ObjectSelector( 0.01, 0.3, 0.08, 0.38, mUserControlledGardener ); mSidePanel->add( mSelector ); mEatButton = new EatButton( 0.01, 0.8, 0.08, 0.08 ); mSidePanel->add( mEatButton ); mEatButton->addActionListener( this ); mGiftButton = new GiftButton( 0.01, 0.9, 0.08, 0.08 ); mSidePanel->add( mGiftButton ); mGiftButton->addActionListener( this ); // all action buttons start out disabled (until we enable appropriate // ones during our first fireRedraw) // otherwise, we see a one-frame flash with all buttons enabled mPlantButton->setEnabled( false ); mWaterButton->setEnabled( false ); mHarvestButton->setEnabled( false ); mEatButton->setEnabled( false ); mGiftButton->setEnabled( false ); mMateButton->setEnabled( false ); mFollowButton->setEnabled( false ); mPoisonButton->setEnabled( false ); mDiscardButton->setEnabled( false ); mRestartButton->setEnabled( false ); // special exceptions: plot, quit, and pause enabled mPlotButton->setEnabled( true ); mQuitButton->setEnabled( true ); mPauseButton->setEnabled( true ); mTutorialPanel = new BorderPanel( 0.15, 0.7, 0.8, 0.3, new Color( 0, 0, 0, 0.75 ) ); //mMainPanel->add( mTutorialPanel ); mStopTutorialButton = new QuitButton( 0.19, 0.7, 0.04, 0.04 ); mTutorialPanel->add( mStopTutorialButton ); mNextTutorialButton = new NextTutorialButton( 0.87, 0.7, 0.04, 0.04 ); mTutorialPanel->add( mNextTutorialButton ); mStopTutorialButton->addActionListener( this ); mNextTutorialButton->addActionListener( this ); mCurrentTutorialKey = NULL; mTutorialEnabled = true; mVideoCaptureMode = false; // video in 5-second batches mNumFramesToCapture = 150; mFramesCaptured = 0; mVideoFramesPerSecond = 30; mFrameCaptureFileNamePrefix = (char *)"video/video_"; mVideoNumber = 8; mVideoFrameWidth = 640; mVideoFrameHeight = 480; //mVideoFrameWidth = 320; //mVideoFrameHeight = 240; // all gardeners added, ready to go // start the sound fade in // (this avoids "garbage" sound being generated as gardeners are created). mSoundPlayer.fadeIn( 4 ); } GameSceneHandler::~GameSceneHandler() { mScreen->removeMouseHandler( this ); mScreen->removeSceneHandler( this ); mScreen->removeRedrawListener( this ); // delete whichever button is not in the panel if( ! mMainPanel->contains( mPauseButton ) ) { delete mPauseButton; } if( ! mMainPanel->contains( mRestartButton ) ) { delete mRestartButton; } // delete tutorial panel if it is not showing if( ! mMainPanel->contains( mTutorialPanel ) ) { delete mTutorialPanel; } // this will recursively delete all of our GUI components delete mMainPanelGuiTranslator; int numOtherGardeners = mOtherGardeners.size(); for( int i=0; ifilter( &blur ); if( fontImage == NULL ) { // default // blank font fontImage = new Image( 256, 256, 4, true ); } mTextGL = new TextGL( fontImage, // use alpha true, // variable character width false, // extra space around each character 0.1, // space is half a character width 0.75 ); delete fontImage; const char *startString = ""; mTutorialTextBlock = new TextBlockGL( 0.19, 0.74, 0.72, 0.22, (char *)startString, mTextGL, 37 ); mTutorialPanel->add( mTutorialTextBlock ); mToolTipButtons.push_back( mPlantButton ); mToolTipButtons.push_back( mPlotButton ); mToolTipButtons.push_back( mWaterButton ); mToolTipButtons.push_back( mHarvestButton ); mToolTipButtons.push_back( mMateButton ); mToolTipButtons.push_back( mFollowButton ); mToolTipButtons.push_back( mPoisonButton ); mToolTipButtons.push_back( mPauseButton ); mToolTipButtons.push_back( mRestartButton ); mToolTipButtons.push_back( mQuitButton ); mToolTipButtons.push_back( mEatButton ); mToolTipButtons.push_back( mGiftButton ); mToolTipButtons.push_back( mDiscardButton ); mToolTipButtons.push_back( mStopTutorialButton ); mToolTipButtons.push_back( mNextTutorialButton ); mToolTipKeys.push_back( "tip_PlantButton" ); mToolTipKeys.push_back( "tip_PlotButton" ); mToolTipKeys.push_back( "tip_WaterButton" ); mToolTipKeys.push_back( "tip_HarvestButton" ); mToolTipKeys.push_back( "tip_MateButton" ); mToolTipKeys.push_back( "tip_LeadButton" ); mToolTipKeys.push_back( "tip_PoisonButton" ); mToolTipKeys.push_back( "tip_PauseButton" ); mToolTipKeys.push_back( "tip_RestartButton" ); mToolTipKeys.push_back( "tip_QuitButton" ); mToolTipKeys.push_back( "tip_EatButton" ); mToolTipKeys.push_back( "tip_GiftButton" ); mToolTipKeys.push_back( "tip_DiscardButton" ); mToolTipKeys.push_back( "tip_StopTutorial" ); mToolTipKeys.push_back( "tip_NextTutorial" ); int numTips = mToolTipButtons.size(); for( int t=0; t *tokens = tokenizeString( languageNameText ); int numTokens = tokens->size(); // first token is name if( numTokens > 0 ) { char *languageName = *( tokens->getElement( 0 ) ); TranslationManager::setLanguage( languageName ); } else { // default // TranslationManager already defaults to English, but // it looks for the language files at runtime before we have set // the current working. // Thus, we specify the default again here so that it looks // for its language files again. TranslationManager::setLanguage( "English" ); } delete [] languageNameText; for( int t=0; tgetElement( t ) ); } delete tokens; } // ready for first tutorial showTutorial( "tutorial_question" ); } void GameSceneHandler::drawScene() { /* glClearColor( mBackgroundColor->r, mBackgroundColor->g, mBackgroundColor->b, mBackgroundColor->a ); */ glDisable( GL_TEXTURE_2D ); glDisable( GL_CULL_FACE ); glDisable( GL_DEPTH_TEST ); // draw world objects here // draw the world mWorld.draw(); // if in middle of specifying a new plot if( mActionState == plotFirstCorner || mActionState == plotSecondCorner ) { // draw cross hairs from edge of screen to mouse position int w = screen->getWidth(); int h = screen->getHeight(); double xLeft, xRight, yBottom, yTop; // NOTE: // Cannot call this inside glBegin / glEnd for some reson // (results in NAN values if we do). projectScreenPointIntoScene( 0, 0, &xLeft, &yBottom ); projectScreenPointIntoScene( w-1, h-1, &xRight, &yTop ); glLineWidth( 1 ); glBegin( GL_LINES ); { glColor4f( 1, 1, 1, 0.5 ); // horizontal glVertex2d( xLeft, mousePositionY ); glVertex2d( xRight, mousePositionY ); // vertical glVertex2d( mousePositionX, yBottom ); glVertex2d( mousePositionX, yTop ); } glEnd(); } // draw a small crosshair to show user's desired position (where moving to) if( mUserControlledGardener->isMoving() ) { Vector3D *p = mUserControlledGardener->getDesiredPosition(); glColor4f( 1, 1, 1, 0.5 ); Angle3D crossAngle( 0, 0, M_PI / 4 ); drawBlurPlus( p, 1, &crossAngle ); delete p; } if( mVideoCaptureMode ) { int w = mScreen->getWidth(); int h = mScreen->getHeight(); // make sure window size change has kicked in // (size change happens after next redraw) // Otherwise, size of first frame may differ from subsequent frames. if( w == mVideoFrameWidth && h == mVideoFrameHeight ) { unsigned char *rgbBytes = new unsigned char[ w * h * 3 ]; glReadPixels( 0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, rgbBytes ); Image frameImage( w, h, 3, false ); double *channels[3]; int c; for( c=0; c<3; c++ ) { channels[c] = frameImage.getChannel( c ); } // image of screen is upside down int outputRow = 0; for( int y=h-1; y>=0; y-- ) { for( int x=0; x= mNumFramesToCapture ) { mVideoCaptureMode = false; // auto-pause after capturing a batch mPaused = true; // next capture uses new video number mVideoNumber ++; // show UI //mScreen->addSceneHandler( mMainPanelGuiTranslator ); // shrink window mScreen->changeWindowSize( 300, 300 ); } } } } void GameSceneHandler::mouseMoved( int inX, int inY ) { projectScreenPointIntoScene( inX, inY, &mousePositionX, &mousePositionY ); Vector3D position( mousePositionX, mousePositionY, 0 ); } void GameSceneHandler::mouseDragged( int inX, int inY ) { double guiX, guiY; mMainPanelGuiTranslator->translate( inX, inY, &guiX, &guiY ); projectScreenPointIntoScene( inX, inY, &mousePositionX, &mousePositionY ); Vector3D position( mousePositionX, mousePositionY, 0 ); switch( mActionState ) { case none: // not in the middle of an action //ignore it break; case plotFirstCorner: // start drawing a new plot mWorld.setGardenerPlot( mUserControlledGardener, &position, &position ); mActionState = plotSecondCorner; break; case plotSecondCorner: // in progress of dragging to select setSecondPlotCorner( &position ); break; case doneDrawingPlot: // should never hit this case printf( "Warning, hit an unexpected case in " "GameSceneHandler::mouseDragged\n" ); break; } } void GameSceneHandler::mousePressed( int inX, int inY ) { double guiX, guiY; mMainPanelGuiTranslator->translate( inX, inY, &guiX, &guiY ); projectScreenPointIntoScene( inX, inY, &mousePositionX, &mousePositionY ); Vector3D position( mousePositionX, mousePositionY, 0 ); switch( mActionState ) { case none: // not in the middle of an action //ignore it break; case plotFirstCorner: // start drawing a new plot mWorld.setGardenerPlot( mUserControlledGardener, &position, &position ); mActionState = plotSecondCorner; break; case plotSecondCorner: // should never hit this case printf( "Warning, hit an unexpected case in " "GameSceneHandler::mousePressed\n" ); break; case doneDrawingPlot: // should never hit this case printf( "Warning, hit an unexpected case in " "GameSceneHandler::mousePressed\n" ); break; } } void GameSceneHandler::mouseReleased( int inX, int inY ) { double guiX, guiY; mMainPanelGuiTranslator->translate( inX, inY, &guiX, &guiY ); char inGuiRegion = true; if( ! mMainPanel->isInside( guiX, guiY ) && ! mSidePanel->isInside( guiX, guiY ) && ( ! mMainPanel->contains( mTutorialPanel ) || ! mTutorialPanel->isInside( guiX, guiY ) ) ) { inGuiRegion = false; } projectScreenPointIntoScene( inX, inY, &mousePositionX, &mousePositionY ); Vector3D position( mousePositionX, mousePositionY, 0 ); switch( mActionState ) { case none: // not in the middle of an action // trying to move? // ignore clicks that are inside the main GUI panel if( !inGuiRegion ) { // move the user's gardener mUserControlledGardener->setDesiredPosition( &position ); } break; case plotFirstCorner: // should never hit this case printf( "Warning, hit an unexpected case in " "GameSceneHandler::mouseReleased\n" ); break; case plotSecondCorner: setSecondPlotCorner( &position ); // done drawing plot if( inGuiRegion ) { // second corner landed in gui region // tell GUI to ignore click mActionState = doneDrawingPlot; } else { mActionState = none; } mPlotButton->setEnabled( true ); if( mWorld.getPlotArea( mUserControlledGardener ) > 0 ) { showTutorial( "tutorial_plant" ); } break; case doneDrawingPlot: // should never hit this case printf( "Warning, hit an unexpected case in " "GameSceneHandler::mouseReleased\n" ); break; } } void GameSceneHandler::fireRedraw() { if( restartFlag ) { // destroy self mScreen->removeSceneHandler( sceneHandler ); mScreen->removeKeyboardHandler( sceneHandler ); mScreen->removeSceneHandler( mMainPanelGuiTranslator ); mScreen->removeMouseHandler( mMainPanelGuiTranslator ); mScreen->removeKeyboardHandler( mMainPanelGuiTranslator ); delete sceneHandler; // construt new sceneHandler = new GameSceneHandler( screen ); sceneHandler->initFromFiles(); restartFlag = false; smoothPositionTransition = true; return; } if( mPaused ) { // ignore redraw event // sleep to avoid wasting CPU cycles Thread::staticSleep( 1000 ); // also ignore time that passes while paused Time::getCurrentTime( &mLastFrameSeconds, &mLastFrameMilliseconds ); return; } globalLock.lock(); // deal with frame timing issues unsigned long lastMillisecondDelta = mFrameMillisecondDelta; // how many milliseconds have passed since the last frame mFrameMillisecondDelta = Time::getMillisecondsSince( mLastFrameSeconds, mLastFrameMilliseconds ); // lock down to mMaxFrameRate frames per second unsigned long minFrameTime = (unsigned long)( 1000 / mMaxFrameRate ); if( mFrameMillisecondDelta < minFrameTime ) { unsigned long timeToSleep = minFrameTime - mFrameMillisecondDelta; Thread::staticSleep( timeToSleep ); // get new frame second delta, including sleep time mFrameMillisecondDelta = Time::getMillisecondsSince( mLastFrameSeconds, mLastFrameMilliseconds ); } // avoid huge position "jumps" if we have a very large delay during a frame // (possibly caused by something going on in the background) // This will favor a slight visual slow down, but this is better than // a disorienting jump // skip this check if we are just starting up if( lastMillisecondDelta != 0 ) { if( mFrameMillisecondDelta > 6 * lastMillisecondDelta ) { // limit: this frame represents at most twice the jump of the last // frame // printf( "Limiting time jump (requested=%lu ms, last=%lu ms)\n", // mFrameMillisecondDelta, lastMillisecondDelta ); if( mFrameMillisecondDelta > 10000 ) { printf( "Time between frames more than 10 seconds:\n" ); // way too big... investigate printf( "Last time = %lu s, %lu ms\n", mLastFrameSeconds, mLastFrameMilliseconds ); Time::getCurrentTime( &mLastFrameSeconds, &mLastFrameMilliseconds ); printf( "current time = %lu s, %lu ms\n", mLastFrameSeconds, mLastFrameMilliseconds ); } mFrameMillisecondDelta = 2 * lastMillisecondDelta; } } double frameSecondsDelta = (double)mFrameMillisecondDelta / 1000.0; // record the time that this frame was drawn Time::getCurrentTime( &mLastFrameSeconds, &mLastFrameMilliseconds ); if( mVideoCaptureMode ) { // ignore true frame time frameSecondsDelta = 1.0 / (double)mVideoFramesPerSecond; } double worldTimePassedInSeconds = frameSecondsDelta; // no time passes while tutorial up // (however, we still used frameSecondsDelta below for tool tip timing) if( mMainPanel->contains( mTutorialPanel ) ) { worldTimePassedInSeconds = 0; } // tell managers about the time delta mWorld.passTime( worldTimePassedInSeconds ); int numOtherGardeners = mOtherGardeners.size(); int i; for( i=0; ipassTime( worldTimePassedInSeconds ); } mNumFrames ++; if( mPrintFrameRate ) { if( mNumFrames % mFrameBatchSize == 0 ) { // finished a batch unsigned long timeDelta = Time::getMillisecondsSince( mFrameBatchStartTimeSeconds, mFrameBatchStartTimeMilliseconds ); double frameRate = 1000 * (double)mFrameBatchSize / (double)timeDelta; printf( "Frame rate = %f frames/second\n", frameRate ); mFrameBatchStartTimeSeconds = mLastFrameSeconds; mFrameBatchStartTimeMilliseconds = mLastFrameMilliseconds; } } // find and remove dead char foundDead = true; while( foundDead ) { foundDead = false; numOtherGardeners = mOtherGardeners.size(); for( i=0; iisDead() ) { GardenerAI *ai = *( mOtherGardenerAI.getElement( i ) ); mOtherGardeners.deleteElementEqualTo( gardener ); mOtherGardenerAI.deleteElementEqualTo( ai ); delete ai; // this will delete gardener mWorld.removeGardener( gardener ); foundDead = true; } } } // timing for tool tips double toolTipShrinkFactor = 0.7; mToolTipButtons.size(); int numTips = mToolTipButtons.size(); for( int t=0; tcontains( mTutorialPanel ) ) { ignoreMouseOver = true; } if( ! ignoreMouseOver && button->isMouseOver() ) { *timerPointer += frameSecondsDelta; } else { *timerPointer = 0; if( mCurrentTipGUIComponent == button ) { // mouse moved out of current button // remove old mMainPanel->remove( mToolTipLabel ); delete mToolTipLabel; mToolTipLabel = NULL; mCurrentTipGUIComponent = NULL; } } if( ! mVideoCaptureMode && *timerPointer >= mToolTipDisplayTime ) { if( mCurrentTipGUIComponent != button ) { // switch tip labels if( mToolTipLabel != NULL ) { // remove old mMainPanel->remove( mToolTipLabel ); delete mToolTipLabel; mToolTipLabel = NULL; mCurrentTipGUIComponent = NULL; } const char *tipKey = *( mToolTipKeys.getElement( t ) ); char *tipString = (char *)TranslationManager::translate( tipKey ); // special case extra prefix for water button // and lead button const char *tipStringPrefix = ""; if( button == mWaterButton ) { if( mUserControlledGardener->getCarryingWater() ) { tipStringPrefix = TranslationManager::translate( "tip_water_dump" ); } else { tipStringPrefix = TranslationManager::translate( "tip_water_pickup" ); } } else if( button == mFollowButton ) { Gardener *closestGardener = mWorld.getClosestGardener( mUserControlledGardener ); if( closestGardener != NULL && closestGardener->getLeader() == mUserControlledGardener ) { // already leading closest // button would drop them tipStringPrefix = (char *)TranslationManager::translate( "tip_stop_leading" ); } else { tipStringPrefix = (char *)TranslationManager::translate( "tip_lead" ); } } char *totalTipString = autoSprintf( "%s%s", tipStringPrefix, tipString ); // center tooltip label at top of screen // 1:1 aspect ratio double height = 0.04; double width = height * strlen( totalTipString ); height *= toolTipShrinkFactor; width *= toolTipShrinkFactor; double actualDrawWidth = height * mTextGL->measureTextWidth( totalTipString ); // because of side panel, center of main panel looks like // it is at 0.55 instead of 0.5 mToolTipLabel = new LabelGL( 0.55 - 0.5 * actualDrawWidth, 0.11, width, height, totalTipString, mTextGL ); delete [] totalTipString; mMainPanel->add( mToolTipLabel ); mCurrentTipGUIComponent = button; } } } // special case: // tool tips for object selector if( mCurrentTipGUIComponent == NULL || mCurrentTipGUIComponent == mSelector ) { // check tip timing for object selector int objectIndex = mSelector->getHoverObject(); if( objectIndex != -1 ) { mSelectorToolTipTimer += frameSecondsDelta; } else { mSelectorToolTipTimer = 0; if( mCurrentTipGUIComponent == mSelector ) { // mouse moved out of selector // remove old mMainPanel->remove( mToolTipLabel ); delete mToolTipLabel; mToolTipLabel = NULL; mCurrentTipGUIComponent = NULL; mCurrentTipStoredItem = -1; } } if( ! mVideoCaptureMode && mSelectorToolTipTimer >= mToolTipDisplayTime ) { if( mCurrentTipGUIComponent != mSelector || objectIndex != mCurrentTipStoredItem ) { // switch tip labels to the selector's lable if( mToolTipLabel != NULL ) { // remove old mMainPanel->remove( mToolTipLabel ); delete mToolTipLabel; mToolTipLabel = NULL; mCurrentTipGUIComponent = NULL; mCurrentTipStoredItem = -1; } // show a tool tip for the selector Storable *storedObject = mUserControlledGardener->getStorable( objectIndex ); const char *tipStringPrefix = ""; const char *tipStringSuffix = ""; if( storedObject->getType() == fruitType ) { tipStringPrefix = TranslationManager::translate( "tip_fruit" ); Fruit *fruit = (Fruit *)storedObject; if( fruit->isPoisoned() ) { tipStringSuffix = TranslationManager::translate( "tip_poisoned" ); } } else { tipStringPrefix = (char *)TranslationManager::translate( "tip_seed" ); Seeds *seed = (Seeds *)storedObject; if( seed->mIdealSoilType == 0 ) { tipStringSuffix = (char *)TranslationManager::translate( "tip_darkSoil" ); } else { tipStringSuffix = (char *)TranslationManager::translate( "tip_lightSoil" ); } } char *totalTipString = autoSprintf( "%s%s", tipStringPrefix, tipStringSuffix ); // center tooltip label at top of screen // 1:1 aspect ratio double height = 0.04; double width = height * strlen( totalTipString ); height *= toolTipShrinkFactor; width *= toolTipShrinkFactor; double actualDrawWidth = height * mTextGL->measureTextWidth( totalTipString ); // because of side panel, center of main panel looks like // it is at 0.55 instead of 0.5 mToolTipLabel = new LabelGL( 0.55 - 0.5 * actualDrawWidth, 0.11, width, height, totalTipString, mTextGL ); delete [] totalTipString; mMainPanel->add( mToolTipLabel ); mCurrentTipGUIComponent = mSelector; mCurrentTipStoredItem = objectIndex; } } } mRestartButton->setEnabled( false ); // check if user dead if( mUserControlledGardener->isDead() ) { showTutorial( "tutorial_death1" ); if( !mUserControlledGardener->isGhost() ) { Vector3D *gardenerPosition = mWorld.getGardenerPosition( mUserControlledGardener ); mWorld.augmentPortal( gardenerPosition, mUserControlledGardener ); delete gardenerPosition; } Gardener *nextOffspring = mWorld.getNextUserControllableGardener(); if( nextOffspring != NULL ) { // remove and destroy mWorld.removeGardener( mUserControlledGardener ); mUserControlledGardener = nextOffspring; mUserControlledGardener->mUserControlling = true; // turn off AI for this gardener int index = mOtherGardeners.getElementIndex( nextOffspring ); GardenerAI *ai = *( mOtherGardenerAI.getElement( index ) ); mOtherGardeners.deleteElementEqualTo( nextOffspring ); mOtherGardenerAI.deleteElementEqualTo( ai ); delete ai; // switch storage that is displayed mSelector->setStorage( mUserControlledGardener ); // smooth switch to new smoothPositionTransition = true; } else { // no offspring left // become ghost mUserControlledGardener->setGhostMode( true ); // tell other gardeners to ignore mWorld.ignoreGardener( mUserControlledGardener ); // show restart mRestartButton->setEnabled( true ); if( mMainPanel->contains( mPauseButton ) ) { // replace pause with restart mMainPanel->remove( mPauseButton ); mMainPanel->add( mRestartButton ); // if this is not our first death, the death1 tutorial // will have already been shown // however, we should still let the player know that the // game is over by showing death1a // check if death1 tutorial already spent char death1Spent = false; int numSpentKeys = mSpentTutorialKeys.size(); for( int k=0; ksetEnabled( false ); mPlotButton->setEnabled( false ); mWaterButton->setEnabled( false ); mHarvestButton->setEnabled( false ); mEatButton->setEnabled( false ); mGiftButton->setEnabled( false ); mMateButton->setEnabled( false ); mFollowButton->setEnabled( false ); mPoisonButton->setEnabled( false ); mDiscardButton->setEnabled( false ); mSelector->setEnabled( false ); // remove selected objects mPlantButton->setSelectedObject( NULL ); mHarvestButton->setSelectedObject( NULL ); mEatButton->setSelectedObject( NULL ); mGiftButton->setSelectedObject( NULL ); mDiscardButton->setSelectedObject( NULL ); mMateButton->setSelectedObject( NULL ); mMateButton->setSecondSelectedObject( NULL ); mFollowButton->setSelectedObject( NULL ); mFollowButton->setSecondSelectedObject( NULL ); } } // set the screen position based on our new gardener position // do this even if dead (ghost mode) Vector3D *position = mWorld.getGardenerPosition( mUserControlledGardener ); char canPlant = mWorld.canPlant( position ); position->mZ = baseViewZ; if( smoothPositionTransition ) { Vector3D *currentScreenPosition = screen->getViewPosition(); Vector3D motionDirection( position ); motionDirection.subtract( currentScreenPosition ); double neededMoveLength = motionDirection.getLength(); motionDirection.normalize(); double ourMoveLength = frameSecondsDelta * smoothTransitionVelocity; if( ourMoveLength > neededMoveLength ) { ourMoveLength = neededMoveLength; // surpassed full move // no longer move smoothly smoothPositionTransition = false; } motionDirection.scale( ourMoveLength ); currentScreenPosition->add( &motionDirection ); screen->setViewPosition( currentScreenPosition ); globalPlayerCurrentPosition.setCoordinates( currentScreenPosition ); delete currentScreenPosition; } else { // instant jump screen->setViewPosition( position ); globalPlayerCurrentPosition.setCoordinates( position ); } delete position; // set z back to zero, because we got position from view position, which // is pulled back in z space globalPlayerCurrentPosition.mZ = 0; // rest of stuff below is for UI component enabling/disabling // state of world accessed by sound thread doesn't change // safe to unlock globalLock.unlock(); if( mUserControlledGardener->isDead() ) { // still dead // don't do anything but update position (ghost mode) // check for win condition, though // ( portal closed ) if( mWorld.isPortalClosed() ) { showTutorial( "tutorial_immortal1" ); } return; } // only allow planting if in plot char inPlot = mWorld.isInPlot( mUserControlledGardener ); char inWater = mWorld.isInWater( mUserControlledGardener ); Seeds *selectedSeeds = mUserControlledGardener->getSelectedSeeds(); mPlantButton->setEnabled( canPlant && inPlot && !inWater && selectedSeeds != NULL ); if( selectedSeeds != NULL ) { mPlantButton->setSelectedObject( selectedSeeds->getSampleLeaf() ); } else { mPlantButton->setSelectedObject( NULL ); } Plant *closePlant = mWorld.getClosestPlotPlant( mUserControlledGardener ); // default to no highlight mWorld.setHighlightPosition( NULL ); // only allow water grabbing if in water if( ! mUserControlledGardener->getCarryingWater() ) { mWaterButton->setFull( false ); char inWater = mWorld.isInWater( mUserControlledGardener ); mWaterButton->setEnabled( inWater ); } else { // already carrying water mWaterButton->setFull( true ); // only allow dumping if close to a plant in plot that is not // done growing yet if( closePlant != NULL && ! closePlant->isFullGrown() ) { mWaterButton->setEnabled( true ); if( mWaterButton->isMouseOver() ) { // highlight closest plant Vector3D *plantPosition = mWorld.getPlantPosition( closePlant ); mWorld.setHighlightPosition( plantPosition ); delete plantPosition; } } else if( mWorld.isInWater( mUserControlledGardener ) ) { // can also dump it back in water mWaterButton->setEnabled( true ); } else { mWaterButton->setEnabled( false ); } } // enable poison button near any plant // consider only "close" plants closePlant = mWorld.getClosestPlant( &globalPlayerCurrentPosition, NULL, false ); if( closePlant != NULL && // only can poison plants not already poisoned closePlant->getPoisonStatus() == 0 ) { mPoisonButton->setEnabled( true ); if( mPoisonButton->isMouseOver() ) { // highlight closest plant Vector3D *plantPosition = mWorld.getPlantPosition( closePlant ); mWorld.setHighlightPosition( plantPosition ); delete plantPosition; } } else { mPoisonButton->setEnabled( false ); } Plant *closeRipePlant = mWorld.getClosestPlotPlant( mUserControlledGardener, false, // true to only consider ripe true ); if( closeRipePlant != NULL ) { // enable harvest button mHarvestButton->setEnabled( true ); mHarvestButton->setSelectedObject( closeRipePlant->peekAtRipeFruit() ); if( mHarvestButton->isMouseOver() ) { // highlight ripe fruit closeRipePlant->highlightFirstRipeFruit( true ); } else { closeRipePlant->highlightFirstRipeFruit( false ); } } else { mHarvestButton->setEnabled( false ); mHarvestButton->setSelectedObject( NULL ); } updateStorageUI(); mMateButton->setEnabled( false ); mMateButton->setSelectedObject( NULL ); mMateButton->setSecondSelectedObject( NULL ); if( !mUserControlledGardener->isPregnant() && ! mWorld.isTargetOfPregnancy( mUserControlledGardener ) ) { // not already pregnant // and not already target of pregnancy Gardener *closestGardener = mWorld.getClosestGardener( mUserControlledGardener ); if( closestGardener != NULL && ! closestGardener->isPregnant() && ! mWorld.isTargetOfPregnancy( closestGardener ) ) { // a gardener is close and not already pregnant // and not already target of a pregnancy double distance = mWorld.getGardenerDistance( mUserControlledGardener, closestGardener ); // is it close enough if( distance < getMaxDistanceForTransactions() ) { // does it like us enough? if( closestGardener->likeEnoughToMate( mUserControlledGardener ) ) { mMateButton->setEnabled( true ); mMateButton->setSelectedObject( mUserControlledGardener ); mMateButton->setSecondSelectedObject( closestGardener ); if( mMateButton->isMouseOver() ) { Vector3D *matePosition = mWorld.getGardenerPosition( closestGardener ); mWorld.setHighlightPosition( matePosition ); delete matePosition; } } } } } mFollowButton->setEnabled( false ); mFollowButton->setStopFollowingMode( false ); mFollowButton->setSelectedObject( NULL ); mFollowButton->setSecondSelectedObject( NULL ); Gardener *closestGardener = mWorld.getClosestGardener( mUserControlledGardener ); if( closestGardener != NULL && closestGardener->getLeader() == NULL ) { // a gardener is close and not already following double distance = mWorld.getGardenerDistance( mUserControlledGardener, closestGardener ); // is it close enough if( distance < getMaxDistanceForTransactions() ) { // does it like us enough? if( closestGardener->likeEnoughToFollow( mUserControlledGardener ) ) { mFollowButton->setEnabled( true ); mFollowButton->setSelectedObject( mUserControlledGardener ); mFollowButton->setSecondSelectedObject( closestGardener ); if( mFollowButton->isMouseOver() ) { Vector3D *followerPosition = mWorld.getGardenerPosition( closestGardener ); mWorld.setHighlightPosition( followerPosition ); delete followerPosition; } } } } else if( closestGardener != NULL && closestGardener->getLeader() == mUserControlledGardener ) { // already following us // enable the "stop following" button mFollowButton->setEnabled( true ); mFollowButton->setStopFollowingMode( true ); mFollowButton->setSelectedObject( mUserControlledGardener ); mFollowButton->setSecondSelectedObject( closestGardener ); if( mFollowButton->isMouseOver() ) { Vector3D *followerPosition = mWorld.getGardenerPosition( closestGardener ); mWorld.setHighlightPosition( followerPosition ); delete followerPosition; } } // handle special tutorial states if( mTutorialEnabled ) { if( mUserControlledGardener->getNutrientLevel( 0 ) < 0.85 || mUserControlledGardener->getNutrientLevel( 1 ) < 0.85 || mUserControlledGardener->getNutrientLevel( 2 ) < 0.85 ) { showTutorial( "tutorial_survival1" ); } SimpleVector *plotPlants = mWorld.getPlotPlants( mUserControlledGardener ); char plantRipe = false; int numPlotPlants = plotPlants->size(); for( int p=0; pgetElement( p ) ); if( plant->isRipe() ) { plantRipe = true; } } delete plotPlants; if( plantRipe ) { showTutorial( "tutorial_harvest1" ); } if( mUserControlledGardener->getLife() < 0.5 ) { showTutorial( "tutorial_lifespan1" ); } Gardener *nextOffspring = mWorld.getNextUserControllableGardener( mUserControlledGardener ); if( nextOffspring != NULL ) { showTutorial( "tutorial_birth1" ); } if( mUserControlledGardener->isStoredFruitPoisoned() ) { showTutorial( "tutorial_poisoned1" ); } if( ! mUserControlledGardener->isDead() && mWorld.isPortalOpen() ) { showTutorial( "tutorial_gate1" ); } int storedFruitCount = mUserControlledGardener->getStoredFruitCount(); if( storedFruitCount > 0 ) { showTutorial( "tutorial_eat1" ); } if( storedFruitCount > 1 ) { showTutorial( "tutorial_gift1" ); } if( mUserControlledGardener->isPregnant() ) { showTutorial( "tutorial_pregnant1" ); } } // handle case where user dropped second plot corner on one of the GUI // panels but NOT on a button (so no false button click was generated) if( mActionState == doneDrawingPlot ) { mActionState = none; } } void GameSceneHandler::updateStorageUI() { Storable *item = mUserControlledGardener->getSelectedStorable(); // can only eat fruit // for now, can only give fruit if( item != NULL && item->getType() == fruitType ) { // fruit selected mEatButton->setEnabled( true ); mEatButton->setSelectedObject( item ); mGiftButton->setEnabled( false ); mGiftButton->setSelectedObject( NULL ); Gardener *closestGardener = mWorld.getClosestGardener( mUserControlledGardener ); if( closestGardener != NULL ) { double distance = mWorld.getGardenerDistance( mUserControlledGardener, closestGardener ); if( distance < getMaxDistanceForTransactions() ) { Fruit *fruit = (Fruit*)item; // can't give a gift of poisoned fruit if( !fruit->isPoisoned() ) { mGiftButton->setEnabled( true ); mGiftButton->setSelectedObject( item ); if( mGiftButton->isMouseOver() ) { Vector3D *recipientPosition = mWorld.getGardenerPosition( closestGardener ); mWorld.setHighlightPosition( recipientPosition ); delete recipientPosition; } } } } } else { mEatButton->setEnabled( false ); mEatButton->setSelectedObject( NULL ); mGiftButton->setEnabled( false ); mGiftButton->setSelectedObject( NULL ); } if( item != NULL ) { mDiscardButton->setSelectedObject( item ); mDiscardButton->setEnabled( true ); } else { mDiscardButton->setSelectedObject( NULL ); mDiscardButton->setEnabled( false ); } // update plant button also Seeds *selectedSeeds = mUserControlledGardener->getSelectedSeeds(); if( selectedSeeds != NULL ) { mPlantButton->setSelectedObject( selectedSeeds->getSampleLeaf() ); } else { mPlantButton->setSelectedObject( NULL ); } } void GameSceneHandler::actionPerformed( GUIComponent *inTarget ) { if( mActionState == doneDrawingPlot ) { // just finished drawing plot, and user dropped second corner // on a button mActionState = none; // ignore button click return; } else if( mActionState != none ) { // in the middle of draggin plot // ignore button presses return; } if( mPaused && !( inTarget == mPauseButton || inTarget == mQuitButton ) ) { // while paused, ignore all clicks except pause and quit return; } if( inTarget == mRestartButton ) { restartFlag = true; } else if( inTarget == mPauseButton ) { mPaused = !mPaused; } else if( inTarget == mQuitButton ) { exit( 0 ); } else if( inTarget == mNextTutorialButton ) { tutorialDone( mCurrentTutorialKey ); } else if( inTarget == mStopTutorialButton ) { // FIXME? Crashes mTutorialEnabled = false; tutorialDone( mCurrentTutorialKey ); } if( mMainPanel->contains( mTutorialPanel ) ) { // ignore other actions, since tutorial is up return; } if( inTarget == mPlantButton ) { Vector3D *position = mWorld.getGardenerPosition( mUserControlledGardener ); double soilCondition = mWorld.getSoilCondition( position ); Seeds *newPlantSeeds = mUserControlledGardener->getSelectedSeeds(); globalSoundEffectsBank->playEffect( effect_plantSeed ); mWorld.addPlant( mUserControlledGardener, new Plant( soilCondition, newPlantSeeds ), position ); // delete seeds after planting mUserControlledGardener->removeSeeds( newPlantSeeds ); delete position; showTutorial( "tutorial_water1" ); } else if( inTarget == mPlotButton ) { mPlotButton->setEnabled( false ); mActionState = plotFirstCorner; } else if( inTarget == mWaterButton ) { // don't worry about gardener position here, since button // enabling/disabling code handles it if( mUserControlledGardener->getCarryingWater() ) { // dump mUserControlledGardener->setCarryingWater( false ); globalSoundEffectsBank->playEffect( effect_dumpWater ); // add water to closest plant in plot mWorld.dumpWater( mUserControlledGardener ); if( mTutorialEnabled ) { SimpleVector *plotPlants = mWorld.getPlotPlants( mUserControlledGardener ); int numPlotPlants = plotPlants->size(); if( numPlotPlants > 0 ) { showTutorial( "tutorial_water3" ); } delete plotPlants; } } else { // fetch globalSoundEffectsBank->playEffect( effect_pickupWater ); mUserControlledGardener->setCarryingWater( true ); if( mTutorialEnabled ) { SimpleVector *plotPlants = mWorld.getPlotPlants( mUserControlledGardener ); int numPlotPlants = plotPlants->size(); if( numPlotPlants > 0 ) { showTutorial( "tutorial_water2" ); } delete plotPlants; } } } else if( inTarget == mPoisonButton ) { // don't worry about gardener position here, since button // enabling/disabling code handles it globalSoundEffectsBank->playEffect( effect_poison ); mWorld.dumpPoison( mUserControlledGardener ); } else if( inTarget == mHarvestButton ) { Plant *closePlant = mWorld.getClosestPlotPlant( mUserControlledGardener, false, // only consider ripe true ); globalSoundEffectsBank->playEffect( effect_pickFruit ); mWorld.harvestPlant( mUserControlledGardener, closePlant ); } else if( inTarget == mMateButton ) { Gardener *closestGardener = mWorld.getClosestGardener( mUserControlledGardener ); if( closestGardener != NULL ) { globalSoundEffectsBank->playEffect( effect_mate ); mWorld.mateGardeners( mUserControlledGardener, closestGardener ); } } else if( inTarget == mFollowButton ) { Gardener *closestGardener = mWorld.getClosestGardener( mUserControlledGardener ); if( closestGardener != NULL ) { if( closestGardener->getLeader() == NULL ) { closestGardener->setLeader( mUserControlledGardener ); mUserControlledGardener->addFollower( closestGardener ); } else { // tell them to stop following closestGardener->setLeader( NULL ); mUserControlledGardener->dropFollower( closestGardener ); } } } else if( false /*inTarget == mEmotionButton*/ ) { // print report printf( "Emotion Report:\n" ); int numOthers = mOtherGardeners.size(); for( int i=0; igetLikeMetric( gardenerB ) ); } else { printf( "[ ---- ] "); } } Gardener *gardenerB = mUserControlledGardener; if( gardenerA != gardenerB ) { printf( "[u: %.1f] ", gardenerA->getLikeMetric( gardenerB ) ); } else { printf( "[ ---- ] "); } printf( "\n" ); } } else if( inTarget == mEatButton ) { globalSoundEffectsBank->playEffect( effect_eatFruit ); mUserControlledGardener->eat(); showTutorial( "tutorial_eat2" ); } else if( inTarget == mGiftButton ) { Fruit *selectedFruit = mUserControlledGardener->getSelectedFruit(); if( selectedFruit != NULL ) { Gardener *closestGardener = mWorld.getClosestGardener( mUserControlledGardener ); if( closestGardener != NULL ) { globalSoundEffectsBank->playEffect( effect_giveFruit ); mWorld.giveFruit( mUserControlledGardener, closestGardener, selectedFruit ); showTutorial( "tutorial_emotion1" ); } else { delete selectedFruit; } } } else if( inTarget == mDiscardButton ) { mUserControlledGardener->deleteSelectedStorable(); } // some actions remove items from storage and destroy them updateStorageUI(); } void GameSceneHandler::setSecondPlotCorner( Vector3D *inPosition ) { // get the existing first corner Vector3D *cornerA, *cornerB; mWorld.getGardenerPlot( mUserControlledGardener, &cornerA, &cornerB ); if( cornerA != NULL ) { // existing first corner, new second corner mWorld.setGardenerPlot( mUserControlledGardener, cornerA, inPosition ); delete cornerA; } if( cornerB != NULL ) { delete cornerB; } } void GameSceneHandler::projectScreenPointIntoScene( int inX, int inY, double *outSceneX, double *outSceneY ) { // first, get our current matrices GLdouble modelMatrix[16]; GLdouble projMatrix[16]; GLint viewport[4]; glGetDoublev( GL_MODELVIEW_MATRIX, modelMatrix ); glGetDoublev( GL_PROJECTION_MATRIX, projMatrix ); glGetIntegerv( GL_VIEWPORT, viewport ); /* From the OpenGL FAQ The concept of window space Z is often confusing. It's the depth buffer value expressed as a GLdouble in the range 0.0 to 1.0. Assuming a default glDepthRange(), a window coordinate with a Z value of 0.0 corresponds to an eye coordinate located on the zNear clipping plane. Similarly, a window space Z value of 1.0 corresponds to an eye space coordinate located on the zFar plane. You can obtain any window space Z value by reading the depth buffer with glReadPixels(). */ // so, use screen z of 0 for near clipping plane, which ScreenGL has // set to 1 unit away from screen. // Thus, we need to find the z buffer value of our world plane (a z=0) // we can call this our "windowZ", since it is how z space is represented // in the window // if we project world (0,0,0) onto the screen, we will get the windowZ // of our world zero double windowXForWorldZero, windowYForWorldZero, windowZForWorldZero; gluProject( 0, 0, 0, modelMatrix, projMatrix, viewport, &windowXForWorldZero, &windowYForWorldZero, &windowZForWorldZero ); // use unproject to map our window coordinates back into the world double xWorld, yWorld, zWorld; gluUnProject( inX, inY, windowZForWorldZero, modelMatrix, projMatrix, viewport, &xWorld, &yWorld, &zWorld ); // need to invert the resulting y and translate it for some reson Vector3D *viewPosition = screen->getViewPosition(); yWorld = -yWorld + 2 * viewPosition->mY; delete viewPosition; *outSceneX = xWorld; *outSceneY = yWorld; } void GameSceneHandler::keyPressed( unsigned char inKey, int inX, int inY ) { } void GameSceneHandler::keyReleased( unsigned char inKey, int inX, int inY ) { // ignore key releases if( true ) { return; } // these keyboard options are useful for testing: if( inKey == 'f' || inKey == 'F' ) { mScreen->changeWindowSize( 640, 480 ); } if( inKey == 's' || inKey == 'S' ) { mScreen->changeWindowSize( 100, 100 ); } if( inKey == 'd' || inKey == 'D' ) { mScreen->changeWindowSize( 300, 300 ); } // hack to force portal to open for quick testing if( inKey == 'p' || inKey == 'P' ) { Vector3D *gardenerPosition = mWorld.getGardenerPosition( mUserControlledGardener ); // pass fresh gardener each time so each ring looks different Gardener temp( gardenerPosition ); mWorld.augmentPortal( gardenerPosition, &temp ); delete gardenerPosition; } // video capture if( false && inKey == 'v' || inKey == 'V' ) { if( !mVideoCaptureMode ) { mVideoCaptureMode = true; // hide UI //mScreen->removeSceneHandler( mMainPanelGuiTranslator ); // video size mScreen->changeWindowSize( mVideoFrameWidth, mVideoFrameHeight ); mFramesCaptured = 0; } } } void GameSceneHandler::specialKeyPressed( int inKey, int inX, int inY ) { } void GameSceneHandler::specialKeyReleased( int inKey, int inX, int inY ) { } void GameSceneHandler::showTutorial( const char *inTutorialKey ) { if( ! mTutorialEnabled ) { // do nothing return; } if( mCurrentTutorialKey == inTutorialKey ) { // already showing this tutorial right now return; } int numSpent = mSpentTutorialKeys.size(); for( int i=0; isetText( tutorialString ); // we may be displaying one tutorial right after another // and leaving the panel up if( ! mMainPanel->contains( mTutorialPanel ) ) { mMainPanel->add( mTutorialPanel ); } } void GameSceneHandler::tutorialDone( const char *inTutorialKey ) { mSpentTutorialKeys.push_back( (char *)inTutorialKey ); mCurrentTutorialKey = NULL; // some tutorials trigger the following tutorial const char *nextTutorial = NULL; // only consider this case if tutorial is still enabled if( mTutorialEnabled ) { if( strcmp( inTutorialKey, "tutorial_question" ) == 0 ) { nextTutorial = "tutorial_welcome1"; } else if( strcmp( inTutorialKey, "tutorial_welcome1" ) == 0 ) { nextTutorial = "tutorial_welcome2"; } else if( strcmp( inTutorialKey, "tutorial_welcome2" ) == 0 ) { nextTutorial = "tutorial_moving"; } else if( strcmp( inTutorialKey, "tutorial_survival1" ) == 0 ) { nextTutorial = "tutorial_survival2"; } else if( strcmp( inTutorialKey, "tutorial_survival2" ) == 0 ) { nextTutorial = "tutorial_survival3"; } else if( strcmp( inTutorialKey, "tutorial_survival3" ) == 0 ) { nextTutorial = "tutorial_survival4"; } else if( strcmp( inTutorialKey, "tutorial_survival4" ) == 0 ) { nextTutorial = "tutorial_seeds1"; } else if( strcmp( inTutorialKey, "tutorial_seeds1" ) == 0 ) { nextTutorial = "tutorial_plot"; } else if( strcmp( inTutorialKey, "tutorial_water3" ) == 0 ) { nextTutorial = "tutorial_plot2"; } else if( strcmp( inTutorialKey, "tutorial_plot2" ) == 0 ) { nextTutorial = "tutorial_plot3"; } else if( strcmp( inTutorialKey, "tutorial_eat2" ) == 0 ) { nextTutorial = "tutorial_seeds2"; } else if( strcmp( inTutorialKey, "tutorial_seeds2" ) == 0 ) { nextTutorial = "tutorial_seeds3"; } else if( strcmp( inTutorialKey, "tutorial_seeds3" ) == 0 ) { nextTutorial = "tutorial_seeds4"; } else if( strcmp( inTutorialKey, "tutorial_seeds4" ) == 0 ) { nextTutorial = "tutorial_seeds5"; } else if( strcmp( inTutorialKey, "tutorial_seeds5" ) == 0 ) { nextTutorial = "tutorial_seeds6"; } else if( strcmp( inTutorialKey, "tutorial_emotion1" ) == 0 ) { nextTutorial = "tutorial_emotion2"; } else if( strcmp( inTutorialKey, "tutorial_emotion2" ) == 0 ) { nextTutorial = "tutorial_emotion3"; } else if( strcmp( inTutorialKey, "tutorial_lifespan1" ) == 0 ) { Gardener *nextOffspring = mWorld.getNextUserControllableGardener( mUserControlledGardener ); if( nextOffspring != NULL ) { nextTutorial = "tutorial_lifespan2a"; } else if( mUserControlledGardener->isPregnant() ) { nextTutorial = "tutorial_lifespan2b"; } else { nextTutorial = "tutorial_lifespan2c"; } } else if( strcmp( inTutorialKey, "tutorial_lifespan2c" ) == 0 ) { nextTutorial = "tutorial_mating1"; } else if( strcmp( inTutorialKey, "tutorial_mating1" ) == 0 ) { nextTutorial = "tutorial_mating2"; } else if( strcmp( inTutorialKey, "tutorial_mating2" ) == 0 ) { nextTutorial = "tutorial_mating3"; } else if( strcmp( inTutorialKey, "tutorial_birth1" ) == 0 ) { nextTutorial = "tutorial_birth2"; } else if( strcmp( inTutorialKey, "tutorial_poisoned1" ) == 0 ) { nextTutorial = "tutorial_poisoned2"; } else if( strcmp( inTutorialKey, "tutorial_death1" ) == 0 ) { nextTutorial = "tutorial_death2"; } else if( strcmp( inTutorialKey, "tutorial_death2" ) == 0 ) { nextTutorial = "tutorial_death3"; } else if( strcmp( inTutorialKey, "tutorial_death3" ) == 0 ) { nextTutorial = "tutorial_death4"; } else if( strcmp( inTutorialKey, "tutorial_death4" ) == 0 ) { nextTutorial = "tutorial_death5"; } else if( strcmp( inTutorialKey, "tutorial_death5" ) == 0 ) { if( mUserControlledGardener->isDead() ) { nextTutorial = "tutorial_death6a"; } else { nextTutorial = "tutorial_death6b"; } } else if( strcmp( inTutorialKey, "tutorial_gate1" ) == 0 ) { nextTutorial = "tutorial_gate2"; } else if( strcmp( inTutorialKey, "tutorial_gate2" ) == 0 ) { nextTutorial = "tutorial_gate3"; } else if( strcmp( inTutorialKey, "tutorial_gate3" ) == 0 ) { nextTutorial = "tutorial_gate4"; } else if( strcmp( inTutorialKey, "tutorial_gate4" ) == 0 ) { nextTutorial = "tutorial_gate5"; } else if( strcmp( inTutorialKey, "tutorial_immortal1" ) == 0 ) { nextTutorial = "tutorial_immortal2"; } else if( strcmp( inTutorialKey, "tutorial_immortal2" ) == 0 ) { nextTutorial = "tutorial_immortal3"; } else if( strcmp( inTutorialKey, "tutorial_immortal3" ) == 0 ) { nextTutorial = "tutorial_immortal4"; } else if( strcmp( inTutorialKey, "tutorial_immortal4" ) == 0 ) { nextTutorial = "tutorial_immortal5"; } } if( nextTutorial != NULL ) { showTutorial( nextTutorial ); } else { mMainPanel->remove( mTutorialPanel ); } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/glCommon.cpp0000640000175000017500000001646210524437261023004 0ustar pabspabs/* * Modification History * * 2006-August-14 Jason Rohrer * Created. * * 2006-November-8 Jason Rohrer * Added corner colors to textured quad function. */ #include "glCommon.h" #include "features.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include "GL/gl.h" #include SingleTextureGL *commonCircleTexture = NULL; SingleTextureGL *commonPlusTexture = NULL; void initCommonTextures() { // generate texture for gardener base // a blurry white circle that is gray at edges int textureSize = 32; double halfTextureSize = 0.5 * textureSize; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // start all pixels as white and transparent for( p=0; pfilter( &blur, 3 ); commonCircleTexture = new SingleTextureGL( textureImage, // no wrap false ); // plus texture // start all pixels as white and transparent for( p=0; pfilter( &blur, 3 ); commonPlusTexture = new SingleTextureGL( textureImage, // no wrap false ); delete textureImage; } void destroyCommonTextures() { if( commonCircleTexture != NULL ) { delete commonCircleTexture; commonCircleTexture = NULL; } if( commonPlusTexture != NULL ) { delete commonPlusTexture; commonPlusTexture = NULL; } } void drawBlurCircle( Vector3D *inCenter, double inRadius ) { if( Features::drawNiceCircles ) { if( commonCircleTexture == NULL ) { initCommonTextures(); } // circle in texture has radius of 0.666 of texture radius // compensate so that edge of circle is at inRadius double squareRadius = inRadius / 0.666; drawTextureQuad( commonCircleTexture, inCenter, squareRadius ); } else { // draw simple line loops glBegin( GL_LINE_LOOP ); { drawCircle( inCenter, inRadius, 10 ); } glEnd(); } } void drawBlurPlus( Vector3D *inCenter, double inRadius, Angle3D *inRotation ) { if( commonPlusTexture == NULL ) { initCommonTextures(); } // circle in texture has radius of 0.666 of texture radius // compensate so that edge of circle is at inRadius double squareRadius = inRadius / 0.666; drawTextureQuad( commonPlusTexture, inCenter, squareRadius, inRotation ); } void drawTextureQuad( SingleTextureGL *inTexture, Vector3D *inCenter, double inRadius, Angle3D *inRotation, Color **inCornerColors ) { double squareRadius = inRadius; Vector3D corners[4]; // first, set up corners relative to 0,0 corners[0].mX = - squareRadius; corners[0].mY = - squareRadius; corners[0].mZ = 0; corners[1].mX = squareRadius; corners[1].mY = - squareRadius; corners[1].mZ = 0; corners[2].mX = squareRadius; corners[2].mY = squareRadius; corners[2].mZ = 0; corners[3].mX = - squareRadius; corners[3].mY = squareRadius; corners[3].mZ = 0; int i; // now add inCenter so that center is at inCenter for( i=0; i<4; i++ ) { // rotate if we have an angle if( inRotation != NULL ) { corners[i].rotate( inRotation ); } corners[i].add( inCenter ); } inTexture->enable(); glBegin( GL_QUADS ); { if( inCornerColors != NULL ) { setGLColor( inCornerColors[0] ); } glTexCoord2f( 0, 0 ); glVertex3d( corners[0].mX, corners[0].mY, corners[0].mZ ); if( inCornerColors != NULL ) { setGLColor( inCornerColors[1] ); } glTexCoord2f( 1, 0 ); glVertex3d( corners[1].mX, corners[1].mY, corners[1].mZ ); if( inCornerColors != NULL ) { setGLColor( inCornerColors[2] ); } glTexCoord2f( 1, 1 ); glVertex3d( corners[2].mX, corners[2].mY, corners[2].mZ ); if( inCornerColors != NULL ) { setGLColor( inCornerColors[3] ); } glTexCoord2f( 0, 1 ); glVertex3d( corners[3].mX, corners[3].mY, corners[3].mZ ); } glEnd(); inTexture->disable(); } void drawCircle( Vector3D *inCenter, double inRadius, int inNumSegments ) { for( int s=0; smX + inRadius * cos( angle ); double y = inCenter->mY + inRadius * sin( angle ); glVertex3d( x, y, inCenter->mZ ); } } void setGLColor( Color *inColor ) { glColor4f( inColor->r, inColor->g, inColor->b, inColor->a ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/GardenerAI2.h0000640000175000017500000000537010512737245022717 0ustar pabspabs/* * Modification History * * 2006-September-28 Jason Rohrer * Created. * * 2006-October-4 Jason Rohrer * Fixed rapid-fire gift bug. * * 2006-October-10 Jason Rohrer * Added a limit on maximum plot size. */ #ifndef GARDENER_AI_2_INCLUDED #define GARDENER_AI_2_INCLUDED #include "Gardener.h" #include "World.h" #define NUM_GARDENER_TASKS 13 enum GardenerTask{ task_none = 0, // need to pick a new task task_water, task_harvest, task_eat, task_createPlot, task_abandonPlot, task_plant, task_expandPlot, task_capturePlant, task_poisonPlant, task_giveGift, task_mate, task_rest }; /** * Artificial intelligence control for a gardener. * * @author Jason Rohrer */ class GardenerAI { public: /** * Constructs an AI. * * All params must be destroyed by caller after this AI is destroyed. * * @param inGardener the gardener to control * @param inWorld the world the gardener is in. */ GardenerAI( Gardener *inGardener, World *inWorld ); ~GardenerAI(); /** * Passes time for this AI. * * @param inTimeDeltaInSeconds the amount of time that has passed. */ void passTime( double inTimeDeltaInSeconds ); private: Gardener *mGardener; World *mWorld; Vector3D *mNextPlantingLocation; Vector3D *mNextPlotLocation; double mSecondsSinceLastGift; double mCurrentRestTime; double mMaxRestTime; GardenerTask mCurrentTask; /** * Gets the closest plant in inGardener's plot that is not * on our gardener's plot. * * Both parameter and return value are managed and destroyed by * mWorld. * * @param inGardener the gardener to look at. * * @return the plant in inGardener's plot, or NULL if no * such plant exists. */ Plant *getClosestPlantInGardenerPlot( Gardener *inGardener ); /** * Gets the driest plant in inGardener's plot. * * @return the plant in inGardener's plot, or NULL if no * such plant exists. Managed by mWorld */ Plant *getDriestPlant(); /** * Expands our plot in the world so that inPlant is contained. * * @param inPlant the plant to contain. Managed by world. * * @return true if expansion successful, or false if expanding * our plot to contain inPlant would make our plot too big. */ char expandOurPlotToContainPlant( Plant *inPlant ); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/Gardener.h0000640000175000017500000004310010544255127022413 0ustar pabspabs/* * Modification History * * 2006-July-2 Jason Rohrer * Created. * * 2006-October-4 Jason Rohrer * Fixed a crash when a baby's parent dies and is removed. * * 2006-October-27 Jason Rohrer * Added forceDead function. Added poisonFruit function. * * 2006-October-30 Jason Rohrer * Added a following threshold gene. * * 2006-November-13 Jason Rohrer * Added a frozen state and a manual fade factor for drawing. * * 2006-November-24 Jason Rohrer * Made most/least liked gardener consistent across multiple function calls. * * 2006-November-25 Jason Rohrer * Added feeding of followers. * * 2006-December-25 Jason Rohrer * Added function to check if any stored fruit poisoned. */ #ifndef GARDENER_INCLUDED #define GARDENER_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include "minorGems/graphics/Color.h" #include "minorGems/util/SimpleVector.h" #include "userInterface/ObjectStorage.h" #include "Fruit.h" #include "Seeds.h" #include "Storable.h" #include "DrawableObject.h" #include "GardenerGenetics.h" #include "sound/MusicPart.h" // used to track color of each gardener scale class ScaleCornerColors { public: // one color per corner Color mColors[4]; // makes a copy of this color set ScaleCornerColors *copy(); }; /** * A gardener. * * @author Jason Rohrer */ class Gardener : public ObjectStorage, public DrawableObject { public: /** * Constructs a gardener with random genetics. * * @param inStartPosition the position to start at. * Destroyed by caller. */ Gardener( Vector3D *inStartPosition ); /** * Constructs a gardener that is a cross of two parents. * * @param inParentA, inParentB the two parents. Destroyed by caller. */ Gardener( Gardener *inParentA, Gardener *inParentB ); virtual ~Gardener(); // the genetics for this gardener GardenerGenetics mGenetics; // true if this gardener is a direct offspring // of the player's gardener char mUserCanControl; // true if the player is currently controlling this gardener char mUserControlling; /** * Gets the gardener's color. * * @return the color. Destroyed by caller. */ Color *getColor(); /** * Gets the music part for this gardener. * * @return the music part. Managed by this gardener. */ MusicPart *getMusicPart(); /** * Sets the position that this gardener wants to move toward. * * @param inPosition the desired position. * Destroyed by caller. */ void setDesiredPosition( Vector3D *inPosition ); /** * Get the position that this gardener wants to move toward. * * @return the position. * Destroyed by caller. */ Vector3D *getDesiredPosition(); /** * Sets whether this gardener is moving. * * @param inMoving true if moving. */ void setMoving( char inMoving ); /** * Gets whether gardener is moving. * * @return true if moving. */ char isMoving(); /** * Sets whether this gardener is carrying water. * * @param inCarryingWater true if carrying water. */ void setCarryingWater( char inCarryingWater ); /** * gets whether this gardener is carrying water. * * @return true if carrying water. */ char getCarryingWater(); /** * Gets the level of a nutrient. * * @param inNutrientIndex the index of the nutrient: * 0 for red, * 1 for green, and * 2 for blue. * * @return the nutrient level in [0,1]. */ double getNutrientLevel( int inNutrientIndex); /** * Gets whether this gardener is pregnant. * * @return true if pregnant. */ char isPregnant(); /** * Gets the amount of life left in this gardener. * * 1 at start of life, Near 0 when near death. * * @return the life value. */ double getLife(); /** * Returns true if this gardener has died. */ char isDead(); /** * Force this gardener to die. */ void forceDead(); /** * Sets this gardeners frozen flag. * Frozen gardeners do not move and are not drawn by the standard * World drawing routine. * * @param inFrozen true to mark as frozen. */ void setFrozen( char inFrozen ); /** * Gets whether this gardener is frozen. * * @return true if frozen. */ char isFrozen(); /** * Sets ghost mode. * * @param inMode true for ghost mode (still moves, but invisible). */ void setGhostMode( char inMode ); /** * Gets ghost mode. * * @param true for ghost mode (still moves, but invisible). */ char isGhost(); /** * Poisons fruit that the gardener is holding. */ void poisonFruit(); /** * Eats the currently selected fruit. */ void eat(); /** * Tell this gardener about another gardener. * * @param inGardener the gardener to be aware of. * Must be destroyed by caller after this gardener is * destroyed. */ void trackOtherGardener( Gardener *inGardener ); /** * Tell this gardener to stop tracking another gardener. * * @param inGardener the gardener to stop tracking. * Must be destroyed by caller after this gardener is * destroyed. */ void untrackOtherGardener( Gardener *inGardener ); /** * Tell this gardener to get angry at another gardener. * * @param inGardener the gardener to get angry at. * Must be destroyed by caller after this gardener is * destroyed. */ void getAngry( Gardener *inGardener ); /** * Tell this gardener to get fully angry at another gardener. * * @param inGardener the gardener to get angry at. * Must be destroyed by caller after this gardener is * destroyed. */ void getMaxAngry( Gardener *inGardener ); /** * Tell this gardener to get friendly at another gardener. * * @param inGardener the gardener to get friendly toward. * Must be destroyed by caller after this gardener is * destroyed. */ void getFriendly( Gardener *inGardener ); /** * Gets how much this gardener likes another. * * @param inGardener the gardener to get the like metric for. * Must be destroyed by caller after this gardener is * destroyed. * * @return a value in [0,1] if gardener tracked, or -1 if untracked. */ double getLikeMetric( Gardener *inGardener ); /** * Gets whether we like another gardener enough to mate with it. * * @param inGardener the other gardener. Must be a gardener we * are already tracking. * * @return true if we like inGardener enough to mate with it. */ char likeEnoughToMate( Gardener *inGardener ); /** * Gets whether we like another gardener enough to follow it. * * @param inGardener the other gardener. Must be a gardener we * are already tracking. * * @return true if we like inGardener enough to follow it. */ char likeEnoughToFollow( Gardener *inGardener ); /** * Gets the gardener that we like the most. * * @return the gardener we like the most, or NULL if our like * metric is not higher than 0.5 for any gardener. */ Gardener *getMostLikedGardener(); /** * Gets the gardener that we like the least. * * @return the gardener we like the least, or NULL if our like * metric is not lower than 0.5 for any gardener. */ Gardener *getLeastLikedGardener(); /** * Sets the current emotion displayed by this gardener. * * @param inEmotion in range [-1, 1], where -1 is most angry, * and 1 is most happy. */ void setEmotionDisplay( double inEmotion ); /** * Sets whether this gardener's plot should be hidden. * * @param inHidden true to hid plot. */ void setPlotHidden( char inHidden ); /** * Gets whether plot is hidden. * * @return true if hidden. */ char isPlotHidden(); /** * Adds item to storage. * * @param inItem the item to store. Will be destroyed by this class. */ void storeItem( Storable *inItem ); /** * Gets the number of fruits stored by this gardener. * * @return the fruit count. */ int getStoredFruitCount(); /** * Removes the selected fruit from storage. * * @param inSaveSeeds true to save the seeds in storage when * removing this fruit from storage. Defaults to false. * @param inGetAnyFruit true to get any stored fruit even * if no fruit is selected. Defaults to false. * * @return the selected fruit, or NULL if there is no fruit. * Must be destroyed by caller. */ Fruit *getSelectedFruit( char inSaveSeeds = false, char inGetAnyFruit = false ); /** * Checks whether any stored fruit is poisoned. * * @return true if at least one stored fruit is poisoned. */ char isStoredFruitPoisoned(); /** * Gets the storage index of a fruit high in a given nutrient. * * @param inNutrientIndex a index of 0, 1, or 2. * * @return the index of a fruit, or -1 if there are no fruits stored. */ int getIndexOfFruitHighInNutrient( int inNutrientIndex ); /** * Gets pointer to selected seeds from storage. * * @return the selected seeds, or NULL if none selected. * Destroyed by this class, and may not be valid across * calls to passTime. */ Seeds *getSelectedSeeds(); /** * Removes seeds from storage and destroys them. * * @param inSeeds the seeds to destroy. Managed by this class. */ void removeSeeds( Seeds *inSeeds ); /** * Gets all the seeds stored by this gardener. * * @return a vector of pointers to all seeds in storage. * Vector destroyed by caller. Seeds destroyed by this class, and * may not be valid across calls to passTime. */ SimpleVector *getAllSeeds(); /** * Gets pointer to selected item from storage. * * @return the selected item, or NULL if none selected. * Destroyed by this class, and may not be valid across * calls to passTime. */ Storable *getSelectedStorable(); /** * Gets a specific storable. * * @param inIndex the index to get. * * @return the item at inIndex, or NULL. * Destroyed by this class, and may not be valid across * calls to passTime. */ Storable *getStorable( int inIndex ); /** * Deletes the selected item. */ void deleteSelectedStorable(); /** * Make this gardener pregnant. * * Should only be called on a gardener that is not already pregnant. * * @param inOffspring the offspring that this gardener will carry. * Destroyed by this class. */ void setPregnant( Gardener *inOffspring ); /** * Gets the parent that we are following. * * @return our parent, or NULL if we're full grown. */ Gardener *getParentToFollow(); /** * Tells this gardener to forget its parent. */ void forgetParent(); /** * Tells this gardener that a given offspring is no longer * following it. */ void dropOutsideOffspring( Gardener *inOffspring ); /** * Feed this gardener. * * Meant to be called by parents on offspring. * * @param inRedNutrient, inGreenNutrient, inBlueNutrient the amount * of each nutrient to feed. */ void feed( double inRedNutrient, double inGreenNutrient, double inBlueNutrient ); /** * Sets the gardener that we should follow. * * @param inLeader the gardener to follow, or NULL to follow no one. * Must be destroyed by caller after this gardener is destroyed. */ void setLeader( Gardener *inLeader ); /** * Gets the gardener that we are following. * * @return our leader, or NULL if we are following no one. * Must be destroyed by caller after this gardener is destroyed. */ Gardener *getLeader(); /** * Add or remove a follower to this gardener. * * @param inFollower the gardener to add or remove. * Must be destroyed by caller after this gardener is destroyed. */ void addFollower( Gardener *inFollower ); void dropFollower( Gardener *inFollower ); /** * Passes time for this gardener. * * @param inTimeDeltaInSeconds the amount of time that has passed. */ void passTime( double inTimeDeltaInSeconds ); // implements the DrawableObject interface void draw( Vector3D *inPosition, double inScale = 1 ); void draw( Vector3D *inPosition, Angle3D *inRotation, double inScale = 1, double inFadeFactor = 1 ); // implements the ObjectStorage interface virtual int getStoredObjects( DrawableObject ***outObjects ); virtual int getSelectedObjectIndex(); virtual void setSelectedObjectIndex( int inIndex ); private: Vector3D mDesiredPosition; Angle3D mLastDrawAngle; SingleTextureGL *mBaseTexture; SingleTextureGL *mScaleTexture; SingleTextureGL *mBonesTexture; SingleTextureGL *mSkinTexture; SingleTextureGL *mEyeTexture; // scale parameters relative to gardener center SimpleVector mScaleRotations; SimpleVector mScaleSizes; SimpleVector mScalePositions; SimpleVector mScaleColors; Angle3D mGlobalScaleAngle; Color mColor; MusicPart *mMovingMusicPart; MusicPart *mMovingHoldingMusicPart; MusicPart *mHoldingMusicPart; MusicPart *mStillMusicPart; char mMoving; char mCarryingWater; double mWaterPickupFraction; double mRedNutrient; double mGreenNutrient; double mBlueNutrient; char mPoisoned; double mLife; double mBaseAgeRate; double mBaseEnergyConsumedPerSecond; char mDead; char mGhostMode; char mFrozen; SimpleVector mOtherGardeners; SimpleVector mLikeMetrics; Gardener *mMostLiked; Gardener *mLeastLiked; double mEmotionDisplay; char mPlotHidden; SimpleVector mStoredItems; int mSelectedStorageIndex; // NULL if not pregnant Gardener *mOffspring; double mPregnancyProgress; // for growing outside womb Gardener *mParent; // 0.5 at birth double mGrowthProgress; SimpleVector mOutsideOffspring; Gardener *mLeader; SimpleVector mFollowers; double mSecondsSinceLastFruitPoisoning; double mNearDeathPoint; void raiseEnergyWithStoredNutrients(); // sets up starting state (common to all constructors) void setUpStartingState(); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/Portal.h0000640000175000017500000000677710544117732022147 0ustar pabspabs/* * Modification History * * 2006-October-26 Jason Rohrer * Created. * * 2006-December-25 Jason Rohrer * Added function for checking closed status of portal. */ #ifndef PORTAL_INCLUDED #define PORTAL_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include "minorGems/util/SimpleVector.h" #include "DrawableObject.h" #include "Gardener.h" #include "PortalLayerGenetics.h" #include "ImmortalGenetics.h" /** * A portal for the end of the game. * * @author Jason Rohrer */ class Portal { public: /** * Constructs a portal. * */ Portal(); virtual ~Portal(); /** * Increasese the level of the portal. * * @param inLevelOpener the gardener that opened this level, or * NULL. Defaults to NULL. */ void upgrade( Gardener *inLevelOpener = NULL ); /** * Gets open status of portal. * * @return true if portal is open. */ char isOpen(); /** * Gets closed status of portal. * * @return true if portal is closed and gardeners have been sent. */ char isClosed(); /** * Sends a gardener through the open portal. * * @param inGardener the gardener to send. Destroyed by caller * after inGardener marked as dead by this class. * @param inStartingPosition the starting position. Destroyed by * caller. * @param inStartingRotation the starting rotation. Destroyed by * caller. */ void sendGardener( Gardener *inGardener, Vector3D *inStartingPosition, Angle3D *inStartingRotation ); /** * Passes time for this portal. * * @param inTimeDeltaInSeconds the amount of time that has passed. */ void passTime( double inTimeDeltaInSeconds ); /** * Draws this portal. * * @param inPosition the center position of this portal. * @param inScale the scale of this portal, defaults to 1. * @param inMaxZ the maximum z location of portal parts to draw. * @param inMinZ the minimum z location of portal parts to draw. * Parts outside the inMaxZ/inMinZ window are skipped. */ void draw( Vector3D *inPosition, double inScale = 1, double inMaxZ = 0, double inMinZ = -100 ); private: int mMaxNumLevels; int mNumLevels; double mTopLevelZ; SingleTextureGL **mLayerTextures; PortalLayerGenetics **mLayerGenetics; double *mLayerSpawnProgress; double mOpeningProgress; char mClosing; double mClosingProgress; double mTimePassed; SimpleVector mPassengers; SimpleVector mPassengerPositions; SimpleVector mPassengerRotations; SimpleVector mPassengerFades; // track genetics of all gardeners sent SimpleVector mImmortals; /** * Closes the portal, sending the immortal gardeners to a file. */ void close(); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/PlantFlower.cpp0000640000175000017500000002762410507000567023464 0ustar pabspabs/* * Modification History * * 2006-August-29 Jason Rohrer * Created. * * 2006-September-13 Jason Rohrer * Fixed bug that caused flower petals to be too narrow. */ #include "PlantFlower.h" #include "features.h" #include "glCommon.h" #include "minorGems/graphics/Image.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include //#include "minorGems/io/file/FileOutputStream.h" //#include "minorGems/graphics/converters/TGAImageConverter.h" PlantFlower::PlantFlower( PlantGenetics *inGenetics ) : mGenetics( inGenetics ) { // generate texture for flower center // a blurry white circle that is gray at edges int textureSize = 32; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // start all pixels as gray and transparent for( p=0; pfilter( &blur, 3 ); mCenterTexture = new SingleTextureGL( textureImage, // no wrap false ); // petal texture contains a blurred triangle // all white, transparent for( p=0; pfilter( &blur, 3 ); mPetalTexture = new SingleTextureGL( textureImage, // no wrap false ); /* File outFileB( NULL, "flowerPetal.tga" ); FileOutputStream *outStreamB = new FileOutputStream( &outFileB ); TGAImageConverter converter; converter.formatImage( textureImage, outStreamB ); delete outStreamB; exit( 0 ); */ delete textureImage; } PlantFlower::~PlantFlower() { delete mCenterTexture; delete mPetalTexture; } void PlantFlower::passTime( double inTimeDeltaInSeconds ) { } void PlantFlower::draw( Vector3D *inPosition, Angle3D *inRotation, double inScale, double inStage ) { int numPetals = (int)( mGenetics.getParameter( flowerPetalCount ) ); double centerRadius = inScale * mGenetics.getParameter( flowerCenterRadius ) * 0.2; Color budColor( 0, mGenetics.getParameter( outerLeafGreen ), 0 ); double budColorWeight = 2 - inStage; if( budColorWeight > 1 ) { budColorWeight = 1; } Color fadeColor( 0.4, 0.2, 0 ); double fadeColorWeight = inStage - 3; if( fadeColorWeight > 1 ) { fadeColorWeight = 1; } if( inStage < 1 ) { // bud growing from zero radius up to full radius centerRadius *= inStage; } else if( inStage > 3 ) { // fading centerRadius *= (4 - inStage); } Angle3D angleIncrement( 0, 0, 2 * M_PI / numPetals ); Angle3D startAngle( inRotation ); double petalRadiusFactor; if( inStage < 1 ) { petalRadiusFactor = 0; } else if( inStage < 2 ){ // petals growing petalRadiusFactor = inStage - 1; } else if( inStage < 3 ) { // holding petalRadiusFactor = 1; } else { // fading petalRadiusFactor = 4 - inStage; } // create points for a single petal and then rotate it to draw each petal Vector3D petalPoints[3]; // point c, in center petalPoints[2].setCoordinates( 0, 0, 0 ); Angle3D petalAngle( 0, 0, mGenetics.getParameter( flowerPetalAngle ) ); // straight up from center double pointARadius = mGenetics.getParameter( flowerPetalPointARadius ) * inScale * 0.2 + centerRadius; pointARadius *= petalRadiusFactor; petalPoints[0].setCoordinates( 0, pointARadius, 0 ); // now rotate petalPoints[0].rotate( &petalAngle ); // rotation relative to petal angle Angle3D pointAAngle( 0, 0, mGenetics.getParameter( flowerPetalPointAAngle ) ); petalPoints[0].rotate( &pointAAngle ); // straight up from center double pointBRadius = mGenetics.getParameter( flowerPetalPointBRadius ) * inScale * 0.2 + centerRadius; pointBRadius *= petalRadiusFactor; petalPoints[1].setCoordinates( 0, pointBRadius, 0 ); // now rotate petalPoints[1].rotate( &petalAngle ); // rotation relative to petal angle Angle3D pointBAngle( 0, 0, mGenetics.getParameter( flowerPetalPointBAngle ) ); petalPoints[1].rotate( &pointBAngle ); Color *petalPointColors[3]; petalPointColors[0] = mGenetics.getColor( flowerPetalPointAColor ); petalPointColors[1] = mGenetics.getColor( flowerPetalPointBColor ); petalPointColors[2] = mGenetics.getColor( flowerPetalPointCColor ); int i; if( inStage >= 1 && inStage < 2 ) { // blend petal colors with bud color for( i=0; i<3; i++ ) { Color *drawColor = Color::linearSum( &budColor, petalPointColors[i], budColorWeight ); delete petalPointColors[i]; petalPointColors[i] = drawColor; } } else if( inStage >= 3 && inStage <= 4 ) { // blend with fade color for( i=0; i<3; i++ ) { Color *drawColor = Color::linearSum( &fadeColor, petalPointColors[i], fadeColorWeight ); delete petalPointColors[i]; petalPointColors[i] = drawColor; } } double maxRadius = pointARadius; if( maxRadius < pointBRadius ) { maxRadius = pointBRadius; } if( maxRadius < centerRadius ) { maxRadius = centerRadius; } if( Features::drawShadows ) { // draw shadow under flower using center texture mCenterTexture->enable(); glBegin( GL_QUADS ); { double centerZ = inPosition->mZ; glColor4f( 0, 0, 0, 0.25 ); glTexCoord2f( 0, 0 ); glVertex3d( inPosition->mX - maxRadius, inPosition->mY - maxRadius, centerZ ); glTexCoord2f( 1, 0 ); glVertex3d( inPosition->mX + maxRadius, inPosition->mY - maxRadius, centerZ ); glTexCoord2f( 1, 1 ); glVertex3d( inPosition->mX + maxRadius, inPosition->mY + maxRadius, centerZ ); glTexCoord2f( 0, 1 ); glVertex3d( inPosition->mX - maxRadius, inPosition->mY + maxRadius, centerZ ); } glEnd(); mCenterTexture->disable(); } // rotate all by start angle for first petal for( i=0; i<3; i++ ) { petalPoints[i].rotate( &startAngle ); } if( inStage >= 1 && inStage < 4 ) { // draw petals mPetalTexture->enable(); glBegin( GL_TRIANGLES ); { for( int p=0; pmX + petalPoints[i].mX, inPosition->mY + petalPoints[i].mY, inPosition->mZ + petalPoints[i].mZ ); // rotate for next petal petalPoints[i].rotate( &angleIncrement ); } } } glEnd(); mCenterTexture->disable(); } for( i=0; i<3; i++ ) { delete petalPointColors[i]; } mCenterTexture->enable(); glBegin( GL_QUADS ); { double centerZ = inPosition->mZ; Color *centerColor = mGenetics.getColor( flowerCenterColor ); Color *drawColor; if( inStage <= 2 ) { drawColor = Color::linearSum( &budColor, centerColor, budColorWeight ); } else if( inStage > 3 && inStage <= 4 ) { drawColor = Color::linearSum( &fadeColor, centerColor, fadeColorWeight ); } else { drawColor = new Color(); drawColor->setValues( centerColor ); } setGLColor( drawColor ); delete centerColor; delete drawColor; glTexCoord2f( 0, 0 ); glVertex3d( inPosition->mX - centerRadius, inPosition->mY - centerRadius, centerZ ); glTexCoord2f( 1, 0 ); glVertex3d( inPosition->mX + centerRadius, inPosition->mY - centerRadius, centerZ ); glTexCoord2f( 1, 1 ); glVertex3d( inPosition->mX + centerRadius, inPosition->mY + centerRadius, centerZ ); glTexCoord2f( 0, 1 ); glVertex3d( inPosition->mX - centerRadius, inPosition->mY + centerRadius, centerZ ); } glEnd(); mCenterTexture->disable(); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/PlantLeaf.h0000640000175000017500000000342210500301450022514 0ustar pabspabs/* * Modification History * * 2006-August-20 Jason Rohrer * Created. */ #ifndef PLANT_LEAF_INCLUDED #define PLANT_LEAF_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include "minorGems/graphics/Color.h" #include "PlantGenetics.h" #include "DrawableObject.h" /** * A drawable plant leaf. * * @author Jason Rohrer */ class PlantLeaf : public DrawableObject { public: /** * Constructs a leaf. * * @param inGenetics the genetics for the shape of this leaf. * Destroyed by caller. */ PlantLeaf( PlantGenetics *inGenetics ); virtual ~PlantLeaf(); double getLeafAreaFraction(); /** * Draws this leaf. * * @param inPosition the position of this leaf's base tip. * Destroyed by caller. * @param inRotation the rotation of this leaf, around its base * tip. Destroyed by caller. * @param inScale the scale of this leaf. * @param outLeafWalkerTerminus pointer to vector where * walker terminal location should be returned. Set to NULL * to ignore terminal location. Defaults to NULL. */ void draw( Vector3D *inPosition, Angle3D *inRotation, double inScale, Vector3D *outLeafWalkerTerminus = NULL ); // implements DrawableObject interface virtual void draw( Vector3D *inPosition, double inScale = 1 ); private: SingleTextureGL *mTexture; double mLeafAreaFraction; Vector3D mLeafWalkerTerminus; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/ImmortalGenetics.h0000640000175000017500000000255010530401436024125 0ustar pabspabs/* * Modification History * * 2006-November-17 Jason Rohrer * Created. */ #ifndef IMMORTAL_GENETICS_INCLUDED #define IMMORTAL_GENETICS_INCLUDED #include "GardenerGenetics.h" enum ImmortalGeneLocator{ soulStartX = 0, soulStartY, soulStartAngle, soulDeltaAngle, soulDeltaDeltaAngle, soulDeltaDeltaDeltaAngle, soulDeltaAngleSineWeight, soulDeltaAngleSineSpeed, cornerASpread, cornerBSpread, cornerCSpread, cornerDSpread }; /** * Genetics for an immortal representation. Based on the genes of the * gardener that was immortalized. * * @author Jason Rohrer */ class ImmortalGenetics : public GardenerGenetics { public: /** * Constructs random genetics */ ImmortalGenetics(); /** * Constructs genetics based on gardener genetics. * * @param inGardener the genetics of the gardener that became immortal. * Destroyed by caller. */ ImmortalGenetics( GardenerGenetics *inGardener ); /** * Maps a given gene to a parameter value. * * @param inLocator a gene locator. * * @return a parameter value for the specified gene. */ double getParameter( ImmortalGeneLocator inLocator ); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/PlantGenetics.h0000640000175000017500000000442710541550331023425 0ustar pabspabs/* * Modification History * * 2006-August-13 Jason Rohrer * Created. * * 2006-December-18 Jason Rohrer * Added seed width. */ #ifndef PLANT_GENETICS_INCLUDED #define PLANT_GENETICS_INCLUDED #include "Genetics.h" #include "minorGems/graphics/Color.h" enum PlantGeneLocator{ idealSoilType = 0, seedWidth, fruitNutrition, parentFruitNutrition, fruitLobeRate, fruitLobeDepth, jointCount, leavesPerJoint, innerLeafColor, outerLeafGreen, leafWalkerDeltaAngle, leafWalkerDeltaDeltaAngle, leafWalkerSpawnIntervalFactor, leafWalkerStartingSpawnInterval, leafWalkerSpawnAngle, leafWalkerSpawnDouble, flowerPetalCount, flowerCenterRadius, flowerPetalAngle, flowerPetalPointARadius, flowerPetalPointAAngle, flowerPetalPointBRadius, flowerPetalPointBAngle, flowerCenterColor, flowerPetalPointAColor, flowerPetalPointBColor, flowerPetalPointCColor, timeBetweenFlowers }; /** * Genetics for a plant. * * @author Jason Rohrer */ class PlantGenetics : public Genetics { public: /** * Constructs random genetics */ PlantGenetics(); /** * Constructs genetics by crossing two parent genetics. * * @param inParentA, inParentB the parent genetics. * Destroyed by caller. */ PlantGenetics( PlantGenetics *inParentA, PlantGenetics *inParentB ); /** * Constructs genetics by copying another. * * @param inGeneticsToCopy the genetics to copy. * Destroyed by caller. */ PlantGenetics( PlantGenetics *inGeneticsToCopy ); /** * Maps a given gene to a parameter value. * * @param inLocator a gene locator. * * @return a parameter value for the specified gene. */ double getParameter( PlantGeneLocator inLocator ); /** * Maps a given gene to a color. * * @param inLocator a gene locator. * * @return a new color. Destroyed by caller. */ Color *getColor( PlantGeneLocator inLocator ); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/GardenerGenetics.h0000640000175000017500000000565610524425557024117 0ustar pabspabs/* * Modification History * * 2006-August-11 Jason Rohrer * Created. * * 2006-October-30 Jason Rohrer * Added a following threshold gene. * * 2006-November-8 Jason Rohrer * Added a copy constructor. */ #ifndef GARDENER_GENETICS_INCLUDED #define GARDENER_GENETICS_INCLUDED #include "Genetics.h" #include "minorGems/graphics/Color.h" enum GardenerGeneLocator{ // behavior modifiers task_none_modifier = 0, task_water_modifier, task_harvest_modifier, task_eat_modifier, task_createPlot_modifier, task_abandonPlot_modifier, task_plant_modifier, task_expandPlot_modifier, task_capturePlant_modifier, task_poisonPlant_modifier, task_giveGift_modifier, task_mate_modifier, task_rest_modifier, // threshold of friendship, in range [0,1] friendshipThreshold, // always at or above frienship threshold matingThreshold, // always at or above mating threshold followingThreshold, storedFruitsBeforeMating, desiredPlantCount, overlapTolerance, pregnancyProgressRate, bodyColor, boneStartSpacing, boneSpacingDelta, boneCurvature, boneCurveFrequency, scaleCornerAColor, scaleCornerBColor, scaleCornerCColor, scaleCornerDColor, scaleCornerAColorDelta, scaleCornerBColorDelta, scaleCornerCColorDelta, scaleCornerDColorDelta, scaleStepSize, scaleSize, scaleWaveStrength, scaleWaveFrequency, eyeSize, pupilSize, eyeSeparation, melodyA, melodyB, melodyC, melodyD, melodyE, melodyF, songArrangement, chanceOfReverseNote }; /** * Genetics for a gardener. * * @author Jason Rohrer */ class GardenerGenetics : public Genetics { public: /** * Constructs random genetics */ GardenerGenetics(); /** * Constructs genetics by crossing two parent genetics. * * @param inParentA, inParentB the parent genetics. * Destroyed by caller. */ GardenerGenetics( GardenerGenetics *inParentA, GardenerGenetics *inParentB ); /** * Maps a given gene to a parameter value. * * @param inLocator a gene locator. * * @return a parameter value for the specified gene. */ double getParameter( GardenerGeneLocator inLocator ); /** * Maps a given gene to a color. * * @param inLocator a gene locator. * * @return a new color. Destroyed by caller. */ Color *getColor( GardenerGeneLocator inLocator ); protected: /** * Constructs a copy of another genetics. * * @param inGenetics the genetics to copy. Destroyed by caller. */ GardenerGenetics( GardenerGenetics *inGenetics ); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/Storable.h0000640000175000017500000000114110465412266022437 0ustar pabspabs/* * Modification History * * 2006-July-27 Jason Rohrer * Created. */ #ifndef STORABLE_INCLUDED #define STORABLE_INCLUDED #include "DrawableObject.h" enum StorableType{ fruitType = 1, seedsType }; /** * Interface for items that can be stored by gardeners. * * @author Jason Rohrer */ class Storable : public DrawableObject { public: virtual ~Storable() { } /** * Get the type of this storable object. * * @return the type. */ virtual StorableType getType() = 0; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/emotionIcons.cpp0000640000175000017500000000110110500350113023637 0ustar pabspabs/* * Modification History * * 2006-July-25 Jason Rohrer * Created. */ #include "emotionIcons.h" #include "glCommon.h" #include void DislikeIcon::draw( Vector3D *inPosition, double inScale ) { // draw a red x glColor4f( 1, 0, 0, 0.5 ); Angle3D angle( 0, 0, M_PI / 4 ); drawBlurPlus( inPosition, inScale, &angle ); } void LikeIcon::draw( Vector3D *inPosition, double inScale ) { // draw a green + glColor4f( 0, 1, 0, 0.5 ); Angle3D angle( 0, 0, 0 ); drawBlurPlus( inPosition, inScale, &angle ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/GardenerAI2.cpp0000640000175000017500000013750310522431627023252 0ustar pabspabs/* * Modification History * * 2006-September-28 Jason Rohrer * Created. * * 2006-October-4 Jason Rohrer * Fixed rapid-fire gift bug. * Changed to stop pursuit of desired position after completing a task. * * 2006-October-10 Jason Rohrer * Added a limit on maximum plot size. * Updated task_abandonPlot to work with new getLeastLikedGardener behavior. * * 2006-November-2 Jason Rohrer * Increased following distance for non-parent. */ #include "GardenerAI2.h" #include "gameFunctions.h" #include #include #include "minorGems/util/random/StdRandomSource.h" extern StdRandomSource globalRandomSource; extern double globalMaxPlotDimension; /** * Gets the maximum dimension of a plot. */ double getMaximumDimension( Vector3D *inCornerA, Vector3D *inCornerB ) { double xDimension = fabs( inCornerB->mX - inCornerA->mX ); double yDimension = fabs( inCornerB->mY - inCornerA->mY ); if( xDimension > yDimension ) { return xDimension; } else { return yDimension; } } GardenerAI::GardenerAI( Gardener *inGardener, World *inWorld ) : mGardener( inGardener ), mWorld( inWorld ), mNextPlantingLocation( NULL ), mNextPlotLocation( NULL ), mSecondsSinceLastGift( 0 ), mCurrentRestTime( 0 ), // three seconds mMaxRestTime( 3 ), mCurrentTask( task_none ) { } GardenerAI::~GardenerAI() { if( mNextPlantingLocation != NULL ) { delete mNextPlantingLocation; mNextPlantingLocation = NULL; } if( mNextPlotLocation != NULL ) { delete mNextPlotLocation; mNextPlotLocation = NULL; } } // an array of gene locators for behavior modifiers // makes it easy to loop over gene locators below GardenerGeneLocator taskModifierGeneLocators[ NUM_GARDENER_TASKS ] = { task_none_modifier, task_water_modifier, task_harvest_modifier, task_eat_modifier, task_createPlot_modifier, task_abandonPlot_modifier, task_plant_modifier, task_expandPlot_modifier, task_capturePlant_modifier, task_poisonPlant_modifier, task_giveGift_modifier, task_mate_modifier, task_rest_modifier }; void GardenerAI::passTime( double inTimeDeltaInSeconds ) { // before doing anything else, check if we are still following parent Gardener *parent = mGardener->getParentToFollow(); // or following another leader Gardener *leader = parent; double followDistance = 10; if( parent == NULL || parent->isDead() ) { leader = mGardener->getLeader(); // a bit longer if leader not parent followDistance = 15; } if( leader != NULL && ! leader->isDead() ) { // follow it if we get too far away Vector3D *destination; if( mWorld->getGardenerDistance( mGardener, leader ) > followDistance ) { // move closer destination = mWorld->getGardenerPosition( leader ); } else { // stay where we are destination = mWorld->getGardenerPosition( mGardener ); } mGardener->setDesiredPosition( destination ); delete destination; return; } // if we got here, we're not following a parent mSecondsSinceLastGift += inTimeDeltaInSeconds; SimpleVector *plants = mWorld->getPlotPlants( mGardener ); int numPlants = plants->size(); // if we don't have a plot, we must always switch to that task Vector3D *plotCenter = mWorld->getPlotCenter( mGardener ); if( plotCenter == NULL ) { mCurrentTask = task_createPlot; } delete plotCenter; if( mCurrentTask == task_none ) { // need to pick a task // we first compute weights for each task // The general idea is to compute a [0,1] weight for each. // In some cases, this is binary, simply indicating whether the task // is sensible or not (example: no sense in eating if we aren't // hungry). // In other cases, this weight is continuous and represents urgency. // However, these [0,1] weights are deterministic and the same // for all individuals. // Individual differences stem from the application of genetic // modifiers that adjust these [0,1] weights. // After the application of modifiers, weights may exceed 1. double weights[ NUM_GARDENER_TASKS ]; GardenerTask taskLabels[ NUM_GARDENER_TASKS ]; // used to normalize weights double weightSum = 0; int t; for( t=0; tgetWaterStatus(); // approaches 1 as driest plant approaches no water weights[t] = ( 0.25 - driestWaterStatus ) / 0.25; } break; } case task_harvest: { Plant *ripePlant = NULL; int i; for( i=0; igetElement( i ) ); if( thisPlant->isRipe() ) { ripePlant = thisPlant; } } if( ripePlant != NULL ) { weights[t] = 1; } else { weights[t] = 0; } break; } case task_eat: { weights[t] = 0; // first check if hungry double lowestLevel = 0.25; int lowIndex = -1; for( int i=0; i<3; i++ ) { double level = mGardener->getNutrientLevel(i); if( level < lowestLevel ) { lowIndex = i; lowestLevel = level; } } if( lowIndex != -1 ) { // make sure we have fruit to eat int index = mGardener->getIndexOfFruitHighInNutrient( lowIndex ); if( index != -1 ) { // grows closer to 1 as level approaches zero weights[t] = (0.25 - lowestLevel) / 0.25; } } break; } case task_createPlot: { // we only create a plot if we don't have one above // we never select this task probabalistically weights[t] = 0; break; } case task_abandonPlot: { weights[t] = 0; double ourPlotArea = mWorld->getPlotArea( mGardener ); if( ourPlotArea > 0 ) { // sum overlaps with other gardners double overlapAreaSum = 0; int numGardeners; Gardener **gardeners = mWorld->getAllGardeners( &numGardeners ); for( int i=0; igetLikeMetric( mGardener ); Gardener *theirLeastLiked = g->getLeastLikedGardener(); double theirLowestLikeMetric = g->getLikeMetric( theirLeastLiked ); if( theirLikeMetricForUs <= theirLowestLikeMetric ) { // they hate us // add in their overlap overlapAreaSum += mWorld->getPlotIntersectionArea( mGardener, g ); } } } delete [] gardeners; double overlapFraction = overlapAreaSum / ourPlotArea; if( overlapFraction > 1 ) { overlapFraction = 1; } double threshold = mGardener->mGenetics.getParameter( overlapTolerance ); if( overlapFraction >= threshold ) { // our sum of overlap with gardeners that hate us // is greater than our threshold // weight depends on how much overlap we have // above our threshold double range = 1 - threshold; if( range > 0 ) { weights[t] = ( overlapFraction - threshold ) / range; } else { weights[t] = 1; } } } break; } case task_plant: { int targetPlantCount = (int)( mGardener->mGenetics.getParameter( desiredPlantCount ) ); int numPlants = plants->size(); SimpleVector *seedsVector = mGardener->getAllSeeds(); int numSeeds = seedsVector->size(); delete seedsVector; if( numSeeds > 0 && numPlants < targetPlantCount ) { // weight shrinks as we near our target count weights[t] = (double)(targetPlantCount - numPlants) / targetPlantCount; } else { weights[t] = 0; } break; } case task_expandPlot: { // we only try to expand the plot if our planting // task fails, so we never select this task // probabalistically weights[t] = 0; break; } case task_capturePlant: { weights[t] = 0; Gardener *leastLiked = mGardener->getLeastLikedGardener(); if( leastLiked != NULL ) { Plant *plantToTake = getClosestPlantInGardenerPlot( leastLiked ); if( plantToTake != NULL ) { double likeMetric = mGardener->getLikeMetric( leastLiked ); // weight depending on our like metric weights[t] = 1 - likeMetric; } } break; } case task_poisonPlant: { weights[t] = 0; Gardener *leastLiked = mGardener->getLeastLikedGardener(); if( leastLiked != NULL ) { Plant *plantToPoison = mWorld->getTendedPlant( leastLiked ); if( plantToPoison != NULL ) { double likeMetric = mGardener->getLikeMetric( leastLiked ); // don't consider poisoning unless // we really don't like them if( likeMetric < 0.25 ) { // weight depending on our like metric weights[t] = (0.25 - likeMetric) / 0.25; } } } break; } case task_giveGift: { weights[t] = 0; if( mSecondsSinceLastGift > 5 ) { Gardener *mostLiked = mGardener->getMostLikedGardener(); if( mostLiked != NULL ) { // only give if we have 2+ more fruits than them // (to avoid back and forth giving when there is an // odd number of fruits between the two of us) if( mGardener->getStoredFruitCount() > mostLiked->getStoredFruitCount() + 1 ) { double likeMetric = mGardener->getLikeMetric( mostLiked ); // weight based on like metric weights[t] = likeMetric; } } } break; } case task_mate: { weights[t] = 0; // if not pregnant // and not target of another pregancy // hand have enough fruit to feel secure if( ! mGardener->isPregnant() && ! mWorld->isTargetOfPregnancy( mGardener ) && mGardener->getStoredFruitCount() >= mGardener->mGenetics.getParameter( storedFruitsBeforeMating ) ) { // we are not pregnant already // we have enough fruit stored // consider mating double ourThreshold = mGardener->mGenetics.getParameter( matingThreshold ); Gardener *mostLiked = mGardener->getMostLikedGardener(); if( mostLiked != NULL ) { double mostLikedMatingThreshold = mostLiked->mGenetics.getParameter( matingThreshold ); double likeMetric = mGardener->getLikeMetric( mostLiked ); if( likeMetric >= ourThreshold && mostLiked->getLikeMetric( mGardener ) >= mostLikedMatingThreshold && ! mostLiked->isPregnant() && ! mWorld->isTargetOfPregnancy( mostLiked ) ) { // we like them enough to mate // and // they like us enough to mate // and // they are not already pregnant // and // they are not already target of // another pregnancy // weight depends on like metric // how much over our mating threshold? double range = 1 - ourThreshold; if( range > 0 ) { weights[t] = ( likeMetric - ourThreshold ) / range; } else { weights[t] = 1; } } } } break; } case task_rest: { // never pick resting probabalistically // instead, select it as default when all other tasks have // no weight, below weights[t] = 0; break; } } // apply genetic modifiers (skip task_none) if( t > 0 ) { weights[t] *= mGardener->mGenetics.getParameter( taskModifierGeneLocators[t] ); } weightSum += weights[t]; } // now we have a weight for each task // normalize if( weightSum == 0 ) { // avoid divide by zero weightSum = 1; } for( t=0; t= randomVariable ) { // found the task hit by our random variable hit = true; mCurrentTask = taskLabels[t]; } } if( mCurrentTask == task_none ) { // this means that all task weights were zero, or that our // random variable was zero // default to resting mCurrentTask = task_rest; } } // we have a task // execute it switch( mCurrentTask ) { case task_none: { printf( "Error: task_none selected by GardenerAI2.\n" ); break; } case task_water: { Plant *driestPlant = getDriestPlant(); if( driestPlant != NULL ) { if( mGardener->getCarryingWater() ) { // already carrying water // move to the driest plant Vector3D *plantPosition = mWorld->getPlantPosition( driestPlant ); Vector3D *gardenerPosition = mWorld->getGardenerPosition( mGardener ); if( ! gardenerPosition->equals( plantPosition ) ) { // move to plant mGardener->setDesiredPosition( plantPosition ); } else { // already at plant // dump water mGardener->setCarryingWater( false ); mWorld->dumpWater( mGardener ); // finished mCurrentTask = task_none; } delete gardenerPosition; delete plantPosition; } else { // there is a dry plant, and we're not carrying water // fetch water if( ! mWorld->isInWater( mGardener ) ) { Vector3D *waterPoint = mWorld->getClosestWater( mGardener ); mGardener->setDesiredPosition( waterPoint ); delete waterPoint; } else { // grab water mGardener->setCarryingWater( true ); } } } else { // no driest plant // make sure we're not still carrying water after // a plant finished growing mGardener->setCarryingWater( false ); // finished mCurrentTask = task_none; } break; } case task_harvest: { Plant *ripePlant = NULL; int i; for( i=0; igetElement( i ) ); if( thisPlant->isRipe() ) { ripePlant = thisPlant; } } if( ripePlant != NULL ) { // move toward it Vector3D *plantPosition = mWorld->getPlantPosition( ripePlant ); Vector3D *gardenerPosition = mWorld->getGardenerPosition( mGardener ); if( ! gardenerPosition->equals( plantPosition ) ) { // move to plant mGardener->setDesiredPosition( plantPosition ); } else { // already at plant // harvest it mWorld->harvestPlant( mGardener, ripePlant ); // finished mCurrentTask = task_none; } delete gardenerPosition; delete plantPosition; } else { // nothing to harvest // finished mCurrentTask = task_none; } break; } case task_eat: { // first find what nutrient we are lowest in double lowestLevel = 0.25; int lowIndex = -1; for( int i=0; i<3; i++ ) { double level = mGardener->getNutrientLevel(i); if( level < lowestLevel ) { lowIndex = i; lowestLevel = level; } } if( lowIndex != -1 ) { // low in at least one nutrient // try to find a fruit high in that nutrient int index = mGardener->getIndexOfFruitHighInNutrient( lowIndex ); if( index != -1 ) { mGardener->setSelectedObjectIndex( index ); // eat selected fruit mGardener->eat(); } } // always finish, since this is an instant action mCurrentTask = task_none; break; } case task_createPlot: { // need to pick a new plot // first pick a random land point in the world if( mNextPlotLocation == NULL ) { mNextPlotLocation = mWorld->getRandomLandPoint(); } Vector3D *gardenerPosition = mWorld->getGardenerPosition( mGardener ); if( ! gardenerPosition->equals( mNextPlotLocation ) ) { // move to next plot location mGardener->setDesiredPosition( mNextPlotLocation ); } else { // there already // once there, create a plot Vector3D a( mNextPlotLocation ); Vector3D b( mNextPlotLocation ); // 20 x 20 a.mX -= 10; a.mY -= 10; b.mX += 10; b.mY += 10; delete mNextPlotLocation; mNextPlotLocation = NULL; // don't check if plot meets our criteria // we will do that in the behavior selection (since // we might decide to abandon our plot) mWorld->setGardenerPlot( mGardener, &a, &b ); // finished mCurrentTask = task_none; } delete gardenerPosition; break; } case task_abandonPlot: { mWorld->setGardenerPlot( mGardener, NULL, NULL ); break; } case task_plant: { if( mNextPlantingLocation == NULL ) { // haven't picked a spot yet char foundPlantable = false; int numTries = 0; int maxNumTries = 100; Vector3D *cornerA, *cornerB; mWorld->getGardenerPlot( mGardener, &cornerA, &cornerB ); while( !foundPlantable && numTries < maxNumTries ) { double x = globalRandomSource.getRandomBoundedDouble( cornerA->mX, cornerB->mX ); double y = globalRandomSource.getRandomBoundedDouble( cornerA->mY, cornerB->mY ); mNextPlantingLocation = new Vector3D( x, y, 0 ); if( mWorld->canPlant( mNextPlantingLocation ) ) { foundPlantable = true; } else { // try again delete mNextPlantingLocation; mNextPlantingLocation = NULL; } numTries++; } delete cornerA; delete cornerB; } if( mNextPlantingLocation != NULL ) { Vector3D *gardenerPosition = mWorld->getGardenerPosition( mGardener ); if( ! gardenerPosition->equals( mNextPlantingLocation ) ) { // move to next plant location mGardener->setDesiredPosition( mNextPlantingLocation ); } else { // at next location: // make sure we can still plant // else pick another location at next time step if( mWorld->canPlant( mNextPlantingLocation ) ) { // plant here double soilCondition = mWorld->getSoilCondition( mNextPlantingLocation ); SimpleVector *seedsVector = mGardener->getAllSeeds(); // find best for this soil Seeds *best = NULL; double minSoilDistance = 2; for( int i=0; isize(); i++ ) { Seeds *seeds = *( seedsVector->getElement( i ) ); double distance = fabs( seeds->mIdealSoilType - soilCondition ); if( distance < minSoilDistance ) { minSoilDistance = distance; best = seeds; } } delete seedsVector; if( best != NULL ) { mWorld->addPlant( mGardener, new Plant( soilCondition, best ), mNextPlantingLocation ); mGardener->removeSeeds( best ); } // finished mCurrentTask = task_none; } delete mNextPlantingLocation; mNextPlantingLocation = NULL; } delete gardenerPosition; } else { // tried to pick a plantable location, but failed // switch to expand task mCurrentTask = task_expandPlot; } break; } case task_expandPlot: { Vector3D *a, *b; mWorld->getGardenerPlot( mGardener, &a, &b ); // compute a vector stretching from b to a Vector3D b_to_a( a ); b_to_a.subtract( b ); // expand plot by 10% in each direction b_to_a.scale( 0.10 ); // push a away from b a->add( &b_to_a ); // also push b away from a // opposite direction b_to_a.scale( -1 ); b->add( &b_to_a ); // make sure it's not too big double maxDimension = getMaximumDimension( a, b ); if( maxDimension <= globalMaxPlotDimension ) { mWorld->setGardenerPlot( mGardener, a, b ); // finished mCurrentTask = task_none; } else { // can't expand, though we need to // abandon plot mCurrentTask = task_abandonPlot; } delete a; delete b; break; } case task_capturePlant: { Gardener *leastLiked = mGardener->getLeastLikedGardener(); if( leastLiked != NULL ) { Plant *plantToTake = getClosestPlantInGardenerPlot( leastLiked ); if( plantToTake != NULL ) { char expanded = expandOurPlotToContainPlant( plantToTake ); if( expanded ) { // every time we successfully take revenge, our anger // toward this gardener lessens a bit mGardener->getFriendly( leastLiked ); } // else our plot cannot get any bigger } } // always finish (this task executes in one timestep) mCurrentTask = task_none; break; } case task_poisonPlant: { Gardener *leastLiked = mGardener->getLeastLikedGardener(); if( leastLiked != NULL ) { Plant *plantToPoison = mWorld->getTendedPlant( leastLiked ); if( plantToPoison != NULL ) { // found candidate // walk to it Vector3D *plantPosition = mWorld->getPlantPosition( plantToPoison ); Vector3D *gardenerPosition = mWorld->getGardenerPosition( mGardener ); if( ! gardenerPosition->equals( plantPosition ) ) { // move to plant mGardener->setDesiredPosition( plantPosition ); } else { // already at plant mWorld->dumpPoison( mGardener ); // every time we take revenge, our anger // toward this gardener lessens a bit mGardener->getFriendly( leastLiked ); // finished mCurrentTask = task_none; } delete gardenerPosition; delete plantPosition; } else { // nothing to poison mCurrentTask = task_none; } } else { // nothing to poison mCurrentTask = task_none; } break; } case task_giveGift: { Gardener *mostLiked = mGardener->getMostLikedGardener(); if( mostLiked != NULL ) { // only give if we have 2+ more fruits than them // (to avoid back and forth giving when there is an // odd number of fruits between the two of us) if( mGardener->getStoredFruitCount() > mostLiked->getStoredFruitCount() + 1 ) { Vector3D *ourPosition = mWorld->getGardenerPosition( mGardener ); Vector3D *otherPosition = mWorld->getGardenerPosition( mostLiked ); double distance = ourPosition->getDistance( otherPosition ); if( distance < getMaxDistanceForTransactions() ) { // close enough to give // find out which nutrient they are low in int lowIndex = -1; double lowValue = 2; for( int i=0; i<3; i++ ) { double value = mostLiked->getNutrientLevel(i); if( value < lowValue ) { lowIndex = i; lowValue = value; } } // try to find a fruit high in that nutrient int index = mGardener->getIndexOfFruitHighInNutrient( lowIndex ); // we will always get a valid index here, because // we have checked that we have stored fruit above mGardener->setSelectedObjectIndex( index ); mWorld->giveFruit( mGardener, mostLiked, // don't save seeds, but get any fruit, even // if fruit not selected mGardener->getSelectedFruit( false, true ) ); // reset our gift timer so that we don't send // rapid-fire gifts before our first one arrives // the reason we need a gift timer, and not // a revenge timer or any other kind of timer, // is because our decision to give a gift is // based on the state of the other gardener, and not // just on our own state. Their state changes // in response to our gift only after they receive // it, so we must wait until they receive it // before we decide to give again. // for revenge actions, and other actions (like eating, // watering, etc.), our decision is based only // on our internal state, which changes as soon // as we execute the action. mSecondsSinceLastGift = 0; // every time we give a gift, // our friendliness toward this gardener is depleted // a bit // Actually, don't do this for now, since for mating // purposes, we want to retain our friendliness // mGardener->getAngry( mostLiked ); // finished mCurrentTask = task_none; } else { // move toward friend mGardener->setDesiredPosition( otherPosition ); } delete ourPosition; delete otherPosition; } else { // they have enough fruit now, cancel mCurrentTask = task_none; } } else { // we no longer have a most liked mCurrentTask = task_none; } break; } case task_mate: { // if not pregnant // and not target of another pregancy // don't check fruit supply (we've decided to mate, so let's do it) if( ! mGardener->isPregnant() && ! mWorld->isTargetOfPregnancy( mGardener ) ) { // we are not pregnant already // we have enough fruit stored // consider mating double ourThreshold = mGardener->mGenetics.getParameter( matingThreshold ); Gardener *mostLiked = mGardener->getMostLikedGardener(); if( mostLiked != NULL ) { double mostLikedMatingThreshold = mostLiked->mGenetics.getParameter( matingThreshold ); if( mGardener->getLikeMetric( mostLiked ) >= ourThreshold && mostLiked->getLikeMetric( mGardener ) >= mostLikedMatingThreshold && ! mostLiked->isPregnant() && ! mWorld->isTargetOfPregnancy( mostLiked ) ) { // we like them enough to mate // and // they like us enough to mate // and // they are not already pregnant // and // they are not already target of another pregnancy Vector3D *ourPosition = mWorld->getGardenerPosition( mGardener ); Vector3D *otherPosition = mWorld->getGardenerPosition( mostLiked ); double distance = ourPosition->getDistance( otherPosition ); if( distance < getMaxDistanceForTransactions() ) { mWorld->mateGardeners( mGardener, mostLiked ); // finished mCurrentTask = task_none; } else { // move toward friend mGardener->setDesiredPosition( otherPosition ); } delete ourPosition; delete otherPosition; } else { // something changed since we decided to mate mCurrentTask = task_none; } } else { // we no longer have a most liked mCurrentTask = task_none; } } else { // we are already pregnant or target of another pregnancy mCurrentTask = task_none; } break; } case task_rest: { mCurrentRestTime += inTimeDeltaInSeconds; if( mCurrentRestTime >= mMaxRestTime ) { // finished mCurrentRestTime = 0; mCurrentTask = task_none; } break; } } if( mCurrentTask == task_none ) { // just finished a task // Make sure we stop moving and don't continue chasing down // the desired position we set as part of our last task. // For example, when giving a gift, we set our desired position // at the gardener, but we can stop moving closer to the gardener // once we have gotten close enough and given the gift. Vector3D *gardenerPosition = mWorld->getGardenerPosition( mGardener ); mGardener->setDesiredPosition( gardenerPosition ); delete gardenerPosition; } delete plants; } Plant *GardenerAI::getClosestPlantInGardenerPlot( Gardener *inGardener ) { SimpleVector *ourPlants = mWorld->getPlotPlants( mGardener ); SimpleVector *otherPlants = mWorld->getPlotPlants( inGardener ); if( otherPlants == NULL ) { if( ourPlants != NULL ) { delete ourPlants; } return NULL; } // first, filter otherPlants to remove overlaps with ourPlants int i=0; while( i < otherPlants->size() ) { Plant *plant = *( otherPlants->getElement( i ) ); if( ourPlants != NULL && ourPlants->getElementIndex( plant ) != -1 ) { // overlap otherPlants->deleteElement( i ); } else { // no overlap i++; } } if( ourPlants != NULL ) { delete ourPlants; } Plant *returnPlant = NULL; int numPlants = otherPlants->size(); if( numPlants > 0 ) { // look for closest to us Vector3D *ourPostion = mWorld->getGardenerPosition( mGardener ); double closestDistance = DBL_MAX; for( i=0; igetElement( i ) ); Vector3D *plantPosition = mWorld->getPlantPosition( plant ); double distance = plantPosition->getDistance( ourPostion ); delete plantPosition; if( distance < closestDistance ) { closestDistance = distance; returnPlant = plant; } } delete ourPostion; } delete otherPlants; return returnPlant; } Plant *GardenerAI::getDriestPlant() { SimpleVector *plants = mWorld->getPlotPlants( mGardener ); int numPlants = plants->size(); Plant *driestPlant = NULL; // ignore plants that have at least 1/4 water double driestWaterStatus = 0.25; for( int i=0; igetElement( i ) ); double waterStatus = thisPlant->getWaterStatus(); if( waterStatus < driestWaterStatus ) { driestPlant = thisPlant; driestWaterStatus = waterStatus; } } delete plants; return driestPlant; } char GardenerAI::expandOurPlotToContainPlant( Plant *inPlant ) { Vector3D *cornerA, *cornerB; mWorld->getGardenerPlot( mGardener, &cornerA, &cornerB ); Vector3D *plantPosition = mWorld->getPlantPosition( inPlant ); Vector3D *closestCornerX; double xDistance; Vector3D *closestCornerY; double yDistance; double cornerAXDistance = plantPosition->mX - cornerA->mX; double cornerBXDistance = plantPosition->mX - cornerB->mX; double cornerAYDistance = plantPosition->mY - cornerA->mY; double cornerBYDistance = plantPosition->mY - cornerB->mY; if( fabs( cornerAXDistance ) < fabs( cornerBXDistance ) ) { closestCornerX = cornerA; xDistance = cornerAXDistance; } else { closestCornerX = cornerB; xDistance = cornerBXDistance; } if( fabs( cornerAYDistance ) < fabs( cornerBYDistance ) ) { closestCornerY = cornerA; yDistance = cornerAYDistance; } else { closestCornerY = cornerB; yDistance = cornerBYDistance; } // make distances a little bit larger (1 unit) to contain plant double extra = 1; if( xDistance < 0 ) { xDistance -= extra; } if( xDistance > 0 ) { xDistance += extra; } if( yDistance < 0 ) { yDistance -= extra; } if( yDistance > 0 ) { yDistance += extra; } // only want to change corners in way that makes plot bigger // thus, to contain inPlant, we may need to expand in only one direction // (in other words, x or y) // we can use the plot area to detect plot contraction double oldArea = fabs( cornerA->mX - cornerB->mX ) * fabs( cornerA->mY - cornerB->mY ); closestCornerX->mX += xDistance; double newArea = fabs( cornerA->mX - cornerB->mX ) * fabs( cornerA->mY - cornerB->mY ); if( newArea < oldArea ) { // contraction // reverse it closestCornerX->mX -= xDistance; } oldArea = fabs( cornerA->mX - cornerB->mX ) * fabs( cornerA->mY - cornerB->mY ); closestCornerY->mY += yDistance; newArea = fabs( cornerA->mX - cornerB->mX ) * fabs( cornerA->mY - cornerB->mY ); if( newArea < oldArea ) { // contraction // reverse it closestCornerY->mY -= yDistance; } char returnValue; if( getMaximumDimension( cornerA, cornerB ) <= globalMaxPlotDimension ) { mWorld->setGardenerPlot( mGardener, cornerA, cornerB ); returnValue = true; } else { // new plot exceeds limit returnValue = false; } delete plantPosition; delete cornerA; delete cornerB; return returnValue; } Cultivation_9+dfsg1_UnixSource/game2/gameSource/World.h0000640000175000017500000006006210720062631021751 0ustar pabspabs/* * Modification History * * 2006-July-2 Jason Rohrer * Created. * * 2006-September-12 Jason Rohrer * Fixed the machine-gun mating bug. * * 2006-October-27 Jason Rohrer * Added portal. * * 2006-December-25 Jason Rohrer * Added function for checking if portal is open. * Added function for checking closed status of portal. * Changed so that gardeners get angry if you mate with their most liked. * * 2007-November-17 Jason Rohrer * Added ignoreGardener function to fix bug in ghost mode. */ #ifndef WORLD_INCLUDED #define WORLD_INCLUDED #include "Plant.h" #include "Gardener.h" #include "Portal.h" #include "FlyingObject.h" #include "SoilMap.h" #include "sound/MusicPart.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" /** * Class that represents the game world and contains all objects. * * @author Jason Rohrer */ class World { public: /** * Constructs a world. * * @param inCornerA, inCornerB the corners defining the * boundary of the world. * Destroyed by caller. */ World( Vector3D *inCornerA, Vector3D *inCornerB ); ~World(); /** * Adds a plant to the world. * * @param inGardener the gardener adding the plant. * Must already be managed by this world. * @param inPlant the plant. * Destroyed by this world. * @param inPosition the position to add the plant at. * Destroyed by caller. */ void addPlant( Gardener *inGardener, Plant *inPlant, Vector3D *inPosition ); /** * Adds a gardener to the world. * * @param inGardener the gardener. * Destroyed by this world. * @param inPosition the position to add the gardener at. * Destroyed by caller. * @param inRotation the starting rotation, or NULL for default. * Destroyed by caller. */ void addGardener( Gardener *inGardener, Vector3D *inPosition, Angle3D *inRotation = NULL ); /** * Removes a gardener from this world and destroys it. * * @param inGardener the gardener. Must be managed by this world. * Destroyed by this world. */ void removeGardener( Gardener *inGardener ); /** * Clears dependencies on gardener without fully removing it. * Other gardeners will henceforth act like this gardener does not * exist. * * @param inGardener the gardener. Must be managed by this world. * Destroyed by this world. */ void ignoreGardener( Gardener *inGardener ); /** * Gets the position of a gardener. * * @param inGardener the gardener in this world to get the position * of. Will be destroyed by this world if this gardener is * already being managed by this world. * * @return the position, or NULL if inGardener is not managed * by this world. Destroyed by caller. */ Vector3D *getGardenerPosition( Gardener *inGardener ); /** * Get the positions of all gardeners in the world. * * @param outNumGardeners pointer to where the number of gardeners * should be returned. * * @return an array of positions. Array and contained positions * must be destroyed by caller. */ Vector3D **getAllGardenerPositions( int *outNumGardeners ); /** * Gets all gardeners in the world. * * @param outNumGardeners pointer to where number of gardeners * should be returned. * * @return an array of gardeners. Array destroyed by caller, gardeners * managed by world. */ Gardener **getAllGardeners( int *outNumGardeners ); /** * Get the music parts for all gardeners in the world. * * @param outNumGardeners pointer to where the number of gardeners * should be returned. * * @return an array of music parts. Array must be destroyed by caller. * Music parts are managed by this world. */ MusicPart **getAllGardenerMusicParts( int *outNumGardeners ); /** * Gets a volume modifier for each music part. * Based on life of each gardener (grow quite near death). * * @param outNumGardeners pointer to where the number of gardeners * should be returned. * * @return an array of values in the range [0,1]. Array destroyed * by caller. */ double *getAllGardenerMusicVolumeModifiers( int *outNumGardeners ); /** * Gets the position of a plant. * * @param inPlant the plant in this world to get the position * of. Will be destroyed by this world if this plant is * already being managed by this world. * * @return the position, or NULL if inPlant is not managed * by this world. Destroyed by caller. */ Vector3D *getPlantPosition( Plant *inPlant ); /** * Sets the plot for a gardener. * * @param inGardener the gardener to set the plot for. * Must be a gardener already managed by this world. * @param inCornerA, inCornerB corners that define a * rectangular plot, or NULL to set no plot. * Destroyed by caller. */ void setGardenerPlot( Gardener *inGardener, Vector3D *inCornerA, Vector3D *inCornerB ); /** * Gets the plot of a gardener. * * @param inGardener the gardener to get the plot for. * @param inCornerA, inCornerB pointers to where the corner * vectors should be returned. Will be set to NULL if * inGardener doesn't have a plot. * Resulting vectors destroyed by caller. */ void getGardenerPlot( Gardener *inGardener, Vector3D **inCornerA, Vector3D **inCornerB ); /** * Gets the area of a gardener's plot. * * @param inGardener the gardener to get the plot area for. * * @return the area. */ double getPlotArea( Gardener *inGardener ); /** * Gets the area of the intersection between gardener plots. * * @param inGardenerA the first gardener. * @param inGardenerB the second gardener. * * @return the area of the plot intersection. */ double getPlotIntersectionArea( Gardener *inGardenerA, Gardener *inGardenerB ); /** * Gets the gardener closest to a given gardener. * * Both parameter and result are managed by world. * * @param inGardener the gardener to check. * * @return the closest gardener, or NULL if there * are no other gardeners. */ Gardener *getClosestGardener( Gardener *inGardener ); /** * Gets the distance between gardeners. * * @param inGardenerA, inGardenerB the gardeners. Managed by world. * * @return the distance between them. */ double getGardenerDistance( Gardener *inGardenerA, Gardener *inGardenerB ); /** * Gets the plant closest to a given plant. * * Both parameter and result are managed by world. * * @param inPlant the plant to check. * * @return the closest plant, or NULL if there * are no other plants. */ Plant *getClosestPlant( Plant *inPlant ); /** * Gets the plant closest to a given position. * * @param inPosition the position to check. Destroyed by caller. * @param inPlantToIgnore a plant to ignore, or NULL to consider all. * Plant managed by this world. Defaults to NULL. * @param inGetEvenIfTooFar true to get the closest plant * to inPosition even if it is "too" far away from it. * If set to false, this function will only return a plant * if inPosition is "close enough" to one. * Defaults to true. * @param inGetEvenIfPoisoned true to get the closest plant * to inPosition even if it is poisoned. * Defaults to false. * * @return the closest plant, or NULL if there * are no other plants. Managed by this world. */ Plant *getClosestPlant( Vector3D *inPosition, Plant *inPlantToIgnore = NULL, char inGetEvenIfTooFar = true, char inGetEvenIfPoisoned = false ); /** * Gets whether there is room for a plant at a given position. * * @param inPosition the position. Destroyed by caller. * * @return true if can plant. */ char canPlant( Vector3D *inPosition ); /** * Gets the closest water to a gardener. * * @param inGardener the gardener to check. Managed by this world. * * @return the position of the closest water point. * Resulting vector destroyed by caller. */ Vector3D *getClosestWater( Gardener *inGardener ); /** * Gets the closest land point to a given position. * * @param inPosition the position to check. Destroyed by caller. * * @return the closest land point. Destroyed by caller. */ Vector3D *getClosestLand( Vector3D *inPosition ); /** * Gets a random point on the land of the world. * * @return a point. Destroyed by caller. */ Vector3D *getRandomLandPoint(); /** * Gets the center of a gardener's plot. * * @param inGardener the gardener to get the plot for. * @return the position of the plot center, or NULL if gardener has * no plot. * Resulting vector destroyed by caller. */ Vector3D *getPlotCenter( Gardener *inGardener ); /** * Gets whether a garderner is in its plot. * * @param inGardener the gardener to check. * Must be a gardener already managed by this world. * * @return true if inGardener is in its plot. */ char isInPlot( Gardener *inGardener ); /** * Gets whether a position is in a gardener's plot. * * @param inGardener the gardener to check. * Must be a gardener already managed by this world. * @param inPosition the position to check. * Destroyed by caller. * * @return true if inPosition is in inGardener's plot. */ char isInPlot( Gardener *inGardener, Vector3D *inPosition ); /** * Gets whether a garderner is in water. * * @param inGardener the gardener to check. * Must be a gardener already managed by this world. * * @return true if inGardener is in water. */ char isInWater( Gardener *inGardener ); /** * Gets whether a point is in water. * * @param inPosition the position to check. * destroyed by caller. * * @return true if inPosition is in water. */ char isInWater( Vector3D *inPosition ); /** * Gets the closest plant in this gardener's plot. * * @param inGardener the gardener to check. * Must be a gardener already managed by this world. * * @param inGetEvenIfTooFar true to get the closest plant * to inGardener even if inGardener is "too" far away from it. * If set to false, this function will only return a plot plant * if inGardener is "close enough" to one. * @param inGetOnlyRipe true to only consider ripe plants. * * @return the closest plant, or NULL if no plant is close * enough. Destroyed by this world. */ Plant *getClosestPlotPlant( Gardener *inGardener, char inGetEvenIfTooFar = false, char inGetOnlyRipe = false ); /** * Gets the plants in a gardener's plot. * * @param inGardener the gardener. * Must be a gardener already managed by this world. * * @return a vector of plants. * Vector must be destroyed by caller. Plants will be * destroyed by this world. */ SimpleVector *getPlotPlants( Gardener *inGardener ); /** * Gets a plant last tended by a gardener. * * @param inGardener the gardener to look for. * Managed by this world. * * @return a plant last tended by inGardener, or NULL if all * plants were last tended by other gardeners. * Managed by this world. */ Plant *getTendedPlant( Gardener *inGardener ); /** * Causes a gardener to harvest a plant, if the gardener is close * enough, and if the plant is in the gardener's plot. * * @param inGardener the gardener. * @param inPlant the plant to harvest. * * Both parameters must already be managed by this world and * will be destroyed by this world. * If inPlant successfully harvested, it will be destroyed by this * call. */ void harvestPlant( Gardener *inGardener, Plant *inPlant ); /** * Dumps water on closest plant in gardener's plot. * * @param inGardener the gardener to dump water from. * Must be a gardener already managed by this world. */ void dumpWater( Gardener *inGardener ); /** * Poisons the closest plant. * * @param inGardener the gardener to send poison from. * Must be managed by this world. */ void dumpPoison( Gardener *inGardener ); /** * Removes a plant from this world. * * @param inPlant the plant to remove. Must be managed by this world. * Destroyed by this world. */ void removePlant( Plant *inPlant ); /** * Gives a fruit from one gardener to another. * * @param inGiver the gardener giving the fruit. Must be managed * by this world. * @param inReceiver the gardener receiving the fruit. Must be managed * by this world. * @param inFruit the fruit to give. * Will be destroyed by this world. */ void giveFruit( Gardener *inGiver, Gardener *inReceiver, Fruit *inFruit ); /** * Flies an emotion icon between two gardeners. * * @param inSource, inTarget the gardeners to pass icon between. * @param inIcon the icon to draw. Will be destroyed when it * reaches the inTarget. * @param inLarge set to true to make icon larger than normal. * Defaults to false. */ void flyEmotionIcon( Gardener *inSource, Gardener *inTarget, DrawableObject *inIcon, char inLarge = false ); /** * Gets the soil condition at a given location in the world. * * @param inPosition the position to test. * Destroyed by caller. * * @return a soil type value in the range [0,1]. */ double getSoilCondition( Vector3D *inPosition ) { return mSoilMap.getSoilCondition( inPosition ); } /** * Mates two gardeners. * * @param inParentA, inParentB the gardeners to mate. Managed by * this world. */ void mateGardeners( Gardener *inParentA, Gardener *inParentB ); /** * Gets whether a gardener is about to get pregnant. * * @param inGardener the gardener to check. Managed by this world. * * @return true if a pregnancy is flying toward this gardener. */ char isTargetOfPregnancy( Gardener *inGardener ); /** * Turns off the target status for a gardener. * * @param inGardener the gardener to turn off target status for. * Managed by this world. */ void cancelTargetOfPregnancy( Gardener *inGardener ); /** * Gets the next gardener that the user can control * * @param inGardenerToSkip a gardener to ignore when looking, or * NULL to consider all gardeners. Defaults to NULL. * * @return a gardener, or NULL if there are no controllable gardeners * left. Managed by this world. */ Gardener *getNextUserControllableGardener( Gardener *inGardenerToSkip = NULL ); /** * Sets a position to highlight. * * @param inPosition the position, or NULL to clear the highlight. * Destroyed by caller. */ void setHighlightPosition( Vector3D *inPosition ); /** * Augments a nearby portal, or starts a new one. * * @param inPosition the position near which to augment a portal. * @param inAugmenter the gardener that augmented the portal. */ void augmentPortal( Vector3D *inPosition, Gardener *inAugmenter ); /** * Checks if portal is open. * * @return true if open. */ char isPortalOpen(); /** * Checks if portal is closed. * * @return true if closed and gardeners have been sent. */ char isPortalClosed(); /** * Passes time in this world. * * @param inTimeDeltaInSeconds the amount of time that has passed. */ void passTime( double inTimeDeltaInSeconds ); /** * Draw this world and all of its objects into the current * OpenGL context. */ void draw(); private: Vector3D mCornerA, mCornerB; SoilMap mSoilMap; SingleTextureGL *mCloudTexture; double mCloudPosition; double mCloudMotionRate; double mGardenerVelocity; double mGardenerRotationVelocity; SimpleVector mPlants; SimpleVector mPlantPositions; // what gardener last tended each plant SimpleVector mLastPlantTender; SimpleVector mGardeners; SimpleVector mGardenerPositions; SimpleVector mGardenerRotations; SimpleVector mGardenerRotationsComplete; SimpleVector mGardenerDestinationForRotation; SimpleVector mGardenerPlotsCornerA; SimpleVector mGardenerPlotsCornerB; Vector3D mPortalPosition; Portal *mPortal; // track if mating flying object is in motion that // is targetting a given gardener (not pregnant yet, but almost) SimpleVector mGardenerTargetOfPregnancy; SimpleVector mFlyingObjects; // how close can two plants be? double mMinPlantingDistance; // how far apart two objects can be and still count as close double mMaxDistanceThatCountsAsClose; // z position of gardeners double mGardenerZ; Vector3D *mHighlightPosition; /** * Tests whether a position is inside a rectangle. * * All params destroyed by caller. * * @param inPosition the position to test. * @param inCornerA, inCornerB the corners of the rectangle. */ char isInRectangle( Vector3D *inPosition, Vector3D *inCornerA, Vector3D *inCornerB ); /** * Gets the intersection of two ranges. * * @param inStartA, inEndA the first range. * @param inStartB, inEndB the second range. * @param outStart, outEnd pointers to where resulting intersection * range should be returned. * * @return true if they intersect. */ char getRangeIntersection( double inStartA, double inEndA, double inStartB, double inEndB, double *outStart, double *outEnd ); /** * Gets the intersection of two rectangles. * * All params and result destroyed by caller. * * @param inFirstCornerA, inFirstCornerB corners of first rectangle. * @param inSecondCornerA, inSecondCornerB corners of first rectangle. * @param outCornerA, outCornerB pointers to where corners of * intersection should be returned. Will be set to NULL if there * is no intersection. */ void getRectangleIntersection( Vector3D *inFirstCornerA, Vector3D *inFirstCornerB, Vector3D *inSecondCornerA, Vector3D *inSecondCornerB, Vector3D **outCornerA, Vector3D **outCornerB ); /** * Gets area of a a rectangle. * * All params destroyed by caller. * * @param inCornerA, inCornerB the corners of the rectangle. * * @return the area. */ double getRectangleArea( Vector3D *inCornerA, Vector3D *inCornerB ); /** * Gets center of a a rectangle. * * All params and result destroyed by caller. * * @param inCornerA, inCornerB the corners of the rectangle. * * @return the center. */ Vector3D *getRectangleCenter( Vector3D *inCornerA, Vector3D *inCornerB ); /** * Removes a gardener from this world and destroys it. * * @param inGardenerIndex the index of the gardener in our vector. */ void removeGardener( int inIndex ); /** * Clears dependencies on gardener without fully removing it. * Other gardeners will henceforth act like this gardener does not * exist. * * @param inGardenerIndex the index of the gardener in our vector. */ void ignoreGardener( int inIndex ); /** * Anger other gardeners about an action that takes place in * a given position. Other gardener only angered if position * is in that gardener's plot. * * @param inGardener the gardener doing the action. * If managed by this world, destroyed by this world. * @param inPosition the position of the action. * Destroyed by caller. */ void angerOthers( Gardener *inGardener, Vector3D *inPosition ); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/GardenerAI.h0000640000175000017500000000341210461654153022627 0ustar pabspabs/* * Modification History * * 2006-July-7 Jason Rohrer * Created. */ #ifndef GARDENER_AI_INCLUDED #define GARDENER_AI_INCLUDED #include "Gardener.h" #include "World.h" /** * Artificial intelligence control for a gardener. * * @author Jason Rohrer */ class GardenerAI { public: /** * Constructs an AI. * * All params must be destroyed by caller after this AI is destroyed. * * @param inGardener the gardener to control * @param inWorld the world the gardener is in. */ GardenerAI( Gardener *inGardener, World *inWorld ); ~GardenerAI(); /** * Passes time for this AI. * * @param inTimeDeltaInSeconds the amount of time that has passed. */ void passTime( double inTimeDeltaInSeconds ); private: Gardener *mGardener; World *mWorld; Vector3D *mNextPlantingLocation; double mSecondsSinceLastGift; double mSecondsSinceLastRevenge; /** * Gets the closest plant in inGardener's plot that is not * on our gardener's plot. * * Both parameter and return value are managed and destroyed by * mWorld. * * @param inGardener the gardener to look at. * * @return the plant in inGardener's plot, or NULL if no * such plant exists. */ Plant *getClosestPlantInGardenerPlot( Gardener *inGardener ); /** * Expands our plot in the world so that inPlant is contained. * * @param inPlant the plant to contain. Managed by world. */ void expandOurPlotToContainPlant( Plant *inPlant ); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/landscape.h0000640000175000017500000000675010750217103022617 0ustar pabspabs #ifndef LANDSCAPE_INCLUDED #define LANDSCAPE_INCLUDED /** * Gets height samples from an "infinite" fractal landscape. * The landscape can change over time by varying t. * * @param x the x coordinate of the sample. * @param y the y coordinate of the sample. * @param t the time of the sample. * @param baseFrequency the frequency to use for the lowest detail component. * @param inRoughness the roughness of the landscape (how much high frequencies * are factored in). Should be in the range [0..1] with 0 making a very * smooth landscape and 1 making a very rough landscape. * @param detail the detail level. Larger numbers result in more * detail. Defaults to 10. * * @return the height of the landscape at the sample point/time. */ double landscape( double inX, double inY, double inT, double inBaseFrequency, double inRoughness, int inDetail = 10 ); /** * Samples height of a landscape that varies in roughness over the xy plane. * * @params same as for landscape, except: * @param inRoughnessChangeFrequency the rate at which roughness changes * over space. Should, in general, be less than inBaseFrequency. * @param inMinRoughness the minimum roughness value, in the range [0..1]. * @param inMaxRoughness the maximum roughness value, in the range [0..1]. * * @return same as for landscape. */ double variableRoughnessLandscape( double inX, double inY, double inT, double inBaseFrequency, double inRoughnessChangeFrequency, double inMinRoughness, double inMaxRoughness, int inDetail ); /** * Computes linearly-blended random values in the range [-1..1] from a * 4d parameterized noise space. * * @param x, y, z, t the 4d floating-point coordinates. * * @return a blended random value in the range [-1..1]. */ double noise4d( double x, double y, double z, double t ); /** * Computes linearly-blended random values in the range [-1..1] from a * 4d parameterized noise space (keeping one dimension constant). * * Should be faster than noise4D. * * @param x, y, z the 3d floating-point coordinates. * * @return a blended random value in the range [-1..1]. */ double noise3d( double x, double y, double z ); /** * Gets a set of randomly-chosen (though stable) points in a given * region of the landscape. * * @param inStartX, inEndX the x region. * @param inStartY, inEndY the y region. * @param inT the time. * @param inSampleStepSize the step size in the sample grid. * Higher values are faster but result in sparser distributions of points. * @param inDensity the density of points, in the range [0,1]. * @param outXCoordinates pointer to where array of x coordinates should * be returned. Array must be destroyed by caller. * @param outYCoordinates pointer to where array of x coordinates should * be returned. Array must be destroyed by caller. * * @return the number of points (the length of outXCoordinates). */ int getRandomPoints( double inStartX, double inEndX, double inStartY, double inEndY, double inT, double inSampleStepSize, double inDensity, double **outXCoordinates, double **outYCoordinates ); #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/DrawableObject.h0000640000175000017500000000156510503240215023530 0ustar pabspabs/* * Modification History * * 2006-July-23 Jason Rohrer * Created. * * 2006-September-17 Jason Rohrer * Added a virtual destructor to fix some memory leaks. */ #ifndef DRAWABLE_OBJECT_INCLUDED #define DRAWABLE_OBJECT_INCLUDED #include "minorGems/math/geometry/Vector3D.h" /** * Interface for drawable objects. * * @author Jason Rohrer */ class DrawableObject { public: /** * Draw this object in the current OpenGL context. * * @param inPosition the position to draw this object at. * @param inScale the scale factor for this object. * Defaults to 1. */ virtual void draw( Vector3D *inPosition, double inScale = 1 ) = 0; // virtual destructor to ensure proper destruction of subclasses virtual ~DrawableObject() { }; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/font.tga0000640000175000017500000060005410544273543022167 0ustar pabspabs """ 222CCC @@@ aaa,,,000­­­xxxXXXXXX---³³³ÕÕÕGGGžžžÙÙÙJJJ€€€¡¡¡000  <<< lllôôô¯¯¯\\\ëëëÞÞÞFFF:::ÅÅÅÅÅÅ:::###¬¬¬æææ\\\îîîèèèQQQ&&&¼¼¼÷÷÷šššXXXXXX222„„„,,,""" 888¯¯¯ÒÒÒFFF{{{úúú¼¼¼$$$???ÍÍÍÈÈÈ;;;:::ÇÇÇÇÇÇ:::###¶¶¶øøøsss^^^íííòòò’’’kkkååå×××III777ÄÄÄ×××___óóó”””QQQµµµSSS XXXXXX444¼¼¼ôôôåååMMM[[[åå嘘˜@@@ÐÐÐÐÐÐ@@@JJJßßßßßßJJJ"""²²²ðððfff †††îî¦ °°°ððð•••²²²ÿÿÿÄÄÄ’’’öööªªªjjjÓÓÓ÷÷÷‚‚‚ <<<ÉÉÉÉÉÉ<<<AAA···òòòâââ{{{eeeïï簾¦777ÆÆÆÆÆÆ777,,,­­­­­­,,,333ÆÆÆáááPPP ²²²ùùùyyyjjjìììØØØ>>>ZZZØØØõõõÙÙÙöööÖÖÖÛÛÛÿÿÿÁÁÁ000BBBÕÕÕÕÕÕBBB """;;;ÏÏÏôôôåååsssqqqøøø³³³""" 888888 &&&&&& ;;;LLL¥¥¥ïïïeeeDDDØØØÝÝÝKKKÊÊÊêêêýýýÿÿÿþþþõõõËËËggg???EEEwwwääääääwwwBBB???:::JJJ”””¦¦¦³³³–––¡¡¡!!!(((¦¦¦ùùùÞÞÞmmm YYYäää––––––äääYYY111ÈÈÈöööiii’’’íííüüüÿÿÿÿÿÿÿÿÿúúúùùùÕÕÕ666‚‚‚ÍÍÍÚÚÚßßßøøøùùùßßßÕÕÕÖÖÖooo XXXÅÅÅÕÕÕÿÿÿçççïïïøøøäääììììììMMM%%%®®®åååæææppp XXXååå•••!!!³³³øøøqqq ¯¯¯ðððggg)))šššóóóþþþþþþûûûûûûìììžžž‚‚‚ÍÍÍÚÚÚßßßøøøùùùßßßÙÙÙæææ¨¨¨"""""" XXXÅÅž¾¾´´´hhheeeqqqYYYaaaUUUŒŒŒïïïßß߃ƒƒxxx666¥¥¥ñññrrr+++¿¿¿òòòlllDDDÁÁÁõõõÙÙÙôôôÎÎÎÜÜÜûûûÏÏÏJJJ???EEEwwwääääääwwwEEEUUU///XXX£££---:::222 tttëëëìì쎎Ž  ŠŠŠóóó³³³000•••íííëëëUUU"""ªªªççç¾¾¾‰‰‰õõõ©©©tttàààñññÂÂÂ---DDDÔÔÔÔÔÔDDD&&&¬¬¬ÞÞÞRRR"""qqqÚÚÚùùù§§§AAA$$$iiiñññììì¾¾¾GGG£££óóóìì쇇‡NNNqqq///iiiïï簾¦%%%bbbÐÐÐßßßMMMAAAÓÓÓÓÓÓAAAfffêêêãããOOOÑÑÑooo @@@ÙÙÙêêê¹¹¹000‰‰‰ÔÔÔ¨¨¨XXXàààùùùÉÉÉ---lllðððñññ‘‘‘ [[[èèè™™™ HHHuuu777ÆÆÆÆÆÆ777000ËËËÿÿÿ­­­KKKíííþþþççç<<<sssggg&&&“““çççßßߣ££"""hhh×××àààMMM €€€éééžžžxxx666  888888 ŠŠŠöööåååNNN###¤¤¤ÛÛÛ """YYYSSS--- HHHuuuQQQ  tttçç猌Œ... LLL !!!((((((777 @@@   111EEE;;;BBB"""‡‡‡ŸŸŸeee^^^222JJJRRRnnn†††mmmzzzfffAAA***€€€£££˜˜˜³³³¥¥¥•••«««ppp$$$;;;¾¾¾;;;;;;¾¾¾;;;!!!111fffeeeeeehhhmmmšššÇÇǃƒƒhhhçççPPP$$$mmmoooQQQ<<>>ÑÑÑÓÓÓ‡‡‡[[[qqqeeeYYY‰‰‰ÏÏÏëëë]]])))ÀÀÀ÷÷÷lll~~~ÿÿÿ~~~{{{üüüùùùÙÙÙÈÈÈ­­­¦¦¦§§§£££ŒŒŒXXX wwwâââóóó¶¶¶&&&$$$yyy°°°ÐÐÐÅÅÅÙÙÙÙÙÙÇÇÇæææùùùèèèNNNFFFØØØØØØ•••vvv®®®ÒÒÒíííòòò‚‚‚ ***¸¸¸ééé”””ssseeebbb¥¥¥ÓÓÓùùùëëëQQQ)))qqqZZZ444ŽŽŽîîîèèè®®®YYY>>>‹‹‹çççÛÛÛGGG±±±øøøÝÝÝ{{{ {{{ñññìììXXX]]]èèèVVVåå庺ºddd))) „„„óóó©©© AAA666 ###²²²ñññkkkAAAÑÑÑÎÎÎ<<<yyyùùùyyysssõõõÓÓÓ___222   iiiåååõõõ«««---===999HHHHHHSSSÁÁÁïïï²²²###ooo÷÷÷ÀÀÀ+++999¤¤¤ôôô¨¨¨LLLàààäääPPPDDD¸¸¸÷÷÷ppp¡¡¡øøøçç纺º... >>>UUUpppYYYeeeqqqYYYeeeqqqOOO777ÌÌÌíí파ŒHHHÙÙÙÙÙÙHHHRRRçççææænnn 222¥¥¥æææ÷÷÷uuu………ÿÿÿ˜˜˜ ///¨¨¨XXX (((¨¨¨òòò™™™...777dddÁÁÁæææZZZ úúúÄÄÄ***yyyùùùyyy^^^íííÛÛÛKKK***LLLCCC888@@@ VVVåååúúúÖÖÖ{{{fffAAAjjjåååÖÖÖPPPWWWèèèÙÙÙ———lllQQQQQQ³³³ùùù¤¤¤@@@ÓÓÓßßß[[[„„„ÿÿÿ{{{„„„ÏÏÏÑÑѳ³³...666ÉÉÉååå÷÷÷äääïïïøøøäääïïïøøøÝÝÝ``` †††–––$$$>>>ÌÌÌÇÇÇ;;;hhhòòò±±±!!!GGGÊÊÊûûûêêêúúú¡¡¡žžžøøøvvv"""```ªªªíííèèè\\\iii}}}ŸŸŸÃÃÃÃÃÃâââòòòßßßGGG>>>ÖÖÖííí„„„;;;CCC   ÿÿÿ¡¡¡DDD;;;FFFÙÙÙãã㸸¸³³³àààÔÔÔÇÇÇÛÛÛzzz$$$ÌÌÌþþþüüüûûûðððúúúððð×××”””###GGGÕÕÕøøøššš)))ÁÁÁöööûûûòòòâââÝÝÝóóóñññvvv¬¬¬öööÈÈÈfffOOO===IIIaaaºººùùùttt666<<<111qqq’’’±±±–––¦¦¦³³³–––¦¦¦»»»¹¹¹QQQ –––ííí×××BBB„„„ÿÿÿ“““QQQÒÒÒÿÿÿéé饥¥îîîØØØ???>>>ØØØííí^^^ŒŒŒìììëëëààà{{{[[[ÿÿÿùùùôôôöööÎÎÎÒÒÒÀÀÀŒŒŒmmmôôôæææ¹¹¹ÆÆÆØØØèèèþþþìììÔÔÔÉÉÉXXX :::ÒÒÒ÷÷÷ÿÿÿáááãããÔÔÔÎÎÎòòòééé’’’VVVííí÷÷÷ÊÊÊnnnÁÁÁååå÷÷÷‡‡‡ :::ÇÇÇÿÿÿÄÄÄ000___áááùùùÞÞÞÃÃÃÔÔÔÔÔÔâââõõõÂÂÂ222EEEÓÓÓÿÿÿéééãããÉÉÉÛÛÛëëëúúú÷÷÷qqq """%%%--- EEE°°°òòòõõõ¥¥¥jjjñññ~~~kkkÔÔÔöööÄÄÄYYYNNNÙÙÙÕÕÕBBBBBBÓÓÓÖÖÖEEE999¥¥¥ïïïíííIII iiiuuunnniii>>>bbbËËËæææSSSTTTëëëùùùïïïáááÜÜÜèèèþþþìììÔÔÔÉÉÉXXX %%%¤¤¤ÙÙÙ¯¯¯WWWMMMCCC>>>”””ëëëííí\\\VVVååå´´´999 +++‘‘‘ñññ£££666½½½ÿÿÿÇÇÇ:::)))ÁÁÁùùù³³³EEE+++AAAAAA^^^ÔÔÔÿÿÿ——— ???ŸŸŸÉÉÉàààÉÉÉØØØâââ÷÷÷ÖÖÖ999,,,JJJ;;;CCCMMM;;;CCCMMM;;;:::AAAØØØÿÿÿßßߢ¢¢000jjjòòòÔÔÔêêêùùùÂÂÂ999DDDÕÕÕÔÔÔBBB>>>ÍÍÍÀÀÀ444EEEÎÎÎóóóééé„„„£££ùùùyyySSSssseeeRRRGGG   ÿÿÿ¡¡¡DDD;;;'''???²²²ÿÿÿ„„„iiiñññ®®®!!!oooööö¦¦¦%%%°°°ìììÍÍÍDDD888ÆÆÆÕÕÕJJJ€€€òòò¤¤¤555KKK===```ÏÏÏúúú‘‘‘222vvv=== &&&­­­ÞÞÞÇÇÇÔÔÔáááÇÇÇÔÔÔáááÇÇÇÅÅÅXXX vvvëëëÂÂÂSSS<<<ÜÜÜüüüöööººº888 tttèèèÓÓÓ;;;hhhõõõÄÄÄ000 QQQÄÄÄÿÿÿÑÑÑiii999000›››ùùùzzzyyyùùùyyy¤¤¤ùùùxxx___ìììÍÍÍVVV###///ZZZ´´´ôôô——— uuuìììÓÓÓYYY JJJàààççç```oooööö¦¦¦qqqðððçççTTT000ÐÐÐúúúÍÍÍ///&&&­­­ÞÞÞÇÇÇÔÔÔáááÇÇÇÔÔÔáááÇÇÇÅÅÅXXX PPP«««AAA €€€òòòííí333PPP€€€àààûûû¬¬¬___ëë륥¥FFFßßß÷÷÷ÝÝÝ¡¡¡~~~yyyyyy~~~rrr;;; XXXÇÇÇÇÇÇžžžuuuRRR===MMMbbbÌÌÌñññhhhyyyùùùyyy???EEEBBBFFF]]]nnn®®®ëëëððð```///ÃÃÃïïïÔÔÔ   ···ÂÂÂããã÷÷÷êêê\\\///ÄÄÄììì,,,ÂÂÂððð¼¼¼WWWRRR???===¶¶¶õõõ˜˜˜ ???PPP>>>IIIkkkËËËõõõ   iiiúúúüüüÚÚÚ:::,,,JJJ;;;CCCMMM;;;CCCMMM;;;:::--- $$$»»»øøøéééÂÂÂäääéééôôôÆÆÆ888///¨¨¨XXX |||üüü÷÷÷öööûûûùùùÿÿÿùùùùùùÿÿÿ÷÷÷¾¾¾((( XXXÒÒÒöööóóóóóóáááÊÊÊáááâââùùùØØØ:::zzzÿÿÿzzz‚‚‚ÍÍÍÚÚÚÕÕÕÙÙÙëëëñññÿÿÿæææ’’’ YYYÔÔÔòòòçççøøøêêêÔÔÔ™™™XXXTTTíííÂÂÂ444```ßßßõõõøøøããããããÔÔÔÒÒÒ÷÷÷ðððddd666ÍÍÍãããÊÊÊÛÛÛêêêûûûÜÜÜDDDƒƒƒ´´´ŠŠŠ///¡¡¡œœœ666000£££âââçççîîî´´´yyy((("""FFFÖÖÖÍÍͯ¯¯|||~~~yyyyyy~~~rrr;;;CCCjjjyyy¯¯¯ÑÑÑÈÈÈáááÖÖÖÅÅÅYYY QQQèèèQQQ‚‚‚ÍÍÍÚÚÚÕÕÕÕÕÕÚÚÚ®®®RRR ===jjj\\\ppp[[[???UUU222 FFFuuu«««   ÏÏÏæææðððááá„„„ŒŒŒÙÙÙÈÈÈ×××ÑÑѹ¹¹RRR  «««þþþùùù OOOZZZ___%%% 666333>>>;;;MMMBBB+++ @@@ ???EEEBBBDDDAAA===WWWdddDDDCCC;;;FFF<<>>,,,ÇÇÇûûûžžž333AAABBBEEEDDDBBBBBB+++555àààþþþ¤¤¤hhhõõõºººbbbCCC:::MMMYYY¾¾¾óóó„„„ ¬¬¬óóómmmhhhõõõ§§§eeeïïï­­­ JJJÞÞÞÞÞÞJJJ```ÕÕÕ÷÷÷ÄÄÄ---===ÛÛÛüüü„„„áááÿÿÿêêꢢ¢'''YYYäää–––¡¡¡ñññmmmttt÷÷÷øøønnnnnnñññ¡¡¡§§§ïïïeee"""¶¶¶ÿÿÿÉÉÉ’’’ùùùyyyqqqùùùÛÛÛFFF###»»»ôôôsss```åååëëë”””MMM@@@hhheeepppÈÈÈúúúÉÉÉ***DDD××××××DDD lllÚÚÚùùù 666ÐÐÐïï便¥777>>>}}}×××øøøµµµ"""eeeïï着ª333,,,DDDrrrÇÇÇééé×××bbb IIIàààñññ···»»»ÔÔÔÕÕÕÚÚÚÔÔÔÕÕÕÖÖÖ¨¨¨&&&...ÒÒÒýýý‘‘‘\\\íííääääääÔÔÔÇÇÇâââÜÜÜïïïðððccc###ªªªåååZZZ___ëëëBBB›››ôôôÉÉÉbbb+++ @@@ÔÔÔäää„„„lll{{{£££ãããúúúÔÔÔNNNVVVíííùùù€€€NNN­­­æææîî£kkkñññ««« ¦¦¦ïïïeee‰‰‰ssseeeïïï§§§¦¦¦ïïïeeeRRRèèèôôôÚÚÚÿÿÿ€€€666ÔÔÔùùùªªª... yyyðððìììXXX###¸¸¸òòòªªªXXXÞÞÞÖÖÖDDDAAAÓÓÓÖÖÖwwwyyyŠŠŠ½½½çççÿÿÿÇÇÇ000tttíííðððÎÎÎØØØòòòûûûÔÔÔQQQNNNÛÛÛ°°°¥¥¥«««½½½ÖÖÖîîîóóóÒÒÒ``` llløøøþþþìììîîîâââ×××ÚÚÚÔÔÔÕÕÕÖÖÖ¨¨¨&&&¦¦¦ïïïeee|||´´´àààÔÔÔÇÇÇâââÕÕÕÈÈÈ¡¡¡$$$,,,³³³ÕÕÕGGG///¨¨¨XXX ‚‚‚ÒÒÒòòòþþþùùùããã¶¶¶>>>´´´ÿÿÿéééòòòõõõôôô÷÷÷ÒÒÒRRRYYYéééëëë[[[†††ïïïòòòeeerrrõõõËËË‚‚‚YYYeeetttvvv¦¦¦‰‰‰QQQ§§§õõõhhh hhhõõõ§§§§§§õõõhhh òòòüüüýýý†††hhhæææêê꺺º­­­ÛÛÛÿÿÿ®®®TTTçççÜÜÜQQQ)))²²²ÜÜÜLLL777ÆÆÆÚÚÚÿÿÿùùùùùùÿÿÿûûûÝÝÝ¢¢¢222zzzØØØ÷÷÷ðððÚÚÚ’’’888+++¬¬¬ÄÄÄìììõõõèèèØØØØØØ™™™CCC :::ÉÉÉÕÕÕzzzcccQQQEEEEEEDDDBBBBBB+++ XXX¨¨¨///***LLLCCC:::MMMCCC222222CCC """‚‚‚ÎÎÎâââîîîÚÚÚÚÚÚÞÞÞrrr 222“““’’’©©©ŸŸŸwwwjjj111///©©©}}}ŽŽŽíííXXX{{{ûûûöööøøøäääïïïøøøíííõõõÞÞÞŸŸŸ"""êêê______ëëëêêê___šššöööûûûyyy………àààíííðððõõõÁÁÁ222}}}SSS---III 888GGGooowwwyyy€€€HHH >>>pppccc;;; ''':::```gggXXXGGG>>>//////"""???HHHQQQ;;;FFFMMM """JJJ...¯¯¯±±±³³³–––¦¦¦´´´˜˜˜WWW+++ XXX¨¨¨//////¨¨¨XXX XXX¨¨¨///kkkxxxGGGaaaeeeeee&&& """""" """"""""" $$$!!!,,,:::!!!    ///___fffZZZKKK::: aaaÆÆÆ¢¢¢³³³¥¥¥–––²²²˜˜˜```;;;DDDNNNnnn@@@222ÂÂÂÛÛÛ¨¨¨³³³¦¦¦ŠŠŠCCC LLLkkkkkkYYYkkkkkkOOO[[[eeehhheeeeeehhheeeeeefffZZZJJJ222222„„„,,, 888888 [[[™™™III 666666 rrr^^^***111 888888 """¨¨¨êêêôôôéééÝÝÝÍÍÍ———oooLLL$$$ÆÆÆüüüíííøøøïïïäääøøøôôôïïïsss(((ŽŽŽÔÔÔÕÕÕãããöööÚÚÚ£££]]]dddôôôûûûëëëøøøòòò÷÷÷ØØØeee áááññññññäääñññòòòááá xxxçççïïïõõõïïïïïïõõõïïïïïïôôôéééÛÛÛ¶¶¶/// ‹‹‹óóó”””777ÆÆÆÆÆÆ777"""²²²ñññÀÀÀ,,,222UUUDDDÔÔÔ}}}wwwÐÐÐCCC@@@×××äää––– YYY&&&¤¤¤ÄÄÄAAAFFFÔÔÔÇÇÇ666XXX«««ÀÀÀÛÛÛçççòòòñññççç‚‚‚¢¢¢ííí“““qqqeeeYYYsss®®®ûûûÉÉÉ+++888¶¶¶îîîñññÛÛÛããã÷÷÷óóóúúúììì±±±@@@€€€ôôôÌÌÌnnnqqqxxxÄÄÄòòòÛÛÛ```CCC×××æææ¿¿¿«««–––«««···ÓÓÓÑÑÑ>>>666˜˜˜§§§§§§¦¦¦«««½½½«««§§§«««ÀÀÀÕÕÕ¶¶¶///rrrööö¦¦¦FFFÙÙÙÖÖÖCCC___ÜÜÜáááQQQ:::ÈÈÈíííTTTnnn÷÷÷¯¯¯ ¢¢¢óóódddtttÛÛÛííí¡¡¡‘‘‘òòòccc£££ïïï”””¬¬¬÷÷÷ÒÒÒ444  ---HHHddd¡¡¡ÛÛÛùùùÑÑÑ000!!!³³³øøøqqqPPPâââÖÖÖBBB'''ÀÀÀúúúçç犊ŠJJJNNNqqqqqq©©©æææøøø¿¿¿(((   ñññ|||:::ÄÄÄúúúÏÏÏ111cccîîîÀÀÀBBB%%%@@@BBB dddìììddd ---DDD222eeeïï簾¦\\\ëëëÚÚÚDDD±±±õõõ{{{óóóÉÉÉ///YYYäää–––ªªªðððfffjjjÖÖÖõõõ kkkÝÝÝééé[[[iiiëëëÛÛÛGGG ƒƒƒðððððð}}} †††ñññòòò€€€ ¥¥¥ïïïeeeXXXéééàààJJJffföööãããuuu wwwßßßÇÇÇ888¢¢¢ãããWWW```ãããØØØEEE[[[çç碢¢zzzÿÿÿzzzeeeïïï§§§]]]ççç²²²&&&†††úúú©©©:::ÍÍÍñññ€€€ eeeïï簾¦ ***¼¼¼ïïïfff kkkãããçççTTTrrrÞÞÞööö···"""666ËËËæææžžžYYYçççóóó––– aaa×××óóó¥¥¥–––åååzzzEEE;;;III]]]²²²ÿÿÿ¸¸¸!!!eeeïïï³³³"""JJJÛÛÛÚÚÚHHH@@@ØØØéééYYY<<<ÉÉÉÉÉÉ<<<qqqùùùÛÛÛxxxqqqAAA(((yyyùùùyyyhhhõõõ§§§lllòòò®®®!!!fffððð°°°!!!ªªªóóóÄÄÄ,,,rrrøøø¶¶¶$$$XXX˜˜˜dddQQQßßßâââOOO¢¢¢ùùù”””………çççççç«««444~~~èèèñññƒƒƒ000ÆÆÆÿÿÿÁÁÁ&&& aaaÕÕÕîî¸---!!!³³³ùùùÖÖÖÕÕÕÆÆÆÛÛÛèèè÷÷÷ëëë___eeeððð­­­ +++HHHÙÙÙÙÙÙHHHMMMáááßßß„„„KKKMMM@@@[[[ŒŒŒëëëâââJJJ444ÍÍÍóóóèèèùùùÔÔÔÂÂÂGGGyyyùùùyyyfffððð©©©kkkñññ«««OOOàààÙÙÙFFF ‚‚‚óóóîîîlll\\\ççç···+++QQQãããùùùñññsss”””ùùùÊÊÊ---iiiðððêêêëëëîîî———"""íííÓÓÓŠŠŠ³³³øøøÕÕÕGGGtttÜÜÜòòòÈÈÈ@@@¥¥¥òòòÓÓÓÕÕÕÆÆÆ×××ÎÎΚššWWW eeeõõõÙÙÙEEE222³³³QQQGGGÆÆÆÅÅÅ999rrrúúúïïïñññëëëéééÓÓÓéééëëëûûûËËË,,, IIIŸŸŸ   ÐÐÐßßßùùùººº~~~ÿÿÿ~~~fffððð¼¼¼)))YYYäää––– ´´´óóóyyy@@@ÔÔÔððð•••PPPäääçççdddªªª÷÷÷áááúúúÍÍÍPPP¾¾¾ñññ~~~GGGêêêÿÿÿððð………&&&­­­îîîöööõõõìììlllŒŒŒèèèøøø®®®999!!!¦¦¦äääxxxEEE;;;FFF:::&&&ÄÄÄÿÿÿ®®®***CCCÚÚÚñññààà···çççÒÒÒ===YYYéééâââûûûýýýùùùìììâââ···°°°JJJ555rrrÞÞÞæææUUUyyyùùùyyyOOOâââàààYYYyyyóóó«««zzzóóó¶¶¶‹‹‹óóóÐÐÐ888"""¸¸¸ôôô£££ãããéé醆†ÐÐÐêêê•••ããã×××CCC,,,¤¤¤ðððýýýðððXXXCCCÎÎÎùùùõõõ‘‘‘gggæææîîî´´´,,,HHHàààîîî```OOOÙÙÙèèèµµµ888[[[¼¼¼ôôôûûûüüü¿¿¿ eeeïïï···œœœÚÚÚùùùùùùááá¡¡¡WWW...¬¬¬ÿÿÿŒŒŒyyyùùùyyy...ÈÈÈùùùËËËWWW%%%<<>> pppÿÿÿŒŒŒ pppÓÓÓlll bbbÓÓÓŠŠŠ666xxxmmmÿÿÿhhh___êêê999ÆÆÆÐÐÐAAAWWWèèèÊÊÊ666zzzÈÈÈìììîîîãããttt $$$```nnn”””½½½åååUUUiiiñññ¤¤¤!!!ÄÄÄüüüÔÔÔ--- ŒŒŒÿÿÿ©©©˜˜˜ççç[[[ ÷÷÷qqqhhhõõõ´´´$$$777///ÃÃÃîîîaaaIIIßßßÚÚÚDDDžžžñññóóóÑÑÑÕÕÕöööÚÚÚ<<<mmm¿¿¿ôôôñññöööôôôÿÿÿ¥¥¥hhhõõõ§§§’’’ÝÝ݆††!!!vvv+++§§§ïïïeee®®®óóónnn ;;;LLL$$$]]]VVVSSS___ïïïãããTTT bbb¹¹¹ÇÇÇZZZ !!!¯¯¯ðððjjjDDDÔÔÔÔÔÔDDDiii~~~{{{|||hhh\\\ðððòòòªªª???LLL¥¥¥ ®®®ñññúúúììì¹¹¹±±±áááþþþ¯¯¯eeeïï簾¦666MMM!!!¥¥¥õõõiii///ÃÃÃíííaaa :::NNNLLL{{{\\\;;;)))222ÆÆÆããã‘‘‘ÁÁÁîîîäääéééžžž555777OOO###¹¹¹óó󌌌 VVV¸¸¸çççÿÿÿðððsss –––äääYYY BBBÕÕÕÕÕÕBBB¢¢¢ôôôúúúùùùÿÿÿøøø¡¡¡hhhõõõ¿¿¿000|||ÿÿÿîîœVVV%%%---ÕÕÕÿÿÿ¾¾¾eeeððð¿¿¿€€€}}}ppp<<< ''' UUUíí혘˜ôôôppp GGGÛÛÛÖÖÖDDD777ÍÍÍéééâââúúúñññnnn###MMM“““ÒÒÒÃÃÃ888!!!°°°òòòòòòÿÿÿééé¾¾¾ðððóóó¥¥¥000šššÌÌÌ¿¿¿ççç444‰‰‰444222£££ÝÝÝöööãããðððþþþ™™™±±±÷÷÷°°°zzzƒƒƒVVVggg¨¨¨¥¥¥fff/// (((<<>>×××ééé^^^€€€ÿÿÿÓÓÓAAA†††ûûû}}}]]]ìììááᥥ¥   €€€yyy~~~ppp999fffïïï¼¼¼000 CCCeeefffeeebbb×××ÕÕÕDDD999ÍÍÍäääSSStttöööÅÅÅ+++þþþìììEEEeeeïïï§§§›››ýýýèèèúúúïï£999lllñññlllBBBÕÕÕÕÕÕBBB”””ûûû€€€DDDÕÕÕÖÖÖFFF¦¦¦ïïïeee¦¦¦ïïïeeeVVVãã㣣£ cccëëëÑÑÑ<<<   ûûû···888€€€ûûûùùùÚÚÚBBB–––äääYYYiiiéééãããNNNAAAÕÕÕããã___fffõõõÑÑÑkkk‰‰‰ÿÿÿ‰‰‰$$$¹¹¹øøøëëë³³³^^^)))EEE!!!QQQâââÔÔÔAAAZZZäääÓÓÓ???WWWèèèÚÚÚBBBPPPãããÛÛÛHHH   þþþèèèAAA"""zzz÷÷÷¨¨¨¢¢¢÷÷÷’’’§§§íííóóóÒÒÒ\\\ ˆˆˆÿÿÿœœœ EEEÚÚÚÚÚÚEEElllðððlllAAAÔÔÔâââQQQ§§§ïïïeee§§§ïïïeeekkk÷÷÷ÏÏÏOOO»»»ÿÿÿªªªfffñññúúúÎÎΫ««ÄÄÄöööæææÿÿÿ¶¶¶'''¨¨¨òòò   kkklllÇÇÇïï簾¦+++ÅÅÅùùùÆÆÆ‰‰‰pppZZZQQQ---///ÅÅÅòòòêêêžžžNNNBBB„„„ñññkkk000¨¨¨íííöööèèè···ÝÝÝŽŽŽDDDÕÕÕÔÔÔBBB+++RRRqqqeeeYYYqqqooo¸¸¸õõõ²²²WWWãã㣣£ :::ÊÊÊßßßQQQ³³³ÿÿÿâââ/// UUU»»»UUU"""BBBÎÎÎüüü¥¥¥©©©òòòmmmnnnÔÔÔÿÿÿãã㈈ˆnnn÷÷÷ààà………???ÍÍÍÍÍÍ???|||ùùù|||'''µµµëëë```¢¢¢óóóddd§§§õõõhhh<<<ØØØóóóÐÐÐŒŒŒ«««òòòñññcccsssÍÍÍçççõõõðððçç眜œíííððð€€€ ”””óóóñññöööéééõõõÂÂÂ777ZZZÙÙÙôôôúúúóóóèèèááᤤ¤"""999¦¦¦ñññûûûàààÖÖÖÍÍÍööökkkwwwÍÍÍïïïåååìì옘˜777ÆÆÆÆÆÆ777"""ŸŸŸÝÝÝøøøïïïäääøøøòòò÷÷÷ïïïjjj```òòòŸŸŸiii¤¤¤---   þþþÀÀÀ ŒŒŒÿÿÿÕÕÕ²²²ÅÅÅúúúãããPPP‰‰‰ÛÛÛPPP===¹¹¹ùùù÷÷÷```&&&ÀÀÀûûûÿÿÿ[[[‚‚‚‚‚‚^^^ïïï^^^^^^«««000wwwÐÐÐCCCëëë___ [[[ÊÊÊ÷÷÷ùùùúúúõõõ   ///VVVgggdddPPP\\\‡‡‡222(((‚‚‚ªªªÉÉÉÓÓÓ°°°??? <<>>((( 111gggcccOOO >>>BBB888@@@  wwwùùùwww  |||ÿÿÿéééèèèÑÑÑìììõõõÐÐп¿¿lllSSS’’’ÅÅÅõõõïïïãããdddxxx<<>>wwwùùù|||°°°ÿÿÿÉÉÉ222+++ÇÇÇöööyyyƒƒƒÿÿÿ§§§(((µµµýýýúúúˆˆˆ§§§øøø ***œœœøøøÿÿÿôôôXXX(((»»»òòòµµµâââùùùœœœ{{{ÄÄÄïïïùùùÚÚÚxxxƒƒƒÿÿÿäääÕÕÕÛÛÛ³³³|||~~~vvvYYY………ûûûéééÀÀÀ°°°ÖÖÖøøøíííùùùpppiiiõõõ¥¥¥---‚‚‚lllˆˆˆÑÑÑäääUUU}}}úúú——— ;;;ÑÑÑëëëcccoooòòònnn:::ÊÊÊÿÿÿ£££tttæææÛÛÛEEE[[[êêêÚÚÚµµµííí÷÷÷üüüÛÛÛ‚‚‚£££íííëëë^^^DDDµµµéééõõõùùùùùù‘‘‘NNN×××òòòúúúÄÄÄ000šššòòòíííÅÅňˆˆEEE!!!zzzùùù˜˜˜DDDAAA+++µµµâââðððñññõõõÏÏÏ©©©ùùùyyynnnóóó——— PPPggg111 ]]]íííîîî]]]eeeñññÞÞÞnnn333 ªªªÿÿÿÃÃÃFFF'''«««ÿÿÿÀÀÀ(((___åååßßß©©©ÛÛÛ÷÷÷”””333ËËËøøøÿÿÿ÷÷÷®®®ÎÎÎÿÿÿôôôûûûúúú£££lllØØØûûûççç™™™òòòààà€€€$$$"""ÃÃÃüüüËËË:::QQQçççôôôÂÂÂxxxvvv£££–––ŸŸŸ)))yyyùùùyyy MMMffffffggg444‚‚‚ÿÿÿ~~~uuuùùù}}}@@@èèèõõõÊÊÊ‘‘‘¼¼¼ÿÿÿÈÈÈ%%%///ÐÐÐúúúãããÊÊÊ‘‘‘QQQ]]]ïïïùùùÓÓÓ¶¶¶äääýýýûûû   œœœùùùûûûööö¥¥¥###:::rrr}}}hhh444–––¦¦¦«««}}}LLLæææùùù×××ggg›››ïïïëë뎎Ž---%%%óóóæææ]]]TTTëëëùùù÷÷÷çççõõõúúúóóóäääòòò\\\zzzÿÿÿzzzyyyùùùyyyiiiÿÿÿiii hhhÊÊÊïïïúúúëëëëëëfffRRRÈÈÈÛÛÛòòòÝÝÝŸŸŸ"""fff»»»ßßßïïïöööÕÕÕããã´´´+++...ÂÂÂ÷÷÷µµµ%%% ;;;ÄÄÄ›››===ppp¿¿¿eeebbb¸¸¸®®®ÌÌÌùùù–––ccc£££ÉÉÉÑÑѱ±±‰‰‰oooYYY```QQQèèèQQQnnnööönnn[[[222dddyyy```NNN***HHHkkkQQQ+++"""LLLccceee000FFF222444‰‰‰444 000 ---"""¡¡¡ÜÜÜîîîÕÕÕÒÒÒBBB000=== @@@ $$$ˆˆˆ$$$ +++MMMVVVDDD888 TRUEVISION-XFILE.Cultivation_9+dfsg1_UnixSource/game2/gameSource/PlantLeaf.cpp0000640000175000017500000005575410510503417023076 0ustar pabspabs/* * Modification History * * 2006-August-20 Jason Rohrer * Created. * * 2006-September-13 Jason Rohrer * Blurred all channels to deal with pixelation in color bands. */ #include "PlantLeaf.h" #include "features.h" #include "minorGems/graphics/Image.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include "minorGems/util/SimpleVector.h" #include #include Image *getCircleImage( int inSize ) { int textureSize = inSize; Image *textureImage = new Image( textureSize, textureSize, 4 ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // start all pixels as white for( i=0; i<3; i++ ) { for( int p=0; pmX = mX; newWalker->mY = mY; Vector3D ourDirection( mDeltaX, mDeltaY, 0 ); Angle3D spawnAngle( 0, 0, inDirection * mSpawnAngle ); ourDirection.rotate( &spawnAngle ); newWalker->mDeltaX = ourDirection.mX; newWalker->mDeltaY = ourDirection.mY; newWalker->mDeltaAngle = inDirection * mDeltaAngle; newWalker->mDeltaDeltaAngle = inDirection * mDeltaDeltaAngle; newWalker->mStepCount = 0; newWalker->mSpawnIntervalFactor = mSpawnIntervalFactor; newWalker->mSpawnInterval = (int)( mSpawnInterval / mSpawnIntervalFactor ); // impose a minimum spawn interval if( newWalker->mSpawnInterval < 2 ) { newWalker->mSpawnInterval = 2; } newWalker->mSpawnDouble = mSpawnDouble; newWalker->mSpawnAngle = mSpawnAngle; return newWalker; } }; Image *getCellularImage( int inSize, PlantGenetics *inGenetics, Vector3D *outLeafWalkerTerminus ) { int textureSize = inSize; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // greenish hues in center, but allow red or blue too Color *innerColor = inGenetics->getColor( innerLeafColor ); Color startColor; startColor.setValues( innerColor ); delete innerColor; // only pure greens at leaf edge Color endColor( 0, inGenetics->getParameter( outerLeafGreen ), 0 ); // start all pixels as end color for( int p=0; p walkers; // start a walker CellWalker *firstWalker = new CellWalker(); // start in center at bottom firstWalker->mX = textureSize / 2; firstWalker->mY = walkerBoundary; // aim up firstWalker->mDeltaX = 0; firstWalker->mDeltaY = 1; // walker parameters from genetics firstWalker->mDeltaAngle = inGenetics->getParameter( leafWalkerDeltaAngle ); firstWalker->mDeltaDeltaAngle = inGenetics->getParameter( leafWalkerDeltaDeltaAngle ); // thes spawn params allow spawn intervals to get both shorter and shorter // and longer and longer as we add additional branches // more variety firstWalker->mSpawnIntervalFactor = inGenetics->getParameter( leafWalkerSpawnIntervalFactor ); firstWalker->mSpawnInterval = (int)( inGenetics->getParameter( leafWalkerStartingSpawnInterval ) ); // wider possible spawn angles produce more interesting variety firstWalker->mSpawnAngle = inGenetics->getParameter( leafWalkerSpawnAngle ); firstWalker->mSpawnDouble = true; if( inGenetics->getParameter( leafWalkerSpawnDouble ) > 0.5 ) { firstWalker->mSpawnDouble = true; } else { firstWalker->mSpawnDouble = false; } // add to walker set as our first walker walkers.push_back( firstWalker ); // channel where we track paths of walkers double *walkerChannel = textureImage->copyChannel( 3 ); char hitEdge = false; int maxNumWalkers = 1000; char allDead = false; while( !hitEdge && walkers.size() <= maxNumWalkers && !allDead ) { allDead = true; // track new walkers SimpleVector newWalkers; // step each walker int numWalkers = walkers.size(); int w; for( w=0; wmDead ) { allDead = false; int oldX = (int)( walker->mX ); int oldY = (int)( walker->mY ); char spawn = walker->step(); // draw pixel at new location // and check if hit edge int x = (int)( walker->mX ); int y = (int)( walker->mY ); if( x < walkerBoundary || x >= textureSize - walkerBoundary || y < walkerBoundary || y >= textureSize - walkerBoundary ) { hitEdge = true; } else { int pixelIndex = y * textureSize + x; if( ( oldX != x || oldY != y ) && walkerChannel[ pixelIndex ] == 1 ) { // hit already filled area // and not simply standing still and hitting our // own last filled pixel // die walker->mDead = true; } else { // fill in image channels[3][ pixelIndex ] = 1; // walkers lay start color channels[0][ pixelIndex ] = startColor.r; channels[1][ pixelIndex ] = startColor.g; channels[2][ pixelIndex ] = startColor.b; // track history in walker channel walkerChannel[ pixelIndex ] = 1; } } if( spawn ) { newWalkers.push_back( walker->spawn( 1 ) ); if( walker->mSpawnDouble ) { // spawn another in opposite direction newWalkers.push_back( walker->spawn( -1 ) ); } } } } // add new walkers int numNew = newWalkers.size(); for( w=0; wmX; double deltaY = startY - walker->mY; double distance = sqrt( deltaX * deltaX + deltaY * deltaY ); if( distance > maxDistance ) { maxX = walker->mX; maxY = walker->mY; maxDistance = distance; } } // (0,0) at bottom/center of texture outLeafWalkerTerminus->mX = ( maxX - (textureSize / 2) ) / textureSize; outLeafWalkerTerminus->mY = maxY / textureSize; // delete walkers for( w=0; wr; channels[1][ neighborPixelIndex ] = fillColor->g; channels[2][ neighborPixelIndex ] = fillColor->b; } } } } // end loop over deltaX } // end loop over deltaY } // end check for full pixel } // end loop over all x in image } // end loop over all y in image delete fillColor; // copy new image into main channel memcpy( channels[3], newChannel, sizeof( double ) * pixelsPerChannel ); delete [] newChannel; } // end loop over expansion steps delete [] isInNeighborhood; delete [] pixelDone; return textureImage; } PlantLeaf::PlantLeaf( PlantGenetics *inGenetics ) { int textureSize = 64; Image *textureImage = getCellularImage( textureSize, inGenetics, &mLeafWalkerTerminus ); // count filled pixels to compute leaf area double *alphaChannel = textureImage->getChannel( 3 ); int pixelsPerChannel = textureSize * textureSize; int numFilled = 0; for( int p=0; pfilter( &blur); mTexture = new SingleTextureGL( textureImage, // no wrap false ); delete textureImage; } PlantLeaf::~PlantLeaf() { delete mTexture; } double PlantLeaf::getLeafAreaFraction() { return mLeafAreaFraction; } void PlantLeaf::draw( Vector3D *inPosition, double inScale ) { Angle3D rotation( 0, 0, 0 ); draw( inPosition, &rotation, inScale, NULL ); } void PlantLeaf::draw( Vector3D *inPosition, Angle3D *inRotation, double inScale, Vector3D *outLeafWalkerTerminus ) { /* SimpleVector growthTips; SimpleVector tipAngles; // draw and insert one segment to start double currentLength = inScale / 2; Vector3D *start = new Vector3D( inPosition ); Vector3D *end = new Vector3D( 0, 0, 0 ); end->mY += currentLength; end->rotate( inRotation ); end->add( start ); glBegin( GL_LINES ); { glVertex3d( start->mX, start->mY, start->mZ ); glVertex3d( end->mX, end->mY, end->mZ ); } glEnd(); delete start; // end is first tip growthTips.push_back( end ); tipAngles.push_back( new Angle3D( inRotation ) ); Angle3D incrementAngle( 0, 0, 0.4 ); int numSteps = 5; while( numSteps > 0 ) { currentLength = currentLength / 2; SimpleVector newTips; SimpleVector newAngles; int numTips = growthTips.size(); int i; for( i=0; iadd( &incrementAngle ); angleB->subtract( &incrementAngle ); Vector3D *tipA = new Vector3D( 0, currentLength, 0 ); Vector3D *tipB = new Vector3D( 0, currentLength, 0 ); tipA->rotate( angleA ); tipB->rotate( angleB ); tipA->add( oldTip ); tipB->add( oldTip ); // draw lines from old tip to new tips glBegin( GL_LINE_STRIP ); { glVertex3d( tipA->mX, tipA->mY, tipA->mZ ); glVertex3d( oldTip->mX, oldTip->mY, oldTip->mZ ); glVertex3d( tipB->mX, tipB->mY, tipB->mZ ); } glEnd(); newTips.push_back( tipA ); newTips.push_back( tipB ); newAngles.push_back( angleA ); newAngles.push_back( angleB ); delete oldTip; delete tipAngle; } // replace with new growthTips.deleteAll(); tipAngles.deleteAll(); numTips = newTips.size(); for( i=0; isetCoordinates( &mLeafWalkerTerminus ); outLeafWalkerTerminus->scale( inScale ); outLeafWalkerTerminus->rotate( inRotation ); outLeafWalkerTerminus->add( inPosition ); } if( Features::drawNicePlantLeaves ) { mTexture->enable(); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex3d( corners[0].mX, corners[0].mY, corners[0].mZ ); glTexCoord2f( 1, 0 ); glVertex3d( corners[1].mX, corners[1].mY, corners[1].mZ ); glTexCoord2f( 1, 1 ); glVertex3d( corners[2].mX, corners[2].mY, corners[2].mZ ); glTexCoord2f( 0, 1 ); glVertex3d( corners[3].mX, corners[3].mY, corners[3].mZ ); } glEnd(); mTexture->disable(); } else { glBegin( GL_LINE_LOOP ); { glVertex3d( corners[0].mX, corners[0].mY, corners[0].mZ ); glVertex3d( corners[1].mX, corners[1].mY, corners[1].mZ ); glVertex3d( corners[2].mX, corners[2].mY, corners[2].mZ ); glVertex3d( corners[3].mX, corners[3].mY, corners[3].mZ ); } glEnd(); } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/features.txt0000640000175000017500000000024610544402364023073 0ustar pabspabslargeWindow 1 drawClouds 1 drawSurfaceNoise 1 drawNiceCircles 1 drawNicePlantLeaves 1 drawComplexGardeners 1 drawSoil 1 drawWater 1 drawShadows 1 drawComplexPortal 1 Cultivation_9+dfsg1_UnixSource/game2/gameSource/testPlantLeaf.cpp0000640000175000017500000000406610531057777024004 0ustar pabspabs #include "PlantLeaf.cpp" #include "PlantGenetics.cpp" #include "Genetics.cpp" //StdRandomSource globalRandomSource( time( NULL ) ); StdRandomSource globalRandomSource( 5002 ); //#include "minorGems/io/file/File.h" #include "minorGems/io/file/FileOutputStream.h" #include "minorGems/graphics/converters/TGAImageConverter.h" #include "minorGems/graphics/converters/JPEGImageConverter.h" #include "minorGems/graphics/converters/unix/JPEGImageConverterUnix.cpp" #include "minorGems/graphics/filters/BoxBlurFilter.h" int fileCount = 0; int main() { int textureSize = 64; int pixelsPerChannel = textureSize * textureSize; //PlantGenetics genetics; for( int i=0; i<50; i++ ) { PlantGenetics genetics; Vector3D terminus; Image *textureImage = getCellularImage( textureSize, &genetics, &terminus ); BoxBlurFilter blur( 1 ); textureImage->filter( &blur); char *fileName = autoSprintf( "leafTexture_%d.tga", fileCount ); fileCount++; File outFileB( NULL, fileName ); delete [] fileName; FileOutputStream *outStreamB = new FileOutputStream( &outFileB ); /* JPEGImageConverter converter( 100 ); // make a 3 channel image showing only alpha data Image *outImage = new Image( textureSize, textureSize, 3 ); memcpy( outImage->getChannel( 0 ), textureImage->getChannel( 3 ), pixelsPerChannel * sizeof( double ) ); memcpy( outImage->getChannel( 1 ), textureImage->getChannel( 1 ), pixelsPerChannel * sizeof( double ) ); memcpy( outImage->getChannel( 2 ), textureImage->getChannel( 3 ), pixelsPerChannel * sizeof( double ) ); converter.formatImage( outImage, outStreamB ); delete outImage; */ TGAImageConverter converter; converter.formatImage( textureImage, outStreamB ); delete outStreamB; delete textureImage; } return 0; } Cultivation_9+dfsg1_UnixSource/game2/gameSource/fontArt/0000750000175000017500000000000011401021141022105 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/gameSource/fontArt/sampleFont.tga0000640000175000017500000060007410531374030024735 0ustar pabspabs* CREATOR: The GIMP's TGA Filter Version 1.2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿCultivation_9+dfsg1_UnixSource/game2/gameSource/fontArt/fontArt.xcf0000640000175000017500000020774010544273543024265 0ustar pabspabsgimp xcf file–ðàÐÀ° €p`P@0 ðàÐÀ° €p`P@0 BBIK§ New Layerÿ     ³ÏIgIs# r}÷"á2´@…HçH÷III'I7IGIW$ÿ)ÿ ÿ)ÿ ÿ)ÿ ÿ)ÿ ÿ)ÿ ÿ)ÿ ÿ)ÿ;ÿ;ÿ;ÿ;ÿ9ÿ9ÿ9ÿhÿ&ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿÿþÿÿÿÿ ÿÿÿÿÿ ÿÿÿþÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿ ÿÿÿÿÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿÿÿ ÿB$÷" (ûa,÷0­xXX(ûlô¯÷\ëÞF:ÅÅ:(û{ú¼$÷?ÍÈ;:ÇÇ:(û[å˜÷@ÐÐ@JßßJ(ûeï¦÷7ÆÆ7,­­,(ûqø³"÷ 88 &&(ûYä–:ûXå•:ûx6:ü :ùA$8ù‰Ô¨X8ù“çߣ"8ù"YS-jû '÷!÷"‡Ÿe^2üJôRn†mzfAó*€£˜³¥•«p$õ8´ûòÈðË=ûXíkó4œåóÿòúðÖa ó ®øôåøïåùó¬$ô@Ïôñ™YÏù¨ûlð ó*¾öè¿–pÊì“ó>ÑÓ‡[qeY‰Ïë]ô±øÝ{ {ñìXû]èóVåºd) „ó©û A6 û#²ñkóRçæn 2¥æ÷uú…ÿ˜ ó/¨X (¨ò™÷.7dÁæZóhò±!GÊûêú¡úžøvó"`ªíè\õi}ŸÃÃâòßGó„ÿ“QÒÿé¥îØ?û>Øí^ ÷Œìëà{õ[ÿùôöÎÒÀŒójñ~kÔöÄYNÙÕBûBÓÖE÷9¥ïíI õiuni>bËæSójòÔêùÂ9DÕÔBû>ÍÀ4øEÎóé„û£ùyó<Üüöº8 tèÓ;ûhõÄ0ø QÄÿÑiù90û›ùzó €òí3P€àû¬û_ë¥ãFß÷Ý¡~yy~r; XÇÇžuR=MbÌñhô$»øéÂäéôÆ8û/¨X ã|ü÷öûùÿùùÿ÷¾( XÒöóóáÊáâùØ:õ0£âçî´y(û"ãFÖͯ|~yy~r;Cjy¯ÑÈáÖÅY øOZ_%ú 63 ÷>;MB+A5ÿ;ÿ;ÿ;ÿ;ÿ;ÿ;ÿKÿþÿÿ ÿÿÿþÿÿ ÿÿ ÿÿþÿÿ ÿÿ ÿÿþÿÿ ÿÿ ÿÿþÿÿ ÿÿ ÿÿþÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿÿþÿÿÿ ÿÿÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿÿ ÿ ÿÿÿ ÿÿÿÿÿe5û2C :û-³ÕG:û#¬æ\:û#¶øs:û"²ðf:û3ÆáP:û ;LJø(( ú7ü @ ø;¾;;¾;ò!1feehmšÇƒûhçPò$moQÖí„;C ÿ¡D;FÙ㸳àÔÇÛzó$Ìþüûðúð×”#ùGÕøšämôæ¹ÆØèþìÔÉX :Ò÷ÿáãÔÎòé’óVí÷ÊnÁå÷‡ ø:ÇÿÄ0äTëùïáÜèþìÔÉX %¤Ù¯WMC>”ëí\óVå´9 +‘ñ£ø6½ÿÇ:ëSseRG ÿ¡D;'?û²ÿ„ûiñ®!úoö¦ù%°ìÍD üyùy ú¤ùxó_ìÍV#/Z´ô—ù uìÓY üyùyó?EBF]n®ëð`ó/ÃïÔ ·Âã÷ê\ú/Äì üzÿzó‚ÍÚÕÙëñÿæ’ô YÔòçøêÔ™XûTíÂ4üQèQó‚ÍÚÕÕÚ®Rö =j\p[?ûU2ü @ ö?EBDAdÿ ÿ+ÿ ÿÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿÿ ÿ ÿÿÿ ÿ ÿÿÿ ÿ ÿÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿþÿÿÿ ÿ ÿ ÿÿ9ÿ ÿ%ÿ ÿ% ÿ ÿ$ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿ# ÿ ÿ#ÿÿ ÿÿÿÿÿÿ ÿ ÿÿ ÿ ÿÿ ÿÿ ÿÿÿdû@ û*ûžÙJ ú€¡0ø  úîèQ ú&¼÷šóXX2„,"û ù^íò’ ùkå×Ió7Ä×_ó”QµS ûXXú †î¦ ú °ð•ó²ÿÄ’öªjÓ÷‚ û<ÉÉ<ú ²ùy újìØ>óZØõÙöÖÛÿÁ0ûBÕÕBû¥ïeûDØÝKôÊêýÿþõËgó?EwääwB?û–äYû1Èöiû’íüÿÿûúùÕ6ó‚ÍÚßøùßÕÖoû!³øqû ¯ðgô)šóþþûûìžó‚ÍÚßøùßÙæ¨"ú¥ñr û+¿òlóDÁõÙôÎÜûÏJó?EwääwEU/ù Šó³0 ù•íëUó"ªç¾‰õ©tàñÂ-ûDÔÔDøiñì¾G ù£óì‡óNq/iï¦%bÐßMûAÓÓA ùàùÉ-ùlðñ‘ó [è™ Huû7ÆÆ7 ùh×àMú €éž ÷x6 û 88 û HuúQ ü ü 8úø  &ö+…««³„5 ô"€‡²¦‘œ["$ô¯úûôûùÊ|ó—ûïøïèÿç»f#ôFØØ•v®Òíò‚ ó*¸é”seb¥ÓùëQù)qZ4ôo÷À+9¤ô¨óLàäPD¸÷pù¡øçº.ôWèÙ—lQQ³ù¤ú@Óß[û„ÿ{ù„Ïѳ.ô)ÁöûòâÝóñvó¬öÈfO=Iaºùtù6<1ò_áùÞÃÔÔâõÂ2óEÓÿéãÉÛëú÷q"ò)Áù³E+AA^Ôÿ— ô?ŸÉàÉØâ÷Ö9"ú8ÆÕJú€ò¤õ5K=`Ïú‘ú2v= úJàç`úoö¦úqðçTú0ÐúÍ/ò,Âð¼WR?=¶õ˜õ ?P>IkËõ úiúüÚ:ò`ßõøããÔÒ÷ðdõ6ÍãÊÛêûÜDúƒ´Šô Fu« Ïæðá„öŒÙÈ×ѹR û÷=WdD÷C;F‹çÛGò >UpYeqYeqOô7ÌíŒHÙÙHò6Éå÷äïøäïøÝ` û†–$û>ÌÇ;òq’±–¦³–¦»¹Q ô–í×Bò "%-ø E°òõ¥ò,J;CM;CM;:÷AØÿߢ0ò&­ÞÇÔáÇÔáÇÅX ø vëÂSò&­ÞÇÔáÇÔáÇÅX ú P«Aò,J;CM;CM;:û- 9ù/¡œ68ù «þù 8ùRÏ *9û %ÿ ÿ)ÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿÿÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿÿ ÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿÿ ÿ ÿÿÿ ÿÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿÿþÿÿ ÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿÿÿ ÿÿ ÿÿþÿÿ ÿÿ ÿ ÿ ÿÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿþÿÿþÿÿ ÿÿ ÿþÿÿÿ ÿÿ ÿþÿÿÿÿ ÿ%ÿÿÿÿÿÿÿ ÿÿ ÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿþÿÿ ÿ ÿÿþÿÿÿþÿÿÿÿ ÿÿþÿÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿ ÿÿÿÿÿ ÿÅ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿÿþÿÿ ÿ ÿÿþÿÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿ ÿÿÿÿ ÿÿÿÿü ú]1.û Qõk÷Ή‡hV;õDcZqg\BúTßí_ôŽÿúíÿòåÒŽ ô cÞîåøðëÒ‚ù ¼üôfó‚ú°f‘«Óìï’ó=ÊôÊš³µÚ΂ù[îûüó›ôpB…ÛãQó¦îÝ["%A?÷ •úÓâÒNó"µøq-ZÅÿ~úVèëŠöEÝóŠÇù·ó-·Þ~‚£Áãóò`úsùÆ3õªÿ± kçßNóLàëôúùöìôúÅ$ûeï¥ô ‹óîz2HÑúžóCÔ׈~ylb©ôëUûYä–óUïþòÈįÖýÛ9û:ÇÇ:ù"°ù…ûrø¾&û ìªûøâÏõïòüõzMââMú ‹ö¤ôgñÝJ<„Sâ`åë”M@hepÈúÉ*D××D lÚù ô6Ðï¥7>}×øµ"ú#¸òªëXÞÖDAÓÖwyнçÿÇ0ôtíðÎØòûÔQúTçÜQí)²ÜL7ÆÚÿùùÿûÝ¢2özØ÷ðÚ’8û}Sî-I 8Gowy€Hù >pc; ü ,ö $!÷,:! ô aÆ¢³¥–²˜`ö;DNn@ö2ÂÛ¨³¦ŠC ôLkkYkkOó$Æüíøïäøôïsó(ŽÔÕãöÚ£]õdôûëøò÷Øe ôáññäñòá ó¢í“qeYs®ûÉ+ã8¶îñÛã÷óúì±@€ôÌnqxÄòÛ`ôC׿¿«–«·ÓÑ>û!³øqûPâÖBã'ÀúçŠJNqq©æø¿( ñ|:ÄúÏ1ôcîÀB%@B û¥ïeûXéàJúföãu ówßÇ8¢ãWú`ãØEû[ç¢ó–åzE;I]²ÿ¸!ûeï³"õJÛÚH@ØéYû<ÉÉ<÷qùÛxqA(ó!³ùÖÕÆÛè÷ë_ãeð­ +HÙÙHMáß„KM@[ŒëâJö4ÍóèùÔÂGô¥òÓÕÆ×ΚW ãeõÙE2³QGÆÅ9rúïñëéÓéëûË,ö IŸ Ðßùºõ!¦äxE;F:ã&Äÿ®*CÚñà·çÒ=Yéâûýùìâ·°J÷5rÞæUûHàî` ãOÙèµ8[¼ôûü¿ eï·œÚùùá¡W. ú¬ÿŒû;ÈÍ? ã `Ôò‡QkÒüþÍqø³(3w¹ëúÑÂlôXkKBDXÃù™ û:ÅÅ: äkàõûàâùøû÷‹ RÝŒ!rÄÐùæLôJíòáÕÔáôîaûXX îF„ÉÔØÆ‡ÁøÜ<+ŸQ ù)F›Ä;õzÐÚÕÔÛÏwû ð+AB+ 8¦Ž"û0 ÷ 6DBDD6 û$û"û û/¨X ûXXû_êû9ÆÐAûhõ´$ ú7û/Ãîaú_ïãT ø b¹ÇZ û!¯ðjú#¹óŒ öV¸çÿðs û–äY ú  ú4‰4õ2£Ýöãðþ™÷±÷°zƒV÷g¨¥f/ü ö³ÿç·xàþ£öÉÿüúÿíö„ïúòùì¨"õGרt/ßÿ¶õžï¦~¸òé`õRåî¥v¥XõuöÀ(Eëý¬ õ«ñk"¦ï¯õ‰á˜ ô‚ùŒMêû¼õ«ñkPßßKú>×é^ó û·8€ûùÚBõ–äYiéãNúAÕã_òfñúΫÄöæÿ¶'õ¨ò klÇï¦õ+ÅùƉpZQ-ósÍçõðçœíð€ õ”óñöéõÂ7õZÙôúóèá¤"ô/VgdP\‡2ö(‚ªÉÓ°?ö (ù 1gcOõ >B8@ ú ó|ÿéèÑìõпlõS’Åõïãdôxÿ9ÿÿ ÿ ÿ ÿÿ ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿþÿÿÿþÿÿ ÿ ÿþÿ ÿ ÿ ÿ ÿþÿ ÿ ÿ ÿ ÿ ÿ ÿÿþÿÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿÿÿÿ ÿ6ÿÿ<ÿ<ÿ ÿÿÿ ÿ ÿÿÿÿÿÿÿþÿÿÿÿÿÿÿþÿÿÿþÿÿÿÿÿÿÿþÿÿÿÿÿÿÿþÿÿÿþÿÿÿÿ ÿÿ ÿÿþÿÿÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿ;ÿö :bfZK:õ $!! ô:ÑîôéÝÍ–N,ò#swYeqYeqR+ò aÆ¢³¥–³¥w4ó :dfheehBJ,óaîÖ³ÀÛèòØÅlò ‘ùúåïøäïøÝŸ"ò$Æüíøïäøñ÷ø²"ó@ÕïðõïïõÐÞ­&ãZå 6-Hp¼Óùê]„øå¢¦³–¦³ŒQò¢í“qeYqi~§^òƒùΩ§§­ÄÆÞ­&òqø³!'G°í§óqôÑF"" û!³øqû ò¯õ† 08J,ûeï¥úYæäRôHÚËL!! ö§òžhecUû2Êón ûYä–û%¶îcôIßí²³¥–³ Xö¤öñõïîìMñ?ÐØF::PM:@ ûqø³!û§õhô\òüìøïäøì¨"ö!·úȧ¦¥!ñhõÈ2 XÅÉéçËÒCûeï¥û ®ñgôfñÛzqeYqa/ö«òwñeï­ XÅÒ÷ùÖÒCûYä–óEÚól„ó¯ û%ºöx ñeï§?’òÑ[@ ûqø³!ê5ªùÚ>,Çûž3ABEDBB+û5àþ¤ óhõºbC:MY¾ó„ ãeïª3,DrÇé×b Iàñ·»ÔÕÚÔÕÖ¨&û.Òý‘ ô\íääÔÇâÜïðcóNÛ°¥«½ÖîóÒ` òløþìîâ×ÚÔÕÖ¨&û¦ïe ô|´àÔÇâÕÈ¡$ô+¬ÄìõèØØ™C ò:ÉÕzcQEEDBB+û X¨/ ô*LC:MC2õ':`gXG>û// û"kü  û é[eheeheefZJ22„,û 88 ú[™I û 6ä6 xçïõïïõïïôéÛ¶/ ‹ó”û7ÆÆ7ú"²ñÀ,û2UûDÔ}äwÐC6˜§§¦«½«§«ÀÕ¶/rö¦ûFÙÖCú_ÜáQú:ÈíTûn÷¯ ë¢óddìd -D2ûeï¦û\ëÚDó±õ{óÉ/ûYä–ûªðfüzÿzûeï§û]ç²&ó†ú©:Íñ€ ñeï¦ *¼ïfüyùyûhõ§ûlò®!õfð°!ªóÄ,ñrø¶$X˜dQßâOüyùyûfð©ûkñ«õOàÙF ‚óîlñ\ç·+Qãùñs”ùÊ-ü~ÿ~ûfð¼)ûYä–ö ´óy@Ôð•ñPäçdª÷áúÍP¾ñ~üyùyóOâàYyó«÷zó¶‹óÐ8ò"¸ô£ãé†Ðê•ã×Cüyùyó.ÈùËW% ûWèÊ6özÈìîãt ÷$`n”½åU ûIßÚDöžñóÑÕöÚ<õm¿ôñöôÿ¥ ûDÔÔDöi~{|hö\ðòª?L¥õ ®ñúì¹±áþ¯ ûBÕÕBö¢ôúùÿø¡öhõ¿0ô|ÿîœV%-Õÿ¾÷ (wù|ô°ÿÉ2+Çöyñƒÿ§(µýúˆ§ø û}ú— ö;Ñëcoònõ:Êÿ£tæÛEñ[êÚµí÷üÛ‚£íë^÷eñÞn3 ôªÿÃF'«ÿÀ(ö_åß©Û÷”ò3Ëøÿ÷®Îÿôûú£÷/ÐúãÊ‘Qô]ïùÓ¶äýû ÷œùûö¥#ò:r}h4–¦«}÷RÈÛòÝŸ"õf»ßïöÕã´+ø.Â÷µ% ú ø*HkQ+ö"Lce0F2ú4‰4:ü ÿÿÿ ÿÿ ÿÿÿ ÿÿ ÿÿÿ ÿÿÿÿÿÿ ÿ ÿÿÿÿÿ ÿ ÿÿÿ ÿ ÿÿÿ ÿ ÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿþÿÿ ÿÿÿÿ ÿ ÿÿÿ ÿ ÿÿþÿÿÿÿ ÿ ÿÿÿ ÿÿÿÿEÿÿÿ ÿÿ ÿÿÿÿÿ ÿÿÿÿÿ ÿÿþÿÿÿÿÿ ÿÿþÿÿÿ ÿ ÿÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿÿÿÿÿÿÿÿ+ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿ ÿ ÿÿþÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿÿ ÿÿÿÿÿ ÿÿÿÿÿÿþÿÿ ÿÿÿÿ ÿ ÿÿÿÿ ÿÿþÿÿÿÿÿÿÿÿÿ ÿÿÿÍÿ4ÿÿÿÿÿÿÿÿÿ ÿ ÿÿþÿÿ ÿ ÿ ÿ ÿÿ ÿÿÿÿ ÿÿÿ ÿ ÿÿ ÿ ÿÿ ÿÿþÿÿÿ ÿÿÿÿ ÿ'ÿ'û&&û ö::MC6( û"ü û,­­,ûXXö XÅÊêÜÅ®- û-¥húA¬H ûJÞÞJû:ÅÅ:ö XÅØûòÚÜI ûPßÊ:ú ™ø«û $û=ÉÉ=û:ÇÇ:ö:rãÖoL ûIÛ×Fópí•G†¶|ûIÛ×FûMââMûNãâMû;ÆÆ;ó œÿž(K˜Ùôò›óYèÚhMC8E[ÖÔCûCÔÔCûHÙÙHó”줺ÚöåÀŒ-óiôíÎâÔÆÛ¿ÚÊ:û:ÇÇ:ûHÙÙHô´ûøÿß³c)óoøëÍâÔÈçðõåKûMââMû;ÆÆ;÷"Áþüæv$òxô®FMC:UèÊ6ûLÞÖBûûHÙÙH÷šïëù¹1úŸõ{ûcî¬ûiöÈ1û!ŒŒ!úgäÛGõ!®ñ­êíÄeû¬ómûhõ§ûeï­ óJÞÞJ`Õ÷Ä-ô=Ûü„áÿê¢'û#ªåZû_ëöB›ôÉb+ ó@Ôä„l{£ãúÔNôVíù€N­æî£û,³ÕGû/¨X õ‚Òòþùã¶>ô´ÿéòõô÷ÒRôYéë[†ïòeû2C û"õ‚ÎâîÚÚÞr õ2“’©Ÿwj1û/©}ûŽíXõ?HQ;FMù û"üJDü ö/_fZK:úr^ û*1 í 88 "¨êôéÝÍ—oLù@×ä– ûYú&¤ÄAìFÔÇ6X«ÀÛçòñç‚ùtÛí¡û‘òcú£ï”ì¬÷Ò4  -Hd¡ÛùÑ0ójÖõ kÝé[ñiëÛG ƒðð} ù†ñò€ ô kãçTrÞö·"ó6ËæžYçó–ù a×ó¥õ¢ù”…çç«4ô~èñƒ0ÆÿÁ&ø aÕî¸-öiðêëî—"ôíÓŠ³øÕG÷tÜòÈ@÷Gêÿð…÷&­îöõìløŒèø®9ø,¤ðýðXøCÎùõ‘øgæî´,÷!°øãÜî„ ø öéù7ÔøÊ:õ…îé€óÛQ÷9£éØnó{ùøÞe?CM6ó säö¤-Àø¶<÷@Âòèq óQäëôôñÍÔá½zóCÛñ¿/EÅÙ£#øl˜âÑq ó FT|¶âÈÔá½zó2³‹/6‹Â8ùl|‚H ö!I;CM6û û2ûMü- ú7ü  ûpÿŒ úpÓl ú bÓŠû6x ûiñ¤ ú!ÄüÔ- ú Œÿ©û˜ç[ ûhõ§ ú’݆ ú!v+û§ïe ûeï¦ ú6 ûM!û¥õi öeð¿€}p< û'  ûUí÷˜ôp õqúôöûúôÔY û)y! ûbî¤öˆû B–8ô™ýûÊŸÕðÍ6ûé­= ûeï§÷€û˜xÐú— ô¸ýØ<SÔú{ûþèD ûhõ§÷x÷åðíÏDó ¶õ~†ôˆ ûŸÿêB ûeï¦ø°ÿýô¸F û9ÍäSûtöÅ+ûþìE ûeï§ö›ýèúï£9ûWèÚBûPãÛHû þèAõ"z÷¨õ¢÷’§íóÒ\ ûW㣠û:ÊßQû³ÿâ/õ U»U"BÎü¥ô©òmnÔÿãˆû`òŸûi¤-û þÀõ ŒÿÕ²ÅúãPô‰ÛP=¹ù÷`û\)û"û1¬<ö2¾ëñæãr ô QŸ+{“$üø û"üÌü 3ûVVûc*ûR$ü(ö/\U90 óJìê‰]Öo õYî¨ J½:ô"¨éçÆÄ¬œ‚ƒó‰êë†-kÒÿrõPããR.½ïeôX¬ÛÐõòÿüüõ’ñãÉ’ì÷Í2õ!¹÷Œ œ÷×<ô !E@k†ÏññeöˆÕúöö´;õ}öÍ?säÝg ÷ /pÂôì–÷*œøÿôX÷(»òµâùœõ{ÄïùÚxöDµéõùù‘÷N×òúÄ0ôšòíňE!ólØûç™òà€$ù"ÃüË:ôQçôÂxv£–Ÿ)óLæù×g›ïëŽö-%óæ]ôTëù÷çõúóäò\ó;Ä›=p¿e÷b¸®Ìù–ôc£Éѱ‰oY`û 0û -ø"¡ÜîÕÒB ù0=&ø+MVD8 &ÿ ÿÿÿÿÿ ÿþÿÿÿÿÿÿ ÿÿÿ ÿÿ ÿÿþÿÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿþÿ ÿÿþÿÿÿ ÿþÿ ÿÿÿÿ ÿÿÿÿ ÿÿÿÿ ÿÿþÿÿÿÿ ÿþÿÿ ÿ ÿÿ ÿþÿÿ ÿ ÿÿÿþÿÿÿ ÿ ÿÿÿþÿÿÿ ÿ ÿÿÿþÿÿÿÿ ÿ;ÿ;ÿ;ÿ ÿÿÿþÿÿ ÿÿÿ ÿ ÿÿÿ ÿ ÿ ÿÿ ÿÿÿ ÿÿ ÿÿÿ ÿÿÿþÿÿþÿÿÿÿÿÿÿÿþÿÿþÿÿÿÿÿþÿÿÿÿÿþÿÿÿÿ ÿÿÿÿþÿÿÿÿ ÿÿÿÿþÿÿÿÿ ÿÿÿÿþÿÿÿÿÿ-ÿû û"ûûEû"û‚‚ ú _ÄVû'¢­.ûZæ™û X¨/ù :Z1û?ÍÍ? ò·ÿßGl÷üˆô’ýó_ë_÷YÙ鹩:ûEÚÚE ò#ÁýûÀ'“ýü£ôŒþû¡§õh÷,ÉøÓ¼ÿÆ+ûIÚÓA ò0Åòèô2Ñüø©ôüñåX§ðgõ^çÕZN×ù— ûaîÃ/ òLáæµû×ÿ=ÿjÿÿ ÿ ÿÿÿ ÿ ÿÿÿ ÿ ÿÿÿÿ ÿ ÿÿÿüÿÿ ÿ ÿÿ ÿ ÿ ÿÿÿþÿÿÿÿÿÿÿÿÿ&ÿÿÿÿ+ÿÿ,ÿÿÉÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿÿÿüÿÿ ÿüÿÿÿÿÿüÿÿ ÿüÿÿÿ ÿ ÿ ÿüÿÿ ÿÿÿüÿÿ ÿüÿÿÿüÿÿÿÿüÿÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿFÿÿÿ ÿÿ ÿ ÿüÿÿ ÿÿ ÿÿ ÿ ÿüÿÿIÿ ÿ ÿ ÿ ÿÿÿ ÿ ÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿÿÿÿ ÿ ÿ ÿ ÿ™ÿÿ8ÿÿÿ ÿÿÿÿ ÿ ÿÿ ÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿüÿÿ ÿÿÿÿÿÿÿüÿÿ ÿÿÿÿÿÿÿüÿÿ ÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿ ÿÿ ÿ ÿ ÿ ÿüÿÿÿ ÿ:ÿ>ÿ=ÿjÿÿ ÿ ÿÿÿ ÿ ÿÿÿ ÿ ÿÿÿÿ ÿ ÿÿÿüÿÿ ÿ ÿÿ ÿ ÿ ÿÿÿþÿÿÿÿÿÿÿÿÿ&ÿÿÿÿ+ÿÿ,ÿÿÉÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿÿÿüÿÿ ÿüÿÿÿÿÿüÿÿ ÿüÿÿÿ ÿ ÿ ÿüÿÿ ÿÿÿüÿÿ ÿüÿÿÿüÿÿÿÿüÿÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿFÿÿÿ ÿÿ ÿ ÿüÿÿ ÿÿ ÿÿ ÿ ÿüÿÿIÿ ÿ ÿ ÿ ÿÿÿ ÿ ÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿÿÿÿ ÿ ÿ ÿ ÿ™ÿÿ8ÿÿÿ ÿÿÿÿ ÿ ÿÿ ÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿüÿÿ ÿÿÿÿÿÿÿüÿÿ ÿÿÿÿÿÿÿüÿÿ ÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿ ÿÿ ÿ ÿ ÿ ÿüÿÿÿ ÿ:ÿ>ÿ=ÿjÿÿ ÿ ÿÿÿ ÿ ÿÿÿ ÿ ÿÿÿÿ ÿ ÿÿÿüÿÿ ÿ ÿÿ ÿ ÿ ÿÿÿþÿÿÿÿÿÿÿÿÿ&ÿÿÿÿ+ÿÿ,ÿÿÉÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿÿÿüÿÿ ÿüÿÿÿÿÿüÿÿ ÿüÿÿÿ ÿ ÿ ÿüÿÿ ÿÿÿüÿÿ ÿüÿÿÿüÿÿÿÿüÿÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ ÿ ÿüÿÿ ÿüÿÿ ÿüÿÿ$ÿÿ ÿ ÿ ÿ ÿÿ ûÿÿ ÿ ÿÿ ÿÿ ÿ ÿ=ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿÿ ÿ ÿ ÿÿ ÿÿ ÿ ÿ ÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿ–ÿÿÿÿÿÿ ÿÿÿÿüÿÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿ ÿ ÿÿÿÿ ÿ ÿ ÿÿÿ ÿ ÿ ÿÿüÿÿÿ ÿÿ ÿÿ ÿ ÿ ÿÿÿ ÿ ÿÿ ÿÿ ÿ ÿÿÿÿ ÿ ÿþÿÿ ÿ ÿÿÿüÿÿÿ ÿÿÿ;ÿ^ÿ=ÿ*ÿ<ÿ=ÿÿ ÿ*ÿÿ ÿ ÿÿÿÿÿüÿÿ ÿ ÿÿÿÿ ÿ ÿÿÿ ÿÿ ÿúÿÿÿÿ ÿÿÿÿ ÿùÿÿÿÿÿ ÿÿÿÿ ÿÿÿ0ÿÿ0ÿ ÿ=ÿ«ÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿÿÿüÿÿÿÿÿ ÿÿ ÿÿÿÿ ÿÿÿ ÿ=ÿ=ÿ=ÿ=ÿ=ÿ$ÿÿ ÿ ÿ ÿ ÿÿ ûÿÿ ÿ ÿÿ ÿÿ ÿ ÿ=ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿÿ ÿ ÿ ÿÿ ÿÿ ÿ ÿ ÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿ–ÿÿÿÿÿÿ ÿÿÿÿüÿÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿ ÿ ÿÿÿÿ ÿ ÿ ÿÿÿ ÿ ÿ ÿÿüÿÿÿ ÿÿ ÿÿ ÿ ÿ ÿÿÿ ÿ ÿÿ ÿÿ ÿ ÿÿÿÿ ÿ ÿþÿÿ ÿ ÿÿÿüÿÿÿ ÿÿÿ;ÿ^ÿ=ÿ*ÿ<ÿ=ÿÿ ÿ*ÿÿ ÿ ÿÿÿÿÿüÿÿ ÿ ÿÿÿÿ ÿ ÿÿÿ ÿÿ ÿúÿÿÿÿ ÿÿÿÿ ÿùÿÿÿÿÿ ÿÿÿÿ ÿÿÿ0ÿÿ0ÿ ÿ=ÿ«ÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿÿÿüÿÿÿÿÿ ÿÿ ÿÿÿÿ ÿÿÿ ÿ=ÿ=ÿ=ÿ=ÿ=ÿ$ÿÿ ÿ ÿ ÿ ÿÿ ûÿÿ ÿ ÿÿ ÿÿ ÿ ÿ=ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿÿ ÿ ÿ ÿÿ ÿÿ ÿ ÿ ÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿ–ÿÿÿÿÿÿ ÿÿÿÿüÿÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿ ÿ ÿÿÿÿ ÿ ÿ ÿÿÿ ÿ ÿ ÿÿüÿÿÿ ÿÿ ÿÿ ÿ ÿ ÿÿÿ ÿ ÿÿ ÿÿ ÿ ÿÿÿÿ ÿ ÿþÿÿ ÿ ÿÿÿüÿÿÿ ÿÿÿ;ÿ^ÿ=ÿ*ÿ<ÿ=ÿÿ ÿ*ÿÿ ÿ ÿÿÿÿÿüÿÿ ÿ ÿÿÿÿ ÿ ÿÿÿ ÿÿ ÿúÿÿÿÿ ÿÿÿÿ ÿùÿÿÿÿÿ ÿÿÿÿ ÿÿÿ0ÿÿ0ÿ ÿ=ÿ«ÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿüÿÿÿÿüÿÿÿÿÿ ÿÿ ÿÿÿÿ ÿÿÿ ÿ=ÿ=ÿ=ÿ=ÿ=ÿÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿÿÿ ÿ ÿÿ(ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿüÿÿ' ÿÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿyÿ6ÿ ÿ%ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿþÿÿÿÿÿÿ ÿüÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿüÿÿÿÿÿÿÿ ÿþÿÿÿÿÿÿ=ÿŽÿ=ÿÿÿÿÿÿ ÿÿ ÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿ ÿ ÿ ÿÿ ÿÿ&ÿÿÿÿ(ÿÿÿÿ&ÿÿƒÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿÿÿ ÿ ÿÿ(ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿüÿÿ' ÿÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿyÿ6ÿ ÿ%ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿþÿÿÿÿÿÿ ÿüÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿüÿÿÿÿÿÿÿ ÿþÿÿÿÿÿÿ=ÿŽÿ=ÿÿÿÿÿÿ ÿÿ ÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿ ÿ ÿ ÿÿ ÿÿ&ÿÿÿÿ(ÿÿÿÿ&ÿÿƒÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿÿÿ ÿ ÿÿ(ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ-ÿ ÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ)ÿüÿÿ ÿüÿÿ ÿÿüÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿüÿÿ' ÿÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿ,ÿ ÿüÿÿyÿ6ÿ ÿ%ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿþÿÿÿÿÿÿ ÿüÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿüÿÿÿÿÿÿÿ ÿþÿÿÿÿÿÿ=ÿŽÿ=ÿÿÿÿÿÿ ÿÿ ÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿ ÿ ÿ ÿÿ ÿÿ&ÿÿÿÿ(ÿÿÿÿ&ÿÿƒÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿÿüÿÿ ÿ ÿÿÿþÿÿ ÿ ÿÿ ÿþÿÿÿ ÿÿüÿÿÿ ÿÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿÿÿüÿÿ ÿÿ ÿÿþÿÿÿ ÿ ÿÿþÿÿÿÿÿüÿÿ ÿüÿÿ ÿÿÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿIÿ6ÿ6ÿÿ7ÿÿÿÿ ÿÿÿÿÿ ÿ ÿÿÿÿÿ ÿÿÿÿÿÿþÿÿ ÿÿÿÿÿ ÿ ÿ ÿÿÿÿ ÿ ÿ ÿÿÿ ÿ ÿÿÿÿÿüÿÿÿ ÿÿÿÿ ÿÿÿÿ*ÿ=ÿ#ÿ ÿ.ÿ ÿ-ÿþÿÿÿ-ÿÿÿ-ÿÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿÿÿ ÿ ÿ ÿ ÿÿ ÿÿÿ ÿÿ ÿÿÿ-ÿÿþÿÿ-ÿ ÿ.ÿ ÿ(ÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿÿüÿÿ ÿ ÿÿÿþÿÿ ÿ ÿÿ ÿþÿÿÿ ÿÿüÿÿÿ ÿÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿÿÿüÿÿ ÿÿ ÿÿþÿÿÿ ÿ ÿÿþÿÿÿÿÿüÿÿ ÿüÿÿ ÿÿÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿIÿ6ÿ6ÿÿ7ÿÿÿÿ ÿÿÿÿÿ ÿ ÿÿÿÿÿ ÿÿÿÿÿÿþÿÿ ÿÿÿÿÿ ÿ ÿ ÿÿÿÿ ÿ ÿ ÿÿÿ ÿ ÿÿÿÿÿüÿÿÿ ÿÿÿÿ ÿÿÿÿ*ÿ=ÿ#ÿ ÿ.ÿ ÿ-ÿþÿÿÿ-ÿÿÿ-ÿÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿÿÿ ÿ ÿ ÿ ÿÿ ÿÿÿ ÿÿ ÿÿÿ-ÿÿþÿÿ-ÿ ÿ.ÿ ÿ(ÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿÿüÿÿ ÿ ÿÿÿþÿÿ ÿ ÿÿ ÿþÿÿÿ ÿÿüÿÿÿ ÿÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿÿ ÿ ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿ*ÿüÿÿ ÿÿÿüÿÿ ÿÿ ÿÿþÿÿÿ ÿ ÿÿþÿÿÿÿÿüÿÿ ÿüÿÿ ÿÿÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿÿ ÿüÿÿ ÿüÿÿIÿ6ÿ6ÿÿ7ÿÿÿÿ ÿÿÿÿÿ ÿ ÿÿÿÿÿ ÿÿÿÿÿÿþÿÿ ÿÿÿÿÿ ÿ ÿ ÿÿÿÿ ÿ ÿ ÿÿÿ ÿ ÿÿÿÿÿüÿÿÿ ÿÿÿÿ ÿÿÿÿ*ÿ=ÿ#ÿ ÿ.ÿ ÿ-ÿþÿÿÿ-ÿÿÿ-ÿÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿÿÿ ÿ ÿ ÿ ÿÿ ÿÿÿ ÿÿ ÿÿÿ-ÿÿþÿÿ-ÿ ÿ.ÿ ÿ(ÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿ ÿ- ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿ ÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿ;ÿ=ÿ ÿÿ ÿ ÿ ÿ ÿÿÿÿ ÿ ÿþÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿ ÿ ÿÿÿÿÿ ÿ ÿÿÿ ÿüÿÿÿÿ ÿ ÿÿ ÿüÿÿÿÿ ÿ ÿÿþÿÿÿ ÿ ÿ ÿÿÿŒÿ:ÿ,ÿÿÿ,ÿÿÿ,ÿ ÿ,ÿ ÿ ÿÿÿ þÿ ÿÿ ÿ ÿÿ ÿ þÿÿÿ þÿ ÿÿÿÿüÿÿ:ÿ<ÿ=ÿ†ÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿ ÿ- ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿ ÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿ;ÿ=ÿ ÿÿ ÿ ÿ ÿ ÿÿÿÿ ÿ ÿþÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿ ÿ ÿÿÿÿÿ ÿ ÿÿÿ ÿüÿÿÿÿ ÿ ÿÿ ÿüÿÿÿÿ ÿ ÿÿþÿÿÿ ÿ ÿ ÿÿÿŒÿ:ÿ,ÿÿÿ,ÿÿÿ,ÿ ÿ,ÿ ÿ ÿÿÿ þÿ ÿÿ ÿ ÿÿ ÿ þÿÿÿ þÿ ÿÿÿÿüÿÿ:ÿ<ÿ=ÿ†ÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿ ÿ- ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿ ÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿÿ ÿ ÿ ÿÿ ÿ ÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿ;ÿ=ÿ ÿÿ ÿ ÿ ÿ ÿÿÿÿ ÿ ÿþÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿ ÿÿÿÿÿÿ ÿÿÿÿÿ ÿ ÿÿÿÿÿ ÿ ÿÿÿ ÿüÿÿÿÿ ÿ ÿÿ ÿüÿÿÿÿ ÿ ÿÿþÿÿÿ ÿ ÿ ÿÿÿŒÿ:ÿ,ÿÿÿ,ÿÿÿ,ÿ ÿ,ÿ ÿ ÿÿÿ þÿ ÿÿ ÿ ÿÿ ÿ þÿÿÿ þÿ ÿÿÿÿüÿÿ:ÿ<ÿ=ÿ†ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿ=ÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿ'ÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ9ÿ9ÿ ÿ+ÿ ÿÿÿ ÿþÿÿÿÿ ÿÿÿÿ ÿÿÿÿÿÿÿÿÿþÿÿüÿÿÿþÿÿÿÿÿÿÿÿÿÿüÿÿÿ ÿÿÿþÿÿüÿÿÿøÿÿÿÿÿ ÿÿÿÿÿþÿÿÿ ÿÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿlûÿÿÿ ÿ+ÿ ÿ*ÿüÿÿ ÿ+ÿüÿÿ ÿ-ÿüÿÿ ÿ ÿ;ÿ;ÿ;ÿ;ÿ;ÿ–ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿ=ÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿ'ÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ9ÿ9ÿ ÿ+ÿ ÿÿÿ ÿþÿÿÿÿ ÿÿÿÿ ÿÿÿÿÿÿÿÿÿþÿÿüÿÿÿþÿÿÿÿÿÿÿÿÿÿüÿÿÿ ÿÿÿþÿÿüÿÿÿøÿÿÿÿÿ ÿÿÿÿÿþÿÿÿ ÿÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿlûÿÿÿ ÿ+ÿ ÿ*ÿüÿÿ ÿ+ÿüÿÿ ÿ-ÿüÿÿ ÿ ÿ;ÿ;ÿ;ÿ;ÿ;ÿ–ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿüÿÿÿüÿÿ ÿ ÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿ=ÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿþÿÿ ÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿ'ÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿ9ÿ9ÿ ÿ+ÿ ÿÿÿ ÿþÿÿÿÿ ÿÿÿÿ ÿÿÿÿÿÿÿÿÿþÿÿüÿÿÿþÿÿÿÿÿÿÿÿÿÿüÿÿÿ ÿÿÿþÿÿüÿÿÿøÿÿÿÿÿ ÿÿÿÿÿþÿÿÿ ÿÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿlûÿÿÿ ÿ+ÿ ÿ*ÿüÿÿ ÿ+ÿüÿÿ ÿ-ÿüÿÿ ÿ ÿ;ÿ;ÿ;ÿ;ÿ;ÿ–€€@@Cultivation_9+dfsg1_UnixSource/game2/gameSource/SoilMap.h0000640000175000017500000000645110500250571022226 0ustar pabspabs/* * Modification History * * 2006-July-27 Jason Rohrer * Created. */ #ifndef SOIL_MAP_INCLUDED #define SOIL_MAP_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include "minorGems/graphics/Color.h" #include "DrawableObject.h" /** * A map of soil conditions. * * @author Jason Rohrer */ class SoilMap : public DrawableObject { public: /** * Constructs a map. * * @param inCornerA, inCornerB the corners of the map in world * coordinates. Destroyed by caller. */ SoilMap( Vector3D *inCornerA, Vector3D *inCornerB ); virtual ~SoilMap(); /** * Gets the soil condition at a given location. * * @param inPosition the position to test the soil at. * Destroyed by caller. * @param inNormalize set to true to normalize result to [0,1]. * Defaults to true. * * @return a value in [0..1], where 0 represents brown soil * and 1 represents green soil. */ double getSoilCondition( Vector3D *inPosition, char inNormalize = true ); /** * Maps a soil condition to a color. * * @param inSoilCondition the condition in [0,1]. * * @return a new Color mapped from inSoilCondition. * Destroyed by caller. */ static Color *mapSoilToColor( double inSoilCondition ); /** * Gets whether a world point is in bounds of land (not in water). * * @param inPosition the poisition to check. Destroyed by caller. * * @return true if in bounds. */ char isInBounds( Vector3D *inPosition ); /** * Gets the closest water boundary point to a given point. * * @param inPosition the poisition to check. Destroyed by caller. * * * @return a boundary point. Destroyed by caller. * If inPosition is on land, closest water point returned. * If inPosition is on water, closest land point returned. */ Vector3D *getClosestBoundaryPoint( Vector3D *inPosition ); // implements the DrawableObject interface // inPosition and inScale are ignored void draw( Vector3D *inPosition, double inScale = 1 ); private: Vector3D mCornerA, mCornerB; SingleTextureGL *mTexture; SingleTextureGL *mWaterBoundaryTexture; SingleTextureGL *mGritTexture; Image *mWaterBoundaryImage; double *mWaterBoundaryImageAlpha; int mWaterBoundaryImageNumPixels; int mWaterBoundaryImageSize; double mMinLandscapeValue, mMaxLandscapeValue; /** * Maps a point in world space to pixel coordinates in the * water boundary image. * * @param inPoint the point in the world. Destroyed by caller. * @param outX, outY pointers to where pixel coordinates should be * returned. */ void mapPointToBoundaryPixel( Vector3D *inPoint, int *outX, int *outY ); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/ImmortalGenetics.cpp0000640000175000017500000000303510530401436024457 0ustar pabspabs/* * Modification History * * 2006-November-17 Jason Rohrer * Created. */ #include "ImmortalGenetics.h" #include ImmortalGenetics::ImmortalGenetics() : GardenerGenetics() { } ImmortalGenetics::ImmortalGenetics( GardenerGenetics *inOpener ) : GardenerGenetics( inOpener ) { } double ImmortalGenetics::getParameter( ImmortalGeneLocator inLocator ) { double geneValue = mGenes[ inLocator ][0]; switch( inLocator ) { case soulStartAngle: return mapValueToRange( geneValue, 0, 2 * M_PI ); case soulDeltaAngle: return mapValueToRange( geneValue, -0.01, 0.01 ); case soulDeltaDeltaAngle: //return mapValueToRange( geneValue, -0.005, 0.005 ); return mapValueToRange( geneValue, -0.00001, 0.00001 ); case soulDeltaDeltaDeltaAngle: return mapValueToRange( geneValue, -0.00005, 0.00005 ); case soulDeltaAngleSineWeight: return mapValueToRange( geneValue, 0, 1 ); case soulDeltaAngleSineSpeed: return mapValueToRange( geneValue, 0, 1 ); case cornerASpread: return mapValueToRange( geneValue, 1, 3 ); case cornerBSpread: return mapValueToRange( geneValue, 1, 3 ); case cornerCSpread: return mapValueToRange( geneValue, 1, 3 ); case cornerDSpread: return mapValueToRange( geneValue, 1, 3 ); // default to returning the gene itself default: return geneValue; } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/Fruit.h0000640000175000017500000000627110520505510021751 0ustar pabspabs/* * Modification History * * 2006-July-23 Jason Rohrer * Created. * * 2006-October-27 Jason Rohrer * Added support for poisoned fruit. */ #ifndef FRUIT_INCLUDED #define FRUIT_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/graphics/Color.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include "Storable.h" #include "Seeds.h" /** * A fruit. * * @author Jason Rohrer */ class Fruit : public Storable { public: /** * Constructs a fruit. * * @param inParentGenetics the genetics of this fruit's parent. * Determines fruit shape and color. * Destroyed by caller. * @param inSeeds the seeds this fruit should contain. * Destroyed by caller. * @param inRipenRate the rate of fruit ripening. */ Fruit( PlantGenetics *inParentGenetics, Seeds *inSeeds, double inRipenRate ); ~Fruit(); /** * Gets the nutrition provided by this fruit if eaten. * * @return nutrition stored as a color. Destroyed by caller. */ Color *getNutrition(); /** * Gets whether this fruit is completely rotten. * * @return true if rotten. */ char isRotten(); /** * Gets whether this fruit is ripe. * * @return true if ripe. */ char isRipe(); /** * Get whether fruit is poisoned. * * @return true if poisoned. */ char isPoisoned(); /** * Poisons this fruit. */ void poison(); /** * Gets a copy of the seeds contained in this fruit. * * @return this fruit's seeds. Must be destroyed by caller. */ Seeds *getSeeds(); /** * Passes time for this fruit. * * @param inTimeDeltaInSeconds the amount of time that has passed. */ void passTime( double inTimeDeltaInSeconds ); // implements the DrawableObject interface // (draws at a default rotation) void draw( Vector3D *inPosition, double inScale = 1 ); // specifies a rotation void draw( Vector3D *inPosition, Angle3D *inRotation, double inScale = 1 ); // implements the Storable interface StorableType getType() { return fruitType; } private: PlantGenetics mParentGenetics; // 0-1 growing // 1-2 ripening // 2-4 ripe (stable) // 4-5 browning // 5-6 rotting double mStatus; // true if poisoned char mPoisoned; Color mNutrition; Seeds mSeeds; double mRipenRate; SingleTextureGL *mShapeTexture; // the last rotation we were drawn at when a rotation was specified // use this as the default rotation when no rotation is specified Angle3D mLastRotation; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/Genetics.cpp0000640000175000017500000000673010477265513022775 0ustar pabspabs/* * Modification History * * 2006-August-11 Jason Rohrer * Created. */ #include "Genetics.h" #include #include #include "minorGems/util/random/StdRandomSource.h" extern StdRandomSource globalRandomSource; Genetics::~Genetics() { for( int i=0; igetNumGenes(); int bNumGenes = inParentB->getNumGenes(); if( bNumGenes < minNumGenes ) { minNumGenes = bNumGenes; } mNumGenes = minNumGenes; mGenes = new double*[ mNumGenes ]; mGeneLengths = new int[ mNumGenes ]; for( int i=0; igetGene( i ); mGeneLengths[i] = chosenParent->getGeneLength( i ); } } Genetics::Genetics( Genetics *inGeneticsToCopy ) : mNumGenes( inGeneticsToCopy->getNumGenes() ) { mGeneLengths = new int[ mNumGenes ]; mGenes = new double*[ mNumGenes ]; for( int i=0; igetGeneLength( i ); mGenes[i] = inGeneticsToCopy->getGene( i ); } } double Genetics::mapValueToRange( double inValue, double inRangeStart, double inRangeEnd ) { return inValue * ( inRangeEnd - inRangeStart ) + inRangeStart; } int Genetics::getNumGenes() { return mNumGenes; } int Genetics::getGeneLength( int inIndex ) { return mGeneLengths[ inIndex ]; } double *Genetics::getGene( int inIndex ) { int length = getGeneLength( inIndex ); double *returnArray = new double[ length ]; memcpy( returnArray, mGenes[ inIndex ], sizeof( double ) * length ); return returnArray; } void Genetics::printGenetics() { for( int i=0; igetNumGenes() ) { return false; } int i; for( i=0; igetGeneLength( i ) ) { return false; } } // same length for( i=0; igetGene( i ); for( int j=0; jr, inColor->g, inColor->b, inColor->a ); * * @param inColor the color to set. Destroyed by caller. */ void setGLColor( Color *inColor ); #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/Makefile.all0000640000175000017500000001137011211244434022715 0ustar pabspabs# # Modification History # # 2006-June-27 Jason Rohrer # Created. Copied from Transcend. # ## # The portion of game2 Makefiles common to all platforms. # # Should not be made manually---used by game2/configure to build Makefiles. ## ROOT_PATH = ../.. USER_INTERFACE_PATH = userInterface SOUND_PATH = sound LAYER_SOURCE = \ glCommon.cpp \ features.cpp\ game.cpp \ World.cpp \ Plant.cpp \ PlantLeaf.cpp \ PlantFlower.cpp \ Fruit.cpp \ Gardener.cpp \ Portal.cpp \ GardenerAI2.cpp \ FlyingObject.cpp \ emotionIcons.cpp \ landscape.cpp \ SoilMap.cpp \ Seeds.cpp \ Genetics.cpp \ GardenerGenetics.cpp \ PlantGenetics.cpp \ PortalLayerGenetics.cpp \ ImmortalGenetics.cpp \ ${USER_INTERFACE_PATH}/ButtonBase.cpp \ ${USER_INTERFACE_PATH}/PlantButton.cpp \ ${USER_INTERFACE_PATH}/PlotButton.cpp \ ${USER_INTERFACE_PATH}/WaterButton.cpp \ ${USER_INTERFACE_PATH}/PoisonButton.cpp \ ${USER_INTERFACE_PATH}/HarvestButton.cpp \ ${USER_INTERFACE_PATH}/EmotionButton.cpp \ ${USER_INTERFACE_PATH}/PauseButton.cpp \ ${USER_INTERFACE_PATH}/RestartButton.cpp \ ${USER_INTERFACE_PATH}/QuitButton.cpp \ ${USER_INTERFACE_PATH}/TextDisplay.cpp \ ${USER_INTERFACE_PATH}/ObjectSelector.cpp \ ${USER_INTERFACE_PATH}/EatButton.cpp \ ${USER_INTERFACE_PATH}/GiftButton.cpp \ ${USER_INTERFACE_PATH}/DiscardButton.cpp \ ${USER_INTERFACE_PATH}/MateButton.cpp \ ${USER_INTERFACE_PATH}/FollowButton.cpp \ ${USER_INTERFACE_PATH}/TexturedPanel.cpp \ ${USER_INTERFACE_PATH}/NextTutorialButton.cpp \ ${USER_INTERFACE_PATH}/TextBlockGL.cpp \ ${SOUND_PATH}/MusicNoteWaveTable.cpp \ ${SOUND_PATH}/MusicPart.cpp \ ${SOUND_PATH}/MusicPlayer.cpp \ ${SOUND_PATH}/SoundPlayer.cpp \ ${SOUND_PATH}/SoundSamples.cpp \ ${SOUND_PATH}/SoundEffectsBank.cpp \ LAYER_OBJECTS = ${LAYER_SOURCE:.cpp=.o} NEEDED_MINOR_GEMS_OBJECTS = \ ${SCREEN_GL_O} \ ${SINGLE_TEXTURE_GL_O} \ ${TYPE_IO_O} \ ${STRING_UTILS_O} \ ${STRING_BUFFER_OUTPUT_STREAM_O} \ ${PATH_O} \ ${TIME_O} \ ${THREAD_O} \ ${MUTEX_LOCK_O} \ ${PNG_IMAGE_CONVERTER_O} \ ${TRANSLATION_MANAGER_O} TEST_SOURCE = TEST_OBJECTS = ${TEST_SOURCE:.cpp=.o} DEPENDENCY_FILE = Makefile.dependencies # targets all: Cultivation clean: rm -f ${DEPENDENCY_FILE} ${LAYER_OBJECTS} ${TEST_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} Cultivation Cultivation: ${LAYER_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} ${EXE_LINK} -o Cultivation ${LAYER_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} ${PLATFORM_LINK_FLAGS} # add this on Unix to support JPEG video frame output # -ljpeg ${ROOT_PATH}/minorGems/graphics/converters/unix/JPEGImageConverterUnix.cpp # sed command for fixing up the dependencies generated by g++ # g++ (pre-3.0) leaves the path off of the .o target # look for a .o file at the beginning of a line (in other words, one # without a path), and replace it with the full-path version. # This should be compatible with g++ 3.0, since we only replace .o names # that occur at the beginning of a line (using the "^" modifier) GAME_2_SED_FIX_COMMAND = sed ' \ s/^ButtonBase.o/$${USER_INTERFACE_PATH}\/ButtonBase.o/; \ s/^PlantButton.o/$${USER_INTERFACE_PATH}\/PlantButton.o/; \ s/^PlotButton.o/$${USER_INTERFACE_PATH}\/PlotButton.o/; \ s/^WaterButton.o/$${USER_INTERFACE_PATH}\/WaterButton.o/; \ s/^PoisonButton.o/$${USER_INTERFACE_PATH}\/PoisonButton.o/; \ s/^HarvestButton.o/$${USER_INTERFACE_PATH}\/HarvestButton.o/; \ s/^EmotionButton.o/$${USER_INTERFACE_PATH}\/EmotionButton.o/; \ s/^PauseButton.o/$${USER_INTERFACE_PATH}\/PauseButton.o/; \ s/^RestartButton.o/$${USER_INTERFACE_PATH}\/RestartButton.o/; \ s/^QuitButton.o/$${USER_INTERFACE_PATH}\/QuitButton.o/; \ s/^TextDisplay.o/$${USER_INTERFACE_PATH}\/TextDisplay.o/; \ s/^ObjectSelector.o/$${USER_INTERFACE_PATH}\/ObjectSelector.o/; \ s/^EatButton.o/$${USER_INTERFACE_PATH}\/EatButton.o/; \ s/^GiftButton.o/$${USER_INTERFACE_PATH}\/GiftButton.o/; \ s/^DiscardButton.o/$${USER_INTERFACE_PATH}\/DiscardButton.o/; \ s/^MateButton.o/$${USER_INTERFACE_PATH}\/MateButton.o/; \ s/^FollowButton.o/$${USER_INTERFACE_PATH}\/FollowButton.o/; \ s/^TexturedPanel.o/$${USER_INTERFACE_PATH}\/TexturedPanel.o/; \ s/^NextTutorialButton.o/$${USER_INTERFACE_PATH}\/NextTutorialButton.o/; \ s/^TextBlockGL.o/$${USER_INTERFACE_PATH}\/TextBlockGL.o/; \ s/^MusicNoteWaveTable.o/$${SOUND_PATH}\/MusicNoteWaveTable.o/; \ s/^MusicPart.o/$${SOUND_PATH}\/MusicPart.o/; \ s/^MusicPlayer.o/$${SOUND_PATH}\/MusicPlayer.o/; \ s/^SoundPlayer.o/$${SOUND_PATH}\/SoundPlayer.o/; \ s/^SoundSamples.o/$${SOUND_PATH}\/SoundSamples.o/; \ ' # build the dependency file ${DEPENDENCY_FILE}: ${LAYER_SOURCE} ${TEST_SOURCE} rm -f ${DEPENDENCY_FILE} ${COMPILE} -MM ${LAYER_SOURCE} ${TEST_SOURCE} >> ${DEPENDENCY_FILE}.temp cat ${DEPENDENCY_FILE}.temp | ${GAME_2_SED_FIX_COMMAND} >> ${DEPENDENCY_FILE} rm -f ${DEPENDENCY_FILE}.temp include ${DEPENDENCY_FILE} Cultivation_9+dfsg1_UnixSource/game2/gameSource/Portal.cpp0000640000175000017500000007575310653414027022500 0ustar pabspabs/* * Modification History * * 2006-October-26 Jason Rohrer * Created. * * 2006-December-25 Jason Rohrer * Added function for checking closed status of portal. * * 2007-July-30 Jason Rohrer * Smoothed linear segmentation of soul trails in larger images. */ #include "Portal.h" #include "glCommon.h" #include "features.h" #include #include "minorGems/graphics/filters/BoxBlurFilter.h" #include "minorGems/io/file/FileOutputStream.h" // #include "minorGems/graphics/converters/TGAImageConverter.h" #include "minorGems/graphics/converters/PNGImageConverter.h" #include "minorGems/util/random/StdRandomSource.h" extern StdRandomSource globalRandomSource; Portal::Portal() : mMaxNumLevels( 5 ), mNumLevels( 0 ), mTopLevelZ( 8 ), mOpeningProgress( 0 ), mClosing( false ), mClosingProgress( 0 ), mTimePassed( 0 ) { mLayerTextures = new SingleTextureGL *[ mMaxNumLevels ]; mLayerGenetics = new PortalLayerGenetics *[ mMaxNumLevels ]; mLayerSpawnProgress = new double[ mMaxNumLevels ]; // generate texture } Portal::~Portal() { int i; for( i=0; igetWidth(); int numChannels = inImage->getNumChannels(); if( numChannels > 4 ) { // only consider first 4 numChannels = 4; } double **channels = new double*[numChannels]; int c; for( c=0; cgetChannel( c ); } // only consider pixels in a square around center int squareRadius = (int)( inRadius + 1 ); for( int dX = -squareRadius; dX <= squareRadius; dX++ ) { int currentX = (int)( inCenterX + dX ); // from circle center double distanceX = inCenterX - currentX; // optimization: ignore pixels that are too far by x alone if( distanceX < inRadius ) { // wrap to index into image if( currentX < 0 ) { currentX += imageSize; } else if( currentX >= imageSize ) { currentX -= imageSize; } for( int dY = -squareRadius; dY <= squareRadius; dY++ ) { int currentY = (int)( inCenterY + dY ); // dist from circle center double distanceY = inCenterY - currentY; double distance = sqrt( distanceY * distanceY + distanceX * distanceX ); if( distance < inRadius ) { // wrap for indexing into image if( currentY < 0 ) { currentY += imageSize; } else if( currentY >= imageSize ) { currentY -= imageSize; } double brighness = ( inRadius - distance ) / inRadius; int pixelIndex = currentY * imageSize + currentX; for( c=0; c 1 ) { channels[c][ pixelIndex ] = 1; } } } } } } delete [] channels; } void Portal::upgrade( Gardener *inLevelOpener ) { if( mNumLevels < mMaxNumLevels ) { if( inLevelOpener != NULL ) { GardenerGenetics *baseGenetics = &( inLevelOpener->mGenetics ); mLayerGenetics[ mNumLevels ] = new PortalLayerGenetics( baseGenetics ); } else { // no opener // use random base genetics mLayerGenetics[ mNumLevels ] = new PortalLayerGenetics(); } mLayerSpawnProgress[ mNumLevels ] = 0; // texture for this layer // a blurry, wire-frame glyph int textureSize = 32; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // start all pixels as white with alpha of zero for( p=0; pgetParameter( glyphStartPositionX ); yPosition = mLayerGenetics[ mNumLevels ]->getParameter( glyphStartPositionY ); double startAngle = mLayerGenetics[ mNumLevels ]->getParameter( glyphStartAngle ); double deltaAngle = mLayerGenetics[ mNumLevels ]->getParameter( glyphDeltaAngle ); // up by one pixel Vector3D currentDirection( 0, 1.0 / textureSize, 0 ); // rotate Angle3D angle( 0, 0, startAngle ); currentDirection.rotate( &angle ); int stepsBeforeDirectionChange = (int)( mLayerGenetics[ mNumLevels ]->getParameter( glyphStepsBeforeDirectionChange ) ); int numSteps = 0; int stepsSinceDirectionChange = 0; // all white with full alpha Color drawColor( 1, 1, 1, 1 ); while( numSteps < 100 ) { // white pixel double x = xPosition * (textureSize - 1); double y = yPosition * (textureSize - 1); drawBlurCircleInImage( textureImage, &drawColor, 0.5, x, y, 1.5 ); if( stepsSinceDirectionChange > stepsBeforeDirectionChange ) { // change direction stepsSinceDirectionChange = 0; Angle3D angle( 0, 0, deltaAngle ); currentDirection.rotate( &angle ); } // bounce off boundaries // don't let them get too close to edge xPosition += currentDirection.mX; if( xPosition < 0.1 ) { xPosition = 0.1; currentDirection.mX = -currentDirection.mX; } if( xPosition > 0.9 ) { xPosition = 0.9; currentDirection.mX = -currentDirection.mX; } yPosition += currentDirection.mY; if( yPosition < 0.1 ) { yPosition = 0.1; currentDirection.mY = -currentDirection.mY; } if( yPosition > 0.9 ) { yPosition = 0.9; currentDirection.mY = -currentDirection.mY; } numSteps++; stepsSinceDirectionChange++; } // blur alpha BoxBlurFilter blur( 1 ); textureImage->filter( &blur, 3 ); /* char *fileName = autoSprintf( "glyph_%d.tga", mNumLevels ); File outFileB( NULL, fileName ); delete [] fileName; FileOutputStream *outStreamB = new FileOutputStream( &outFileB ); TGAImageConverter converter; converter.formatImage( textureImage, outStreamB ); delete outStreamB; */ mLayerTextures[ mNumLevels ] = new SingleTextureGL( textureImage, // no wrap false ); delete textureImage; mNumLevels++; } } char Portal::isOpen() { if( mClosing ) { return false; } if( mOpeningProgress == 1 ) { return true; } else { return false; } } char Portal::isClosed() { if( mClosing && mClosingProgress == 1 ) { return true; } else { return false; } } void Portal::sendGardener( Gardener *inGardener, Vector3D *inStartingPosition, Angle3D *inStartingRotation ) { inGardener->setFrozen( true ); mPassengers.push_back( inGardener ); mPassengerPositions.push_back( new Vector3D( inStartingPosition ) ); mPassengerRotations.push_back( new Angle3D( inStartingRotation ) ); mPassengerFades.push_back( 1.0 ); } void Portal::passTime( double inTimeDeltaInSeconds ) { mTimePassed += inTimeDeltaInSeconds; for( int i=0; i 1 ) { mLayerSpawnProgress[i] = 1; } } // if reached max and last layer done spawning if( mNumLevels == mMaxNumLevels && mLayerSpawnProgress[ mNumLevels - 1 ] == 1 ) { // open portal over course of 2 seconds mOpeningProgress += inTimeDeltaInSeconds / 2; // cap if( mOpeningProgress > 1 ) { mOpeningProgress = 1; } } if( mOpeningProgress == 1 ) { // open // cause any passengers to rise up // in units per second double portalRiseRate = 3; double fadeZStart = mTopLevelZ / 2; // fade so that we hit zero at top // but we only start fading half way from top double portalFadeRate = 1.0 / ( fadeZStart / portalRiseRate ); int numPassengers = mPassengers.size(); for( int i=0; imZ -= portalRiseRate * inTimeDeltaInSeconds; if( (- position->mZ) >= fadeZStart ) { *( mPassengerFades.getElement( i ) ) -= portalFadeRate * inTimeDeltaInSeconds; if( *( mPassengerFades.getElement( i ) ) < 0 ) { *( mPassengerFades.getElement( i ) ) = 0; } } } // now remove any that are done char foundDone = true; while( foundDone ) { foundDone = false; int numPassengers = mPassengers.size(); for( int i=0; imZ) >= mTopLevelZ ) { // done passing through Gardener *gardener = *( mPassengers.getElement( i ) ); Angle3D *rotation = *( mPassengerRotations.getElement( i ) ); delete position; delete rotation; mPassengerPositions.deleteElement( i ); mPassengerRotations.deleteElement( i ); mPassengers.deleteElement( i ); mPassengerFades.deleteElement( i ); gardener->setFrozen( false ); gardener->forceDead(); // save copy of genetics to immortalize gardener mImmortals.push_back( new ImmortalGenetics( &( gardener->mGenetics ) ) ); foundDone = true; if( gardener->mUserControlling ) { // start closing now mClosing = true; } } } } if( mClosing && mClosingProgress < 1 ) { // close portal over course of 2 seconds mClosingProgress += inTimeDeltaInSeconds / 2; // cap if( mClosingProgress > 1 ) { mClosingProgress = 1; // actually close the portal close(); } } } } void Portal::draw( Vector3D *inPosition, double inScale, double inMaxZ, double inMinZ ) { if( mClosing && mClosingProgress >= 1.0 ) { // draw nothing return; } // portal brightens only glBlendFunc( GL_SRC_ALPHA, GL_ONE ); Vector3D levelPosition( inPosition ); double zStep = mTopLevelZ / ( mMaxNumLevels - 1 ); for( int i=0; i= inMinZ && levelPosition.mZ <= inMaxZ ) { if( i == 0 ) { // draw all passing gardeners below the lowest level // back to normal blend function when drawing gardeners glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); int numPassengers = mPassengers.size(); for( int i=0; idraw( position, rotation, // scale 4, fade); } // back to portal blend mode: brightens only glBlendFunc( GL_SRC_ALPHA, GL_ONE ); } double param_quadRotationSpeed = mLayerGenetics[i]->getParameter( quadRotationSpeed ); double param_quadRotationDelta = mLayerGenetics[i]->getParameter( quadRotationDelta ); double param_offsetRadius = mLayerGenetics[i]->getParameter( offsetRadius ); double param_offsetSineWeight = mLayerGenetics[i]->getParameter( offsetSineWeight ); double param_offsetSineSpeed = mLayerGenetics[i]->getParameter( offsetSineSpeed ); double param_offsetSineSineSpeed = mLayerGenetics[i]->getParameter( offsetSineSineSpeed ); double param_quadDensity = mLayerGenetics[i]->getParameter( quadDensity ); double param_quadScale = mLayerGenetics[i]->getParameter( quadScale ); double param_quadScaleSineWeight = mLayerGenetics[i]->getParameter( quadScaleSineWeight ); double param_quadScaleSineSpeed = mLayerGenetics[i]->getParameter( quadScaleSineSpeed ); double param_quadScaleSineSineSpeed = mLayerGenetics[i]->getParameter( quadScaleSineSineSpeed ); // modify parameters if portal not open yet /* param_quadRotationSpeed = mOpeningProgress * param_quadRotationSpeed + ( 1 - mOpeningProgress ) * 0; param_quadRotationDelta = mOpeningProgress * param_quadRotationDelta + ( 1 - mOpeningProgress ) * 0; */ // draw a circle of quads Angle3D quadRotation( 0, 0, param_quadRotationSpeed * mTimePassed ); Angle3D quadRotationDelta( 0, 0, param_quadRotationDelta ); // use scale colors from underlying gardener genetics as starting // colors Color *cornerColors[4]; cornerColors[0] = mLayerGenetics[i]->getColor( scaleCornerAColor ); cornerColors[1] = mLayerGenetics[i]->getColor( scaleCornerBColor ); cornerColors[2] = mLayerGenetics[i]->getColor( scaleCornerCColor ); cornerColors[3] = mLayerGenetics[i]->getColor( scaleCornerDColor ); cornerColors[0]->a = mLayerGenetics[i]->getParameter( cornerAAlpha ); cornerColors[1]->a = mLayerGenetics[i]->getParameter( cornerBAlpha ); cornerColors[2]->a = mLayerGenetics[i]->getParameter( cornerCAlpha ); cornerColors[3]->a = mLayerGenetics[i]->getParameter( cornerDAlpha ); // force cornerA alpha to be 1 so that portal layer is always // visible cornerColors[0]->a = 1.0; Color *cornerDeltaColors[4]; cornerDeltaColors[0] = mLayerGenetics[i]->getColor( scaleCornerAColorDelta ); cornerDeltaColors[1] = mLayerGenetics[i]->getColor( scaleCornerBColorDelta ); cornerDeltaColors[2] = mLayerGenetics[i]->getColor( scaleCornerCColorDelta ); cornerDeltaColors[3] = mLayerGenetics[i]->getColor( scaleCornerDColorDelta ); float colorDeltas[4][3]; int p, c; for( p=0; p<4; p++ ) { // modify alpha for each corner color // fade in during spawn cornerColors[p]->a *= mLayerSpawnProgress[i]; // next compute animated color deltas Color *color = cornerDeltaColors[p]; float redComponent = color->r; for( c=0; c<3; c++ ) { float componentValue = (*color)[c]; colorDeltas[p][c] = componentValue + componentValue * // use red component as color cycle speed sin( redComponent * mTimePassed * 5 ); } } double aStep = 0.1 / param_quadDensity; if( ! Features::drawComplexPortal ) { // 3-times fewer glyphs in each layer aStep *= 3; } // round so that aStep is a fraction of the form (2*PI)/n double invAStep = (2 * M_PI) / aStep; aStep = (2 * M_PI) / ( (int)invAStep ); for( double a=0; a< 2 * M_PI; a += aStep ) { double xOffset = param_offsetRadius * inScale + param_offsetSineWeight * inScale * sin( param_offsetSineSpeed * a + sin( param_offsetSineSineSpeed * mTimePassed ) ); xOffset += 10 * inScale * ( 1 - mLayerSpawnProgress[i] ); if( mClosing ) { xOffset *= ( 1.0 - mClosingProgress ); } Vector3D offset( xOffset, 0, 0 ); Angle3D angle( 0, 0, a ); offset.rotate( &angle ); offset.add( &levelPosition ); double quadSize = param_quadScale * inScale + ( inScale * param_quadScaleSineWeight ) * sin( param_quadScaleSineSpeed * a + sin( param_quadScaleSineSineSpeed * mTimePassed ) ); // shrink quads if not open quadSize = mOpeningProgress * quadSize + ( 1 - mOpeningProgress ) * 0.4 * quadSize; if( mClosing ) { quadSize *= ( 1.0 - mClosingProgress ); } drawTextureQuad( mLayerTextures[i], &offset, quadSize, &quadRotation, cornerColors ); quadRotation.add( &quadRotationDelta ); // bounce off value boundaries at 0 and 1 for( p=0; p<4; p++ ) { Color *cornerColor = cornerColors[p]; for( c=0; c<3; c++ ) { (*cornerColor)[c] += (*colorDeltas)[c]; if( (*cornerColor)[c] > 1 ) { (*cornerColor)[c] = 1 - ( (*cornerColor)[c] - 1 ); colorDeltas[p][c] *= -1; } else if( (*cornerColor)[c] < 0 ) { (*cornerColor)[c] = 0 + ( 0 - (*cornerColor)[c]); colorDeltas[p][c] *= -1; } } } } for( p=0; p<4; p++ ) { delete cornerColors[p]; delete cornerDeltaColors[p]; } } levelPosition.mZ -= zStep; } // back to normal blend function glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } void Portal::close() { int baseImageSize = 40; int numImmortals = mImmortals.size(); int imageSize = baseImageSize * numImmortals; // image with all black pixels Image *image = new Image( imageSize, imageSize, 3, true ); double *channels[3]; //int pixelsPerChannel = imageSize * imageSize; int c; //int p; for( c=0; c<3; c++ ) { channels[c] = image->getChannel( c ); } double *currentX = new double[ numImmortals ]; double *currentY = new double[ numImmortals ]; double *deltaAngle = new double[ numImmortals ]; double *deltaDeltaAngle = new double[ numImmortals ]; double *deltaDeltaDeltaAngle = new double[ numImmortals ]; double *deltaAngleSineWeight = new double[ numImmortals ]; double *deltaAngleSineSpeed = new double[ numImmortals ]; Color *colors = new Color[ numImmortals ]; Color **cornerColors = new Color*[ numImmortals ]; double **cornerSpreads = new double*[ numImmortals ]; Vector3D *currentDirection = new Vector3D[ numImmortals ]; ImmortalGenetics **immortalGenes = mImmortals.getElementArray(); int i; for( i=0; igetParameter( soulStartX ); currentY[i] = immortalGenes[i]->getParameter( soulStartY ); deltaAngle[i] = immortalGenes[i]->getParameter( soulDeltaAngle ); deltaDeltaAngle[i] = immortalGenes[i]->getParameter( soulDeltaDeltaAngle ); deltaDeltaDeltaAngle[i] = immortalGenes[i]->getParameter( soulDeltaDeltaDeltaAngle ); deltaAngleSineWeight[i] = immortalGenes[i]->getParameter( soulDeltaAngleSineWeight ); deltaAngleSineSpeed[i] = immortalGenes[i]->getParameter( soulDeltaAngleSineSpeed ); double startAngle = immortalGenes[i]->getParameter( soulStartAngle ); // up by one pixel currentDirection[i].setCoordinates( 0, 1.0 / imageSize, 0 ); // rotate Angle3D angle( 0, 0, startAngle ); currentDirection[i].rotate( &angle ); Color *tempColor = immortalGenes[i]->getColor( bodyColor ); colors[i].setValues( tempColor ); delete tempColor; cornerColors[i] = new Color[4]; cornerSpreads[i] = new double[4]; for( int v=0; v<4; v++ ) { // hack: can loop over scale colors A through G because they // are in order in the enum tempColor = immortalGenes[i]->getColor( (GardenerGeneLocator)( scaleCornerAColor + v ) ); cornerColors[i][v].setValues( tempColor ); delete tempColor; // use same hack here cornerSpreads[i][v] = immortalGenes[i]->getParameter( (ImmortalGeneLocator)( scaleCornerAColor + v ) ); } } delete [] immortalGenes; // execute steps for( int s=0; s<1000; s++ ) { for( i=0; i= imageSize ) { cornerPixelX[v] -= imageSize; } if( cornerPixelY[v] >= imageSize ) { cornerPixelY[v] -= imageSize; } } // draw center drawBlurCircleInImage( image, &(colors[i]), 0.33, x, y, 1.5 ); // draw corners for( v=0; v<4; v++ ) { drawBlurCircleInImage( image, &(cornerColors[i][v]), 0.066, cornerPixelX[v], cornerPixelY[v], 1.5 ); } // take a sub-step // sub-step rotation double fractional_s = s + (double)ss / (double)( numImmortals - 1 ); // rotate Angle3D angle( 0, 0, subStepFraction * ( deltaAngle[i] + deltaAngleSineWeight[i] * sin( fractional_s * deltaAngleSineSpeed[i] ) ) ); currentDirection[i].rotate( &angle ); // sub-step the angle deltaAngle[i] += subStepFraction * deltaDeltaAngle[i]; // sub-step the deltaDelta angle deltaDeltaAngle[i] += subStepFraction * deltaDeltaDeltaAngle[i]; // sub-step step currentX[i] += currentDirection[i].mX; currentY[i] += currentDirection[i].mY; // wrap if( currentX[i] > 1 ) { currentX[i] -= 1; } else if( currentX[i] < 0 ) { currentX[i] += 1; } if( currentY[i] > 1 ) { currentY[i] -= 1; } else if( currentY[i] < 0 ) { currentY[i] += 1; } } /* // old delta code, before we moved this stuff into // the sub-steps // problem: because we rotated only after each batch of substeps, // each batch of substeps resulted in a straight line segment // these line segments were highly visible in larger images (for // example, when there are 16 immortals). // moving these rotation steps into the substeps (and breaking them // up into rotation substeps) fixed the problem // rotate Angle3D angle( 0, 0, deltaAngle[i] + deltaAngleSineWeight[i] * sin( s * deltaAngleSineSpeed[i] ) ); currentDirection[i].rotate( &angle ); // step angle deltaAngle[i] += deltaDeltaAngle[i]; // step deltaDelta deltaDeltaAngle[i] += deltaDeltaDeltaAngle[i]; */ } } for( i=0; i void testPortal() { printf( "Test int = %ld\n", globalRandomSource.getRandomInt() ); // call again to put in different state printf( "Test int = %ld\n", globalRandomSource.getRandomInt() ); Portal portal; while( ! portal.isOpen() ) { portal.upgrade(); portal.passTime( 10000 ); } int numGardeners = 16; for( int i=0; imUserControlling = true; } portal.sendGardener( gardener, &startPosition, &startRotation ); portal.passTime( 10000 ); delete gardener; } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/features.cpp0000640000175000017500000000543410544402364023042 0ustar pabspabs/* * Modification History * * 2006-September-27 Jason Rohrer * Created. */ #include "features.h" #include "minorGems/io/file/File.h" #include // set defaults // all default to true unless set to 0 in features.txt char Features::largeWindow = true; char Features::drawClouds = true; char Features::drawSurfaceNoise = true; char Features::drawNiceCircles = true; char Features::drawNicePlantLeaves = true; char Features::drawComplexGardeners = true; char Features::drawSoil = true; char Features::drawWater = true; char Features::drawShadows = true; char Features::drawComplexPortal = true; // map from string names to feature variable pointers // used in the loop below to read from features.txt int numFeatures = 10; const char *nameMap[10] = { "largeWindow", "drawClouds", "drawSurfaceNoise", "drawNiceCircles", "drawNicePlantLeaves", "drawComplexGardeners", "drawSoil", "drawWater", "drawShadows", "drawComplexPortal" }; char *variableMap[10] = { &( Features::largeWindow ), &( Features::drawClouds ), &( Features::drawSurfaceNoise ), &( Features::drawNiceCircles ), &( Features::drawNicePlantLeaves ), &( Features::drawComplexGardeners ), &( Features::drawSoil ), &( Features::drawWater ), &( Features::drawShadows ), &( Features::drawComplexPortal ) }; void initializeFeatures() { FILE *featuresFile = fopen( "features.txt", "r" ); char stringBuffer[100]; int switchValue; if( featuresFile != NULL ) { int numRead = 2; while( numRead == 2 ) { // read more // read a string and a number numRead = fscanf( featuresFile, " %99s %d ", stringBuffer, &switchValue ); if( numRead == 2 ) { // process these values // look for a match in our feature map char found = false; for( int i=0; i mFlowerTerminusIndicies; SimpleVector mFlowerStages; SimpleVector mFlowerAngles; char mLeafTerminiiSet; SimpleVector mLeafTerminii; double mTimeSinceLastFlower; SimpleVector mFruit; // fruit attatched to leaf terminus after flower fades SimpleVector mFruitTerminusIndices; SimpleVector mFruitAngles; char mHighlightRipeFruit; double mWaterStatus; char mJustWatered; // become poisoned gradually double mPoisonStatus; char mPoisoned; double mGrowth; double mTimeSinceLastFruit; double mTimeBetweenFruits; double mWaterConsumptionRate; double mGrowthRate; double mCurrentSoilType; double mStartZAngle; SingleTextureGL *mJointCapTexture; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/languages/0000750000175000017500000000000011401021142022437 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/gameSource/languages/Portuguese.txt0000640000175000017500000002174310730301302025355 0ustar pabspabstip_PlantButton "Semear a semente seleccionada" tip_PlotButton "Escolher um novo pedaco de terra" tip_water_pickup "Encher " tip_water_dump "Regar " tip_WaterButton "Agua" tip_HarvestButton "Colher o fruto" tip_MateButton "Amigo" tip_lead "Dirigir" tip_stop_leading "Parar de dirigir" tip_LeadButton "outro jardineiro" tip_PoisonButton "Envenenar uma planta" tip_PauseButton "Pausa" tip_RestartButton "Recomecar" tip_QuitButton "Sair" tip_EatButton "Comer o fruto seleccionado" tip_GiftButton "Dar o fruto seleccionado" tip_DiscardButton "Deitar fora o elemento seleccionado" tip_fruit "Frutos conservados" tip_poisoned "(envenenado)" tip_seed "Semente conservada" tip_darkSoil "-- gosta de solos escuros" tip_lightSoil "-- gosta de solos claros" tip_StopTutorial "Parar o tutorial" tip_NextTutorial "Continuar o tutorial" tutorial_question "Voce gostaria de ver o tutorial? Clique no botao verde 'Continuar' para iniciar o tutorial, ou clique sobre o botao vermelho 'Parar' para fechar o tutorial." tutorial_welcome1 "As janelas do tutorial pausam o jogo. Quando terminar a leitura de uma mensagem, clique no botao verde 'Continuar'." tutorial_welcome2 "Este tutorial vai orienta-lo atraves de um jogo inteiro de Cultivation. Mas atencao : isto pode revelar detalhes e estrategias que seria mais divertido descobrir por conta propria." tutorial_moving "Primeira coisa : passear. Clique no mapa para mover o seu jardineiro. Clique no botao verde 'Continuar' para experimentar." tutorial_survival1 "Seu primeiro objectivo e sobreviver. Se voce nao comer, os seus nutrientes tornar-se-ao esgotados, e voce vai envelhecer mais depressa." tutorial_survival2 "Ha 3 nutrientes: Vermelho, Amarelo e Violeta. As 3 esferas na zona traseira do seu jardineiro mostram seus niveis nutricionais." tutorial_survival3 "Voce usa mais nutrientes quando voce passeia ou carregando algo como agua." tutorial_survival4 "Voce pode abastecer-se em nutrientes comendo fruta, mas voce precisara obter-la primeiro. Uma maneira de obter frutos e cultivar-los." tutorial_seeds1 "Voce comeca o jogo com algumas sementes armazenadas em sua barra lateral esquerda. Voce pode planta-las para obter frutos." tutorial_plot "Mas antes que voce possa fazer isso, e necessario que voce escolha um pedaco de terra para si mesmo. Clique no botao Escolher e, em seguida, clique e arraste um rectangulo no mapa." tutorial_plant "Agora entramos em seu enredo, clique em uma das sementes armazenadas para selecionar-la e, em seguida, clique no botao 'Semear'." tutorial_water1 "As plantulas precisam de agua para crescer. Mova para a agua e pressione o botao 'Agua' para pegar agua." tutorial_water2 "Mova de volta ao seu plantula e pressione o botao 'Agua' novamente para regar." tutorial_water3 "Um circulo colorido sobre uma planta significa que ela parou de crescer e precisa de mais agua. Quando uma planta cresceu, voce pode parar de rega-la." tutorial_plot2 "Voce so pode se ocupar das plantas que se encontram em seu enredo, mas voce pode alterar o seu enredo, a qualquer momento." tutorial_plot3 "Seu enredo pode coincidir com os de outras parcelas jardineiros. Nesse caso, voce e os outros estarao em conflito sobre as plantas na sobreposicao." tutorial_harvest1 "Voce tem alguns frutos maduros em seu enredo. Mova perto da planta e carrugue o botao 'Colher'." tutorial_eat1 "A fruta esta agora armazenada em sua barra lateral esquerda. Clique na fruta para seleciona-la e, em seguida, clique no botao 'Comer' para comer-la." tutorial_eat2 "Um fruto de uma determinada cor e elevado no nutriente dessa cor, mas ela contem apenas vestigios dos outros nutrientes." tutorial_seeds2 "Note que comer um fruto deu-lhe uma nova semente. Cada fruta contem uma semente." tutorial_seeds3 "A cor de uma semente mostra que tipo de solo ela prefere. Sementes claras preferem solos claros, bem como as sementes escuras solos escuros. Um tipo de solo ideal ajuda uma planta a crescer mais depressa." tutorial_seeds4 "O circulo colorido ao lado de cada semente mostra de que cor serao os frutos produzidos pelas plantas resultantes." tutorial_seeds5 "Para produzir frutas, as plantas cruzam-se com qualquer planta perto delas. Das sementes de frutos resultantes irao crescer plantas hibridas." tutorial_seeds6 "Colocando certas plantas proximas umas das outras, voce pode seletivamente obter melhores frutos. Por exemplo, voce pode tentar criar plantas que produzem mais frutos." tutorial_gift1 "Voce tambem pode dar frutos para outro jardineiro. Primeiro, meta-se perto do outro jardineiro. Clique na fruta para seleciona-la e, em seguida, clique no botao 'Dar'." tutorial_emotion1 "Seu dom fez que o jardineiro o aprecie mais. + O '+' verde que voou de volta para voce indicou o prazer dele." tutorial_emotion2 "Se voce ve um 'x' vermelho voando em direcao a seu jardineiro, significa que voce esta fazendo outro jardineiro zangado." tutorial_emotion3 "Voce pode zangar os outros jardineiros ao plantar ou colher nas parcelas deles. Isso so pode acontecer se as parcelas estao em sobreposicao." tutorial_lifespan1 "Sua vida e representada pela bola de plasma no centro do seu jardineiro. Ja perdeu metade da sua dimensao, o que significa que a sua vida ja passou de metade." tutorial_lifespan2a "Voce produziu uma ninhada, assim voce tera controle dela quando voce morrer." tutorial_lifespan2b "Voce esta gravida. Caso voce pode dar a luz com exito antes que voce morrer, voce vai assumir o controle dela quando termina a sua vida actual." tutorial_lifespan2c "Seu relogio biologico avanca. Esta na hora de pensar no acasalamento. Se voce nao tem descendencia quando voce morrer, o jogo vai acabar!" tutorial_mating1 "Para simpatisar, meta-se perto de outro jardineiro. Se ele vos apreciar o suficiente, e, se nao estiver ja gravida, o botao 'Amigo' sera activado." tutorial_mating2 "Se ele nao gostar o suficiente para acasalamento, de-lhe uma prenda ou duas. Talvez sejam frutos da paixao..." tutorial_mating3 "Voce pode zangar outro jardineiro pelo acasalamento com um jardineiro que ela gosta muito. Tenha cuidado quando voce escolhe um amigo." tutorial_pregnant1 "Voce esta gravida. Mantenha os tres niveis de nutrientes, a fim de que a gravidez possa progredir." tutorial_birth1 "Parabens, voce so deu origem a um bebe saltitante ... jardineiro! O seu bebe ira seguir - vos e partilhar todos os alimentos que ingerimos por um tempo." tutorial_birth2 "Comer uma dieta equilibrada para manter o seu bebe crescido. Quando sera crescido que chegue saira por si mesmo." tutorial_poisoned1 "Voce passou num solo envenenado, no entanto alguns dos seus frutos armazenados ficaram envenenados." tutorial_poisoned2 "Comer frutos envenenados vai provocar uma morte rapida." tutorial_death1 "Voce morreu. Note o anel que fara no local de sua morte. Este e o primeiro anel no portao de imortalidade." tutorial_death2 "Quatro aneis mais devem ser abertos antes que o portao abra. Os jardineiros que voce controla sao especiais: eles tem o poder de abrir estes aneis por morrer." tutorial_death3 "Para abrir mais aneis, e preciso levar o seu futuro jardineiros volta para o centro do portao logo antes de morrer." tutorial_death4 "Se o seu jardineiro morre longe do local de um portao existente, nenhum novo anel sera aberta." tutorial_death5 "O portao e destruido por um terreno envenenado . Caso isso aconteca, voce tera que iniciar um novo portao noutro sitio." tutorial_death1a "Voce simplesmente morreu novamente. Desta vez, voce nao tem descendencia, para que o seu jogo esta terminado. Carregue no botao 'Recomecar' para iniciar um novo jogo." tutorial_death6a "Infelizmente, voce nao tem descendencia que possa controlar, para que o jogo esta terminado. Carregue no botao 'Recomecar' para iniciar um novo jogo." tutorial_death6b "Voce tomou o controle da sua proxima ninhada." tutorial_gate1 "A porta esta aberta a imortalidade. Cada jardineiro que passe no portao ira se tornar imortal." tutorial_gate2 "Quando o seu jardineiro entrar no portao, ele fecha-se, e nenhum outro jardineiro podera passar atraves dele." tutorial_gate3 "Se voce quiser levar algum jardineiros com voce, voce tera que leva-los atraves do portao sem passar o portao voce mesmo." tutorial_gate4 "Voce pode usar o botao 'Dirigir' para fazer isso. Mas so os jardineiros que o apreciam vao seguir-lo." tutorial_gate5 "Quanto mais jardineiros voce salvar, a sua maior recompensa." tutorial_immortal1 "O portao de imortalidade esta agora fechado, e os jardineiros que voce escolheu foram eternizados." tutorial_immortal2 "Voce recebeu sua recompensa, mas talvez nao a tenha encontrado ainda. A recompensa e algo que voce pode levar com voce fora do jogo." tutorial_immortal3 "Saia do jogo e, em seguida, dar uma olhada ao redor para achar sua recompensa. Ou, se voce na gosta de procurar, clique no botao Continuar deste tutorial para uma dica." tutorial_immortal4 "Tem certeza de que quer uma dica?" tutorial_immortal5 "Olhe por um ficheiro PNG imortal em sua pasta Cultivation. Voce deve ser capaz de ve-lo com um navegador web." Cultivation_9+dfsg1_UnixSource/game2/gameSource/languages/English.txt0000640000175000017500000002117510544426736024631 0ustar pabspabstip_PlantButton "Plant selected seed" tip_PlotButton "Pick a new plot of land" tip_water_pickup "Pick up " tip_water_dump "Dump " tip_WaterButton "water" tip_HarvestButton "Harvest fruit" tip_MateButton "Mate" tip_lead "Lead " tip_stop_leading "Stop leading " tip_LeadButton "another gardener" tip_PoisonButton "Poison a plant" tip_PauseButton "Pause" tip_RestartButton "Restart" tip_QuitButton "Quit" tip_EatButton "Eat selected fruit" tip_GiftButton "Give selected fruit" tip_DiscardButton "Discard selected item" tip_fruit "Stored fruit " tip_poisoned "(poisoned)" tip_seed "Stored seed " tip_darkSoil "-- likes dark soil" tip_lightSoil "-- likes light soil" tip_StopTutorial "Stop tutorial" tip_NextTutorial "Continue tutorial" tutorial_question "Would you like to view the tutorial? Click the green Continue button to start the tutorial, or click the red Stop button to skip the tutorial." tutorial_welcome1 "The tutorial window freezes the game in the background. When you're done reading a given tutorial message, click the green Continue button." tutorial_welcome2 "This tutorial will walk you through a full game of Cultivation. Fair warning: it may reveal details and strategies that would be more fun to discover on your own." tutorial_moving "First things first: moving around. Click on the map to move your gardener. Click the green Continue button to try it." tutorial_survival1 "Your first goal is basic survival. If you don't eat, your nutrients will become depleted, and you will age faster." tutorial_survival2 "There are three nutrients: Red, Yellow, and Purple. The three spheres at the rear of your gardener show your nutrient levels." tutorial_survival3 "You use nutrients faster when you are actively moving around or carrying something like water." tutorial_survival4 "You can replenish nutrients by eating fruit, but you need to obtain some first. One way to get fruit is to grow it yourself." tutorial_seeds1 "You start the game with some seeds stored in your left side bar. You can plant them to grow fruit." tutorial_plot "But before you can do that, you need to pick a plot of land for yourself. Click the Plot button and then click-and-drag a rectangle on the map." tutorial_plant "Now move into your plot, click a stored seed to select it, and then click the Plant button." tutorial_water1 "Your seedling needs water to grow. Move into the water and press the Water button to pick up water." tutorial_water2 "Move back to your seedling and press the Water button again to dump the water." tutorial_water3 "A tan circle over a plant means it has stopped growing and needs more water. Once a plant is full-grown, you can stop watering it." tutorial_plot2 "You can only tend plants that are in your plot, but you can change your plot at any time." tutorial_plot3 "Your plot can overlap with the plots of other gardeners. In that case, you and the others would be in conflict over the plants in the overlap." tutorial_harvest1 "You have some ripe fruit in your plot. Move near the plant and press the Harvest button." tutorial_eat1 "A fruit is now stored in your left side bar. Click the fruit to select it, and then click the Eat button to eat it." tutorial_eat2 "A fruit of a given color is high in that color nutrient, but it contains only trace amounts of the other color nutrients." tutorial_seeds2 "Notice that eating a fruit gave you another stored seed. Each fruit contains one seed." tutorial_seeds3 "The color of a seed shows what kind of soil it prefers. Light seeds like light soil, and dark seeds like dark soil. An ideal soil type makes a plant grow faster." tutorial_seeds4 "The colored circle next to each seed shows what color fruit will be produced by the resulting plant." tutorial_seeds5 "To produce fruit, plants crossbreed with whatever plant is closest to them. Seeds from the resulting fruit will grow genetic hybrid plants." tutorial_seeds6 "By placing certain plants close together, you can selectively breed desirable traits. For example, you might try to breed plants with heavy fruit loads." tutorial_gift1 "You can also give fruit to another gardener. First, move near the other gardener. Click the fruit to select it, and then click the Give button." tutorial_emotion1 "Your gift made that gardener like you a bit more. The green + that flew back to you indicated the other gardener's pleasure." tutorial_emotion2 "If you see a red x flying toward your gardener, that means you are making another gardener angry." tutorial_emotion3 "You can make other gardeners angry by planting in their plots or harvesting from their plants. This can only happen if your plots overlap." tutorial_lifespan1 "Your lifespan is represented by the plasma ball at the center of your gardener. It just shrunk to half its size, so that means your life is half over." tutorial_lifespan2a "You already have produced an offspring, so you will take control of that offspring when you die." tutorial_lifespan2b "You're already pregnant. If you can successfully give birth before you die, you will take control of that offspring when your current life ends." tutorial_lifespan2c "Your biological clock is ticking. It's time to think about mating. If you have no offspring when you die, your game will end." tutorial_mating1 "To mate, move near another gardener. If they like you enough, and if they are not already pregnant, the Mate button will be enabled." tutorial_mating2 "If they don't like you enough to mate, give them a gift or two. What's that old saying... Candy is dandy? Well, fruit will have to suffice." tutorial_mating3 "You can make another gardener angry by mating with a gardener that it likes a lot. Be careful when you pick a mate." tutorial_pregnant1 "You're pregnant. Keep all three nutrient levels up so that the pregnancy can progress." tutorial_birth1 "Congratulations, you just gave birth to a bouncing baby... gardener! Your baby will follow you and share all the food you eat for a while." tutorial_birth2 "Eat a balanced diet to keep your baby growing. Once full grown, it will head out on its own." tutorial_poisoned1 "You just moved across poisoned ground, and some of your stored fruit became poisoned." tutorial_poisoned2 "Eating poisoned fruit will bring on a quick death." tutorial_death1 "You just died. Notice the ring that will form at the location of your death. That is the first ring in the gate to immortality." tutorial_death2 "Four more rings must be opened before the gate itself opens. The gardeners you control are special: they have the power to open these rings by dying." tutorial_death3 "To open more rings, you need to bring your future gardeners back to the center of the gate right before they die." tutorial_death4 "If your gardener dies away from the site of an existing gate, no new ring will be opened." tutorial_death5 "A gate is destroyed by poisoned ground. If this happens, you will need to start a new gate elsewhere." tutorial_death1a "You just died again. This time, you have no offspring to take control of, so your game is over. Hit the Restart button to start a new game." tutorial_death6a "Unfortunately, you have no offspring to take control of, so your game is over. Hit the Restart button to start a new game." tutorial_death6b "You just took control of your next offspring." tutorial_gate1 "The gate to immortality is now open. Each gardener that moves into the gate will become immortal." tutorial_gate2 "When your gardener moves into the gate, the gate will close, and no other gardeners will be able to pass through it." tutorial_gate3 "If you want to take some gardeners with you, you will need to lead them through the gate without stepping into the gate yourself." tutorial_gate4 "You can use the Lead button to do this. Gardeners will only follow you if they like you a lot, though." tutorial_gate5 "The more gardeners you save, the larger your reward." tutorial_immortal1 "The gate to immortality has now closed, and the gardeners you chose have been immortalized." tutorial_immortal2 "You have received your reward, but you might not have found it yet. The reward is something that you can take with you outside the game." tutorial_immortal3 "Quit the game, and then take a look around for your reward. Or, if you are a spoil-sport, click the Continue button in this tutorial for a hint." tutorial_immortal4 "Are you sure you want a hint?" tutorial_immortal5 "Look at the immortal PNG file in in your Cultivation folder. You should be able to view it with a web browser." Cultivation_9+dfsg1_UnixSource/game2/gameSource/languages/French.txt0000640000175000017500000002341010626536772024442 0ustar pabspabstip_PlantButton "Planter la graine selectionnee" tip_PlotButton "Choisir un terrain" tip_water_pickup "Prendre " tip_water_dump "Lacher " tip_WaterButton "Eau" tip_HarvestButton "Recolter fruit" tip_MateButton "Ami" tip_lead "Conduire" tip_stop_leading "Arreter de conduire " tip_LeadButton "un autre jardinier" tip_PoisonButton "Empoisonner" tip_PauseButton "Pause" tip_RestartButton "Reprendre" tip_QuitButton "Quitter" tip_EatButton "Manger le fruit selectionne" tip_GiftButton "Donner le fruit selectionne" tip_DiscardButton "Jeter l'objet selectionne" tip_fruit "Fruits stockes " tip_poisoned "(empoisonnee)" tip_seed "Graine stockee " tip_darkSoil "-- aime le sol sombre" tip_lightSoil "-- aime le sol clair" tip_StopTutorial "Arreter le tutoriel" tip_NextTutorial "Continuer le tutoriel" tutorial_question "Voulez-vous suivre le tutoriel ? Cliquez sur le bouton vert 'Continuer' pour le demarrer , ou cliquez sur le bouton rouge 'Arreter' pour fermer le tutoriel." tutorial_welcome1 "La fenetre de tutoriel gele le jeu en arriere-plan. Quand vous avez termine de lire un message du tutoriel, cliquez sur le bouton vert 'Continuer'." tutorial_welcome2 "Ce tutoriel vous fera suivre une partie complete de Cultivation, mais attention ! : il pourrait reveler des details et des strategies qu'il serait plus amusant de decouvrir vous-meme..." tutorial_moving "Premiere chose : se promener. Cliquez sur la carte pour deplacer votre jardinier. Cliquez sur le bouton vert 'Continuer' pour essayer." tutorial_survival1 "Votre premier objectif est la survie. Si vous ne mangez pas, vos nutriments vont s'epuiser, et vous vieillirez plus rapidement." tutorial_survival2 "Il y a 3 nutriments : Rouge, Jaune, et Violet. Les 3 spheres a l'arriere de votre jardinier montre les niveaux des nutriments." tutorial_survival3 "Vous utilisez plus de nutriments si vous bougez beaucoup ou portez des choses telles que de l'eau." tutorial_survival4 "Vous pouvez refaire le plein de nutriments en mangeant des fruits, mais il faut d'abord en obtenir. Une facon de faire est de les faire pousser vous-meme." tutorial_seeds1 "Vous demarrez le jeu avec quelques graines stockees dans la panneau lateral gauche. Vous pouvez les planter pour faire pousser des fruits." tutorial_plot "Mais avant de faire cela, vous devez delimiter votre terrain. Cliquez sur le bouton 'Choisir un terrain' puis selectionnez un rectangle sur la carte." tutorial_plant "Maintenant deplacez-vous sur votre terrain, cliquez sur une graine stockee pour la selectionner, puis cliquez sur le bouton 'Planter'." tutorial_water1 "Vos semences ont besoin d'eau pour croitre. Rendez vous sur l'eau puis pressez le bouton 'Eau' pour prendre de l'eau.." tutorial_water2 " Retournez a vos plantations et pressez le bouton 'Eau' a nouveau pour lacher l'eau." tutorial_water3 "Un halo circulaire au dessus d'une plante indique que sa croissance est stoppee et qu'elle necessite plus d'eau. Une fois qu'une plante a fini sa croissance, vous pouvez arreter de l'arroser." tutorial_plot2 "Vous pouvez uniquement vous occuper des plantes se trouvant sur votre terrain, mais vous pouvez changer votre terrain a tout moment." tutorial_plot3 "Votre terrain peut chevaucher les terrains d'autres jardiniers. Dans ce cas vous et les autres jardiniers serez en conflit sur les plantes se trouvant dans la zone de chevauchement." tutorial_harvest1 "Vous avez des fruits murs sur votre terrain. Placez vous pres de la plante et pressez le bouton 'Recolter'." tutorial_eat1 "Un fruit est maintenant stocke dans le panneau lateral. Cliquez sur le fruit pour le selectionner, puis cliquez sur le bouton 'Manger' pour le manger." tutorial_eat2 "Un fruit d'une couleur a une haute valeur dans le nutriment de cette couleur, mais contient seulement de faibles quantites des nutriments des autres couleurs." tutorial_seeds2 "Remarquez que manger un fruit vous a donne une nouvelle graine. Chaque fruit contient une graine." tutorial_seeds3 "La couleur d'une graine montre le type de terrain qu'elle prefere : les graines claires preferent les sols clairs, les graines sombres les sols sombres. Une graine plantee sur son sol favori a une croissance optimale." tutorial_seeds4 "Le cercle colore a cote de chaque graine indique de quelle couleur sera le fruit produit par la plante qui en resultera." tutorial_seeds5 "Pour produire des fruits, les plantes se croisent avec n'importe quelle plante pres d'elles. Les graines du fruit resultant produiront des plantes genetiquement hybrides." tutorial_seeds6 "En placant certaines plantes ensemble, vous pouvez faire obtenir de maniere selective certaines capacites voulues. Par exemple vous pouvez essayer de faire pousser des plantes qui produisent de grandes quantites de fruits." tutorial_gift1 "Vous pouvez egalement donner un fruit a un autre jardinier. D'abord, placez-vous pres du jardinier. Selectionnez le fruit que vous souhaitez donner, puis cliquez sur le bouton 'Donner'." tutorial_emotion1 "Grace a ce cadeau, ce jardinier vous apprecie plus. Le '+' vert qui vous arrive indique la satisfaction du jardinier." tutorial_emotion2 "Si vous voyez un 'x' rouge voler vers votre jardinier, cela signifie que vous mettez en colere un autre jardinier." tutorial_emotion3 " Vous pouver facher les autres jardiniers en plantant sur leur terrain ou en recoltant leurs plantes. Cela ne peut se produire que si les terrains se chevauchent." tutorial_lifespan1 " Votre duree de vie est representee par la boule de plasma au centre de votre jardinier. Il a diminue de moitie, ce qui signifie que votre vie est a moitie termine." tutorial_lifespan2a " Vous avez deja produit une descendance, dont vous prendrez le controle a votre mort." tutorial_lifespan2b " Vous etes feconde. Si vous arrivez a donner naissance avant de mourir, vous prendrez le controle de ce descendant a la fin de votre vie." tutorial_lifespan2c " Votre horloge biologique tourne. Il est temps de penser a sa faire des amis. Si vous n'avez pas de descendance a votre mort, le jeu se terminera !" tutorial_mating1 "Pour sympathiser, placez vous pres d'un jardinier. S'il vous apprecie assez et que vous n'etes pas deja feconde,le bouton "Ami" sera active." tutorial_mating2 "S'ils ne vous apprecie pas assez, donnez lui un fruit ou deux. Esperons que ce soient les fruits de la passion ;) " tutorial_mating3 "Vous pouvez mettre un colere un jardinier en faisant ami-ami avec un jardinier qu'il apprecie beaucoup. Faites attention quand vous choisissez votre ami." tutorial_pregnant1 "Vous etes feconde. Gardez un niveau suffisant pour les 3 nutriments afin que la grossesse progresse.." tutorial_birth1 "Felicitation ! Vous venez de donner naissance a un bebe jardinier fretillant... Votre bebe vous suivra et partagera votre nourriture pdenant un moment." tutorial_birth2 "Mangez equilibre pour que votre bebe grandisse. Une fois assez grand il sortira de lui-meme." tutorial_poisoned1 "Vous venez de vous deplacer sur un sol empoisonne, et certains de vos fruits ont ete contamines." tutorial_poisoned2 "Manger des fruits empoisonnes vous provoquera une mort rapide." tutorial_death1 "Vous venez de mourir. Remarquez l'anneau qui se forme a l'endroit de votre mort : c'est le premier anneau dans le portail pour l'immortalite." tutorial_death2 "4 anneaux supplementaires doivent etre crees pour que le portail s'ouvre. Les jardiniers que vous controlez sont speciaux : ils ont le pouvoir d'ouvrir ces anneaux en mourrant." tutorial_death3 "Pour ouvrir plus d'anneaux, vous devez ramener vos futurs jardiniers au centre du portail juste avant qu'ils ne meurent." tutorial_death4 "Si votre jardinier meurt ailleurs qu'a un portail, aucun nouvel anneau ne sera ouvert.." tutorial_death5 "Un portail peut etre detruit par un sol empoisonne. Si cela se produit, vous devrez creer un autre portail ailleurs." tutorial_death1a "Vous etes mort a nouveau. Cette fois, vous n'avez pas de descendance que vous pouvez controler, donc la partie est terminee. Cliquez sur le bouton "Redemarrer" pour commencer une nouvelle partie." tutorial_death6a "Malheureusement,vous n'avez pas de descendance que vous pouvez controler, donc la partie est terminee. Cliquez sur le bouton "Redemarrer" pour commencer une nouvelle partie." tutorial_death6b "Vous venez de prendre le controle de votre descendance." tutorial_gate1 "Le portail vers l'immortalite est maintenant ouvert. Chaque jardinier qui passera ce portail deviendra immortel." tutorial_gate2 "Quand votre jardinier entrera dans le portail, celui-ci se refermera, et aucun autre jardinier ne pourra passer a travers." tutorial_gate3 "Si vous souhaitez emmener des jardiniers avec vous, vous devrez les conduire a travers le portail sans passer le portail vous-meme." tutorial_gate4 "Vous pouvez utiliser le bouton 'Conduire' pour cela. Neanmoins seuls les jardiniers qui vous apprecient vous suivront." tutorial_gate5 "Plus vous sauvez de jardiniers, plus grande sera votre recompense." tutorial_immortal1 "La porte vers l'immortalite est maintenant refermee, et les jardiniers que vous avez choisis sont maintenant immortels." tutorial_immortal2 "Vous avez recu une recompense, mais vous ne l'avez peut etre pas encore decouverte. Cette recompense est quelque chose que vous pouvez prendre avec vous en dehors du jeu." tutorial_immortal3 "Quittez le jeu et jetez un oeil aux alentours pour trouver votre recompense.Si vous donnez votre langue au chat, cliquez sur le bouton 'Continuer' pour avoir un indice..." tutorial_immortal4 "Vous etes sur de vouloir un indice ?" tutorial_immortal5 "Examinez le fichier PNG immortel dans votre dossier Cultivation. Vous devriez pouvoir le visualiser avec un navigateur web." Cultivation_9+dfsg1_UnixSource/game2/gameSource/GardenerAI.cpp0000640000175000017500000006406610504011527023164 0ustar pabspabs/* * Modification History * * 2006-July-7 Jason Rohrer * Created. * * 2006-September-12 Jason Rohrer * Fixed so that we don't try to mate with a pregnant partner. * Fixed the machine-gun mating bug. */ #include "GardenerAI.h" #include "gameFunctions.h" #include #include #include "minorGems/util/random/StdRandomSource.h" extern StdRandomSource globalRandomSource; GardenerAI::GardenerAI( Gardener *inGardener, World *inWorld ) : mGardener( inGardener ), mWorld( inWorld ), mNextPlantingLocation( NULL ), mSecondsSinceLastGift( 0 ), mSecondsSinceLastRevenge( 0 ) { } GardenerAI::~GardenerAI() { if( mNextPlantingLocation != NULL ) { delete mNextPlantingLocation; } } void GardenerAI::passTime( double inTimeDeltaInSeconds ) { // before doing anything else, check if we are still following parent Gardener *parent = mGardener->getParentToFollow(); if( parent != NULL && ! parent->isDead() ) { // follow it if we get too far away Vector3D *destination; if( mWorld->getGardenerDistance( mGardener, parent ) > 10 ) { // move closer destination = mWorld->getGardenerPosition( parent ); } else { // stay where we are destination = mWorld->getGardenerPosition( mGardener ); } mGardener->setDesiredPosition( destination ); delete destination; return; } mSecondsSinceLastGift += inTimeDeltaInSeconds; mSecondsSinceLastRevenge += inTimeDeltaInSeconds; // first check if hungry int lowIndex = -1; for( int i=0; i<3; i++ ) { if( mGardener->getNutrientLevel(i) == 0 ) { lowIndex = i; } } if( lowIndex != -1 ) { // low in at least one nutrient // try to find a fruit high in that nutrient int index = mGardener->getIndexOfFruitHighInNutrient( lowIndex ); if( index != -1 ) { mGardener->setSelectedObjectIndex( index ); // eat selected fruit mGardener->eat(); } } // next deal with creating plot Vector3D *plotCenter = mWorld->getPlotCenter( mGardener ); if( plotCenter == NULL ) { // need to pick a new plot // walk toward water until we hit it, or until we get // too close to other gardeners Vector3D *waterPoint = mWorld->getClosestWater( mGardener ); double minPleasantDistance = 10; char tooCloseToOtherGardeners = false; Gardener *closestGardener = mWorld->getClosestGardener( mGardener ); if( closestGardener != NULL ) { Vector3D *ourPosition = mWorld->getGardenerPosition( mGardener ); Vector3D *closestGardenerPosition = mWorld->getGardenerPosition( closestGardener ); double distance = ourPosition->getDistance( closestGardenerPosition ); if( distance < minPleasantDistance ) { tooCloseToOtherGardeners = true; } delete ourPosition; delete closestGardenerPosition; } if( ! tooCloseToOtherGardeners && ! mWorld->isInWater( mGardener ) ) { mGardener->setDesiredPosition( waterPoint ); } else { // we just hit the water, or we came too close to other // gardeners // create our plot Vector3D *position = mWorld->getGardenerPosition( mGardener ); Vector3D a( position ); // vector pointing away from water center Vector3D rayFromWaterPoint( position ); rayFromWaterPoint.subtract( waterPoint ); rayFromWaterPoint.normalize(); // plot diagonal of 20 world units rayFromWaterPoint.scale( 20 ); // add this ray to our position position->add( &rayFromWaterPoint ); Vector3D b( position ); delete position; // thus, we pick a plot bordering the water that has a diagonal // length roughly equal to the water's radius // this can be a "skinny" rectangle, though, so widen it if // needed double diffX = fabs( a.mX - b.mX ); double diffY = fabs( a.mY - b.mY ); if( diffX < diffY ) { // taller than wide double increase = diffY - diffX; if( a.mX < b.mX ) { b.mX += increase; } else { a.mX += increase; } } if( diffY < diffX ) { // wider than tall double increase = diffX - diffY; if( a.mY < b.mY ) { b.mY += increase; } else { a.mY += increase; } } mWorld->setGardenerPlot( mGardener, &a, &b ); } delete waterPoint; return; } // we have a plot // check that there are enough plants in it int targetPlantCount = (int)( mGardener->mGenetics.getParameter( desiredPlantCount ) ); SimpleVector *plants = mWorld->getPlotPlants( mGardener ); int numPlants = plants->size(); SimpleVector *seedsVector = mGardener->getAllSeeds(); int numSeeds = seedsVector->size(); delete seedsVector; if( numSeeds > 0 && numPlants < targetPlantCount ) { // plant more if( mNextPlantingLocation == NULL ) { // haven't picked a spot yet char foundPlantable = false; int numTries = 0; int maxNumTries = 10; while( !foundPlantable && numTries < maxNumTries ) { Vector3D *cornerA, *cornerB; mWorld->getGardenerPlot( mGardener, &cornerA, &cornerB ); double x = globalRandomSource.getRandomBoundedDouble( cornerA->mX, cornerB->mX ); double y = globalRandomSource.getRandomBoundedDouble( cornerA->mY, cornerB->mY ); mNextPlantingLocation = new Vector3D( x, y, 0 ); delete cornerA; delete cornerB; if( mWorld->canPlant( mNextPlantingLocation ) ) { foundPlantable = true; } else { // try again delete mNextPlantingLocation; mNextPlantingLocation = NULL; } numTries++; } } if( mNextPlantingLocation != NULL ) { Vector3D *gardenerPosition = mWorld->getGardenerPosition( mGardener ); if( ! gardenerPosition->equals( mNextPlantingLocation ) ) { // move to next plant location mGardener->setDesiredPosition( mNextPlantingLocation ); } else { // at next location: // make sure we can still plant // else pick another location at next time step if( mWorld->canPlant( mNextPlantingLocation ) ) { // plant here double soilCondition = mWorld->getSoilCondition( mNextPlantingLocation ); SimpleVector *seedsVector = mGardener->getAllSeeds(); // find best for this soil Seeds *best = NULL; double minSoilDistance = 2; for( int i=0; isize(); i++ ) { Seeds *seeds = *( seedsVector->getElement( i ) ); double distance = fabs( seeds->mIdealSoilType - soilCondition ); if( distance < minSoilDistance ) { minSoilDistance = distance; best = seeds; } } delete seedsVector; if( best != NULL ) { mWorld->addPlant( mGardener, new Plant( soilCondition, best ), mNextPlantingLocation ); mGardener->removeSeeds( best ); } } delete mNextPlantingLocation; mNextPlantingLocation = NULL; } delete gardenerPosition; } else { // tried to pick a plantable location, but failed // expand plot Vector3D *a, *b; mWorld->getGardenerPlot( mGardener, &a, &b ); // compute a vector stretching from b to a Vector3D b_to_a( a ); b_to_a.subtract( b ); // expand plot by 10% in each direction b_to_a.scale( 0.10 ); // push a away from b a->add( &b_to_a ); // also push b away from a // opposite direction b_to_a.scale( -1 ); b->add( &b_to_a ); mWorld->setGardenerPlot( mGardener, a, b ); delete a; delete b; } delete plants; delete plotCenter; return; } // else we have enough plants (or no seeds left) // if any are ripe, harvest them Plant *ripePlant = NULL; int i; for( i=0; igetElement( i ) ); if( thisPlant->isRipe() ) { ripePlant = thisPlant; } } if( ripePlant != NULL ) { // move toward it Vector3D *plantPosition = mWorld->getPlantPosition( ripePlant ); Vector3D *gardenerPosition = mWorld->getGardenerPosition( mGardener ); if( ! gardenerPosition->equals( plantPosition ) ) { // move to plant mGardener->setDesiredPosition( plantPosition ); } else { // already at plant // harvest it mWorld->harvestPlant( mGardener, ripePlant ); } delete gardenerPosition; delete plantPosition; delete plants; delete plotCenter; return; } // else no ripe plants // water plants Plant *driestPlant = NULL; // ignore plants that have at least 1/4 water double driestWaterStatus = 0.25; for( int i=0; igetElement( i ) ); double waterStatus = thisPlant->getWaterStatus(); if( waterStatus < driestWaterStatus ) { driestPlant = thisPlant; driestWaterStatus = waterStatus; } } if( mGardener->getCarryingWater() ) { // already carrying water // move to the driest plant if( driestPlant != NULL ) { // found driest // walk to it Vector3D *plantPosition = mWorld->getPlantPosition( driestPlant ); Vector3D *gardenerPosition = mWorld->getGardenerPosition( mGardener ); if( ! gardenerPosition->equals( plantPosition ) ) { // move to plant mGardener->setDesiredPosition( plantPosition ); } else { // already at plant // dump water mGardener->setCarryingWater( false ); mWorld->dumpWater( mGardener ); } delete gardenerPosition; delete plantPosition; } else { // else no dry plant found // wait and do nothing // head to plot center and wait mGardener->setDesiredPosition( plotCenter ); } } else if( driestPlant != NULL ) { // there is a dry plant, and we're not carrying water // fetch water if( ! mWorld->isInWater( mGardener ) ) { Vector3D *waterPoint = mWorld->getClosestWater( mGardener ); mGardener->setDesiredPosition( waterPoint ); delete waterPoint; } else { // grab water mGardener->setCarryingWater( true ); } } else { // no dry plant, and not carrying water char tryingToMate = false; // if not pregnant // and not target of another pregancy // hand have enough fruit to feel secure if( ! mGardener->isPregnant() && ! mWorld->isTargetOfPregnancy( mGardener ) && mGardener->getStoredFruitCount() >= mGardener->mGenetics.getParameter( storedFruitsBeforeMating ) ) { // we are not pregnant already // we have enough fruit stored // consider mating double ourThreshold = mGardener->mGenetics.getParameter( matingThreshold ); Gardener *mostLiked = mGardener->getMostLikedGardener(); if( mostLiked != NULL ) { double mostLikedMatingThreshold = mostLiked->mGenetics.getParameter( matingThreshold ); if( mGardener->getLikeMetric( mostLiked ) >= ourThreshold && mostLiked->getLikeMetric( mGardener ) >= mostLikedMatingThreshold && ! mostLiked->isPregnant() && ! mWorld->isTargetOfPregnancy( mostLiked ) ) { // we like them enough to mate // and // they like us enough to mate // and // they are not already pregnant // and // they are not already target of another pregnancy tryingToMate = true; Vector3D *ourPosition = mWorld->getGardenerPosition( mGardener ); Vector3D *otherPosition = mWorld->getGardenerPosition( mostLiked ); double distance = ourPosition->getDistance( otherPosition ); if( distance < getMaxDistanceForTransactions() ) { mWorld->mateGardeners( mGardener, mostLiked ); } else { // move toward friend mGardener->setDesiredPosition( otherPosition ); } delete ourPosition; delete otherPosition; } } } // check if we should give fruit to a neighbor char tryingToGiveFruit = false; // wait five seconds between gifts to give them // time to arrive before we reasses the situation if( !tryingToMate && mSecondsSinceLastGift > 5 ) { Gardener *mostLiked = mGardener->getMostLikedGardener(); if( mostLiked != NULL ) { // only give if we have 2+ more fruits than them // (to avoid back and forth giving when there is an // odd number of fruits between the two of us) if( mGardener->getStoredFruitCount() > mostLiked->getStoredFruitCount() + 1 ) { // we have fruit to spare compared to our best friend tryingToGiveFruit = true; Vector3D *ourPosition = mWorld->getGardenerPosition( mGardener ); Vector3D *otherPosition = mWorld->getGardenerPosition( mostLiked ); double distance = ourPosition->getDistance( otherPosition ); if( distance < getMaxDistanceForTransactions() ) { // close enough to give // find out which nutrient they are low in int lowIndex = -1; double lowValue = 2; for( int i=0; i<3; i++ ) { double value = mostLiked->getNutrientLevel(i); if( value < lowValue ) { lowIndex = i; lowValue = value; } } // try to find a fruit high in that nutrient int index = mGardener->getIndexOfFruitHighInNutrient( lowIndex ); // we will always get a valid index here, because // we have checked that we have stored fruit above mGardener->setSelectedObjectIndex( index ); mWorld->giveFruit( mGardener, mostLiked, // don't save seeds, but get any fruit, even // if fruit not selected mGardener->getSelectedFruit( false, true ) ); // reset timer mSecondsSinceLastGift = 0; // every time we give a gift, // our friendliness toward this gardener is depleted // a bit // Actually, don't do this for now, since for mating // purposes, we want to retain our friendliness // mGardener->getAngry( mostLiked ); } else { // move toward friend mGardener->setDesiredPosition( otherPosition ); } delete ourPosition; delete otherPosition; } } } char gettingRevenge = false; if( !tryingToMate && !tryingToGiveFruit ) { // consider getting revenge if( mSecondsSinceLastRevenge > 5 ) { Gardener *leastLiked = mGardener->getLeastLikedGardener(); if( leastLiked != NULL ) { Plant *plantToTake = getClosestPlantInGardenerPlot( leastLiked ); if( plantToTake != NULL ) { expandOurPlotToContainPlant( plantToTake ); gettingRevenge = true; // reset timer mSecondsSinceLastRevenge = 0; // every time we take revenge, our anger // toward this gardener lessens a bit mGardener->getFriendly( leastLiked ); } else { // none to take (our plots overlap perfectly) // try looking for a plant to poison Plant *plantToPoison = mWorld->getTendedPlant( leastLiked ); if( plantToPoison != NULL ) { // found candidate gettingRevenge = true; // walk to it Vector3D *plantPosition = mWorld->getPlantPosition( plantToPoison ); Vector3D *gardenerPosition = mWorld->getGardenerPosition( mGardener ); if( ! gardenerPosition->equals( plantPosition ) ) { // move to plant mGardener->setDesiredPosition( plantPosition ); } else { // already at plant mWorld->dumpPoison( mGardener ); // reset timer mSecondsSinceLastRevenge = 0; // every time we take revenge, our anger // toward this gardener lessens a bit mGardener->getFriendly( leastLiked ); } delete gardenerPosition; delete plantPosition; } } } } } if( !tryingToMate && !tryingToGiveFruit && !gettingRevenge ) { // head to plot center and wait mGardener->setDesiredPosition( plotCenter ); } } if( plotCenter != NULL ) { delete plotCenter; } delete plants; } Plant *GardenerAI::getClosestPlantInGardenerPlot( Gardener *inGardener ) { SimpleVector *ourPlants = mWorld->getPlotPlants( mGardener ); SimpleVector *otherPlants = mWorld->getPlotPlants( inGardener ); if( otherPlants == NULL ) { if( ourPlants != NULL ) { delete ourPlants; } return NULL; } // first, filter otherPlants to remove overlaps with ourPlants int i=0; while( i < otherPlants->size() ) { Plant *plant = *( otherPlants->getElement( i ) ); if( ourPlants != NULL && ourPlants->getElementIndex( plant ) != -1 ) { // overlap otherPlants->deleteElement( i ); } else { // no overlap i++; } } if( ourPlants != NULL ) { delete ourPlants; } Plant *returnPlant = NULL; int numPlants = otherPlants->size(); if( numPlants > 0 ) { // look for closest to us Vector3D *ourPostion = mWorld->getGardenerPosition( mGardener ); double closestDistance = DBL_MAX; for( i=0; igetElement( i ) ); Vector3D *plantPosition = mWorld->getPlantPosition( plant ); double distance = plantPosition->getDistance( ourPostion ); delete plantPosition; if( distance < closestDistance ) { closestDistance = distance; returnPlant = plant; } } delete ourPostion; } delete otherPlants; return returnPlant; } void GardenerAI::expandOurPlotToContainPlant( Plant *inPlant ) { Vector3D *cornerA, *cornerB; mWorld->getGardenerPlot( mGardener, &cornerA, &cornerB ); Vector3D *plantPosition = mWorld->getPlantPosition( inPlant ); Vector3D *closestCornerX; double xDistance; Vector3D *closestCornerY; double yDistance; double cornerAXDistance = plantPosition->mX - cornerA->mX; double cornerBXDistance = plantPosition->mX - cornerB->mX; double cornerAYDistance = plantPosition->mY - cornerA->mY; double cornerBYDistance = plantPosition->mY - cornerB->mY; if( fabs( cornerAXDistance ) < fabs( cornerBXDistance ) ) { closestCornerX = cornerA; xDistance = cornerAXDistance; } else { closestCornerX = cornerB; xDistance = cornerBXDistance; } if( fabs( cornerAYDistance ) < fabs( cornerBYDistance ) ) { closestCornerY = cornerA; yDistance = cornerAYDistance; } else { closestCornerY = cornerB; yDistance = cornerBYDistance; } // make distances a little bit larger (1 unit) to contain plant double extra = 1; if( xDistance < 0 ) { xDistance -= extra; } if( xDistance > 0 ) { xDistance += extra; } if( yDistance < 0 ) { yDistance -= extra; } if( yDistance > 0 ) { yDistance += extra; } // only want to change corners in way that makes plot bigger // thus, to contain inPlant, we may need to expand in only one direction // (in other words, x or y) // we can use the plot area to detect plot contraction double oldArea = fabs( cornerA->mX - cornerB->mX ) * fabs( cornerA->mY - cornerB->mY ); closestCornerX->mX += xDistance; double newArea = fabs( cornerA->mX - cornerB->mX ) * fabs( cornerA->mY - cornerB->mY ); if( newArea < oldArea ) { // contraction // reverse it closestCornerX->mX -= xDistance; } oldArea = fabs( cornerA->mX - cornerB->mX ) * fabs( cornerA->mY - cornerB->mY ); closestCornerY->mY += yDistance; newArea = fabs( cornerA->mX - cornerB->mX ) * fabs( cornerA->mY - cornerB->mY ); if( newArea < oldArea ) { // contraction // reverse it closestCornerY->mY -= yDistance; } mWorld->setGardenerPlot( mGardener, cornerA, cornerB ); delete plantPosition; delete cornerA; delete cornerB; } Cultivation_9+dfsg1_UnixSource/game2/gameSource/landscape.cpp0000640000175000017500000003145310750217103023150 0ustar pabspabs/* * Modification History * * 2006-September-26 Jason Rohrer * Switched to cosine interpolation. * Added optimizations discovered with profiler. Reduced running time by 18%. */ #include "landscape.h" #include #include #include "minorGems/util/SimpleVector.h" double landscape( double inX, double inY, double inT, double inBaseFrequency, double inRoughness, int inDetail ) { if( inDetail < 0 ) { return 0.0; } else { // frequency of octave double frequency = inBaseFrequency * pow( 2, inDetail ); double amplitude = pow( inRoughness, inDetail ); return amplitude * noise4d( inX * frequency, inY * frequency, // index different planes of noise inDetail, inT ) + landscape( inX, inY, inT, inBaseFrequency, inRoughness, inDetail - 1 ); } } double variableRoughnessLandscape( double inX, double inY, double inT, double inBaseFrequency, double inRoughnessChangeFrequency, double inMinRoughness, double inMaxRoughness, int inDetail ) { double roughnessFreqX = inX * inRoughnessChangeFrequency; double roughnessFreqY = inY * inRoughnessChangeFrequency; // use low-frequency noise 4d to select landscape roughness // between 0 and 1 double roughness = ( noise4d( 6, roughnessFreqX, roughnessFreqY, inT ) + 1 ) / 2; // move roughness into specified range roughness = roughness * ( inMaxRoughness - inMinRoughness ) + inMinRoughness; return landscape( inX, inY, inT, inBaseFrequency, roughness, inDetail ); } int getRandomPoints( double inStartX, double inEndX, double inStartY, double inEndY, double inT, double inSampleStepSize, double inDensity, double **outXCoordinates, double **outYCoordinates ) { SimpleVector *xCoordinates = new SimpleVector(); SimpleVector *yCoordinates = new SimpleVector(); // discretize startX and start Y so that sample grid for differently-placed // windows always meshes // use ceil to ensure that starting points are always inside the // inStart/inEnd bounds double discretizedStartX = inSampleStepSize * ceil( inStartX / inSampleStepSize ); double discretizedStartY = inSampleStepSize * ceil( inStartY / inSampleStepSize ); // put a point wherever we have a zero-crossing double lastSample = 1; for( double x=discretizedStartX; x<=inEndX; x+=inSampleStepSize ) { for( double y=discretizedStartY; y<=inEndY; y+=inSampleStepSize ) { double landscapeSample = variableRoughnessLandscape( 30 * x + 1000, 30 * y + 1000, inT + 1000, 0.01, 0.001, 0.25, 0.65, 0 ); // shift landscape up to reduce chance of zero-crossing landscapeSample = (1-inDensity) * 0.5 + 0.5 + landscapeSample ; if( landscapeSample < 0 && lastSample >= 0 || landscapeSample >= 0 && landscapeSample < 0 ) { // sign change // hit xCoordinates->push_back( x ); yCoordinates->push_back( y ); } lastSample = landscapeSample; } } *outXCoordinates = xCoordinates->getElementArray(); *outYCoordinates = yCoordinates->getElementArray(); int numPoints = xCoordinates->size(); delete xCoordinates; delete yCoordinates; return numPoints; } /** * Computes a 32-bit random number. * Use linear congruential method. * * @param inSeed the seed to use. */ // this is the readable version of the funcion // it has been turned into a set of macros below inline unsigned int random32_readable( unsigned int inSeed ) { // this is the true hot-spot of the entire landscape function // thus, optimization is warranted. // multiplier = 3141592621 // use hex to avoid warnings //unsigned int multiplier = 0xBB40E62D; //unsigned int increment = 1; // better: // unsigned int multiplier = 196314165 // unsigned int increment = 907633515 // this will automatically be mod-ed by 2^32 because of the limit // of the unsigned int type // return multiplier * inSeed + increment; //return 0xBB40E62D * inSeed + 1; //return 196314165 * inSeed + 907633515; //int n = ( inSeed << 13 ) ^ inSeed; //return n * (n * n * 15731 + 789221) + 1376312589; //const unsigned int Num1 = (inSeed * 0xFEA09B9DU) + 1; //const unsigned int Num2 = ((inSeed * 0xB89C8895U) + 1) >> 16; //return Num1 ^ Num2; /* unsigned int rseed=(inSeed*15064013)^(inSeed*99991+604322121)^(inSeed*45120321)^(inSeed*5034121+13); const unsigned int Num1 = (inSeed * 0xFEA09B9DU) + 1; const unsigned int Num2 = ((inSeed * 0xB89C8895U) + 1) >> 16; rseed *= Num1 ^ Num2; return rseed; */ const unsigned int Num1 = (inSeed * 0xFEA09B9DU) + 1; const unsigned int Num2 = ((inSeed^Num1) * 0x9C129511U) + 1; const unsigned int Num3 = (inSeed * 0x2512CFB8U) + 1; const unsigned int Num4 = ((inSeed^Num3) * 0xB89C8895U) + 1; const unsigned int Num5 = (inSeed * 0x6BF962C1U) + 1; const unsigned int Num6 = ((inSeed^Num5) * 0x4BF962C1U) + 1; return Num2 ^ (Num4 >> 11) ^ (Num6 >> 22); } // faster as a set of macros #define Num1( inSeed ) \ ( ( inSeed * 0xFEA09B9DU ) + 1 ) #define Num2( inSeed ) \ ( ( ( inSeed ^ Num1( inSeed ) ) * 0x9C129511U ) + 1 ) #define Num3( inSeed ) \ ( ( inSeed * 0x2512CFB8U ) + 1 ) #define Num4( inSeed ) \ ( ( ( inSeed ^ Num3( inSeed ) ) * 0xB89C8895U ) + 1 ) #define Num5( inSeed ) \ ( ( inSeed * 0x6BF962C1U ) + 1 ) #define Num6( inSeed ) \ ( ( ( inSeed ^ Num5( inSeed ) ) * 0x4BF962C1U ) + 1 ) #define random32( inSeed ) \ ( Num2( inSeed ) ^ (Num4( inSeed ) >> 11) ^ (Num6( inSeed ) >> 22) ) #define invMaxIntAsDouble 2.32830643708e-10 // 1/(x/2) = 2*(1/x) //double invHalfMaxIntAsDouble = 2 * invMaxIntAsDouble; // 2.32830643708e-10 //+ 2.32830643708e-10 //------------------- // 4.65661287416e-10 #define invHalfMaxIntAsDouble 4.65661287416e-10 #define mixFour( x, y, z, t ) ( x ^ (y * 57) ^ (z * 131) ^ (t * 2383) ) /** * Maps 4d integer coordinates into a [-1..1] noise space. * * @param x, y, z, t the 4d coordinates. * * @return a random value in the range [-1..1] */ // keep readable version around for reference // it has been replaced by a macro below inline double noise4dInt32_readable( unsigned int x, unsigned int y, unsigned int z, unsigned int t ) { //double maxIntAsDouble = 4294967295.0; // modular addition automatic // multiply x, y, z, and t by distinct primes to // avoid correllations. // using xor ( ^ ) here seems to avoid correllations that show // up when using addition. // mix x, y, z, and t unsigned int randomSeed = x ^ y * 57 ^ z * 131 ^ t * 2383; // a random value between 0 and max unsigned int unsigned int randomValue = random32( randomSeed ); // a random value between 0 and 2 double zeroTwoValue = randomValue * invHalfMaxIntAsDouble; // a random value between -1 and 1 return zeroTwoValue - 1; } // noise4dInt32 function call itself was the slowest spot in code // (found with profiler) // turn into a set of macros // matches original parameter format #define noise4dInt32( x, y, z, t ) \ random32( mixFour( x, y, z, t ) ) \ * invHalfMaxIntAsDouble - 1 // problem: now that random32 is a macro, we are passing the unevaluated // expression, ( x ^ (y * 57) ^ (z * 131) ^ (t * 2383) ), down into it. // it is being evaluated 6 times within the depths of the random32 macro // thus, we need to provide a new format where the caller can precompute // the mix for us. This is even faster. #define noise1dInt32( precomputedMix ) \ random32( precomputedMix ) \ * invHalfMaxIntAsDouble - 1 /* * The following functions (blendNoiseNd) do 4d linear interpolation * one dimension at a time. * * The end result is 8 calls to blendNoise1d (and 16 calls to noise4dInt32). * * This method was inspired by the functional implementations---I am * decomposing a complicated problem into sub-problems that are easier * to solve. */ // faster than f * b + (1-f) * a // one less multiply #define linearInterpolation( t, a, b ) ( a + t * ( b - a ) ) /** * Blends 4d discrete (integer-parameter) noise function along one dimension * with 3 fixed integer parameters. */ inline double blendNoise1d( double x, unsigned int y, unsigned int z, unsigned int t ) { double floorX = floor( x ); unsigned int floorIntX = (unsigned int)floorX; if( floorX == x ) { unsigned int precomputedMix = mixFour( floorIntX, y, z, t ); return noise1dInt32( precomputedMix ); } else { unsigned int ceilIntX = floorIntX + 1; // cosine interpolation // from http://freespace.virgin.net/hugo.elias/models/m_perlin.htm double ft = ( x - floorX ) * M_PI; double f = ( 1 - cos( ft ) ) * .5; // need to pre-store intermediate values because noise4dInt32 is a // macro // thus, we end up calling the noise1dInt32 function instead unsigned int precomputedMix = mixFour( floorIntX, y, z, t ); double valueAtFloor = noise1dInt32( precomputedMix ); precomputedMix = mixFour( ceilIntX, y, z, t ); double valueAtCeiling = noise1dInt32( precomputedMix ); return linearInterpolation( f, valueAtFloor, valueAtCeiling ); } } /** * Blends 4d discrete (integer-parameter) noise function along 2 dimensions * with 2 fixed integer parameters. */ double blendNoise2d( double x, double y, unsigned int z, unsigned int t ) { double floorY = floor( y ); unsigned int floorIntY = (unsigned int)floorY; if( floorY == y ) { return blendNoise1d( x, floorIntY, z, t ); } else { unsigned int ceilIntY = floorIntY + 1; // cosine interpolation // from http://freespace.virgin.net/hugo.elias/models/m_perlin.htm double ft = ( y - floorY ) * M_PI; double f = ( 1 - cos( ft ) ) * .5; return ( f ) * blendNoise1d( x, ceilIntY, z, t ) + ( 1 - f ) * blendNoise1d( x, floorIntY, z, t ); } } /** * Blends 4d discrete (integer-parameter) noise function along 3 dimensions * with 1 fixed integer parameters. */ double blendNoise3d( double x, double y, double z, unsigned int t ) { double floorZ = floor( z ); unsigned int floorIntZ = (unsigned int)floorZ; if( floorZ == z ) { return blendNoise2d( x, y, floorIntZ, t ); } else { unsigned int ceilIntZ = floorIntZ + 1; // cosine interpolation // from http://freespace.virgin.net/hugo.elias/models/m_perlin.htm double ft = ( z - floorZ ) * M_PI; double f = ( 1 - cos( ft ) ) * .5; return ( f ) * blendNoise2d( x, y, ceilIntZ, t ) + ( 1 - f ) * blendNoise2d( x, y, floorIntZ, t ); } } /** * Blends 4d discrete (integer-parameter) noise function along 4 dimensions. */ double noise4d( double x, double y, double z, double t ) { double floorT = floor( t ); unsigned int floorIntT = (unsigned int)floorT; if( floorT == t ) { return blendNoise3d( x, y, z, floorIntT ); } else { unsigned int ceilIntT = floorIntT + 1; // cosine interpolation // from http://freespace.virgin.net/hugo.elias/models/m_perlin.htm double ft = ( t - floorT ) * M_PI; double f = ( 1 - cos( ft ) ) * .5; return ( f ) * blendNoise3d( x, y, z, ceilIntT ) + ( 1 - f ) * blendNoise3d( x, y, z, floorIntT ); } } /** * Blends 4d discrete (integer-parameter) noise function along 3 dimensions * to get a 3D noise function. */ double noise3d( double x, double y, double z ) { return blendNoise3d( x, y, z, 0 ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/PortalLayerGenetics.h0000640000175000017500000000310510531066076024604 0ustar pabspabs/* * Modification History * * 2006-November-8 Jason Rohrer * Created. */ #ifndef PORTAL_LAYER_GENETICS_INCLUDED #define PORTAL_LAYER_GENETICS_INCLUDED #include "GardenerGenetics.h" enum PortalLayerGeneLocator{ // behavior modifiers glyphStartPositionX = 0, glyphStartPositionY, glyphStepsBeforeDirectionChange, glyphStartAngle, glyphDeltaAngle, quadRotationSpeed, quadRotationDelta, offsetRadius, offsetSineWeight, offsetSineSpeed, offsetSineSineSpeed, quadDensity, quadScale, quadScaleSineWeight, quadScaleSineSpeed, quadScaleSineSineSpeed, cornerAAlpha, cornerBAlpha, cornerCAlpha, cornerDAlpha }; /** * Genetics for a portal layer. Based on the genes of the gardener that * opened the portal layer. * * @author Jason Rohrer */ class PortalLayerGenetics : public GardenerGenetics { public: /** * Constructs random genetics */ PortalLayerGenetics(); /** * Constructs genetics based on gardener genetics. * * @param inOpener the genetics of the gardener that opened this layer. * Destroyed by caller. */ PortalLayerGenetics( GardenerGenetics *inOpener ); /** * Maps a given gene to a parameter value. * * @param inLocator a gene locator. * * @return a parameter value for the specified gene. */ double getParameter( PortalLayerGeneLocator inLocator ); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/Genetics.h0000640000175000017500000000543310477265513022441 0ustar pabspabs/* * Modification History * * 2006-August-11 Jason Rohrer * Created. */ #ifndef GENETICS_INCLUDED #define GENETICS_INCLUDED /** * Generic genetics base class. * * @author Jason Rohrer */ class Genetics { public: virtual ~Genetics(); /** * Gets the number of genes. */ int getNumGenes(); /** * Gets the length of a gene. * * @param inIndex the index of the gene in [0, getNumGenes() - 1]. * * @return the length of the gene. */ int getGeneLength( int inIndex ); /** * Gets a gene. * * @param inIndex a value in [0, getNumGenes() - 1] * * @return an array of values in [0,1]. * The array has length getGeneLength( inIndex ). * The array must be destroyed by caller. */ double *getGene( int inIndex ); /** * Print the genetics to standard out. */ void printGenetics(); /** * Gets wether two genetics have equal gene values. * * @param inGenetics the other genetics to test. * Destroyed by caller. * * @return true if equal. */ char equals( Genetics *inGenetics ); protected: /** * Constructs random genetics. * * @param inNumGenes the number of genes. * @param inGeneLengths the length of each gene. Destroyed by caller. */ Genetics( int inNumGenes, int *inGeneLengths ); /** * Constructs genetics by crossing two parent genetics. * * Parent genetics must be of the same type (same number of genes, * and same length for each pair of corresponding genes). * * @param inParentA, inParentB the parent genetics. * Destroyed by caller. */ Genetics( Genetics *inParentA, Genetics *inParentB ); /** * Copies another genetics. * * @param inGeneticsToCopy the genetics to copy. Destroyed by caller. */ Genetics( Genetics *inGeneticsToCopy ); /** * Map a value in the range [0,1] to a different range. * * @param inValue the value in [0,1] to map. * @param inRangeStart, inRangeEnd the range to map to. * * @return the mapped value. */ static double mapValueToRange( double inValue, double inRangeStart, double inRangeEnd ); int mNumGenes; int *mGeneLengths; double **mGenes; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/0000750000175000017500000000000011401021142021621 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/gameSource/sound/MusicPlayer.h0000640000175000017500000000302610502631632024245 0ustar pabspabs/* * Modification History * * 2004-August-22 Jason Rohrer * Created. */ #ifndef MUSIC_PLAYER_INCLUDED #define MUSIC_PLAYER_INCLUDED #include "SoundSamples.h" #include "MusicNoteWaveTable.h" #include "minorGems/util/SimpleVector.h" /** * Class that plays music notes * * @author Jason Rohrer */ class MusicPlayer { public: /** * Constructs a player. * Reads configuration using the LevelDirectoryManager. * * @param inSamplesPerSecond the sample rate. * @param inWaveTable the wave table to use when rendering notes. * Must be destroyed by caller after this class is destroyed. * @param inPartLength the length of music parts in seconds. */ MusicPlayer( unsigned long inSamplesPerSecond, MusicNoteWaveTable *inWaveTable, double inPartLength ); ~MusicPlayer(); /** * Gets more samples of music from this player. * * @param inNumSamples the number of samples to get. * * @return a buffer of samples. Must be destroyed by caller. */ SoundSamples *getMoreMusic( unsigned long inNumSamples ); protected: MusicNoteWaveTable *mWaveTable; SimpleVector *mActiveNotes; SimpleVector *mNotePositions; double mPartLengthInSeconds; double mCurrentPlayTime; double mSampleRate; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/SoundSamples.h0000640000175000017500000000434210502323232024421 0ustar pabspabs/* * Modification History * * 2004-July-17 Jason Rohrer * Created. * * 2004-August-12 Jason Rohrer * Added a constructor that can specify all sound data. */ #ifndef SOUND_SAMPLES_INCLUDED #define SOUND_SAMPLES_INCLUDED /** * Class that encapsulates a buffer of sound samples. * * @author Jason Rohrer */ class SoundSamples { public: unsigned long mSampleCount; float *mLeftChannel; float *mRightChannel; /** * Constructs a sound samples object. * * @param inSampleCount the number of samples. * @param inLeftChannel samples for the left channel. * Will be destroyed when this class is destroyed. * @param inRightChannel samples for the right channel. * Will be destroyed when this class is destroyed. */ SoundSamples( unsigned long inSampleCount, float *inLeftChannel, float *inRightChannel ); /** * Constructs a sound samples object filled with 0 samples. * * @param inSampleCount the number of samples. */ SoundSamples( unsigned long inSampleCount ); /** * Constructs a sound samples object by copying another object. * * @param inSamplesToCopy the object to copy. * Must be destroyed by caller. */ SoundSamples( SoundSamples *inSamplesToCopy ); /** * Constructs a sound samples object by copying samples from * another object. * * @param inSamplesToCopy the object to copy. * Must be destroyed by caller. * @param inNumToCopy the number of samples from the start of * inSamplesToCopy to take. */ SoundSamples( SoundSamples *inSamplesToCopy, unsigned long inNumToCopy ); ~SoundSamples(); /** * Trims samples from the beginning of this sound. * * @param inNumSamplesToDrop the number of samples at the beginning * of this sound to drop. */ void trim( unsigned long inNumSamplesToDrop ); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/SoundFilter.h0000640000175000017500000000164210502323232024242 0ustar pabspabs/* * Modification History * * 2004-July-22 Jason Rohrer * Created. */ #ifndef SOUND_FILTER_INCLUDED #define SOUND_FILTER_INCLUDED #include "SoundSamples.h" /** * Interface for a class that can filter sound. * * @author Jason Rohrer */ class SoundFilter { public: /** * Filters sound samples. * * @param inSamples the samples to filter. * Must be destroyed by caller. * * @return the resulting samples in a newly constructed object. * Must be destroyed by caller. */ virtual SoundSamples *filterSamples( SoundSamples *inSamples ) = 0; // virtual destructor to ensure proper destruction of classes that // implement this interface virtual ~SoundFilter(); }; // does nothing, needed to make compiler happy inline SoundFilter::~SoundFilter() { } #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/MusicNoteWaveTable.cpp0000640000175000017500000002017110510475044026046 0ustar pabspabs/* * Modification History * * 2004-August-22 Jason Rohrer * Created. * * 2004-August-31 Jason Rohrer * Added brief fade-in at note start to reduce clicks. * * 2006-October-3 Jason Rohrer * Optimized using profiler. */ #include "MusicNoteWaveTable.h" #include "minorGems/util/SimpleVector.h" #include #include extern double globalShortestNoteLength; extern double globalLongestNoteLength; MusicNote::MusicNote( int inFrequencyIndex, int inLengthIndex, char inReversed ) : mFrequencyIndex( inFrequencyIndex ), mLengthIndex( inLengthIndex ), mReversed( inReversed ) { } MusicNote *MusicNote::copy() { return new MusicNote( mFrequencyIndex, mLengthIndex, mReversed ); } MusicNoteWaveTable::MusicNoteWaveTable( unsigned long inSamplesPerSecond ){ // read frequencies and lengths from files SimpleVector *frequencyVector = new SimpleVector(); SimpleVector *lengthVector = new SimpleVector(); FILE *musicNotePitchesFILE = NULL; FILE *musicNoteLengthsFILE = NULL; if( musicNotePitchesFILE != NULL ) { double readValue; int numRead = 1; while( numRead == 1 ) { numRead = fscanf( musicNotePitchesFILE, "%lf", &readValue ); if( numRead == 1 ) { frequencyVector->push_back( readValue ); } } fclose( musicNotePitchesFILE ); } else { // default pitches // Note N,, means N two octaves down // N'' means two octaves up /* // This one sounds pretty good // but not enough notes, too bland // A,, frequencyVector->push_back( 110 ); // D, frequencyVector->push_back( 146.832 ); // A, frequencyVector->push_back( 220 ); // D frequencyVector->push_back( 293.665 ); // G frequencyVector->push_back( 391.995 ); // C' frequencyVector->push_back( 523.251 ); // E' frequencyVector->push_back( 659.255 ); // G' frequencyVector->push_back( 783.991 ); */ // Instead, use entire two-octaves from c-major scale // Problem: there can be some discords. // However: much more interesting sounding than the two-chord version // above // base note: C, double baseNote = 130.8127827; int majorScaleSteps[7] = {2,2,1,2,2,2,1}; int scaleIndex = 0; int notePower = 0; // two octaves while( notePower < 25 ) { frequencyVector->push_back( baseNote * pow( 2, notePower / 12.0 ) ); notePower += majorScaleSteps[ scaleIndex ]; scaleIndex ++; if( scaleIndex >= 7 ) { // wrap around scaleIndex = 0; } } /* // These are the notes from Transcend level 001 frequencyVector->push_back( 220 ); frequencyVector->push_back( 277.183 ); frequencyVector->push_back( 329.628 ); frequencyVector->push_back( 440 ); frequencyVector->push_back( 554.365 ); frequencyVector->push_back( 659.255 ); */ } if( musicNoteLengthsFILE != NULL ) { double readValue; int numRead = 1; while( numRead == 1 ) { numRead = fscanf( musicNoteLengthsFILE, "%lf", &readValue ); if( numRead == 1 ) { lengthVector->push_back( readValue ); } } fclose( musicNoteLengthsFILE ); } else { // default lengths lengthVector->push_back( globalLongestNoteLength ); lengthVector->push_back( globalShortestNoteLength ); } mFrequencyCount = frequencyVector->size(); mLengthCount = lengthVector->size(); double *frequencies = frequencyVector->getElementArray(); mLengthsInSeconds = lengthVector->getElementArray(); delete frequencyVector; delete lengthVector; mSampleTable = new float**[ mFrequencyCount ]; mSampleCounts = new unsigned long[ mLengthCount ]; for( int F=0; F lengthInSamples ) { numFadeInSamples = lengthInSamples / 2; } // optimizations (found with profiler) // pull these out of inner loop float lengthInSamplesMinusOne = (float)lengthInSamples - 1.0f; float inv_lengthInSamplesMinusOne = 1.0f / lengthInSamplesMinusOne; float *theseSamples = mSampleTable[F][L]; for( unsigned long i=0; imFrequencyIndex, inNote->mLengthIndex, outNumSamples ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/MusicPart.cpp0000640000175000017500000001120210503514503024243 0ustar pabspabs/* * Modification History * * 2004-August-22 Jason Rohrer * Created. * * 2004-August-26 Jason Rohrer * Added parameter to control character of part. */ #include "MusicPart.h" #include #include "minorGems/util/random/StdRandomSource.h" extern StdRandomSource globalRandomSource; MusicPart::MusicPart( MusicNoteWaveTable *inWaveTable, double inPartLengthInSeconds, double *inMelody, int inNumNotes, char inHighNotes, char inShortNotes, double inChanceOfReverseNote ) : mWaveTable( inWaveTable ), mNotes( new SimpleVector() ) { int frequencyCount = inWaveTable->getFrequencyCount(); int lengthCount = inWaveTable->getLengthCount(); // populate our note vector with notes, using inMelody to select pitches // decide note length using our parameter int lengthIndex; if( inShortNotes ) { lengthIndex = lengthCount - 1; } else { lengthIndex = 0; } // pick a frequency range int rangeStart, rangeEnd; if( inHighNotes ) { rangeEnd = frequencyCount - 1; rangeStart = rangeEnd - frequencyCount / 2; } else { rangeStart = 0; rangeEnd = rangeStart + frequencyCount / 2; } double totalLength = 0; int melodyIndex = 0; while( totalLength < inPartLengthInSeconds ) { // add another note double thisNoteMelody = inMelody[ melodyIndex ]; // put into range int frequencyIndex = (int)( thisNoteMelody * ( rangeEnd - rangeStart ) + rangeStart ); char noteReversed; // flip a weighted coin to determine if this note should be played // in reverse if( globalRandomSource.getRandomDouble() < inChanceOfReverseNote ) { noteReversed = true; } else { noteReversed = false; } mNotes->push_back( new MusicNote( frequencyIndex, lengthIndex, noteReversed ) ); // add this note's length to our total totalLength += inWaveTable->getLengthInSeconds( lengthIndex ); // next melody note melodyIndex ++; if( melodyIndex >= inNumNotes ) { // wrap back to first note melodyIndex = 0; } } // note lengths sum to a total length that may be beyond the limit if( totalLength > inPartLengthInSeconds ) { // drop the last note int lastNoteIndex = mNotes->size() - 1; delete *( mNotes->getElement( lastNoteIndex ) ); mNotes->deleteElement( lastNoteIndex ); // could do something more intelligent here... // like drop the note that results in a total length that // is closest to the inPartLengthInSeconds } } MusicPart::~MusicPart() { int numNotes = mNotes->size(); for( int i=0; igetElement( i ) ); } delete mNotes; } double MusicPart::getPartLengthInSeconds() { return mPartLengthInSeconds; } int MusicPart::getNotesStartingInInterval( double inStartTimeInSeconds, double inLengthInSeconds, MusicNote ***outNotes, double **outNoteStartOffsetsInSeconds ) { SimpleVector *returnNotes = new SimpleVector(); SimpleVector *returnStartOffsets = new SimpleVector(); double endTimeInSeconds = inStartTimeInSeconds + inLengthInSeconds; // walk through notes looking for those that start in the interval int numNotes = mNotes->size(); double currentNoteStartTime = 0; for( int i=0; igetElement( i ) ); double noteLength = mWaveTable->getLengthInSeconds( note->mLengthIndex ); if( currentNoteStartTime >= inStartTimeInSeconds ) { // add the note returnNotes->push_back( note->copy() ); returnStartOffsets->push_back( currentNoteStartTime - inStartTimeInSeconds ); } // else skip the note currentNoteStartTime += noteLength; } int numNotesReturned = returnNotes->size(); *outNotes = returnNotes->getElementArray(); *outNoteStartOffsetsInSeconds = returnStartOffsets->getElementArray(); delete returnNotes; delete returnStartOffsets; return numNotesReturned; } Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/MusicPart.h0000640000175000017500000000645310503514503023724 0ustar pabspabs/* * Modification History * * 2004-August-22 Jason Rohrer * Created. * * 2004-August-26 Jason Rohrer * Added parameter to control character of part. * * 2006-September-18 Jason Rohrer * Changed to accept a fixed melody in constructor. */ #ifndef MUSIC_PART_INCLUDED #define MUSIC_PART_INCLUDED #include "MusicNoteWaveTable.h" #include "minorGems/util/SimpleVector.h" /** * A sequence of music notes. * * @author Jason Rohrer */ class MusicPart { public: /** * Constructs a randomized part. * Reads configuration using the LevelDirectoryManager. * * @param inWaveTable the wave table to pick random notes from. * Must be destroyed by caller after this class is destroyed. * @param inPartLengthInSeconds the length of this music part. * @param inMelody an array of values in [0,1] for selecting melody * notes. Must be destroyed by caller. * @param inNumNotes the number of elements in inMelody. * @param inHighNotes true to use high end of scale, false to use * low end. * @param inShortNotes true to use short (fast) notes, false to use * long (slow) notes. * @param inChanceOfReverseNote value in [0,1] specifying probability * of each note being played in reverse. */ MusicPart( MusicNoteWaveTable *inWaveTable, double inPartLengthInSeconds, double *inMelody, int inNumNotes, char inHighNotes, char inShortNotes, double inChanceOfReverseNote ); ~MusicPart(); /** * Gets this part's length. * * @return the length in seconds. */ double getPartLengthInSeconds(); /** * Gets the notes that should start playing in a given time interval. * * Note that only notes that *start* in the interval are returned. * Notes that are playing during the interval, but that start before * the interval, are ignored. * * @param inStartTimeInSeconds the start of the interval * Must be in the range [ 0, getPartLengthInSeconds() ]. * @param inLengthInSeconds the length of the interval. * @param outNotes pointer to where an array of notes * should be returned. * The returned array and the notes must be destroyed by caller. * @param outNoteStartOffsetsInSeconds pointer to where an array of * note start offsets should be returned. * Offsets are in seconds beyond inStartTimeInSeconds. * The returned array must be destroyed by caller. * * @return the number of notes that start in the interval * (in other words, the length of the returned arrays). */ int getNotesStartingInInterval( double inStartTimeInSeconds, double inLengthInSeconds, MusicNote ***outNotes, double **outNoteStartOffsetsInSeconds ); protected: MusicNoteWaveTable *mWaveTable; SimpleVector *mNotes; double mPartLengthInSeconds; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/SoundPlayer.h0000640000175000017500000001654210510311327024257 0ustar pabspabs/* * Modification History * * 2004-July-17 Jason Rohrer * Created. * * 2004-July-21 Jason Rohrer * Changed to use a callback to reduce latency. * * 2004-August-9 Jason Rohrer * Added a limit on the number of simultaneous sounds. * * 2004-August-12 Jason Rohrer * Parameterized the sample rate. * * 2004-August-13 Jason Rohrer * Added Mutex to protect members that are accessed by multiple threads. * * 2004-August-14 Jason Rohrer * Changed to fade sounds out before dropping them. * * 2004-August-15 Jason Rohrer * Added a function for getting the sample rate. * Added volume modifier parameter to playSoundNow function. * * 2004-August-20 Jason Rohrer * Added priority flags. * * 2004-August-23 Jason Rohrer * Added music. * * 2004-August-25 Jason Rohrer * Added function for setting music loudness. * * 2004-August-31 Jason Rohrer * Added function for removing filters. * * 2006-September-19 Jason Rohrer * Added global loudness and fade-in. * * 2006-October-2 Jason Rohrer * Added a separate lock for music player to deal with deadlock. */ #ifndef SOUND_PLAYER_INCLUDED #define SOUND_PLAYER_INCLUDED #include "SoundSamples.h" #include "SoundFilter.h" #include "PlayableSound.h" #include "minorGems/sound/portaudio/pa_common/portaudio.h" #include "minorGems/sound/portaudio/pablio/pablio.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/system/MutexLock.h" /** * Class that plays both running background music and realtime sounds. * * @author Jason Rohrer */ class SoundPlayer { public: /** * Constructs a sound player. * * @param inSampleRate the number of samples per second. * Should be a "standard" rate, like 44100, 22050, etc. * @param inMaxSimultaneousRealtimeSounds the number of simultaneous * realtime sounds to allow. When this limit is reached, * older sounds are silenced prematurely to make way for * newer sounds. * @param inMusicPlayer the player to get music from, or NULL * to disable music. Defaults to NULL. * Typed as (void*) to avoid an include loop. * Must be destroyed by caller after this class is destroyed. * @param inMusicLoudness an adjustment for music loudness in the * range [0,1]. Defaults to 1. * @param inGlobalLoudness the global adjustment for loudness. * In the range [0,1]. Defaults to 1. */ SoundPlayer( int inSampleRate, int inMaxSimultaneousRealtimeSounds, void *inMusicPlayer = NULL, double inMusicLoudness = 1, double inGlobalLoudness = 1 ); ~SoundPlayer(); /** * Sets the music player. * * @param inMusicPlayer the player to get music from, or NULL * to disable music. Typed as (void*) to avoid an include loop. * Must be destroyed by caller after this class is destroyed. */ void setMusicPlayer( void *inMusicPlayer ); /** * Sets the loudess of the music. * * @param inMusicLoudness the loudness in the range [0,1]. */ void setMusicLoudness( double inMusicLoudness ); /** * Sets the global loudness. * * @param inLoudness the loudness in the range [0,1]. */ void setGlobalLoudness( double inLoudness ); /** * Triggers global loudness to fade up to 1 over time. * * @param inFadeTimeInSeconds the time until a loudness of 1 * is reached. */ void fadeIn( double inFadeTimeInSeconds ); /** * Mixes a sound to the speakers as quickly as possible. * * This call does not adjust the volume level of the samples * before mixing them with other realtime sounds or the background * music. * * @param inSamples the samples to play. * Must be destroyed by caller. * @param inPriorityFlag true if this sound should have * high priority, or false for low priority. Defaults to false. * @param inLoudnessModifier the value to adjust the sound's * loudness by, in [0,1], when playing. Defaults to 1. */ void playSoundNow( SoundSamples *inSamples, char inPriorityFlag = false, double inLoudnessModifier = 1.0 ); /** * Same as earlier playSoundNow, except that it takes a PlayableSound * that must be destroyed by the caller. */ void playSoundNow( PlayableSound *inSound, char inPriorityFlag = false, double inLoudnessModifier = 1.0 ); /** * Add the next section of music to be played. * * This call drives the sound player to send audio to the speakers. * * @param inSamples the samples to play. * Must be destroyed by caller. */ void addMoreMusic( SoundSamples *inSamples ); /** * Called by the internal portaudio callback function. */ void getSamples( void *outputBuffer, unsigned long inFramesInBuffer ); /** * Adds a filter to the end of the chain that will process * samples before sending them to the speakers. * * @param inFilter the filter to add. * Will be destroyed when this class is destroyed. */ void addFilter( SoundFilter *inFilter ); /** * Removes and destroys all filters. */ void removeAllFilters(); /** * Gets the current sample rate. * * @return the sample rate, in samples per second. */ unsigned long getSampleRate(); protected: MutexLock *mLock; // separate lock for setting music player // deals with a deadlock issue MutexLock *mMusicPlayerLock; unsigned long mSampleRate; char mAudioInitialized; int mMaxSimultaneousRealtimeSounds; // Typed as (void*) to avoid an include loop. void *mMusicPlayer; double mMusicLoudness; double mGlobalLoudness; char mFadingIn; int mNumFadeFramesRemaining; PortAudioStream *mAudioStream; // realtime sounds that should be mixed into the next to-speaker call SimpleVector *mRealtimeSounds; SimpleVector *mPriorityFlags; // one modifier for each realtime sound SimpleVector *mSoundLoudnessModifiers; // one flag for each realtime sound, indicating whether it should // be dropped (faded out) during the next frame SimpleVector *mSoundDroppedFlags; SimpleVector *mFilterChain; /** * Checks the sound set to ensure that our max simultaneous limit * is being met. * * Not thread-safe: should be called with mLock already locked. */ void checkForExcessSounds(); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/SoundEffectsBank.h0000640000175000017500000000244110656633213025203 0ustar pabspabs/* * Modification History * * 2007-August-7 Jason Rohrer * Created. */ #ifndef SOUND_EFFECTS_BANK_INCLUDED #define SOUND_EFFECTS_BANK_INCLUDED #include "SoundSamples.h" #include "SoundPlayer.h" #define numSoundEffects 8 enum SoundEffectKey { effect_plantSeed = 0, effect_pickupWater, effect_dumpWater, effect_poison, effect_pickFruit, effect_eatFruit, effect_giveFruit, effect_mate }; /** * Class that encapsulates a collection of playable sound effects. * * @author Jason Rohrer */ class SoundEffectsBank { public: /** * Constructs a bank. * * @param inPlayer where the sound effects should be played. * Destroyed by caller after this class is destroyed. */ SoundEffectsBank( SoundPlayer *inPlayer ); ~SoundEffectsBank(); /** * Plays a specified sound effect. * * @param inKey the key for the effect to play. */ void playEffect( SoundEffectKey inKey ); protected: SoundPlayer *mSoundPlayer; SoundSamples *mEffectSamples[ numSoundEffects ]; double mEffectLoudness[ numSoundEffects ]; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/MusicPlayer.cpp0000640000175000017500000002650410504026343024605 0ustar pabspabs/* * Modification History * * 2004-August-22 Jason Rohrer * Created. * * 2004-August-23 Jason Rohrer * Fixed bug in note offsets. * * 2004-August-24 Jason Rohrer * Added missing support for reversed notes and stereo split. * Changed to use constant power panning. */ #include "MusicPlayer.h" #include "MusicPart.h" #include "minorGems/math/geometry/Vector3D.h" #include "../World.h" extern World *globalWorld; extern Vector3D globalPlayerCurrentPosition; // needed to synchronize between GL thread and sound thread #include "minorGems/system/MutexLock.h" extern MutexLock globalLock; MusicPlayer::MusicPlayer( unsigned long inSamplesPerSecond, MusicNoteWaveTable *inWaveTable, double inPartLength ) : mWaveTable( inWaveTable ), mActiveNotes( new SimpleVector() ), mNotePositions( new SimpleVector() ), mPartLengthInSeconds( inPartLength ), mCurrentPlayTime( 0 ), mSampleRate( inSamplesPerSecond ) { } MusicPlayer::~MusicPlayer() { int numActiveNotes = mActiveNotes->size(); for( int i=0; igetElement( i ) ); } delete mActiveNotes; delete mNotePositions; } SoundSamples *MusicPlayer::getMoreMusic( unsigned long inNumSamples ) { double bufferLengthInSeconds = (double)inNumSamples / (double)mSampleRate; if( mCurrentPlayTime + bufferLengthInSeconds > mPartLengthInSeconds ) { // this buffer request falls off end of music // split into two separate parts unsigned long samplesInFirstPart = (unsigned long)( ( mPartLengthInSeconds - mCurrentPlayTime ) * mSampleRate ); SoundSamples *firstPart = getMoreMusic( samplesInFirstPart ); // reset play position mCurrentPlayTime = 0; int samplesInSecondPart = inNumSamples - samplesInFirstPart; SoundSamples *secondPart = getMoreMusic( samplesInSecondPart ); // combine the two SoundSamples *bothParts = new SoundSamples( inNumSamples ); memcpy( bothParts->mLeftChannel, firstPart->mLeftChannel, samplesInFirstPart * sizeof( float ) ); memcpy( bothParts->mRightChannel, firstPart->mRightChannel, samplesInFirstPart * sizeof( float ) ); // skip in bothParts to location where second part should go memcpy( &( bothParts->mLeftChannel[ samplesInFirstPart ] ), secondPart->mLeftChannel, samplesInSecondPart * sizeof( float ) ); memcpy( &( bothParts->mRightChannel[ samplesInFirstPart ] ), secondPart->mRightChannel, samplesInSecondPart * sizeof( float ) ); delete firstPart; delete secondPart; return bothParts; } globalLock.lock(); // get parts and positions of gardeners int numParts = 0; Vector3D **positions = globalWorld->getAllGardenerPositions( &numParts ); MusicPart **musicParts = globalWorld->getAllGardenerMusicParts( &numParts ); double *volumeModifiers = globalWorld->getAllGardenerMusicVolumeModifiers( &numParts ); // get center position from current player position Vector3D *centerPosition = new Vector3D( &globalPlayerCurrentPosition ); if( numParts == 0 ) { // no pieces in sculpture... nothing to play delete [] positions; delete [] musicParts; delete [] volumeModifiers; delete centerPosition; globalLock.unlock(); // return silent samples return new SoundSamples( inNumSamples ); } int i; for( i=0; igetNotesStartingInInterval( mCurrentPlayTime, bufferLengthInSeconds, ¬es, ¬eStartOffsets ); // render each note and add it to our list of active notes for( int j=0; jmapNoteToSamples( notes[j], &numNoteSamples ); unsigned long totalSamples = numSilentSamples + numNoteSamples; SoundSamples *samplesObject = new SoundSamples( totalSamples ); float *leftChannel = samplesObject->mLeftChannel; float *rightChannel = samplesObject->mRightChannel; // compute stereo panning position /* * Notes by Phil Burk (creator of portaudio): * * If you want to keep the power constant, then (L^2 + R^2) * should be constant. One way to do that is to use sine and * cosine curves for left and right because * (sin^2 + cos^2) = 1. * * pan = 0.0 to PI/2 * LeftGain(pan) = cos(pan) * RightGain(pan) = sin(pan) */ // idea: // First, grab distance for overall gain double distanceFromCenter = centerPosition->getDistance( partPosition ); // limit how much distance affects volume distanceFromCenter *= 0.1; // lower r limit of 1 --- distance can only decrease volume, not // increase it as source gets closer than 1 unit away if( distanceFromCenter < 1 ) { distanceFromCenter = 1; } // compute a vector pointing to part Vector3D vectorToPart( partPosition ); vectorToPart.subtract( centerPosition ); // flip stuff behind us so that it is in front of us // stereo mapping for stuff beind and in front is the same if( vectorToPart.mY < 0 ) { vectorToPart.mY *= -1; } // watch out for near-zero vector if( vectorToPart.getLength() < 0.01 ) { // default to dead center vectorToPart.setCoordinates( 0, 1, 0 ); } // compute angle between hard-right vector and the vector to part // this will always be in the range [0,PI] (because we've reversed // negative y values in vector to part) // want to take cos of angle as right gain and sin as left gain, so // map [0, Pi] space in front of us to [0, PI/2] Vector3D rightVector( 1, 0, 0 ); Angle3D *angle = rightVector.getZAngleTo( &vectorToPart ); double halfZAngle = angle->mZ / 2; delete angle; //double rightGain = cos( halfZAngle ); //double leftGain = sin( halfZAngle ); // actually, for some reason, left and right are reversed // (x coordinates increase as we move left on the screen) // don't waste time figuring out why double leftGain = cos( halfZAngle ); double rightGain = sin( halfZAngle ); // modify gain with distance, // 1 / r^2 double distanceFactor = 1.0 / ( distanceFromCenter * distanceFromCenter ); leftGain *= distanceFactor; rightGain *= distanceFactor; leftGain *= volumeModifiers[i]; rightGain *= volumeModifiers[i]; // samplesObject starts out with all 0 samples // just fill in the note samples beyond the starting silent // region char reversed = notes[j]->mReversed; for( unsigned long k=0; kpush_back( samplesObject ); mNotePositions->push_back( 0 ); delete notes[j]; } delete [] notes; delete [] noteStartOffsets; delete partPosition; // don't delete the music parts, since they are managed by world } delete [] positions; delete [] musicParts; delete [] volumeModifiers; delete centerPosition; // unlock here, since we're done touching shared data globalLock.unlock(); // now we have added all notes that start playing sometime during // this buffer // next mix the samples from active notes that play during this buffer SoundSamples *returnSamples = new SoundSamples( inNumSamples ); float *returnLeftChannel = returnSamples->mLeftChannel; float *returnRightChannel = returnSamples->mRightChannel; for( i=0; isize(); i++ ) { SoundSamples *noteSamples = *( mActiveNotes->getElement( i ) ); unsigned long notePosition = *( mNotePositions->getElement( i ) ); unsigned long numNoteSamples = noteSamples->mSampleCount; unsigned long numSamplesToPlay = inNumSamples; char noteFinished = false; if( numSamplesToPlay + notePosition > numNoteSamples ) { // buffer goes beyond the end of this note numSamplesToPlay = numNoteSamples - notePosition; noteFinished = true; } float *noteLeftChannel = noteSamples->mLeftChannel; float *noteRightChannel = noteSamples->mRightChannel; for( unsigned long j=0; jgetElement( i ) ) = notePosition; if( noteFinished ) { delete noteSamples; mActiveNotes->deleteElement( i ); mNotePositions->deleteElement( i ); // back up in for loop to reflect this note dropping out i--; } } // advance the grid position mCurrentPlayTime += bufferLengthInSeconds; // wrap while( mCurrentPlayTime > mPartLengthInSeconds ) { mCurrentPlayTime -= mPartLengthInSeconds; } return returnSamples; } Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/SoundPlayer.cpp0000640000175000017500000003560310510311327024611 0ustar pabspabs/* * Modification History * * 2004-July-17 Jason Rohrer * Created. * * 2004-July-21 Jason Rohrer * Changed to use a callback to reduce latency. * * 2004-August-9 Jason Rohrer * Added a limit on the number of simultaneous sounds. * * 2004-August-12 Jason Rohrer * Parameterized the sample rate. * * 2004-August-13 Jason Rohrer * Added Mutex to protect members that are accessed by multiple threads. * * 2004-August-14 Jason Rohrer * Changed to fade sounds out before dropping them. * * 2004-August-15 Jason Rohrer * Added a function for getting the sample rate. * Added volume modifier parameter to playSoundNow function. * * 2004-August-20 Jason Rohrer * Added priority flags. * * 2004-August-23 Jason Rohrer * Added music. * * 2004-August-25 Jason Rohrer * Added lock in setMusicPlayer function. * Added function for setting music loudness. * * 2004-August-31 Jason Rohrer * Added function for removing filters. * * 2006-September-19 Jason Rohrer * Added global loudness and fade-in. * * 2006-October-2 Jason Rohrer * Added a separate lock for music player to deal with deadlock. */ #include "SoundPlayer.h" #include "MusicPlayer.h" #include // callback passed into portaudio static int portaudioCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData ) { SoundPlayer *player = (SoundPlayer *)userData; player->getSamples( outputBuffer, framesPerBuffer ); return 0; } /** * Class that wraps SoundSamples in a PlayableSound. */ class SamplesPlayableSound : public PlayableSound { public: /** * Constructs a playable sound. * * @param inSamples the samples to play. * Must be destroyed by caller. */ SamplesPlayableSound( SoundSamples *inSamples ); ~SamplesPlayableSound(); // implements the PlayableSound interface virtual SoundSamples *getMoreSamples( unsigned long inNumSamples ); virtual PlayableSound *copy(); protected: SoundSamples *mRemainingSamples; }; SamplesPlayableSound::SamplesPlayableSound( SoundSamples *inSamples ) : mRemainingSamples( new SoundSamples( inSamples ) ) { } SamplesPlayableSound::~SamplesPlayableSound() { delete mRemainingSamples; } SoundSamples *SamplesPlayableSound::getMoreSamples( unsigned long inNumSamples ) { SoundSamples *returnSamples = new SoundSamples( mRemainingSamples, inNumSamples ); mRemainingSamples->trim( returnSamples->mSampleCount ); return returnSamples; } PlayableSound *SamplesPlayableSound::copy() { return new SamplesPlayableSound( mRemainingSamples ); } SoundPlayer::SoundPlayer( int inSampleRate, int inMaxSimultaneousRealtimeSounds, void *inMusicPlayer, double inMusicLoudness, double inGlobalLoudness ) : mLock( new MutexLock() ), mMusicPlayerLock( new MutexLock() ), mSampleRate( inSampleRate ), mMaxSimultaneousRealtimeSounds( inMaxSimultaneousRealtimeSounds ), mMusicPlayer( inMusicPlayer ), mMusicLoudness( inMusicLoudness ), mGlobalLoudness( inGlobalLoudness ), mFadingIn( false ), mNumFadeFramesRemaining( 0 ), mRealtimeSounds( new SimpleVector() ), mPriorityFlags( new SimpleVector() ), mSoundLoudnessModifiers( new SimpleVector() ), mSoundDroppedFlags( new SimpleVector() ), mFilterChain( new SimpleVector() ) { PaError error = Pa_Initialize(); if( error == paNoError ) { error = Pa_OpenStream( &mAudioStream, paNoDevice,// default input device 0, // no input paFloat32, // 32 bit floating point input NULL, Pa_GetDefaultOutputDeviceID(), 2, // stereo output paFloat32, // 32 bit floating point output NULL, mSampleRate, 1024, // frames per buffer 0, // number of buffers, if zero then use default minimum paClipOff, // we won't output out of range samples so // don't bother clipping them portaudioCallback, (void *)this ); // pass self-pointer to callback function if( error == paNoError ) { error = Pa_StartStream( mAudioStream ); if( error == paNoError ) { mAudioInitialized = true; } else { fprintf( stderr, "Error starting audio stream\n" ); Pa_CloseStream( mAudioStream ); } } else { fprintf( stderr, "Error opening audio stream\n" ); Pa_Terminate(); } } else { fprintf( stderr, "Error initializing audio framework\n" ); } if( error != paNoError ) { fprintf( stderr, "Error number: %d\n", error ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( error ) ); mAudioInitialized = false; } } SoundPlayer::~SoundPlayer() { if( mAudioInitialized ) { PaError error = Pa_StopStream( mAudioStream ); if( error == paNoError ) { error = Pa_CloseStream( mAudioStream ); if( error != paNoError ) { fprintf( stderr, "Error closingaudio stream\n" ); } } else { fprintf( stderr, "Error stopping audio stream\n" ); } Pa_Terminate(); if( error != paNoError ) { fprintf( stderr, "Error number: %d\n", error); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( error) ); } } delete mLock; delete mMusicPlayerLock; int i; int numSounds = mRealtimeSounds->size(); for( i=0; igetElement( i ) ); } delete mRealtimeSounds; delete mPriorityFlags; delete mSoundLoudnessModifiers; delete mSoundDroppedFlags; int numFilters = mFilterChain->size(); for( i=0; igetElement( i ) ); } delete mFilterChain; } void SoundPlayer::setMusicPlayer( void *inMusicPlayer ) { mMusicPlayerLock->lock(); mMusicPlayer = inMusicPlayer; mMusicPlayerLock->unlock(); } void SoundPlayer::setMusicLoudness( double inMusicLoudness ) { mLock->lock(); mMusicLoudness = inMusicLoudness; mLock->unlock(); } void SoundPlayer::setGlobalLoudness( double inLoudness ) { mLock->lock(); mGlobalLoudness = inLoudness; mLock->unlock(); } void SoundPlayer::fadeIn( double inFadeTimeInSeconds ) { mLock->lock(); mFadingIn = true; mNumFadeFramesRemaining = (int)( mSampleRate * inFadeTimeInSeconds ); mLock->unlock(); } void SoundPlayer::getSamples( void *outputBuffer, unsigned long inFramesInBuffer ) { SoundSamples *mixingBuffer = new SoundSamples( inFramesInBuffer ); unsigned long bufferLength = inFramesInBuffer; mLock->lock(); // add each pending realtime sound to the buffer int i = 0; // we may be removing sounds from the buffer as we use them up // i is adjusted inside the while loop while( isize() ) { PlayableSound *realtimeSound = *( mRealtimeSounds->getElement( i ) ); double loudnessModifier = *( mSoundLoudnessModifiers->getElement( i ) ); SoundSamples *samples = realtimeSound->getMoreSamples( bufferLength ); unsigned long mixLength = samples->mSampleCount; char shouldDrop = *( mSoundDroppedFlags->getElement( i ) ); // fade out if we should drop float fadeFactor = 1; unsigned long maxJ = mixLength - 1; for( unsigned long j=0; jmLeftChannel[j] += loudnessModifier * fadeFactor * samples->mLeftChannel[j]; mixingBuffer->mRightChannel[j] += loudnessModifier * fadeFactor * samples->mRightChannel[j]; if( shouldDrop ) { fadeFactor = ( maxJ - j ) / (float)maxJ; } } delete samples; if( mixLength < bufferLength || shouldDrop ) { // we have used up all samples of this sound or // it is flagged to be dropped delete realtimeSound; mRealtimeSounds->deleteElement( i ); mPriorityFlags->deleteElement( i ); mSoundLoudnessModifiers->deleteElement( i ); mSoundDroppedFlags->deleteElement( i ); // don't increment i, since the next element drops into the current // index } else { // increment i to move on to the next sound i++; } } // Deadlock problem: // MusicPlayer locks the globalLock while it accesses music data from world // However, world thread, while it has globalLock locked, tries to set // the music volume, which requires locking our main lock mLock // // Bad order of events: // World locks globalLock // SoundPlayer locks mLock // SoundPlayer calls MusicPlayer to get samples // MusicPlayer waits for globalLock to get notes // World calls setMusicLoudness, waiting for SoundPlayer's mLock // => DEADLOCK // grab a local copy of music loudness before unlocking main lock double localMusicLoundless = mMusicLoudness; // unlock main lock while we access the music player mLock->unlock(); // lock the specific lock mMusicPlayerLock->lock(); if( mMusicPlayer != NULL ) { // cast out of void * MusicPlayer *player = (MusicPlayer *)mMusicPlayer; // mix in the music SoundSamples *musicSamples = player->getMoreMusic( inFramesInBuffer ); for( unsigned long j=0; jmLeftChannel[j] += musicSamples->mLeftChannel[j] * localMusicLoundless; mixingBuffer->mRightChannel[j] += musicSamples->mRightChannel[j] * localMusicLoundless; } delete musicSamples; } mMusicPlayerLock->unlock(); // re-lock the main lock mLock->lock(); // filter the samples int numFilters = mFilterChain->size(); SoundSamples *filteredSamples = new SoundSamples( mixingBuffer ); delete mixingBuffer; for( i=0; igetElement( i ) ); SoundSamples *nextOutput = filter->filterSamples( filteredSamples ); delete filteredSamples; filteredSamples = nextOutput; } mLock->unlock(); float *samples = (float *)outputBuffer; unsigned long numSamples = 2 * bufferLength; unsigned long j; unsigned long frameNumber = 0; // if fading in, adjust global volume as we go double globalLoudnessDelta = 0; if( mFadingIn ) { // want to reach loudness of 1 after mNumFadeFramesRemaining double loudnessChangeLeft = 1 - mGlobalLoudness; globalLoudnessDelta = loudnessChangeLeft / mNumFadeFramesRemaining; } for( j=0; jmLeftChannel[frameNumber]; samples[j+1] = mGlobalLoudness * filteredSamples->mRightChannel[frameNumber]; frameNumber++; if( mFadingIn ) { mGlobalLoudness += globalLoudnessDelta; if( mGlobalLoudness >= 1 ) { // done mGlobalLoudness = 1; mFadingIn = false; mNumFadeFramesRemaining = 0; } } } if( mFadingIn ) { // not done fading in yet // update frames remaining mNumFadeFramesRemaining -= inFramesInBuffer; } delete filteredSamples; } void SoundPlayer::checkForExcessSounds() { int numSounds = mRealtimeSounds->size(); if( numSounds > mMaxSimultaneousRealtimeSounds ) { // flag the oldest unflagged, low-priority sound // skip sounds that are already flagged or are priority sounds int i=0; while( igetElement( i ) ) || *( mPriorityFlags->getElement( i ) ) ) ) { i++; } if( igetElement( i ) ) = true; } else { // else all low-priority sounds are already flagged // try again, ignoring priority flags i=0; while( igetElement( i ) ) ) { i++; } if( igetElement( i ) ) = true; } // else all sounds are already flagged } } } void SoundPlayer::playSoundNow( SoundSamples *inSamples, char inPriorityFlag, double inLoudnessModifier ) { mLock->lock(); mRealtimeSounds->push_back( new SamplesPlayableSound( inSamples ) ); mPriorityFlags->push_back( inPriorityFlag ); mSoundLoudnessModifiers->push_back( inLoudnessModifier ); mSoundDroppedFlags->push_back( false ); checkForExcessSounds(); mLock->unlock(); } void SoundPlayer::playSoundNow( PlayableSound *inSound, char inPriorityFlag, double inLoudnessModifier ) { mLock->lock(); mRealtimeSounds->push_back( inSound->copy() ); mPriorityFlags->push_back( inPriorityFlag ); mSoundLoudnessModifiers->push_back( inLoudnessModifier ); mSoundDroppedFlags->push_back( false ); checkForExcessSounds(); mLock->unlock(); } void SoundPlayer::addMoreMusic( SoundSamples *inSamples ) { } void SoundPlayer::addFilter( SoundFilter *inFilter ) { mLock->lock(); mFilterChain->push_back( inFilter ); mLock->unlock(); } void SoundPlayer::removeAllFilters() { mLock->lock(); int numFilters = mFilterChain->size(); for( int i=0; igetElement( i ) ); } mFilterChain->deleteAll(); mLock->unlock(); } unsigned long SoundPlayer::getSampleRate() { return mSampleRate; } Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/MusicNoteWaveTable.h0000640000175000017500000000710410502323232025504 0ustar pabspabs/* * Modification History * * 2004-August-22 Jason Rohrer * Created. */ #ifndef MUSIC_NOTE_WAVE_TABLE_INCLUDED #define MUSIC_NOTE_WAVE_TABLE_INCLUDED /** * Class representing a note that can be mapped into the wave table. */ class MusicNote { public: /** * Constructs a note. * * @param inFrequencyIndex the frequency parameter in the wave table. * @param inLengthIndex the length parameter in the wave table. * @param inReversed true if this note's samples should be played * in reverse. Defaults to false. */ MusicNote( int inFrequencyIndex, int inLengthIndex, char inReversed = false ); /** * Makes a copy of this note. * * @return a copy. * Must be destroyed by caller. */ MusicNote *copy(); int mFrequencyIndex; int mLengthIndex; char mReversed; }; /** * Class that contains pre-rendered sample table for each possible * note waveform. * * @author Jason Rohrer */ class MusicNoteWaveTable { public: /** * Constructs a wave table. * Reads configuration using the LevelDirectoryManager. * * @param inSamplesPerSecond the sample rate. */ MusicNoteWaveTable( unsigned long inSamplesPerSecond ); ~MusicNoteWaveTable(); /** * Gets the number of frequencies contained in this table. * * @return the number of frequencies. */ int getFrequencyCount(); /** * Gets the number of lengths contained in this table. * * @return the number of lengths. */ int getLengthCount(); /** * Gets the length associated with a length index. * * @param inLengthIndex the index to map. * * @return the length in seconds. */ double getLengthInSeconds( int inLengthIndex ); /** * Maps freqency and length index parameters to wave samples. * * @param inFrequencyIndex the frequency index, in the range * [ 0, getFrequencyCount() ). * @param inLengthIndex the length index, in the range * [ 0, getLengthCount() ). * @param outNumSamples pointer to where the number of samples * should be returned (the size of the returned array). * * @return an array of samples. Will be destroyed by this * class. Should not be modified by caller. */ float *mapParametersToSamples( int inFrequencyIndex, int inLengthIndex, unsigned long *outNumSamples ); /** * Maps a note to wave samples. * * @param inNote the note to map. * Must be destroyed by caller. * @param outNumSamples pointer to where the number of samples * should be returned (the size of the returned array). * * @return an array of samples. Will be destroyed by this * class. Should not be modified by caller. */ float *mapNoteToSamples( MusicNote *inNote, unsigned long *outNumSamples ); protected: int mFrequencyCount; int mLengthCount; float ***mSampleTable; unsigned long *mSampleCounts; double *mLengthsInSeconds; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/SoundSamples.cpp0000640000175000017500000000546310502323232024761 0ustar pabspabs/* * Modification History * * 2004-July-17 Jason Rohrer * Created. * * 2004-August-12 Jason Rohrer * Added a constructor that can specify all sound data. */ #include "SoundSamples.h" #include SoundSamples::SoundSamples( unsigned long inSampleCount, float *inLeftChannel, float *inRightChannel ) : mSampleCount( inSampleCount ), mLeftChannel( inLeftChannel ), mRightChannel( inRightChannel ) { } SoundSamples::SoundSamples( unsigned long inSampleCount ) : mSampleCount( inSampleCount ), mLeftChannel( new float[ inSampleCount ] ), mRightChannel( new float[ inSampleCount ] ) { // zero out the channels; for( unsigned long i=0; imSampleCount; mLeftChannel = new float[ mSampleCount ]; mRightChannel = new float[ mSampleCount ]; memcpy( (void *)( mLeftChannel ), (void *)( inSamplesToCopy->mLeftChannel ), mSampleCount * sizeof( float ) ); memcpy( (void *)( mRightChannel ), (void *)( inSamplesToCopy->mRightChannel ), mSampleCount * sizeof( float ) ); } SoundSamples::SoundSamples( SoundSamples *inSamplesToCopy, unsigned long inNumToCopy ) { mSampleCount = inSamplesToCopy->mSampleCount; if( inNumToCopy < mSampleCount ) { mSampleCount = inNumToCopy; } mLeftChannel = new float[ mSampleCount ]; mRightChannel = new float[ mSampleCount ]; memcpy( (void *)( mLeftChannel ), (void *)( inSamplesToCopy->mLeftChannel ), mSampleCount * sizeof( float ) ); memcpy( (void *)( mRightChannel ), (void *)( inSamplesToCopy->mRightChannel ), mSampleCount * sizeof( float ) ); } SoundSamples::~SoundSamples() { delete [] mRightChannel; delete [] mLeftChannel; } void SoundSamples::trim( unsigned long inNumSamplesToDrop ) { unsigned long newSampleCount = mSampleCount - inNumSamplesToDrop; float *newLeftChannel = new float[ newSampleCount ]; float *newRightChannel = new float[ newSampleCount ]; // copy samples, skipping inNumSamplesToDrop memcpy( (void *)( newLeftChannel ), (void *)( &( mLeftChannel[ inNumSamplesToDrop ] ) ), newSampleCount * sizeof( float ) ); memcpy( (void *)( newRightChannel ), (void *)( &( mRightChannel[ inNumSamplesToDrop ] ) ), newSampleCount * sizeof( float ) ); delete [] mLeftChannel; delete [] mRightChannel; mSampleCount = newSampleCount; mLeftChannel = newLeftChannel; mRightChannel = newRightChannel; } Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/SoundEffectsBank.cpp0000640000175000017500000001303610656633213025540 0ustar pabspabs/* * Modification History * * 2007-August-7 Jason Rohrer * Created. */ #include "SoundEffectsBank.h" #include "minorGems/util/random/StdRandomSource.h" extern int globalSoundSampleRate; extern StdRandomSource globalRandomSource; SoundEffectsBank::SoundEffectsBank( SoundPlayer *inPlayer ) : mSoundPlayer( inPlayer ) { double effectTime = 0.5; int numEffectSamples = (int)( effectTime * globalSoundSampleRate ); float *left = new float[ numEffectSamples ]; float *right = new float[ numEffectSamples ]; int i; // swish swish swish for planting (digging sound) for( i=0; iplaySoundNow( mEffectSamples[ inKey ], false, mEffectLoudness[ inKey ] ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/sound/PlayableSound.h0000640000175000017500000000220610502323232024543 0ustar pabspabs/* * Modification History * * 2004-August-6 Jason Rohrer * Created. */ #ifndef PLAYABLE_SOUND_INCLUDED #define PLAYABLE_SOUND_INCLUDED #include "SoundSamples.h" /** * Interface for a sound source that can produce more samples on demand. * * @author Jason Rohrer */ class PlayableSound { public: /** * Gets more samples from this sound. * * @param inNumSamples the number of samples to get. * * @return the sound samples. Must be destroyed by caller. * At most inNumSamples are returned. If less than inNumSamples * are returned, this indicates that the end of the sound * has been reached. */ virtual SoundSamples *getMoreSamples( unsigned long inNumSamples ) = 0; /** * Makes a copy of this sound. * * @return a copy. * Must be destroyed by caller. */ virtual PlayableSound *copy() = 0; virtual ~PlayableSound(); }; // to make the compilers happy inline PlayableSound::~PlayableSound() { } #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/gameFunctions.h0000640000175000017500000000161610504021065023460 0ustar pabspabs/* * Modification History * * 2006-August-13 Jason Rohrer * Created. */ #include "Gardener.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" int getMaxDistanceForTransactions(); double getGardenerZPosition(); /** * Adds an AI-controlled gardener to the game. * * @param inGardener the gardener to add. Destroyed by game. * @param inPosition the starting position for inGardener. * Destroyed by caller. * @param inRotation the starting rotation, or NULL for default. * Destroyed by caller. */ void addGardenerToGame( Gardener *inGardener, Vector3D *inPosition, Angle3D *inRotation = NULL ); /** * Adjusts music volume to avoid clipping. * * Should be called every time gardener count changes. * * @param inCount the number of gardeners. */ void setNumGardeners( int inCount ); Cultivation_9+dfsg1_UnixSource/game2/gameSource/Seeds.h0000640000175000017500000000360410500352550021722 0ustar pabspabs/* * Modification History * * 2006-July-27 Jason Rohrer * Created. */ #ifndef SEEDS_INCLUDED #define SEEDS_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include "minorGems/graphics/Color.h" #include "Storable.h" #include "PlantGenetics.h" #include "PlantLeaf.h" /** * A inexhaustable "pile" of plant seeds. * * @author Jason Rohrer */ class Seeds : public Storable { public: // Constructs random seeds Seeds(); /** * Constructs seeds by crossing seed characteristics from two * parents. * * Parent fruit color is always taken from parent A. * * @param inParentA, inParentB the parent seeds. Destroyed by caller. */ Seeds( Seeds *inParentA, Seeds *inParentB ); /** * Copies seeds. * * @param inSeedsToCopy the seeds to copy. * Destroyed by caller. */ Seeds( Seeds *inSeedsToCopy ); ~Seeds(); PlantGenetics mGenetics; double mIdealSoilType; Color mFruitNutrition; Color mParentFruitNutition; /** * Gets a sample leaf for this plant. * * @return a leaf. Destroyed when this seed destroyed. */ PlantLeaf *getSampleLeaf(); // implements the DrawableObject interface void draw( Vector3D *inPosition, double inScale = 1 ); // implements the Storable interface StorableType getType() { return seedsType; } protected: // map genetics to our parameters void getParametersFromGenetics(); PlantLeaf *mSampleLeaf; SingleTextureGL *mSeedTexture; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/SoilMap.cpp0000640000175000017500000003761310653400310022562 0ustar pabspabs/* * Modification History * * 2006-July-27 Jason Rohrer * Created. */ #include "SoilMap.h" #include "landscape.h" #include "features.h" #include "glCommon.h" #include "minorGems/graphics/Image.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include "minorGems/util/random/StdRandomSource.h" extern StdRandomSource globalRandomSource; #include //#include "minorGems/io/file/FileOutputStream.h" //#include "minorGems/graphics/converters/TGAImageConverter.h" double baseCosFunction( double inX ) { return cos( inX * M_PI + M_PI ) * 0.5 + 0.5; } double multiCosFunction( double inX ) { return baseCosFunction( baseCosFunction( baseCosFunction( baseCosFunction( inX ) ) ) ); } double maxMultiCosFunction = multiCosFunction( 1 ); double imageSmothingFunction( double inX ) { return multiCosFunction( inX ) / maxMultiCosFunction; } double islandTimeSeed = globalRandomSource.getRandomInt(); SoilMap::SoilMap( Vector3D *inCornerA, Vector3D *inCornerB ) : mCornerA( inCornerA ), mCornerB( inCornerB ) { // pick a new seed each time a soil map is generated islandTimeSeed = globalRandomSource.getRandomInt(); int textureSize = 64; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; for( int i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // 0, 0 in images is at cornerA // (textureSize-1), (textureSize-1) in image is at cornerB double xSpan = mCornerB.mX - mCornerA.mX; double xStart = mCornerA.mX; double ySpan = mCornerB.mY - mCornerA.mY; double yStart = mCornerA.mY; Vector3D position( 0, 0, 0 ); int numPixels = textureSize * textureSize; double *landscapeValues = new double[ numPixels ]; // track max and min so that we can normalize values mMaxLandscapeValue = DBL_MIN; mMinLandscapeValue = DBL_MAX; int x; for( x=0; x mMaxLandscapeValue ) { mMaxLandscapeValue = landscapeValue; } } } // normalize and map into image colors for( int pixelIndex = 0; pixelIndex < numPixels; pixelIndex++ ) { // normalize double landscapeValue = landscapeValues[ pixelIndex ]; landscapeValue -= mMinLandscapeValue; landscapeValue = landscapeValue / ( mMaxLandscapeValue - mMinLandscapeValue ); // change from continuous to binary // continuous is linear // binary is a step function // cos is like a smoothed step function (looks better) // though the underlying landscape parameter is binary for ouside // calls to getSoilCondition landscapeValue = imageSmothingFunction( landscapeValue ); /* if( landscapeValue < 0.5 ) { landscapeValue = 0; } else { landscapeValue = 1; } */ Color *blend = mapSoilToColor( landscapeValue ); channels[0][ pixelIndex ] = blend->r; channels[1][ pixelIndex ] = blend->g; channels[2][ pixelIndex ] = blend->b; channels[3][ pixelIndex ] = 1; delete blend; } delete [] landscapeValues; int blurRadius = 1; BoxBlurFilter blur( blurRadius ); // blur image (skip bluring alpha) textureImage->filter( &blur, 0 ); textureImage->filter( &blur, 1 ); textureImage->filter( &blur, 2 ); mTexture = new SingleTextureGL( textureImage, false ); // now generate grit // set all pixels to white int p; for( p=0; pgetChannel( i ); } numPixels = textureSize * textureSize; // set all pixels to white and solid for( p=0; pfilter( &blur, 3 ); // wrap to hide edge mWaterBoundaryTexture = new SingleTextureGL( textureImage, true ); /* File outFileB( NULL, "boundary.tga" ); FileOutputStream *outStreamB = new FileOutputStream( &outFileB ); TGAImageConverter converter; converter.formatImage( textureImage, outStreamB ); delete outStreamB; exit( 0 ); */ mWaterBoundaryImage = textureImage; mWaterBoundaryImageAlpha = channels[3]; mWaterBoundaryImageNumPixels = numPixels; mWaterBoundaryImageSize = textureSize; } SoilMap::~SoilMap() { delete mTexture; delete mWaterBoundaryTexture; delete mWaterBoundaryImage; delete mGritTexture; } double SoilMap::getSoilCondition( Vector3D *inPosition, char inNormalize ) { // call copied from customHighDetailLandscape in // subreal forever double landscapeValue = variableRoughnessLandscape( 10*inPosition->mX, 10*inPosition->mY, islandTimeSeed, 0.01, 0.001, 0.25, 0.65, 5 ); if( inNormalize ) { landscapeValue -= mMinLandscapeValue; landscapeValue = landscapeValue / ( mMaxLandscapeValue - mMinLandscapeValue ); // clip if( landscapeValue < 0 ) { landscapeValue = 0; } if( landscapeValue > 1 ) { landscapeValue = 1; } // change from continous to binary if( landscapeValue < 0.5 ) { landscapeValue = 0; } else { landscapeValue = 1; } } return landscapeValue; } Color *SoilMap::mapSoilToColor( double inSoilCondition ) { Color greenColor( 0.5, 0.5, 0.2 ); Color brownColor( 0.3, 0.2, 0 ); return Color::linearSum( &greenColor, &brownColor, inSoilCondition ); } void SoilMap::mapPointToBoundaryPixel( Vector3D *inPoint, int *outX, int *outY ) { // first, map inPoint coordinates to 0,1 double pointX = inPoint->mX; double pointY = inPoint->mY; pointX = (pointX - mCornerA.mX) / ( mCornerB.mX - mCornerA.mX ); pointY = (pointY - mCornerA.mY) / ( mCornerB.mY - mCornerA.mY ); *outX = (int)( pointX * ( mWaterBoundaryImageSize - 1 ) ); *outY = (int)( pointY * ( mWaterBoundaryImageSize - 1 ) ); } char SoilMap::isInBounds( Vector3D *inPosition ) { int x, y; mapPointToBoundaryPixel( inPosition, &x, &y ); if( y < 0 || y >= mWaterBoundaryImageSize || x < 0 || x >= mWaterBoundaryImageSize ) { // completely outside image return false; } double alphaValue = mWaterBoundaryImageAlpha[ y * mWaterBoundaryImageSize + x ]; if( alphaValue == 0 ) { return true; } else { return false; } } Vector3D *SoilMap::getClosestBoundaryPoint( Vector3D *inPosition ) { int x, y; char onLand = isInBounds( inPosition ); mapPointToBoundaryPixel( inPosition, &x, &y ); // for now, examine all pixels int closestX = -1; int closestY = -1; double closestDistance = DBL_MAX; for( int yIndex=0; yIndex 0.5 || // OR // closest land point if on water !onLand && mWaterBoundaryImageAlpha[ pixelIndex ] == 0 ) { closestDistance = distance; closestY = yIndex; closestX = xIndex; } } } } } } // have closest x and y // map back into world double worldX = (double)closestX / (double)mWaterBoundaryImageSize; worldX = worldX * (mCornerB.mX - mCornerA.mX) + mCornerA.mX; double worldY = (double)closestY / (double)mWaterBoundaryImageSize; worldY = worldY * (mCornerB.mY - mCornerA.mY) + mCornerA.mY; return new Vector3D( worldX, worldY, 0 ); } void SoilMap::draw( Vector3D *inPosition, double inScale ) { glColor4f( 1, 1, 1, 1 ); if( Features::drawSoil ) { mTexture->enable(); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex2d( mCornerA.mX, mCornerA.mY ); glTexCoord2f( 1, 0 ); glVertex2d( mCornerB.mX, mCornerA.mY ); glTexCoord2f( 1, 1 ); glVertex2d( mCornerB.mX, mCornerB.mY ); glTexCoord2f( 0, 1 ); glVertex2d( mCornerA.mX, mCornerB.mY ); } glEnd(); mTexture->disable(); } else { // draw some grid points for motion reference glPointSize( 2 ); double spacing = 3; glColor4f( 1, 0.5, 0, 0.25 ); double xStart, xEnd, yStart, yEnd; xStart = mCornerA.mX; yStart = mCornerA.mY; xEnd = mCornerB.mX; yEnd = mCornerB.mY; Vector3D position( 0, 0, 0 ); glBegin( GL_POINTS ); { for( double y=yStart; y<=yEnd; y+=spacing ) { for( double x=xStart; x<=xEnd; x+=spacing ) { position.setCoordinates( x, y, 0 ); // skip drawing out-of-bounds points (in water) if( isInBounds( &position ) ) { Color *drawColor = mapSoilToColor( getSoilCondition( &position ) ); setGLColor( drawColor ); glVertex2d( x, y ); delete drawColor; } } } } glEnd(); } if( Features::drawWater ) { // cover with water glColor4f( 0, 0, 1, 1 ); mWaterBoundaryTexture->enable(); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex2d( mCornerA.mX, mCornerA.mY ); glTexCoord2f( 1, 0 ); glVertex2d( mCornerB.mX, mCornerA.mY ); glTexCoord2f( 1, 1 ); glVertex2d( mCornerB.mX, mCornerB.mY ); glTexCoord2f( 0, 1 ); glVertex2d( mCornerA.mX, mCornerB.mY ); } glEnd(); mWaterBoundaryTexture->disable(); } if( Features::drawSurfaceNoise ) { // cover with high-res grit double textureMappingRadius = 12; // stretch twice as large as underlying textures glColor4f( 0, 0, 0, 0.1 ); mGritTexture->enable(); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex2d( 2 * mCornerA.mX, 2 * mCornerA.mY ); glTexCoord2f( textureMappingRadius, 0 ); glVertex2d( 2 * mCornerB.mX, 2 * mCornerA.mY ); glTexCoord2f( textureMappingRadius, textureMappingRadius ); glVertex2d( 2 * mCornerB.mX, 2 * mCornerB.mY ); glTexCoord2f( 0, textureMappingRadius ); glVertex2d( 2 * mCornerA.mX, 2 * mCornerB.mY ); } glEnd(); mGritTexture->disable(); } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/PortalLayerGenetics.cpp0000640000175000017500000000410210531140321025117 0ustar pabspabs/* * Modification History * * 2006-November-8 Jason Rohrer * Created. */ #include "PortalLayerGenetics.h" #include PortalLayerGenetics::PortalLayerGenetics() : GardenerGenetics() { } PortalLayerGenetics::PortalLayerGenetics( GardenerGenetics *inOpener ) : GardenerGenetics( inOpener ) { } double PortalLayerGenetics::getParameter( PortalLayerGeneLocator inLocator ) { double geneValue = mGenes[ inLocator ][0]; switch( inLocator ) { case glyphStartPositionX: case glyphStartPositionY: return mapValueToRange( geneValue, 0.1, 0.9 ); case glyphStepsBeforeDirectionChange: return (int)mapValueToRange( geneValue, 5, 50 ); case glyphStartAngle: return mapValueToRange( geneValue, 0, 2 * M_PI ); case glyphDeltaAngle: return mapValueToRange( geneValue, 0, 0.5 * M_PI ); case quadRotationSpeed: return mapValueToRange( geneValue, 0.5, 1 ); case quadRotationDelta: return mapValueToRange( geneValue, 0, 0.5 ); case offsetRadius: return mapValueToRange( geneValue, .5, 1 ); case offsetSineWeight: return mapValueToRange( geneValue, 0, 0.25 ); case offsetSineSpeed: return mapValueToRange( geneValue, 0, 10 ); case offsetSineSineSpeed: return mapValueToRange( geneValue, 0, 1 ); case quadDensity: // looks better with no density variation // all high density // return mapValueToRange( geneValue, 0.2, 0.5 ); return mapValueToRange( geneValue, 0.5, 0.5 ); case quadScale: return mapValueToRange( geneValue, 0.3, 0.7 ); case quadScaleSineWeight: return mapValueToRange( geneValue, 0, 0.2 ); case quadScaleSineSpeed: return mapValueToRange( geneValue, 0, 4 ); case quadScaleSineSineSpeed: return mapValueToRange( geneValue, 0, 1 ); // default to returning the gene itself default: return geneValue; } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/Seeds.cpp0000640000175000017500000001272010541550740022262 0ustar pabspabs/* * Modification History * * 2006-July-27 Jason Rohrer * Created. */ #include "Seeds.h" #include "SoilMap.h" #include "glCommon.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include #include "minorGems/util/random/StdRandomSource.h" extern StdRandomSource globalRandomSource; Seeds::Seeds() : mGenetics(), mSampleLeaf( NULL ) { getParametersFromGenetics(); } Seeds::Seeds( Seeds *inParentA, Seeds *inParentB ) : mGenetics( &( inParentA->mGenetics ), &( inParentB->mGenetics ) ), mSampleLeaf( NULL ) { getParametersFromGenetics(); } Seeds::Seeds( Seeds *inSeedsToCopy ) : mGenetics( &( inSeedsToCopy->mGenetics ) ), mSampleLeaf( NULL ) { getParametersFromGenetics(); } Seeds::~Seeds() { if( mSampleLeaf != NULL ) { delete mSampleLeaf; mSampleLeaf = NULL; } delete mSeedTexture; } void Seeds::getParametersFromGenetics() { mIdealSoilType = mGenetics.getParameter( idealSoilType ); Color *nutrition = mGenetics.getColor( fruitNutrition ); Color *parentNutrition = mGenetics.getColor( parentFruitNutrition ); mFruitNutrition.setValues( nutrition ); mParentFruitNutition.setValues( parentNutrition ); delete nutrition; delete parentNutrition; double seedWidthParameter = mGenetics.getParameter( seedWidth ); // generate texture // a blurry white seed shape (point down) int textureSize = 32; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } double radius = textureSize / 3; int y; int x; // all white, transparent for( p=0; p 0 ) { angle = acos( xRelative / pixelRadius ); } else { angle = 0; } if( yRelative < 0 ) { angle = 2 * M_PI - angle; } angle += M_PI; // compute rose function at this angle to determine radius double functionRadius = 2 * radius * sin( 3 * angle ); if( pixelRadius <= functionRadius ) { alphaValue = 1; } else { alphaValue = 0; } channels[3][ pixelIndex ] = alphaValue; } } // blur alpha int blurRadius = 1; BoxBlurFilter blur( blurRadius ); textureImage->filter( &blur, 3 ); mSeedTexture = new SingleTextureGL( textureImage, // no wrap false ); delete textureImage; } PlantLeaf *Seeds::getSampleLeaf() { if( mSampleLeaf == NULL ) { mSampleLeaf = new PlantLeaf( &mGenetics ); } return mSampleLeaf; } void Seeds::draw( Vector3D *inPosition, double inScale ) { //double radius = inScale * 0.6; double radius = inScale; Color ripeColor; if( mFruitNutrition.r == 1 ) { ripeColor.setValues( 1, 0, 0, 1 ); } else if( mFruitNutrition.g == 1 ) { // show g nutrient as yellow, not green ripeColor.setValues( 1, 1, 0, 1 ); } else if( mFruitNutrition.b == 1 ) { // show b nutrient as purple, not blue ripeColor.setValues( 0.5, 0, 1, 1 ); } Vector3D nutritionCenter( inPosition ); Vector3D seedCenter( inPosition ); // draw nutrition in bottom corner nutritionCenter.mX += radius / 2; nutritionCenter.mY -= radius / 2; setGLColor( &ripeColor ); drawBlurCircle( &nutritionCenter, radius / 4 ); // seed in center of rectangle Color *seedColor = SoilMap::mapSoilToColor( mIdealSoilType ); Color shadowColor; if( mIdealSoilType == 0 ) { shadowColor.setValues( 1, 1, 1, 1 ); } else { shadowColor.setValues( 0, 0, 0, 1 ); } // draw shadow glColor4f( shadowColor.r, shadowColor.g, shadowColor.b, 1 ); drawTextureQuad( mSeedTexture, &seedCenter, 1.1 * radius ); // draw seed glColor4f( seedColor->r, seedColor->g, seedColor->b, 1 ); drawTextureQuad( mSeedTexture, &seedCenter, radius ); delete seedColor; } Cultivation_9+dfsg1_UnixSource/game2/gameSource/emotionIcons.h0000640000175000017500000000112010461455200023316 0ustar pabspabs/* * Modification History * * 2006-July-25 Jason Rohrer * Created. */ #ifndef EMOTION_ICONS_INCLUDED #define EMOTION_ICONS_INCLUDED /** * A collection of DrawableObject classes for emotional transactions. */ #include "DrawableObject.h" class DislikeIcon : public DrawableObject { public: // implements DrawableObject void draw( Vector3D *inPosition, double inScale = 1 ); }; class LikeIcon : public DrawableObject { public: // implements DrawableObject void draw( Vector3D *inPosition, double inScale = 1 ); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/PlantFlower.h0000640000175000017500000000320710477576332023136 0ustar pabspabs/* * Modification History * * 2006-August-29 Jason Rohrer * Created. */ #ifndef PLANT_FLOWER_INCLUDED #define PLANT_FLOWER_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include "minorGems/graphics/Color.h" #include "PlantGenetics.h" /** * A drawable plant flower. * * @author Jason Rohrer */ class PlantFlower { public: /** * Constructs a flower. * * @param inGenetics the genetics for the shape of this flower. * Destroyed by caller. */ PlantFlower( PlantGenetics *inGenetics ); ~PlantFlower(); /** * Passes time for this flower. * * @param inTimeDeltaInSeconds the amount of time that has passed. */ void passTime( double inTimeDeltaInSeconds ); /** * Draws this flower. * * @param inPosition the position of this flower's base tip. * Destroyed by caller. * @param inRotation the rotation of this flower, around its base * tip. Destroyed by caller. * @param inScale the scale of this flower. * @param inStage ([0,1] bud growth, [1,2] bud bloom, [2,3] hold, * [3,4] fade) */ void draw( Vector3D *inPosition, Angle3D *inRotation, double inScale, double inStage ); private: PlantGenetics mGenetics; SingleTextureGL *mCenterTexture; SingleTextureGL *mPetalTexture; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/GardenerGenetics.cpp0000640000175000017500000001564710531140364024437 0ustar pabspabs/* * Modification History * * 2006-August-11 Jason Rohrer * Created. * * 2006-October-30 Jason Rohrer * Added a following threshold gene. * * 2006-November-8 Jason Rohrer * Added a copy constructor. */ #include "GardenerGenetics.h" extern int globalNumNotesPerMelody; extern double globalMusicSongLength; extern double globalShortestNoteLength; int gardenerGeneLengths[48] = { // behavior modifiers 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // behavior thresholds 1, 1, 1, 1, 1, 1, 1, // body color 3, // bones 1, 1, 1, 1, // scale corner colors 3, 3, 3, 3, // scale corner color deltas 3, 3, 3, 3, // scale spacing and size 1, 1, 1, 1, // eyes 1, 1, 1, // melodies globalNumNotesPerMelody, globalNumNotesPerMelody, globalNumNotesPerMelody, globalNumNotesPerMelody, globalNumNotesPerMelody, globalNumNotesPerMelody, // arrangement // how many sections? // compute how many notes would be in song // if all short notes were used // then compute how many melody sections // would be needed to produce that many notes (int)( ( globalMusicSongLength / globalShortestNoteLength ) / globalNumNotesPerMelody // add extra sections just to be safe + 2 * globalNumNotesPerMelody ), // chance of reverse note 1 }; GardenerGenetics::GardenerGenetics() : Genetics( 48, gardenerGeneLengths ) { } GardenerGenetics::GardenerGenetics( GardenerGenetics *inGenetics ) : Genetics( inGenetics ) { } GardenerGenetics::GardenerGenetics( GardenerGenetics *inParentA, GardenerGenetics *inParentB ) : Genetics( inParentA, inParentB ) { } double GardenerGenetics::getParameter( GardenerGeneLocator inLocator ) { double geneValue = mGenes[ inLocator ][0]; switch( inLocator ) { // special cases where we map gene into parameter space case matingThreshold: { double friendship = getParameter( friendshipThreshold ); // gene determines how much more we add to frienship threshold // to get mating threshold double matingIncrement = (1.0 - friendship) * geneValue; return friendship + matingIncrement; } case followingThreshold: { double mating = getParameter( matingThreshold ); // gene determines how much more we add to mating threshold // to get following threshold double followingIncrement = (1.0 - mating) * geneValue; return mating + followingIncrement; } case storedFruitsBeforeMating: // min of 0, max of 6 return (int)( mapValueToRange( geneValue, 0, 6.9 ) ); case desiredPlantCount: // min of 1 plant, max of 6 return (int)( mapValueToRange( geneValue, 1, 6.9 ) ); case overlapTolerance: // 25% of gardeners can tolerate an overlap fraction of 100% return mapValueToRange( geneValue, 0, 1.33333 ); case pregnancyProgressRate: return mapValueToRange( geneValue, 0.01, 0.02 ); case boneStartSpacing: return mapValueToRange( geneValue, 3, 10 ); case boneSpacingDelta: return mapValueToRange( geneValue, -2, 2 ); case boneCurvature: return mapValueToRange( geneValue, 3, 10 ); case boneCurveFrequency: return mapValueToRange( geneValue, 0.75, 1.75 ); case scaleStepSize: return mapValueToRange( geneValue, 0.14, 0.2 ); case scaleSize: return mapValueToRange( geneValue, 0.15, 0.2 ); case scaleWaveStrength: return mapValueToRange( geneValue, 0, 0.2 ); case scaleWaveFrequency: return mapValueToRange( geneValue, 0, 10 ); case eyeSize: return mapValueToRange( geneValue, 0.5, 1.5 ); case pupilSize: return mapValueToRange( geneValue, 0.5, 1.5 ); case eyeSeparation: return mapValueToRange( geneValue, 1, 1.5 ); case chanceOfReverseNote: return mapValueToRange( geneValue, 0, 0.1 ); case task_none_modifier: // never select task_none return 0; // all other modifiers mapped to range [0.5, 2] case task_water_modifier: case task_harvest_modifier: case task_eat_modifier: case task_createPlot_modifier: case task_plant_modifier: case task_expandPlot_modifier: case task_capturePlant_modifier: case task_poisonPlant_modifier: case task_giveGift_modifier: case task_mate_modifier: case task_rest_modifier: return mapValueToRange( geneValue, 0.5, 2 ); // default to returning the gene itself default: return geneValue; } } Color *GardenerGenetics::getColor( GardenerGeneLocator inLocator ) { double *geneValue = mGenes[ inLocator ]; int geneLength = mGeneLengths[ inLocator ]; if( geneLength < 3 ) { return new Color( geneValue[0], geneValue[0], geneValue[0] ); } else { if( inLocator == scaleCornerAColorDelta || inLocator == scaleCornerBColorDelta || inLocator == scaleCornerCColorDelta || inLocator == scaleCornerDColorDelta ) { // limit range of delta colors double deltaR, deltaG, deltaB; deltaR = mapValueToRange( geneValue[0], -0.1, 0.1 ); deltaG = mapValueToRange( geneValue[1], -0.1, 0.1 ); deltaB = mapValueToRange( geneValue[2], -0.1, 0.1 ); // delta for alpha always zero return new Color( deltaR, deltaG, deltaB, 0 ); } return new Color( geneValue[0], geneValue[1], geneValue[2] ); } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/PlantGenetics.cpp0000640000175000017500000001467110542020557023765 0ustar pabspabs/* * Modification History * * 2006-August-13 Jason Rohrer * Created. * * 2006-December-18 Jason Rohrer * Added seed width. */ #include "PlantGenetics.h" #include int plantGeneLengths[28] = { 1, 1, 3, 3, 1, 1, // fruit shape 1, 1, // leaf color 3, 1, // leaf shape 1, 1, 1, 1, 1, 1, 1, // flower shape 1, 1, 1, 1, 1, 1, // flower color 3, 3, 3, 3, // flower timing 1 }; PlantGenetics::PlantGenetics() : Genetics( 28, plantGeneLengths ) { } PlantGenetics::PlantGenetics( PlantGenetics *inParentA, PlantGenetics *inParentB ) : Genetics( inParentA, inParentB ) { } PlantGenetics::PlantGenetics( PlantGenetics *inGeneticsToCopy ) : Genetics( inGeneticsToCopy ) { } double PlantGenetics::getParameter( PlantGeneLocator inLocator ) { double geneValue = mGenes[ inLocator ][0]; switch( inLocator ) { case idealSoilType: if( geneValue < 0.5 ) { return 0; } else { return 1; } case seedWidth: // special case: // ignore seed width gene // instead, seed width depends on leavesPerJoint geneValue = mGenes[ leavesPerJoint ][0]; return mapValueToRange( geneValue, -1, 0.5 ); case fruitLobeRate: return mapValueToRange( geneValue, 0, 5 ); case fruitLobeDepth: return mapValueToRange( geneValue, 0, 1 ); case jointCount: // map from 2 to 5, integer return (int)( mapValueToRange( geneValue, 2, 5.999 ) ); case leavesPerJoint: // map from 1 to 5, integer return (int)( mapValueToRange( geneValue, 1, 5.999 ) ); case outerLeafGreen: return mapValueToRange( geneValue, 0.35, 1 ); case leafWalkerDeltaAngle: return mapValueToRange( geneValue, -0.01, 0.01 ); case leafWalkerDeltaDeltaAngle: return mapValueToRange( geneValue, -0.005, 0.005 ); case leafWalkerSpawnIntervalFactor: return mapValueToRange( geneValue, 0.3333, 3 ); case leafWalkerStartingSpawnInterval: return mapValueToRange( geneValue, 3, 20 ); case leafWalkerSpawnAngle: return mapValueToRange( geneValue, -0.75 * M_PI, 0.75 * M_PI ); case flowerPetalCount: return (int)( mapValueToRange( geneValue, 3, 10 ) ); case flowerCenterRadius: return mapValueToRange( geneValue, 0.1, 1.0 ); case flowerPetalAngle: return mapValueToRange( geneValue, -0.2 * M_PI, 0.2 * M_PI ); case flowerPetalPointARadius: return mapValueToRange( geneValue, 0.3, 1.0 ); case flowerPetalPointAAngle: return mapValueToRange( geneValue, 0.1 * M_PI, 0.2 * M_PI ); case flowerPetalPointBRadius: return mapValueToRange( geneValue, 0.3, 1.0 ); case flowerPetalPointBAngle: return mapValueToRange( geneValue, -0.2 * M_PI, -0.1 * M_PI ); case timeBetweenFlowers: return mapValueToRange( geneValue, 1, 30 ); // default to returning the gene itself default: return geneValue; break; } } Color *PlantGenetics::getColor( PlantGeneLocator inLocator ) { double *geneValue = mGenes[ inLocator ]; int geneLength = mGeneLengths[ inLocator ]; if( geneLength < 3 ) { return new Color( geneValue[0], geneValue[0], geneValue[0] ); } else if ( inLocator == innerLeafColor ) { // red and blue between 0 and 0.5 // green between 0.35 and 1 return new Color( geneValue[0] * 0.5, geneValue[1] * 0.65 + 0.35, geneValue[2] * 0.5 ); } else if ( inLocator == flowerCenterColor || inLocator == flowerPetalPointAColor || inLocator == flowerPetalPointBColor || inLocator == flowerPetalPointCColor ) { // make sure green not the highest color component double colorValues[3]; colorValues[0] = geneValue[0]; colorValues[1] = geneValue[1]; colorValues[2] = geneValue[2]; if( colorValues[1] > colorValues[0] && colorValues[1] > colorValues[2] ) { // lower it colorValues[1] = colorValues[0]; } /* // map highest component to 1.0 and leave others alone // this avoids black flower centers int highestIndex = -1; double highestValue = 0; int i; for( i=0; i<3; i++ ) { if( geneValue[i] > highestValue ) { highestValue = geneValue[i]; highestIndex = i; } } double colorValues[3]; for( i=0; i<3; i++ ) { if( i == highestIndex ) { colorValues[i] = 1.0; } else { colorValues[i] = geneValue[i]; } } */ return new Color( colorValues[0], colorValues[1], colorValues[2] ); } else if( inLocator == fruitNutrition || inLocator == parentFruitNutrition ) { // map highest component to 1 and others to 0.25 int highestIndex = -1; double highestValue = 0; int i; for( i=0; i<3; i++ ) { if( geneValue[i] > highestValue ) { highestValue = geneValue[i]; highestIndex = i; } } double colorValues[3]; for( i=0; i<3; i++ ) { if( i == highestIndex ) { colorValues[i] = 1.0; } else { colorValues[i] = 0.25; } } return new Color( colorValues[0], colorValues[1], colorValues[2] ); } else { return new Color( geneValue[0], geneValue[1], geneValue[2] ); } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/testGardener.cpp0000640000175000017500000000117610501767735023663 0ustar pabspabs #include "Gardener.cpp" #include "GardenerGenetics.cpp" #include "Genetics.cpp" #include "PlantGenetics.cpp" #include "Seeds.cpp" #include "Fruit.cpp" #include "SoilMap.cpp" #include "glCommon.cpp" #include "landscape.cpp" //StdRandomSource globalRandomSource( time( NULL ) ); StdRandomSource globalRandomSource( 5002 ); void addGardenerToGame(Gardener *, Vector3D *, Angle3D *) { // dummy } int fileCount = 0; int main() { for( int i=0; i<50; i++ ) { Vector3D startPosition( 0, 0, 0 ); Gardener *gardener = new Gardener( &startPosition ); delete gardener; } return 0; } Cultivation_9+dfsg1_UnixSource/game2/gameSource/Gardener.cpp0000640000175000017500000020474511377047545022774 0ustar pabspabs/* * Modification History * * 2006-July-2 Jason Rohrer * Created. * * 2006-October-4 Jason Rohrer * Fixed a crash when a baby's parent dies and is removed. * Made water pickup and dumping smoother. * Fixed an eye drawing bug. * * 2006-October-10 Jason Rohrer * Fixed crashing bug. * Improved selection of least/most-liked gardeners when there is a tie. * * 2006-October-26 Jason Rohrer * Added a limit of 10 stored items. * * 2006-October-27 Jason Rohrer * Added forceDead function. Added poisonFruit function. * Changed to ignore poisoned fruits in getIndexOfFruitHighInNutrient. * * 2006-October-30 Jason Rohrer * Added a following threshold gene. * * 2006-November-2 Jason Rohrer * Fixed so that mLife drops to 0 when forced dead. * * 2006-November-13 Jason Rohrer * Added a frozen state and a manual fade factor for drawing. * * 2006-November-24 Jason Rohrer * Made most/least liked gardener consistent across multiple function calls. * Fixed birth position bug when parent moving. * * 2006-November-25 Jason Rohrer * Added feeding of followers. * * 2006-November-25 Jason Rohrer * Fixed so that nutrients pass through to babies of followers. * * 2006-December-25 Jason Rohrer * Added function to check if any stored fruit poisoned. */ #include "Gardener.h" #include "features.h" #include "gameFunctions.h" #include "glCommon.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include "minorGems/util/random/StdRandomSource.h" extern StdRandomSource globalRandomSource; extern MusicNoteWaveTable globalWaveTable; extern double globalMusicSongLength; extern int globalNumNotesPerMelody; extern double globalShortestNoteLength; #include "World.h" extern World *globalWorld; #include #include //#include "minorGems/io/file/FileOutputStream.h" //#include "minorGems/graphics/converters/TGAImageConverter.h" ScaleCornerColors *ScaleCornerColors::copy() { ScaleCornerColors *returnValue = new ScaleCornerColors(); for( int i=0; i<4; i++ ) { returnValue->mColors[i].setValues( &( mColors[i] ) ); } return returnValue; } Gardener::Gardener( Vector3D *inStartPosition ) : mGenetics(), mDesiredPosition( inStartPosition ), mLastDrawAngle( 0, 0, 0 ), mParent( NULL ), mGrowthProgress( 1 ) { setUpStartingState(); } Gardener::Gardener( Gardener *inParentA, Gardener *inParentB ) : mGenetics( &( inParentA->mGenetics ), &( inParentB->mGenetics ) ), mLastDrawAngle( 0, 0, 0 ), mParent( inParentA ), mGrowthProgress( 0.35 ) { // set desired position, to start, to parentA's position Vector3D *position = inParentA->getDesiredPosition(); mDesiredPosition.setCoordinates( position ); delete position; setUpStartingState(); } int gardenerLargeTextureSize = 64; Image gardenerLargeTextureImage( gardenerLargeTextureSize, gardenerLargeTextureSize, 4, false ); int gardenerSmallTextureSize = 32; Image gardenerSmallTextureImage( gardenerSmallTextureSize, gardenerSmallTextureSize, 4, false ); void Gardener::setUpStartingState() { mSecondsSinceLastFruitPoisoning = 0; mLeader = NULL; mMostLiked = NULL; mLeastLiked = NULL; mUserCanControl = false; mUserControlling = false; mMoving = false; mCarryingWater = false; mWaterPickupFraction = 0; mRedNutrient = 1; mGreenNutrient = 1; mBlueNutrient = 1; mPoisoned = false; mLife = 1.0; mBaseAgeRate = 0.0005; mBaseEnergyConsumedPerSecond = 0.01; mDead = false; mGhostMode = false; mFrozen = false; mEmotionDisplay = 0; mPlotHidden = true; mSelectedStorageIndex = -1; mOffspring = NULL; mPregnancyProgress = 0; // start fading out at 5% life mNearDeathPoint = 0.05; Color *c = mGenetics.getColor( bodyColor ); mColor.setValues( c ); delete c; // generate notes from genetic melody parts int numNotes = (int)( globalMusicSongLength / globalShortestNoteLength ) // extra to be safe + 10; double *notes = new double[numNotes]; double *arrangement = mGenetics.getGene( songArrangement ); int numArrangementParts = mGenetics.getGeneLength( songArrangement ); double *melodies[6]; melodies[0] = mGenetics.getGene( melodyA ); melodies[1] = mGenetics.getGene( melodyB ); melodies[2] = mGenetics.getGene( melodyC ); melodies[3] = mGenetics.getGene( melodyD ); melodies[4] = mGenetics.getGene( melodyE ); melodies[5] = mGenetics.getGene( melodyF ); // only have a repertoire of a subset of melodies // gardeners with larger eyes and pupils have more melodies to choose // from and thus more complex songs double eyeFactor = mGenetics.getParameter( pupilSize ) + mGenetics.getParameter( eyeSize ); // eye factor is in range 1 to 3, map to [0,1]; eyeFactor = (eyeFactor - 1) / 2; // map to int from 1 to 6 int numMelodiesAvailable = (int)( eyeFactor * 5.99 ) + 1; int currentSection = 0; int currentSectionNote = 0; for( int n=0; n= globalNumNotesPerMelody ) { // on to next section currentSectionNote = 0; currentSection++; if( currentSection >= numArrangementParts ) { // wrap back currentSection = 0; } } } delete [] arrangement; for( int m=0; m<6; m++ ) { delete [] melodies[m]; } double reverseNote = mGenetics.getParameter( chanceOfReverseNote ); mStillMusicPart = new MusicPart( &globalWaveTable, globalMusicSongLength, notes, numNotes, false, false, reverseNote ); // high notes when holding water mHoldingMusicPart = new MusicPart( &globalWaveTable, globalMusicSongLength, notes, numNotes, true, false, reverseNote ); // low fast notes mMovingMusicPart = new MusicPart( &globalWaveTable, globalMusicSongLength, notes, numNotes, false, true, reverseNote ); // high fast notes mMovingHoldingMusicPart = new MusicPart( &globalWaveTable, globalMusicSongLength, notes, numNotes, true, true, reverseNote ); delete [] notes; // generate texture for gardener base // a blurry white circle that is gray at edges int textureSize = gardenerSmallTextureSize; double halfTextureSize = 0.5 * textureSize; Image *textureImage = &gardenerSmallTextureImage; double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // start all pixels as gray and transparent for( p=0; pfilter( &blur, 3 ); mBaseTexture = new SingleTextureGL( textureImage, // no wrap false ); // now scale texture // a simple square with blurred edges // start all pixels as white and transparent for( p=0; pfilter( &blur, 3 ); mScaleTexture = new SingleTextureGL( textureImage, // no wrap false ); // now build overlay bone texture // higher res textureSize = gardenerLargeTextureSize; halfTextureSize = 0.5 * textureSize; textureImage = &gardenerLargeTextureImage; pixelsPerChannel = textureSize * textureSize; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } radius = textureSize / 3; // start all pixels as white and transparent for( p=0; p= yStep ) { // place bone here for( int stripeLine = 0; stripeLine < stripeWidth; stripeLine++ ) { double yRelative = (y + stripeLine) - halfTextureSize; for( x=0; xfilter( &blur, 3 ); mBonesTexture = new SingleTextureGL( textureImage, // no wrap false ); // now build skin texture // start all pixels as white and transparent for( p=0; pfilter( &blur, 3 ); mSkinTexture = new SingleTextureGL( textureImage, // no wrap false ); // eye texture // start all pixels as black and transparent for( p=0; pfilter( &blur, 0 ); textureImage->filter( &blur, 3 ); // copy blurred color data memcpy( channels[1], channels[0], pixelsPerChannel * sizeof( double ) ); memcpy( channels[2], channels[0], pixelsPerChannel * sizeof( double ) ); mEyeTexture = new SingleTextureGL( textureImage, // no wrap false ); /* File outFileB( NULL, "scale.tga" ); FileOutputStream *outStreamB = new FileOutputStream( &outFileB ); TGAImageConverter converter; converter.formatImage( textureImage, outStreamB ); delete outStreamB; exit( 0 ); */ // now create scale locations // walker goes back and forth across gardener, laying rows of scales // scales only laid inside the round radius of the gardener double scaleBoundaryRadius = 0.6666; double scaleStep = mGenetics.getParameter( scaleStepSize ); ScaleCornerColors currentColors; Color *aColor = mGenetics.getColor( scaleCornerAColor ); Color *bColor = mGenetics.getColor( scaleCornerBColor ); Color *cColor = mGenetics.getColor( scaleCornerCColor ); Color *dColor = mGenetics.getColor( scaleCornerDColor ); currentColors.mColors[0].setValues( aColor ); currentColors.mColors[1].setValues( bColor ); currentColors.mColors[2].setValues( cColor ); currentColors.mColors[3].setValues( dColor ); delete aColor; delete bColor; delete cColor; delete dColor; ScaleCornerColors deltaColors; Color *aColorDelta = mGenetics.getColor( scaleCornerAColorDelta ); Color *bColorDelta = mGenetics.getColor( scaleCornerBColorDelta ); Color *cColorDelta = mGenetics.getColor( scaleCornerCColorDelta ); Color *dColorDelta = mGenetics.getColor( scaleCornerDColorDelta ); deltaColors.mColors[0].setValues( aColorDelta ); deltaColors.mColors[1].setValues( bColorDelta ); deltaColors.mColors[2].setValues( cColorDelta ); deltaColors.mColors[3].setValues( dColorDelta ); delete aColorDelta; delete bColorDelta; delete cColorDelta; delete dColorDelta; double scaleAngle = 0; double scaleDeltaAngle = 0.1; double ourScaleSize = mGenetics.getParameter( scaleSize ); double sineStrength = mGenetics.getParameter( scaleWaveStrength ); double sineFrequency = mGenetics.getParameter( scaleWaveFrequency ); // take square shape of scale into account double maxScaleRadius = sqrt( 2 * ourScaleSize * ourScaleSize ); // make sure scale positions are centered (we want a scale to be at 0,0) double limit = (int)(1 / scaleStep) * scaleStep; for( double bodyY = -limit; bodyY <= limit; bodyY += scaleStep ) { for( double bodyX = -limit; bodyX <= limit; bodyX += scaleStep ) { double adjustedBodyY = bodyY + sineStrength * sin( sineFrequency * bodyX ); double pointRadius = sqrt( bodyX * bodyX + adjustedBodyY * adjustedBodyY ); if( pointRadius <= (scaleBoundaryRadius - maxScaleRadius) ) { // place a scale Vector3D *scalePosition = new Vector3D( bodyX, adjustedBodyY, 0 ); mScalePositions.push_back( scalePosition ); mScaleRotations.push_back( scaleAngle ); mScaleSizes.push_back( ourScaleSize ); mScaleColors.push_back( currentColors.copy() ); // adjust angle scaleAngle += scaleDeltaAngle; // adjust colors for( int c=0; c<4; c++ ) { Color *thisColor = &( currentColors.mColors[c] ); Color *thisDeltaColor = &( deltaColors.mColors[c] ); // loop over r, g, b, and a components for( int v=0; v<4; v++ ) { (*thisColor)[v] += (*thisDeltaColor)[v]; // wrap around if( (*thisColor)[v] < 0 ) { (*thisColor)[v] = 1; } else if( (*thisColor)[v] > 1 ) { (*thisColor)[v] = 0; } } } } } } /* // walker starts in center of gardener, walks up, lays first scale // lays scale at every step double walkerX = 0; double walkerY = 0; double walkerDeltaX = 0; double walkerDeltaY = 0.2; double walkerDeltaAngle = 0; double walkerDeltaDeltaAngle = 0.05; ScaleCornerColors currentColors; currentColors.mColors[0].setValues( 1, 0, 0, 1 ); currentColors.mColors[1].setValues( 1, 1, 0, 1 ); currentColors.mColors[2].setValues( 0, 1, 0, 1 ); currentColors.mColors[3].setValues( 0, 0, 1, 1 ); ScaleCornerColors deltaColors; deltaColors.mColors[0].setValues( -0.1, 0, 0.1, 0 ); deltaColors.mColors[1].setValues( -0.1, 1, 0.1, 0 ); deltaColors.mColors[2].setValues( 0, 0, 0, 0 ); deltaColors.mColors[3].setValues( 0, 0, 0, 0 ); double scaleAngle = 0; double scaleDeltaAngle = 0.1; double scaleSize = 0.1; double scaleDeltaSize = 0.01; for( int s=0; s<20; s++ ) { // add a scale Vector3D *scalePosition = new Vector3D( walkerX, walkerY, 0 ); mScalePositions.push_back( scalePosition ); mScaleRotations.push_back( scaleAngle ); mScaleSizes.push_back( scaleSize ); mScaleColors.push_back( currentColors.copy() ); // advance walker walkerX += walkerDeltaX; walkerY += walkerDeltaY; if( walkerX < -0.66 || walkerX > 0.66 ) { // bounce, reversing direction walkerDeltaX *= -1; // reset delta angle walkerDeltaAngle = 0; } if( walkerY < -0.66 || walkerY > 0.66 ) { // bounce, reversing direction walkerDeltaY *= -1; // reset delta angle walkerDeltaAngle = 0; } // adjust direction Vector3D direction( walkerDeltaX, walkerDeltaY, 0 ); Angle3D deltaAngle( 0, 0, walkerDeltaAngle ); direction.rotate( &deltaAngle ); walkerDeltaX = direction.mX; walkerDeltaY = direction.mY; // adjust angles walkerDeltaAngle += walkerDeltaDeltaAngle; scaleAngle += scaleDeltaAngle; // adjust size scaleSize += scaleDeltaSize; // adjust colors for( int c=0; c<4; c++ ) { Color *thisColor = &( currentColors.mColors[c] ); Color *thisDeltaColor = &( deltaColors.mColors[c] ); // loop over r, g, b, and a components for( int v=0; v<4; v++ ) { (*thisColor)[v] += (*thisDeltaColor)[v]; // wrap around if( (*thisColor)[v] < 0 ) { (*thisColor)[v] = 1; } else if( (*thisColor)[v] > 1 ) { (*thisColor)[v] = 0; } } } } // end loop over all scales to place */ mGlobalScaleAngle.setComponents( 0, 0, 0 ); } Gardener::~Gardener() { delete mBaseTexture; delete mScaleTexture; delete mBonesTexture; delete mSkinTexture; delete mEyeTexture; int i; int numScales = mScaleSizes.size(); for( i=0; i 5 ) { int numItems = mStoredItems.size(); char found = false; for( int i=0; igetType() == fruitType ) { Fruit *fruit = (Fruit *)item; if( ! fruit->isPoisoned() ) { fruit->poison(); found = true; mSecondsSinceLastFruitPoisoning = 0; } } } } } void Gardener::eat() { // get fruit, save seeds in storage, and get any fruit if none selected Fruit *fruit = getSelectedFruit( true, true ); if( fruit != NULL ) { if( ! fruit->isPoisoned() ) { Color *nutrition = fruit->getNutrition(); this->feed( nutrition->r, nutrition->g, nutrition->b ); delete nutrition; } else { // eating a poisoned fruit // jump to near death state mLife = mNearDeathPoint; mPoisoned = true; } delete fruit; } } void Gardener::trackOtherGardener( Gardener *inGardener ) { mOtherGardeners.push_back( inGardener ); // start off luke warm toward everyone // but liking them a little bit mLikeMetrics.push_back( 0.6 ); // reset mMostLiked = NULL; mLeastLiked = NULL; } void Gardener::untrackOtherGardener( Gardener *inGardener ) { int index = mOtherGardeners.getElementIndex( inGardener ); if( index != -1 ) { mOtherGardeners.deleteElement( index ); mLikeMetrics.deleteElement( index ); } // reset mMostLiked = NULL; mLeastLiked = NULL; } void Gardener::getAngry( Gardener *inGardener ) { int index = mOtherGardeners.getElementIndex( inGardener ); if( index != -1 ) { // decrease like metric double metric = *( mLikeMetrics.getElement( index ) ); metric -= 0.1; if( metric < 0 ) { metric = 0; } *( mLikeMetrics.getElement( index ) ) = metric; } // reset mMostLiked = NULL; mLeastLiked = NULL; } void Gardener::getMaxAngry( Gardener *inGardener ) { int index = mOtherGardeners.getElementIndex( inGardener ); if( index != -1 ) { // decrease like metric all the way down *( mLikeMetrics.getElement( index ) ) = 0; } // reset mMostLiked = NULL; mLeastLiked = NULL; } void Gardener::getFriendly( Gardener *inGardener ) { int index = mOtherGardeners.getElementIndex( inGardener ); if( index != -1 ) { // increase like metric double metric = *( mLikeMetrics.getElement( index ) ); metric += 0.1; if( metric > 1 ) { metric = 1; } *( mLikeMetrics.getElement( index ) ) = metric; } // reset mMostLiked = NULL; mLeastLiked = NULL; } double Gardener::getLikeMetric( Gardener *inGardener ) { int index = mOtherGardeners.getElementIndex( inGardener ); if( index != -1 ) { return *( mLikeMetrics.getElement( index ) ); } else { return -1; } } char Gardener::likeEnoughToMate( Gardener *inGardener ) { int index = mOtherGardeners.getElementIndex( inGardener ); if( index != -1 ) { double likeMetric = *( mLikeMetrics.getElement( index ) ); double matingLevel = mGenetics.getParameter( matingThreshold ); if( likeMetric >= matingLevel ) { return true; } } // default return false; } char Gardener::likeEnoughToFollow( Gardener *inGardener ) { int index = mOtherGardeners.getElementIndex( inGardener ); if( index != -1 ) { double likeMetric = *( mLikeMetrics.getElement( index ) ); double followLevel = mGenetics.getParameter( followingThreshold ); if( likeMetric >= followLevel ) { return true; } } // default return false; } Gardener *Gardener::getMostLikedGardener() { if( mMostLiked != NULL ) { return mMostLiked; } int numOthers = mOtherGardeners.size(); // ignore gardeners that are at or below our friendship threshold double highestMetric = mGenetics.getParameter( friendshipThreshold ) + 0.01; // track all gardeners that are equally high SimpleVector highestGardeners; for( int i=0; i= highestMetric ) { Gardener *other = *( mOtherGardeners.getElement( i ) ); // ignore offspring that are following us // ignore already dead if( other->getParentToFollow() != this && ! other->isDead() ) { if( metric == highestMetric ) { // add another to list highestGardeners.push_back( other ); } else if( metric > highestMetric ) { // a new high // clear the list highestGardeners.deleteAll(); // start with a new list highestGardeners.push_back( other ); highestMetric = metric; } } } } int numHigh = highestGardeners.size(); if( numHigh == 0 ) { return NULL; } else { // pick one at random int pick = globalRandomSource.getRandomBoundedInt( 0, numHigh - 1 ); // save for future calls mMostLiked = *( highestGardeners.getElement( pick ) ); return mMostLiked; } } Gardener *Gardener::getLeastLikedGardener() { if( mLeastLiked != NULL ) { return mLeastLiked; } int numOthers = mOtherGardeners.size(); // ignore gardeners that are at or above our friendship threshold double lowestMetric = mGenetics.getParameter( friendshipThreshold ) - 0.01; // track all gardeners that are equally low SimpleVector lowestGardeners; for( int i=0; igetParentToFollow() != this && ! other->isDead() ) { if( metric == lowestMetric ) { // add another to list lowestGardeners.push_back( other ); } else if( metric < lowestMetric ) { // a new low // clear the list lowestGardeners.deleteAll(); // start with a new list lowestGardeners.push_back( other ); lowestMetric = metric; } } } } int numLow = lowestGardeners.size(); if( numLow == 0 ) { return NULL; } else { // pick one at random int pick = globalRandomSource.getRandomBoundedInt( 0, numLow - 1 ); // save for future calls mLeastLiked = *( lowestGardeners.getElement( pick ) ); return mLeastLiked; } } void Gardener::setEmotionDisplay( double inEmotion ) { mEmotionDisplay = inEmotion; } void Gardener::setPlotHidden( char inHidden ) { mPlotHidden = inHidden; } char Gardener::isPlotHidden() { return mPlotHidden; } void Gardener::storeItem( Storable *inItem ) { mStoredItems.push_back( inItem ); // limit of 10 items while( mStoredItems.size() > 10 ) { // drop oldest item delete *( mStoredItems.getElement( 0 ) ); mStoredItems.deleteElement( 0 ); } } int Gardener::getStoredFruitCount() { int numItems = mStoredItems.size(); int count = 0; for( int i=0; igetType() == fruitType ) { Fruit *fruit = (Fruit *)item; // ignore poisoned fruit if( !fruit->isPoisoned() ) { count ++; } } } return count; } char Gardener::isStoredFruitPoisoned() { int numItems = mStoredItems.size(); for( int i=0; igetType() == fruitType ) { Fruit *fruit = (Fruit *)item; if( fruit->isPoisoned() ) { return true; } } } return false; } Fruit *Gardener::getSelectedFruit( char inSaveSeeds, char inGetAnyFruit ) { Storable *item = getSelectedStorable(); if( item != NULL ) { if( inGetAnyFruit && item->getType() != fruitType ) { // fruit not selected // search for any fruit // search through storage for first fruit encountered // and set our selection to that char found = false; int numItems = mStoredItems.size(); for( int i=0; igetType() == fruitType ) { mSelectedStorageIndex = i; found = true; } } item = getSelectedStorable(); } if( item->getType() == fruitType ) { if( inSaveSeeds ) { // replace fruit with seeds Fruit *fruit = (Fruit *)item; Seeds *seeds = fruit->getSeeds(); *( mStoredItems.getElement( mSelectedStorageIndex ) ) = seeds; } else { // simply remove fruit from storage mStoredItems.deleteElement( mSelectedStorageIndex ); if( mSelectedStorageIndex >= mStoredItems.size() ) { mSelectedStorageIndex = mStoredItems.size() - 1; } } return (Fruit *)item; } } return NULL; } int Gardener::getIndexOfFruitHighInNutrient( int inNutrientIndex ) { int numItems = mStoredItems.size(); int fruitIndex = -1; char foundHigh = false; for( int i=0; igetType() == fruitType ) { Fruit *fruit = (Fruit *)item; if( ! fruit->isPoisoned() ) { Color *nutrition = fruit->getNutrition(); double nutrientLevel = (*nutrition)[ inNutrientIndex ]; delete nutrition; if( !foundHigh ) { // found a fruit // take note of it, but keep searching fruitIndex = i; } if( nutrientLevel == 1 ) { // found high-nutrient fruit, so stop searching foundHigh = true; } } } } return fruitIndex; } Seeds *Gardener::getSelectedSeeds() { Storable *item = getSelectedStorable(); if( item != NULL ) { if( item->getType() == seedsType ) { return (Seeds *)item; } } return NULL; } void Gardener::removeSeeds( Seeds *inSeeds ) { int numItems = mStoredItems.size(); for( int i=0; i= numLeft ) { mSelectedStorageIndex = numLeft - 1; } return; } } } SimpleVector *Gardener::getAllSeeds() { SimpleVector *seedsVector = new SimpleVector(); int numItems = mStoredItems.size(); for( int i=0; igetType() == seedsType ) { seedsVector->push_back( (Seeds*)item ); } } return seedsVector; } Storable *Gardener::getSelectedStorable() { if( mSelectedStorageIndex == -1 ) { if( mStoredItems.size() > 0 ) { // default to first fruit mSelectedStorageIndex = 0; } } if( mSelectedStorageIndex != -1 ) { Storable *item = *( mStoredItems.getElement( mSelectedStorageIndex ) ); return item; } else { return NULL; } } Storable *Gardener::getStorable( int inIndex ) { return *( mStoredItems.getElement( inIndex ) ); } void Gardener::deleteSelectedStorable() { if( mSelectedStorageIndex != -1 ) { Storable *item = *( mStoredItems.getElement( mSelectedStorageIndex ) ); delete item; mStoredItems.deleteElement( mSelectedStorageIndex ); int newItemCount = mStoredItems.size(); if( mSelectedStorageIndex >= newItemCount ) { mSelectedStorageIndex = newItemCount - 1; } } } void Gardener::passTime( double inTimeDeltaInSeconds ) { if( mDead ) { return; } double energyRate = mBaseEnergyConsumedPerSecond; double energyConsumed = energyRate * inTimeDeltaInSeconds; double energyRateFactor = 1; if( mMoving ) { energyRateFactor *= 2; } if( mCarryingWater ) { energyRateFactor *= 2; } if( mOffspring != NULL ) { // pregnant energyRateFactor *= 2; } if( mGrowthProgress < 1 ) { // still growing // growing consumes energy faster than other activities energyRateFactor *= 4; } energyConsumed *= energyRateFactor; // energy usage quadruples if moving and carrying water at same time // energy rate octuples if moving, carrying water, and pregnant mRedNutrient -= energyConsumed; mGreenNutrient -= energyConsumed; mBlueNutrient -= energyConsumed; // pregnancy cannot progress if low in any nutrient char pregnancyCanProgress = true; double agingFactor = 1; // age faster if we're low on nutrients if( mRedNutrient <= 0 ) { agingFactor *= 2; // never negative mRedNutrient = 0; pregnancyCanProgress = false; } if( mGreenNutrient <= 0 ) { agingFactor *= 2; mGreenNutrient = 0; pregnancyCanProgress = false; } if( mBlueNutrient <= 0 ) { agingFactor *= 2; mBlueNutrient = 0; pregnancyCanProgress = false; } if( mPoisoned ) { agingFactor *= 8; } // for testing only // pregnancyCanProgress = true; mLife -= mBaseAgeRate * inTimeDeltaInSeconds * agingFactor; if( mLife <= 0 ) { mLife = 0; mDead = true; if( mOffspring != NULL ) { // destroy offspring (end pregnancy) delete mOffspring; mOffspring = NULL; } } // age fruit int numItems = mStoredItems.size(); for( int i=0; igetType() == fruitType ) { Fruit *fruit = (Fruit *)item; fruit->passTime( inTimeDeltaInSeconds ); if( fruit->isRotten() ) { // remove delete fruit; mStoredItems.deleteElement( i ); // back up in loop numItems = mStoredItems.size(); i--; if( mSelectedStorageIndex >= numItems ) { mSelectedStorageIndex = numItems - 1; } } } } if( mOffspring != NULL && pregnancyCanProgress ) { double rate = mGenetics.getParameter( pregnancyProgressRate ); mPregnancyProgress += rate * inTimeDeltaInSeconds; if( mPregnancyProgress >= 1 ) { // done // give birth // stick baby into world at our current position Vector3D *position = globalWorld->getGardenerPosition( this ); // give some seeds SimpleVector *allSeeds = getAllSeeds(); int numSeeds = allSeeds->size(); if( numSeeds > 0 ) { // give last seeds to offspring to give them a start Seeds *ourLastSeeds = *( allSeeds->getElement( numSeeds - 1 ) ); // give copy mOffspring->storeItem( new Seeds( ourLastSeeds ) ); } delete allSeeds; // user can control offspring if user can control us mOffspring->mUserCanControl = mUserCanControl; // use our rotation for offspring start addGardenerToGame( mOffspring, position, &mLastDrawAngle ); delete position; // save pointer so that we can feed it mOutsideOffspring.push_back( mOffspring ); mOffspring = NULL; mPregnancyProgress = 0; } } // can grow if same conditions for pregnancy possible if( mGrowthProgress < 1 && pregnancyCanProgress ) { // grow at pregnancy progress rate double rate = mGenetics.getParameter( pregnancyProgressRate ); mGrowthProgress += rate * inTimeDeltaInSeconds; if( mGrowthProgress >= 1 ) { mGrowthProgress = 1; if( mParent != NULL ) { // tell parent to stop feeding us mParent->dropOutsideOffspring( this ); // forget parent forgetParent(); } } } // water pickup animations // animations take 1 second if( mCarryingWater && mWaterPickupFraction < 1 ) { mWaterPickupFraction += inTimeDeltaInSeconds; if( mWaterPickupFraction > 1 ) { mWaterPickupFraction = 1; } } else if( !mCarryingWater && mWaterPickupFraction > 0 ) { mWaterPickupFraction -= inTimeDeltaInSeconds; if( mWaterPickupFraction < 0 ) { mWaterPickupFraction = 0; } } // rotate scales mGlobalScaleAngle.mZ += 0.5 * inTimeDeltaInSeconds * energyRateFactor; mSecondsSinceLastFruitPoisoning += inTimeDeltaInSeconds; } void Gardener::draw( Vector3D *inPosition, double inScale ) { Angle3D defaultAngle( 0, 0, 0 ); draw( inPosition, &defaultAngle, inScale ); } void Gardener::draw( Vector3D *inPosition, Angle3D *inRotation, double inScale, double inFadeFactor ) { // smooth transition to full transparent at end of life double endOfLifeTransparentFactor = 1; if( mLife <= mNearDeathPoint ) { endOfLifeTransparentFactor = mLife / mNearDeathPoint; } // add caller's fade factor endOfLifeTransparentFactor *= inFadeFactor; mLastDrawAngle.setComponents( inRotation ); double drawScale = inScale * mGrowthProgress; double radius = drawScale; Vector3D corners[4]; // first, set up corners relative to 0,0 corners[0].mX = - radius; corners[0].mY = - radius; corners[0].mZ = 0; corners[1].mX = radius; corners[1].mY = - radius; corners[1].mZ = 0; corners[2].mX = radius; corners[2].mY = radius; corners[2].mZ = 0; corners[3].mX = - radius; corners[3].mY = radius; corners[3].mZ = 0; int i; // now rotate around center // then add inPosition so that center is at inPosition for( i=0; i<4; i++ ) { corners[i].rotate( inRotation ); corners[i].add( inPosition ); } if( Features::drawShadows ) { // first draw shadow at z = 0 glColor4f( 0, 0, 0, 0.25 * endOfLifeTransparentFactor ); mBaseTexture->enable(); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex3d( corners[0].mX, corners[0].mY, 0 ); glTexCoord2f( 1, 0 ); glVertex3d( corners[1].mX, corners[1].mY, 0 ); glTexCoord2f( 1, 1 ); glVertex3d( corners[2].mX, corners[2].mY, 0 ); glTexCoord2f( 0, 1 ); glVertex3d( corners[3].mX, corners[3].mY, 0 ); } glEnd(); mBaseTexture->disable(); } // base becomes transparent with age glColor4f( mColor.r, mColor.g, mColor.b, mLife * endOfLifeTransparentFactor); mBaseTexture->enable(); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex3d( corners[0].mX, corners[0].mY, corners[0].mZ ); glTexCoord2f( 1, 0 ); glVertex3d( corners[1].mX, corners[1].mY, corners[1].mZ ); glTexCoord2f( 1, 1 ); glVertex3d( corners[2].mX, corners[2].mY, corners[2].mZ ); glTexCoord2f( 0, 1 ); glVertex3d( corners[3].mX, corners[3].mY, corners[3].mZ ); } glEnd(); mBaseTexture->disable(); if( Features::drawComplexGardeners ) { // now draw scales Angle3D scaleRotation( 0, 0, 0 ); int numScales = mScaleSizes.size(); // scale patch shrinks as we age double scalePatchSizeFactor = mLife; mScaleTexture->enable(); glBegin( GL_QUADS ); { Vector3D scaleCorners[4]; for( i=0; imColors[0].a = endOfLifeTransparentFactor; colors->mColors[1].a = endOfLifeTransparentFactor; colors->mColors[2].a = endOfLifeTransparentFactor; colors->mColors[3].a = endOfLifeTransparentFactor; setGLColor( &( colors->mColors[0] ) ); glTexCoord2f( 0, 0 ); glVertex3d( scaleCorners[0].mX, scaleCorners[0].mY, scaleCorners[0].mZ ); setGLColor( &( colors->mColors[1] ) ); glTexCoord2f( 1, 0 ); glVertex3d( scaleCorners[1].mX, scaleCorners[1].mY, scaleCorners[1].mZ ); setGLColor( &( colors->mColors[2] ) ); glTexCoord2f( 1, 1 ); glVertex3d( scaleCorners[2].mX, scaleCorners[2].mY, scaleCorners[2].mZ ); setGLColor( &( colors->mColors[3] ) ); glTexCoord2f( 0, 1 ); glVertex3d( scaleCorners[3].mX, scaleCorners[3].mY, scaleCorners[3].mZ ); } } glEnd(); mScaleTexture->disable(); // draw bones // bones become white with age Color white( 1, 1, 1, 1 ); Color *boneColor = Color::linearSum( &mColor, &white, mLife ); glColor4f( boneColor->r, boneColor->g, boneColor->b, 0.5 * endOfLifeTransparentFactor ); delete boneColor; // bones brighten only glBlendFunc( GL_SRC_ALPHA, GL_ONE ); mBonesTexture->enable(); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex3d( corners[0].mX, corners[0].mY, corners[0].mZ ); glTexCoord2f( 1, 0 ); glVertex3d( corners[1].mX, corners[1].mY, corners[1].mZ ); glTexCoord2f( 1, 1 ); glVertex3d( corners[2].mX, corners[2].mY, corners[2].mZ ); glTexCoord2f( 0, 1 ); glVertex3d( corners[3].mX, corners[3].mY, corners[3].mZ ); } glEnd(); mBonesTexture->disable(); // back to normal blend function glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } // draw markers if( mWaterPickupFraction > 0 ) { Vector3D waterCorners[4]; double waterRadius = radius * 0.5 * mWaterPickupFraction; // first, set up corners relative to 0,0 waterCorners[0].mX = - waterRadius; waterCorners[0].mY = - waterRadius; waterCorners[0].mZ = 0; waterCorners[1].mX = waterRadius; waterCorners[1].mY = - waterRadius; waterCorners[1].mZ = 0; waterCorners[2].mX = waterRadius; waterCorners[2].mY = waterRadius; waterCorners[2].mZ = 0; waterCorners[3].mX = - waterRadius; waterCorners[3].mY = waterRadius; waterCorners[3].mZ = 0; // now rotate around center // then add inPosition so that center is at inPosition for( i=0; i<4; i++ ) { waterCorners[i].rotate( inRotation ); waterCorners[i].add( inPosition ); } glColor4f( 0, 0, 1, 0.75 * endOfLifeTransparentFactor); mBaseTexture->enable(); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex3d( waterCorners[0].mX, waterCorners[0].mY, waterCorners[0].mZ ); glTexCoord2f( 1, 0 ); glVertex3d( waterCorners[1].mX, waterCorners[1].mY, waterCorners[1].mZ ); glTexCoord2f( 1, 1 ); glVertex3d( waterCorners[2].mX, waterCorners[2].mY, waterCorners[2].mZ ); glTexCoord2f( 0, 1 ); glVertex3d( waterCorners[3].mX, waterCorners[3].mY, waterCorners[3].mZ ); } glEnd(); mBaseTexture->disable(); } // now draw nutrition indicators double maxNutrientRadius = radius * 0.25; // arrange them in a semi-circle // "up" direction on gardener body Vector3D nutrientPosition( 0, maxNutrientRadius * 2, 0 ); // first nutrient on rear left of gardener Angle3D nutrientAngle( 0, 0, M_PI - M_PI / 5 ); Angle3D nextNutrientOffsetAngle( 0, 0, M_PI / 5 ); double nutrientAlpha = 1 * endOfLifeTransparentFactor; Color nutrientColors[3]; nutrientColors[0].setValues( 1, 0, 0, nutrientAlpha ); nutrientColors[1].setValues( 1, 1, 0, nutrientAlpha ); nutrientColors[2].setValues( 0.5, 0, 1, nutrientAlpha ); double nutrientLevels[3]; nutrientLevels[0] = mRedNutrient; nutrientLevels[1] = mGreenNutrient; nutrientLevels[2] = mBlueNutrient; Vector3D nutrientCorners[4]; // first, set up corners relative to 0,0 nutrientCorners[0].mX = - maxNutrientRadius; nutrientCorners[0].mY = - maxNutrientRadius; nutrientCorners[0].mZ = 0; nutrientCorners[1].mX = maxNutrientRadius; nutrientCorners[1].mY = - maxNutrientRadius; nutrientCorners[1].mZ = 0; nutrientCorners[2].mX = maxNutrientRadius; nutrientCorners[2].mY = maxNutrientRadius; nutrientCorners[2].mZ = 0; nutrientCorners[3].mX = - maxNutrientRadius; nutrientCorners[3].mY = maxNutrientRadius; nutrientCorners[3].mZ = 0; for( int n=0; n<3; n++ ) { Vector3D thisNutrientCenterCorners[4]; Vector3D thisNutrientBorderCorners[4]; for( i=0; i<4; i++ ) { thisNutrientCenterCorners[i].setCoordinates( &( nutrientCorners[i] ) ); thisNutrientBorderCorners[i].setCoordinates( &( nutrientCorners[i] ) ); // shrink center only thisNutrientCenterCorners[i].scale( nutrientLevels[n] ); // rotate and position both center and border thisNutrientCenterCorners[i].add( &nutrientPosition ); thisNutrientCenterCorners[i].rotate( &nutrientAngle ); thisNutrientCenterCorners[i].rotate( inRotation ); thisNutrientCenterCorners[i].add( inPosition ); thisNutrientBorderCorners[i].add( &nutrientPosition ); thisNutrientBorderCorners[i].rotate( &nutrientAngle ); thisNutrientBorderCorners[i].rotate( inRotation ); thisNutrientBorderCorners[i].add( inPosition ); } // draw shadow first glColor4f( 0, 0, 0, 0.5 * endOfLifeTransparentFactor ); mBaseTexture->enable(); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex3d( thisNutrientBorderCorners[0].mX, thisNutrientBorderCorners[0].mY, thisNutrientBorderCorners[0].mZ ); glTexCoord2f( 1, 0 ); glVertex3d( thisNutrientBorderCorners[1].mX, thisNutrientBorderCorners[1].mY, thisNutrientBorderCorners[1].mZ ); glTexCoord2f( 1, 1 ); glVertex3d( thisNutrientBorderCorners[2].mX, thisNutrientBorderCorners[2].mY, thisNutrientBorderCorners[2].mZ ); glTexCoord2f( 0, 1 ); glVertex3d( thisNutrientBorderCorners[3].mX, thisNutrientBorderCorners[3].mY, thisNutrientBorderCorners[3].mZ ); } glEnd(); // now draw center setGLColor( &( nutrientColors[n] ) ); mBaseTexture->enable(); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex3d( thisNutrientCenterCorners[0].mX, thisNutrientCenterCorners[0].mY, thisNutrientCenterCorners[0].mZ ); glTexCoord2f( 1, 0 ); glVertex3d( thisNutrientCenterCorners[1].mX, thisNutrientCenterCorners[1].mY, thisNutrientCenterCorners[1].mZ ); glTexCoord2f( 1, 1 ); glVertex3d( thisNutrientCenterCorners[2].mX, thisNutrientCenterCorners[2].mY, thisNutrientCenterCorners[2].mZ ); glTexCoord2f( 0, 1 ); glVertex3d( thisNutrientCenterCorners[3].mX, thisNutrientCenterCorners[3].mY, thisNutrientCenterCorners[3].mZ ); } glEnd(); mBaseTexture->disable(); // rotate for next nutrient nutrientAngle.add( &nextNutrientOffsetAngle ); } if( mOffspring != NULL ) { // draw pregnancy double pregnancyScale = (0.7 * mPregnancyProgress + 0.3) * drawScale; mOffspring->draw( inPosition, inRotation, pregnancyScale ); } // draw skin glColor4f( mColor.r, mColor.g, mColor.b, 0.5 * endOfLifeTransparentFactor ); mSkinTexture->enable(); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex3d( corners[0].mX, corners[0].mY, corners[0].mZ ); glTexCoord2f( 1, 0 ); glVertex3d( corners[1].mX, corners[1].mY, corners[1].mZ ); glTexCoord2f( 1, 1 ); glVertex3d( corners[2].mX, corners[2].mY, corners[2].mZ ); glTexCoord2f( 0, 1 ); glVertex3d( corners[3].mX, corners[3].mY, corners[3].mZ ); } glEnd(); mSkinTexture->disable(); // now draw eyes double eyeRadius = radius / 5; eyeRadius *= mGenetics.getParameter( eyeSize ); double separation = 2 * eyeRadius * mGenetics.getParameter( eyeSeparation ); // go up until eyes would hit top edge double bodyRadius = 0.66 * radius - eyeRadius; double halfSeparation = separation / 2; double eyeHeight = 0; if( halfSeparation >= bodyRadius ) { separation = 2 * bodyRadius; halfSeparation = bodyRadius; // leave eye height at zero } else { // height is opposite leg of triangle to keep eyes inside bodyRadius // safe to take sqrt because bodyRadius is greater than halfSeparation // avoid taking sqrt when bodyRadius eqals halfSeparation because // round-off errors cause sqrt to return a NAN error sometimes (like // when compiler optimizations are on) eyeHeight = sqrt( bodyRadius * bodyRadius - halfSeparation * halfSeparation ); } double squishFactor = 1; Angle3D eyeAngle( 0, 0, 0 ); Color eyeColor( 1, 1, 1, 1 * endOfLifeTransparentFactor ); if( mEmotionDisplay < 0 ) { squishFactor += mEmotionDisplay * 0.5; eyeAngle.mZ = - mEmotionDisplay * M_PI * 0.5; // eyes become red with anger eyeColor.g += mEmotionDisplay; eyeColor.b += mEmotionDisplay; } else { // eyes become light blue with love eyeColor.r -= mEmotionDisplay * 0.5; eyeColor.g -= mEmotionDisplay * 0.5; } Vector3D leftCorners[4]; // first, set up corners relative to 0,0 leftCorners[0].mX = - eyeRadius; leftCorners[0].mY = - eyeRadius * squishFactor; leftCorners[0].mZ = 0; leftCorners[1].mX = eyeRadius; leftCorners[1].mY = - eyeRadius * squishFactor; leftCorners[1].mZ = 0; leftCorners[2].mX = eyeRadius; leftCorners[2].mY = eyeRadius * squishFactor; leftCorners[2].mZ = 0; leftCorners[3].mX = - eyeRadius; leftCorners[3].mY = eyeRadius * squishFactor; leftCorners[3].mZ = 0; // right corners are copy of left Vector3D rightCorners[4]; for( i=0; i<4; i++ ) { rightCorners[i].setCoordinates( &( leftCorners[i] ) ); } // move eye into position relative to gardener center // then rotate around center // then add inPosition so that center is at inPosition for( i=0; i<4; i++ ) { leftCorners[i].rotate( &eyeAngle ); leftCorners[i].mY += eyeHeight; // go out from center for left eye leftCorners[i].mX -= separation / 2; leftCorners[i].rotate( inRotation ); leftCorners[i].add( inPosition ); } mEyeTexture->enable(); setGLColor( &eyeColor ); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex3d( leftCorners[0].mX, leftCorners[0].mY, leftCorners[0].mZ ); glTexCoord2f( 1, 0 ); glVertex3d( leftCorners[1].mX, leftCorners[1].mY, leftCorners[1].mZ ); glTexCoord2f( 1, 1 ); glVertex3d( leftCorners[2].mX, leftCorners[2].mY, leftCorners[2].mZ ); glTexCoord2f( 0, 1 ); glVertex3d( leftCorners[3].mX, leftCorners[3].mY, leftCorners[3].mZ ); } glEnd(); mEyeTexture->disable(); // opposite for right eyeAngle.scale( -1 ); for( i=0; i<4; i++ ) { rightCorners[i].rotate( &eyeAngle ); rightCorners[i].mY += eyeHeight; // go out from center for right eye rightCorners[i].mX += separation / 2; rightCorners[i].rotate( inRotation ); rightCorners[i].add( inPosition ); } mEyeTexture->enable(); setGLColor( &eyeColor ); glBegin( GL_QUADS ); { glTexCoord2f( 0, 0 ); glVertex3d( rightCorners[0].mX, rightCorners[0].mY, rightCorners[0].mZ ); glTexCoord2f( 1, 0 ); glVertex3d( rightCorners[1].mX, rightCorners[1].mY, rightCorners[1].mZ ); glTexCoord2f( 1, 1 ); glVertex3d( rightCorners[2].mX, rightCorners[2].mY, rightCorners[2].mZ ); glTexCoord2f( 0, 1 ); glVertex3d( rightCorners[3].mX, rightCorners[3].mY, rightCorners[3].mZ ); } glEnd(); mEyeTexture->disable(); } int Gardener::getStoredObjects( DrawableObject ***outObjects ) { if( outObjects != NULL ) { *outObjects = (DrawableObject **)( mStoredItems.getElementArray() ); } return mStoredItems.size(); } int Gardener::getSelectedObjectIndex() { return mSelectedStorageIndex; } void Gardener::setSelectedObjectIndex( int inIndex ) { mSelectedStorageIndex = inIndex; } void Gardener::setPregnant( Gardener *inOffspring ) { if( mOffspring != NULL ) { delete mOffspring; } mOffspring = inOffspring; mPregnancyProgress = 0; } Gardener *Gardener::getParentToFollow() { return mParent; } void Gardener::forgetParent() { mParent = NULL; } void Gardener::dropOutsideOffspring( Gardener *inOffspring ) { mOutsideOffspring.deleteElementEqualTo( inOffspring ); } void Gardener::feed( double inRedNutrient, double inGreenNutrient, double inBlueNutrient ) { Color nutrition( inRedNutrient, inGreenNutrient, inBlueNutrient ); // share with outside offspring and followers int numOffspring = mOutsideOffspring.size(); int numFollowers = mFollowers.size(); int numToFeed = numFollowers + numOffspring; // split evenly among self and offspring nutrition.weightColor( 1.0 / ( numToFeed + 1 ) ); mRedNutrient += nutrition.r; if( mRedNutrient > 1 ) { mRedNutrient = 1; } mGreenNutrient += nutrition.g; if( mGreenNutrient > 1 ) { mGreenNutrient = 1; } mBlueNutrient += nutrition.b; if( mBlueNutrient > 1 ) { mBlueNutrient = 1; } int i; for( i=0; ifeed( nutrition.r, nutrition.g, nutrition.b ); } for( i=0; ifeed( nutrition.r, nutrition.g, nutrition.b ); } } void Gardener::setLeader( Gardener *inLeader ) { mLeader = inLeader; } Gardener *Gardener::getLeader() { return mLeader; } void Gardener::addFollower( Gardener *inFollower ) { mFollowers.push_back( inFollower ); } void Gardener::dropFollower( Gardener *inFollower ) { mFollowers.deleteElementEqualTo( inFollower ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/language.txt0000640000175000017500000000001010531375551023030 0ustar pabspabsEnglish Cultivation_9+dfsg1_UnixSource/game2/gameSource/Fruit.cpp0000640000175000017500000002046610520505510022306 0ustar pabspabs/* * Modification History * * 2006-July-23 Jason Rohrer * Created. * * 2006-October-27 Jason Rohrer * Added support for poisoned fruit. */ #include "Fruit.h" #include "glCommon.h" #include #include "minorGems/graphics/filters/BoxBlurFilter.h" //#include "minorGems/io/file/FileOutputStream.h" //#include "minorGems/graphics/converters/TGAImageConverter.h" Fruit::Fruit( PlantGenetics *inParentGenetics, Seeds *inSeeds, double inRipenRate ) : mParentGenetics( inParentGenetics ), mStatus( 0 ), mPoisoned( false ), mSeeds( inSeeds ), mRipenRate( inRipenRate ), mLastRotation( 0, 0, 0 ) { Color *nutritionColor = mParentGenetics.getColor( fruitNutrition ); mNutrition.setValues( nutritionColor ); delete nutritionColor; // generate the texture // a blurry white circle that is gray at edges int textureSize = 32; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // start all pixels as gray and transparent for( p=0; p 0 ) { angle = acos( xRelative / pixelRadius ); } else { angle = 0; } if( yRelative < 0 ) { angle = 2 * M_PI - angle; } // compute function at this angle to determine radius double functionRadius = baseRadius * lobeDepth * sin( lobeRate * angle ) + baseRadius; if( pixelRadius <= functionRadius ) { alphaValue = 1; // color based on radius double grayWeight = pixelRadius / functionRadius; double colorLevel = ( 1 - grayWeight ) * 1 + grayWeight * 0.5; channels[0][ pixelIndex ] = colorLevel; channels[1][ pixelIndex ] = colorLevel; channels[2][ pixelIndex ] = colorLevel; } else { alphaValue = 0; } channels[3][ pixelIndex ] = alphaValue; } } // blur alpha BoxBlurFilter blur( blurRadius ); textureImage->filter( &blur, 3 ); mShapeTexture = new SingleTextureGL( textureImage, // no wrap false ); /* File outFileB( NULL, "fruit.tga" ); FileOutputStream *outStreamB = new FileOutputStream( &outFileB ); TGAImageConverter converter; converter.formatImage( textureImage, outStreamB ); delete outStreamB; exit( 0 ); */ delete textureImage; } Fruit::~Fruit() { delete mShapeTexture; } Color *Fruit::getNutrition() { return mNutrition.copy(); } char Fruit::isRotten() { return ( mStatus >= 6 ); } char Fruit::isRipe() { return ( mStatus >= 2 ); } char Fruit::isPoisoned() { return mPoisoned; } void Fruit::poison() { mPoisoned = true; } Seeds *Fruit::getSeeds() { return new Seeds( &mSeeds ); } void Fruit::passTime( double inTimeDeltaInSeconds ) { double progressRate = mRipenRate; mStatus += inTimeDeltaInSeconds * progressRate; if( mStatus > 6 ) { mStatus = 6; } } void Fruit::draw( Vector3D *inPosition, double inScale ) { // default rotation draw( inPosition, &mLastRotation, inScale ); } void Fruit::draw( Vector3D *inPosition, Angle3D *inRotation, double inScale ) { double radius; Color drawColor; double leafGreen = mParentGenetics.getParameter( outerLeafGreen ); Color ripeColor; if( mNutrition.r == 1 ) { ripeColor.setValues( 1, 0, 0, 1 ); } else if( mNutrition.g == 1 ) { // show g nutrient as yellow, not green ripeColor.setValues( 1, 1, 0, 1 ); } else if( mNutrition.b == 1 ) { // show b nutrient as purple, not blue ripeColor.setValues( 0.5, 0, 1, 1 ); } // turn light grey when rotted Color rotColor( 0.8, 0.8, 0.8, 1 ); if( mStatus < 1 ) { // growing radius = inScale * mStatus; drawColor.setValues( 0, leafGreen, 0, 1 ); } else { radius = inScale; if( mStatus >= 2 && mStatus <=4 ) { // ripe/stable drawColor.setValues( &ripeColor ); } else if( mStatus < 2 ) { // ripening Color unripe( 0, leafGreen, 0, 1 ); Color *mix = Color::linearSum( &ripeColor, &unripe, mStatus - 1 ); drawColor.setValues( mix ); delete mix; } else if( mStatus < 5 ){ // browning Color *mix = Color::linearSum( &rotColor, &ripeColor, mStatus - 4 ); drawColor.setValues( mix ); delete mix; } else { // rotting drawColor.setValues( &rotColor ); // shrink down to half size as we rot radius *= 0.5 * ( 6 - mStatus ) + 0.5; } } // fruit black if poisoned if( mPoisoned ) { drawColor.setValues( 0, 0, 0, 1 ); } Vector3D corners[4]; // first, set up corners relative to 0,0 corners[0].mX = - radius; corners[0].mY = - radius; corners[0].mZ = 0; corners[1].mX = radius; corners[1].mY = - radius; corners[1].mZ = 0; corners[2].mX = radius; corners[2].mY = radius; corners[2].mZ = 0; corners[3].mX = - radius; corners[3].mY = radius; corners[3].mZ = 0; int i; // now rotate around center // then add inPosition so that center is at inPosition for( i=0; i<4; i++ ) { corners[i].rotate( inRotation ); corners[i].add( inPosition ); } mShapeTexture->enable(); glBegin( GL_QUADS ); { setGLColor( &drawColor ); glTexCoord2f( 0, 0 ); glVertex3d( corners[0].mX, corners[0].mY, corners[0].mZ ); glTexCoord2f( 1, 0 ); glVertex3d( corners[1].mX, corners[1].mY, corners[1].mZ ); glTexCoord2f( 1, 1 ); glVertex3d( corners[2].mX, corners[2].mY, corners[2].mZ ); glTexCoord2f( 0, 1 ); glVertex3d( corners[3].mX, corners[3].mY, corners[3].mZ ); } glEnd(); mShapeTexture->disable(); /* // shrinks vertically as it rots double heightRadius = inScale * mRotStatus; double widthRadius = inScale; glColor4f( mNutrition.r, mNutrition.g, mNutrition.b, 1 ); glBegin( GL_TRIANGLES ); { glVertex2d( inPosition->mX - widthRadius, inPosition->mY - heightRadius ); glVertex2d( inPosition->mX, inPosition->mY + heightRadius ); glVertex2d( inPosition->mX + widthRadius, inPosition->mY - heightRadius ); } glEnd(); */ // save a new default rotation mLastRotation.setComponents( inRotation ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/0000750000175000017500000000000011401021142023270 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/NextTutorialButton.cpp0000640000175000017500000000153110542024543027650 0ustar pabspabs/* * Modification History * * 2006-December-19 Jason Rohrer * Created. */ #include "NextTutorialButton.h" #include "../glCommon.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include NextTutorialButton::NextTutorialButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : RestartButton( inAnchorX, inAnchorY, inWidth, inHeight ) { } void NextTutorialButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { // draw a green back arrow glColor4f( 0, 1, 0, inAlpha ); // compensate for small radius of texture double drawRadius = inRadius / 0.6666; // hack: draw texture backwards using a negative radius drawTextureQuad( mArrowTexture, inCenter, -drawRadius ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/PlotButton.cpp0000640000175000017500000000163310500361111026114 0ustar pabspabs/* * Modification History * * 2006-July-4 Jason Rohrer * Created. * * 2006-July-5 Jason Rohrer * Changed to subclass ButtonBase. */ #include "PlotButton.h" #include PlotButton::PlotButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight ) { } void PlotButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { // white square glLineWidth( 1 ); glColor4f( 1, 1, 1, inAlpha ); glBegin( GL_LINE_LOOP ); { glVertex2d( inCenter->mX - inRadius, inCenter->mY - inRadius ); glVertex2d( inCenter->mX - inRadius, inCenter->mY + inRadius ); glVertex2d( inCenter->mX + inRadius, inCenter->mY + inRadius ); glVertex2d( inCenter->mX + inRadius, inCenter->mY - inRadius ); } glEnd(); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/EmotionButton.cpp0000640000175000017500000000161610454235325026630 0ustar pabspabs/* * Modification History * * 2006-July-9 Jason Rohrer * Created. */ #include "EmotionButton.h" #include EmotionButton::EmotionButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight ) { } void EmotionButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { // draw a red heart glColor4f( 1, 0, 0, inAlpha ); glBegin( GL_TRIANGLE_STRIP ); { glVertex2d( inCenter->mX - inRadius, inCenter->mY + inRadius ); glVertex2d( inCenter->mX, inCenter->mY + inRadius / 2 ); glVertex2d( inCenter->mX, inCenter->mY - inRadius ); // one more vert to define second triangle glVertex2d( inCenter->mX + inRadius, inCenter->mY + inRadius ); } glEnd(); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/FollowButton.h0000640000175000017500000000271210521716704026123 0ustar pabspabs/* * Modification History * * 2006-October-30 Jason Rohrer * Created. */ #include "ButtonBase.h" // inherit the gift button's Arrow icon #include "GiftButton.h" // for a red "x" graphic #include "../emotionIcons.h" /** * Follow button. */ class FollowButton : public GiftButton { public: /** * See parameter description from ButtonGL. */ FollowButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); /** * Toggles this button's display of the "stop following" icon. * * @param inStopFollowing true to switch "stop following" icon on. */ void setStopFollowingMode( char inStopFollowing ); /** * Sets a second selected object that this button should apply to. * * @param inObject the object that is selected, or NULL for none. * Destroyed by caller after a different object is set with * a call to setSelectedObject. */ virtual void setSecondSelectedObject( DrawableObject *inObject ); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); protected: DrawableObject *mSecondSelectedObject; char mStopFollowingMode; DislikeIcon mRedX; }; Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/ObjectSelector.h0000640000175000017500000000462510541347766026413 0ustar pabspabs/* * Modification History * * 2006-July-23 Jason Rohrer * Created. * * 2006-December-17 Jason Rohrer * Added support for mouse hover. */ #ifndef OBJECT_SELECTOR_INCLUDED #define OBJECT_SELECTOR_INCLUDED #include "ObjectStorage.h" #include "minorGems/graphics/openGL/gui/GUIComponentGL.h" /** * Base class for all game buttons. * * Handles drawing button background and switching between pressed * and unpressed, as well as enabled and disabled, states. */ class ObjectSelector : public GUIComponentGL { public: /** * Constructs a Selector that is backed by a storage object. * * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @paramm inStorage the storage that this Selector displays. * Must be destroyed after this Selector is destroyed. */ ObjectSelector( double inAnchorX, double inAnchorY, double inWidth, double inHeight, ObjectStorage *inStorage ); /** * Sets the storage that this selector depicts. * * @param inStorage the underlying storage. * Must be destroyed after this Selector is destroyed. * Previous storage can be destroyed after it is replaced * with a call to setStorage. */ void setStorage( ObjectStorage *inStorage ); /** * Gets the object that the mouse is hovering over. * * @return the object index in the displayed ObjectStorage, or * -1 if no object is under the mouse. */ int getHoverObject(); // override functions in GUIComponentGL virtual void mouseMoved( double inX, double inY ); virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); virtual void fireRedraw(); protected: ObjectStorage *mStorage; int mHoverObjectIndex; /** * Gets the vertical draw units allocated to each object. */ double getDrawUnitsPerObject(); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/GiftButton.h0000640000175000017500000000145110521506107025543 0ustar pabspabs/* * Modification History * * 2006-July-24 Jason Rohrer * Created. */ #ifndef GIFT_BUTTON_INCLUDED #define GIFT_BUTTON_INCLUDED #include "ButtonBase.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" /** * Gift button. */ class GiftButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ GiftButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); virtual ~GiftButton(); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); protected: SingleTextureGL *mArrowTexture; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/TextDisplay.cpp0000640000175000017500000000403610455471567026306 0ustar pabspabs/* * Modification History * * 2006-July-13 Jason Rohrer * Created. */ #include "TextDisplay.h" #include "minorGems/util/stringUtils.h" #include #include TextDisplay::TextDisplay( const char *inLongestPossibleText, Color *inColor, double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mColor(), mText( NULL ) { mColor.setValues( inColor ); // compute scale factor int i; int length = strlen( inLongestPossibleText ); int totalWidth = 0; for( i=0; igetChannel( i ); } // for now, fill randomly double lowColorValue = 0.2; double lowAlphaValue = 0.2; for( p=0; pfilter( &blur ); mBackgroundTexture = new SingleTextureGL( textureImage, // wrap true ); delete textureImage; // set filter for texture to nearest neighbor mBackgroundTexture->enable(); //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); mBackgroundTexture->disable(); } TexturedPanel::~TexturedPanel() { delete mBackgroundTexture; } void TexturedPanel::fireRedraw() { double alphaBottomRight, alphaTopRight, alphaBottomLeft, alphaTopLeft; double xTextureAnchorMax, yTextureAnchorMax; if( mVertical ) { alphaBottomRight = 0; alphaTopRight = 0; alphaBottomLeft = 1; alphaTopLeft = 1; xTextureAnchorMax = mWidth / mHeight; yTextureAnchorMax = 1; } else { alphaBottomRight = 1; alphaTopRight = 0; alphaBottomLeft = 1; alphaTopLeft = 0; xTextureAnchorMax = 1; yTextureAnchorMax = mHeight / mWidth; } mBackgroundTexture->enable(); glBegin( GL_QUADS ); { double widthToSkip = 0; if( !mVertical ) { // don't draw in left corner to leave room for special block widthToSkip = mHeight; } double widthToSkipFraction = widthToSkip / mWidth; glColor4f( 1, 1, 1, alphaBottomLeft ); glTexCoord2f( widthToSkipFraction, 0 ); glVertex2d( mAnchorX + widthToSkip, mAnchorY ); glColor4f( 1, 1, 1, alphaBottomRight ); glTexCoord2f( xTextureAnchorMax, 0 ); glVertex2d( mAnchorX + mWidth, mAnchorY ); glColor4f( 1, 1, 1, alphaTopRight ); glTexCoord2f( xTextureAnchorMax, yTextureAnchorMax ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glColor4f( 1, 1, 1, alphaTopLeft ); glTexCoord2f( widthToSkipFraction, yTextureAnchorMax ); glVertex2d( mAnchorX + widthToSkip, mAnchorY + mHeight ); if( !mVertical ) { // special block in lower-left corner // so that this panel blends with the left sidebar panel // a square block, mHeight by mHeight double widthFraction = mHeight / mWidth; glColor4f( 1, 1, 1, alphaBottomLeft ); glTexCoord2f( 0, 0 ); glVertex2d( mAnchorX, mAnchorY ); glColor4f( 1, 1, 1, alphaBottomRight ); glTexCoord2f( xTextureAnchorMax * widthFraction, 0 ); glVertex2d( mAnchorX + mHeight, mAnchorY ); glColor4f( 1, 1, 1, alphaTopRight ); glTexCoord2f( xTextureAnchorMax * widthFraction, yTextureAnchorMax ); glVertex2d( mAnchorX + mHeight, mAnchorY + mHeight ); glColor4f( 1, 1, 1, 1 ); glTexCoord2f( 0, yTextureAnchorMax ); glVertex2d( mAnchorX, mAnchorY + mHeight ); } } glEnd(); mBackgroundTexture->disable(); // call the supercalss redraw GUIContainerGL::fireRedraw(); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/RestartButton.h0000640000175000017500000000150410542024543026277 0ustar pabspabs/* * Modification History * * 2006-July-6 Jason Rohrer * Created. */ #ifndef RESTART_BUTTON_INCLUDED #define RESTART_BUTTON_INCLUDED #include "ButtonBase.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" /** * Button that initiates the planting action. */ class RestartButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ RestartButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); virtual ~RestartButton(); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); protected: SingleTextureGL *mArrowTexture; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/TextBlockGL.h0000640000175000017500000000171510542236321025604 0ustar pabspabs/* * Modification History * * 2006-December-19 Jason Rohrer * Created. */ #ifndef TEXT_BLOCK_GL_INCLUDED #define TEXT_BLOCK_GL_INCLUDED #include "minorGems/graphics/openGL/gui/LabelGL.h" /** * A multi-line text label for OpenGL-based GUIs. * * @author Jason Rohrer */ class TextBlockGL : public LabelGL { public: /** * Constructs a TextBlock. * * Parameters are same as for LabelGL constructor, except: * * @param inMaxCharactersPerLine the maximum number of characters * per line of the text block. */ TextBlockGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, char *inString, TextGL *inText, int inMaxCharactersPerLine ); // override fireRedraw in LabelGL virtual void fireRedraw(); protected: unsigned long mMaxCharactersPerLine; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/MateButton.cpp0000640000175000017500000000236110500320447026072 0ustar pabspabs/* * Modification History * * 2006-August-14 Jason Rohrer * Created. */ #include "MateButton.h" #include "../glCommon.h" #include MateButton::MateButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight ), mSecondSelectedObject( NULL ) { } void MateButton::setSecondSelectedObject( DrawableObject *inObject ) { mSecondSelectedObject = inObject; } void MateButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { // draw gardeners side by side double offset = inRadius / 3; double drawRadius = inRadius - offset; // gardeners take up only 0.666 of their radius drawRadius = drawRadius / 0.6666; Vector3D secondCenter( inCenter ); secondCenter.mY += offset; if( mSecondSelectedObject != NULL ) { // draw selected object mSecondSelectedObject->draw( &secondCenter, drawRadius ); } Vector3D firstCenter( inCenter ); firstCenter.mY -= offset; if( mSelectedObject != NULL ) { // draw selected object mSelectedObject->draw( &firstCenter, drawRadius ); } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/HarvestButton.cpp0000640000175000017500000000111710500301450026607 0ustar pabspabs/* * Modification History * * 2006-July-23 Jason Rohrer * Created. */ #include "HarvestButton.h" #include HarvestButton::HarvestButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight ) { } void HarvestButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { if( mSelectedObject != NULL ) { // draw selected object mSelectedObject->draw( inCenter, inRadius ); } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/EmotionButton.h0000640000175000017500000000112210454235325026265 0ustar pabspabs/* * Modification History * * 2006-July-9 Jason Rohrer * Created. */ #include "ButtonBase.h" /** * Button that initiates the planting action. */ class EmotionButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ EmotionButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); }; Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/DiscardButton.h0000640000175000017500000000120710500301450026211 0ustar pabspabs/* * Modification History * * 2006-August-15 Jason Rohrer * Created. */ #include "ButtonBase.h" #include "../DrawableObject.h" /** * Button that initiates the planting action. */ class DiscardButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ DiscardButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); }; Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/RestartButton.cpp0000640000175000017500000000702310500344732026633 0ustar pabspabs/* * Modification History * * 2006-July-6 Jason Rohrer * Created. */ #include "RestartButton.h" #include "../glCommon.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include RestartButton::RestartButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight ) { // construct arrow texture // a blurry white triangle // black border int textureSize = 32; double halfTextureSize = 0.5 * textureSize; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // start all pixels as black and transparent for( p=0; p 1 ) { currentAlpha = 1; } if( currentAlpha < 0 ) { currentAlpha = 0; } } // blur alpha int blurRadius = 1; BoxBlurFilter blur( blurRadius ); // blur all channels textureImage->filter( &blur, 3 ); // rgb are identical, so blur one and copy textureImage->filter( &blur, 0 ); memcpy( channels[1], channels[0], pixelsPerChannel * sizeof( double ) ); memcpy( channels[2], channels[0], pixelsPerChannel * sizeof( double ) ); mArrowTexture = new SingleTextureGL( textureImage, // no wrap false ); delete textureImage; } RestartButton::~RestartButton() { delete mArrowTexture; } void RestartButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { // draw a green back arrow glColor4f( 0, 1, 0, inAlpha ); // compensate for small radius of texture double drawRadius = inRadius / 0.6666; drawTextureQuad( mArrowTexture, inCenter, drawRadius ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/FollowButton.cpp0000640000175000017500000000356610542013044026454 0ustar pabspabs/* * Modification History * * 2006-October-30 Jason Rohrer * Created. */ #include "FollowButton.h" #include "../glCommon.h" #include FollowButton::FollowButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : GiftButton( inAnchorX, inAnchorY, inWidth, inHeight ), mSecondSelectedObject( NULL ), mStopFollowingMode( false ) { // override value set by GiftButton mFadeLeft = false; } void FollowButton::setStopFollowingMode( char inStopFollowing ) { mStopFollowingMode = inStopFollowing; } void FollowButton::setSecondSelectedObject( DrawableObject *inObject ) { mSecondSelectedObject = inObject; } void FollowButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { // draw second gardner following first double offset = 2 * inRadius / 3; double drawRadius = inRadius / 3; // gardeners take up only 0.666 of their radius drawRadius = drawRadius / 0.66; Vector3D firstCenter( inCenter ); firstCenter.mY += offset; if( mSelectedObject != NULL ) { // draw selected object mSelectedObject->draw( &firstCenter, drawRadius ); } Vector3D secondCenter( inCenter ); secondCenter.mY -= offset; if( mSecondSelectedObject != NULL ) { // draw selected object mSecondSelectedObject->draw( &secondCenter, drawRadius ); } // draw arrow in between // rotate so vertical Angle3D arrowRotation( 0, 0, M_PI/2 ); glColor4f( 1, 1, 1, inAlpha ); // make arrow bigger double arrowRadius = drawRadius; drawTextureQuad( mArrowTexture, inCenter, arrowRadius, &arrowRotation ); if( mStopFollowingMode ) { // cover with a red X mRedX.draw( inCenter, inRadius ); } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/ButtonBase.cpp0000640000175000017500000000536710542012011026056 0ustar pabspabs/* * Modification History * * 2006-July-4 Jason Rohrer * Created. */ #include "ButtonBase.h" #include "../glCommon.h" #include #include "minorGems/graphics/filters/BoxBlurFilter.h" #include "minorGems/util/random/StdRandomSource.h" extern StdRandomSource globalRandomSource; ButtonBase::ButtonBase( double inAnchorX, double inAnchorY, double inWidth, double inHeight, char inFadeLeft ) : ButtonGL( inAnchorX, inAnchorY, inWidth, inHeight ), mFadeLeft( inFadeLeft ), mIconCenter( mAnchorX + mHeight / 2, mAnchorY + mHeight / 2, 0 ), mSelectedObject( NULL ) { mIconRadius = mWidth / 3; if( mHeight < mWidth ) { mIconRadius = mHeight / 3; } } ButtonBase::~ButtonBase() { } void ButtonBase::setSelectedObject( DrawableObject *inObject ) { mSelectedObject = inObject; } void ButtonBase::drawPressed() { drawWithColor( 0.1f, 0.1f, 0.1f ); } void ButtonBase::drawUnpressed() { drawWithColor( 0.3f, 0.3f, 0.3f ); } void ButtonBase::drawWithColor( float inRed, float inGreen, float inBlue ) { double alpha = 1.0; if( ! isEnabled() ) { alpha = 0.25; } double alphaBottomRight, alphaTopRight, alphaBottomLeft, alphaTopLeft; if( mFadeLeft ) { alphaBottomRight = alpha; alphaTopRight = alpha; alphaBottomLeft = 0; alphaTopLeft = 0; } else { alphaBottomRight = 0; alphaTopRight = alpha; alphaBottomLeft = 0; alphaTopLeft = alpha; } glBegin( GL_QUADS ); { glColor4f( inRed, inGreen, inBlue, alphaBottomLeft ); glVertex2d( mAnchorX, mAnchorY ); glColor4f( inRed, inGreen, inBlue, alphaTopLeft ); glVertex2d( mAnchorX, mAnchorY + mHeight ); glColor4f( inRed, inGreen, inBlue, alphaTopRight ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glColor4f( inRed, inGreen, inBlue, alphaBottomRight ); glVertex2d( mAnchorX + mWidth, mAnchorY ); } glEnd(); if( isEnabled() ) { // add a border glBegin( GL_LINE_LOOP ); { glColor4f( 0.7f, 0.7f, 0.7f, alphaBottomLeft ); glVertex2d( mAnchorX, mAnchorY ); glColor4f( 0.7f, 0.7f, 0.7f, alphaTopLeft ); glVertex2d( mAnchorX, mAnchorY + mHeight ); glColor4f( 0.7f, 0.7f, 0.7f, alphaTopRight ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glColor4f( 0.7f, 0.7f, 0.7f, alphaBottomRight ); glVertex2d( mAnchorX + mWidth, mAnchorY ); } glEnd(); } drawIcon( &mIconCenter, mIconRadius, alpha ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/TextDisplay.h0000640000175000017500000000307410455471567025754 0ustar pabspabs/* * Modification History * * 2006-July-13 Jason Rohrer * Created. */ #ifndef TEXT_DISPLAY_INCLUDED #define TEXT_DISPLAY_INCLUDED #include "minorGems/graphics/Color.h" #include "minorGems/graphics/openGL/gui/GUIComponentGL.h" /** * A simple text display that uses glutStrokeCharacter to render text. */ class TextDisplay : public GUIComponentGL { public: /** * Constructs a component. * * @param inLongestPossibleText the longest possible text * that this display might contain. Used to compute a constant * scale factor for all texts. * @param inColor the color of the text. * Destroyed by caller. * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. */ TextDisplay( const char *inLongestPossibleText, Color *inColor, double inAnchorX, double inAnchorY, double inWidth, double inHeight ); virtual ~TextDisplay(); /** * Sets the text to display. * * @param inText the text. */ void setText( const char *inText ); // overrides function from GUIComponentGL virtual void fireRedraw(); protected: Color mColor; char *mText; double mScaleFactor; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/GiftButton.cpp0000640000175000017500000000756610542007740026115 0ustar pabspabs/* * Modification History * * 2006-July-24 Jason Rohrer * Created. */ #include "GiftButton.h" #include "../glCommon.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include GiftButton::GiftButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight, true ) { // construct arrow texture // a blurry white triangle // black border int textureSize = 32; double halfTextureSize = 0.5 * textureSize; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // start all pixels as black and transparent for( p=0; pendColumn; x-- ) { int startY = (int)( halfTextureSize - currentRadius ); int endY = (int)( halfTextureSize + currentRadius ); for( y=startY; y 1 ) { currentAlpha = 1; } if( currentAlpha < 0 ) { currentAlpha = 0; } } // blur alpha int blurRadius = 1; BoxBlurFilter blur( blurRadius ); // blur all channels textureImage->filter( &blur, 3 ); // rgb are identical, so blur one and copy textureImage->filter( &blur, 0 ); memcpy( channels[1], channels[0], pixelsPerChannel * sizeof( double ) ); memcpy( channels[2], channels[0], pixelsPerChannel * sizeof( double ) ); mArrowTexture = new SingleTextureGL( textureImage, // no wrap false ); delete textureImage; } GiftButton::~GiftButton() { delete mArrowTexture; } void GiftButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { // ofset of arrow and item from center double offset = inRadius / 3; double drawRadius = inRadius - offset; Vector3D targetCenter( inCenter ); targetCenter.mX -= offset; if( mSelectedObject != NULL ) { // draw selected object mSelectedObject->draw( &targetCenter, drawRadius ); } Vector3D arrowCenter( inCenter ); arrowCenter.mX += offset; // radius of arrow icon is 0.666 of texture radius drawRadius = drawRadius / 0.6666; glColor4f( 1, 1, 1, inAlpha ); drawTextureQuad( mArrowTexture, &arrowCenter, drawRadius ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/PauseButton.h0000640000175000017500000000136610540313065025734 0ustar pabspabs/* * Modification History * * 2006-December-14 Jason Rohrer * Created. */ #include "ButtonBase.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" /** * Button that initiates the planting action. */ class PauseButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ PauseButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); virtual ~PauseButton(); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); protected: SingleTextureGL *mPauseTexture; }; Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/ObjectSelector.cpp0000640000175000017500000001314110544426202026721 0ustar pabspabs/* * Modification History * * 2006-July-23 Jason Rohrer * Created. * * 2006-December-17 Jason Rohrer * Added support for mouse hover. * * 2006-December-26 Jason Rohrer * Fixed a bug in border drawing. */ #include "ObjectSelector.h" #include ObjectSelector::ObjectSelector( double inAnchorX, double inAnchorY, double inWidth, double inHeight, ObjectStorage *inStorage ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mStorage( inStorage ), mHoverObjectIndex( -1 ) { } void ObjectSelector::setStorage( ObjectStorage *inStorage ) { mStorage = inStorage; } int ObjectSelector::getHoverObject() { // make sure it's still in-bounds int objectCount = mStorage->getStoredObjects( NULL ); if( mHoverObjectIndex >= objectCount ) { mHoverObjectIndex = -1; } return mHoverObjectIndex; } void ObjectSelector::mouseMoved( double inX, double inY ) { // reset hover object mHoverObjectIndex = -1; if( isInside( inX, inY ) ) { // map inY to one of our objects int objectCount = mStorage->getStoredObjects( NULL ); double relativeY = inY - mAnchorY; double drawUnitsPerObject = getDrawUnitsPerObject(); int index = (int)( relativeY / drawUnitsPerObject ); if( index < objectCount && index >= 0 ) { mHoverObjectIndex = index; } } } void ObjectSelector::mouseDragged( double inX, double inY ) { } void ObjectSelector::mousePressed( double inX, double inY ) { } void ObjectSelector::mouseReleased( double inX, double inY ) { if( isInside( inX, inY ) ) { // map inY to one of our objects int objectCount = mStorage->getStoredObjects( NULL ); double relativeY = inY - mAnchorY; double drawUnitsPerObject = getDrawUnitsPerObject(); int index = (int)( relativeY / drawUnitsPerObject ); if( index < objectCount && index >= 0 ) { mStorage->setSelectedObjectIndex( index ); } else { // leave existing selection // mStorage->setSelectedObjectIndex( -1 ); } } } void ObjectSelector::fireRedraw() { // first, draw background double alpha = 1.0; if( ! isEnabled() ) { alpha = 0.25; } double alphaBottomRight, alphaTopRight, alphaBottomLeft, alphaTopLeft; alphaBottomRight = alpha; alphaTopRight = alpha; alphaBottomLeft = 0; alphaTopLeft = 0; float r = 0.3f; float g = 0.3f; float b = 0.3f; glBegin( GL_QUADS ); { glColor4f( r, g, b, alphaBottomLeft ); glVertex2d( mAnchorX, mAnchorY ); glColor4f( r, g, b, alphaTopLeft ); glVertex2d( mAnchorX, mAnchorY + mHeight ); glColor4f( r, g, b, alphaTopRight ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glColor4f( r, g, b, alphaBottomRight ); glVertex2d( mAnchorX + mWidth, mAnchorY ); } glEnd(); if( ! isEnabled() ) { // don't draw any objects return; } DrawableObject **objects; int objectCount = mStorage->getStoredObjects( &objects ); if( objectCount > 0 ) { // draw the objects int selectedIndex = mStorage->getSelectedObjectIndex(); double drawUnitsPerObject = getDrawUnitsPerObject(); double drawX = mAnchorX + mWidth / 2; double drawY = mAnchorY + drawUnitsPerObject / 2; for( int i=0; idraw( &drawPosition, // let object fill half the space drawUnitsPerObject / 2 ); drawY += drawUnitsPerObject; } } delete [] objects; // do this on top of objects so that it's not covered by selected // object's highlight // add a border glBegin( GL_LINE_LOOP ); { glColor4f( 0.7f, 0.7f, 0.7f, alphaBottomLeft ); glVertex2d( mAnchorX, mAnchorY ); glColor4f( 0.7f, 0.7f, 0.7f, alphaTopLeft ); glVertex2d( mAnchorX, mAnchorY + mHeight ); glColor4f( 0.7f, 0.7f, 0.7f, alphaTopRight ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glColor4f( 0.7f, 0.7f, 0.7f, alphaBottomRight ); glVertex2d( mAnchorX + mWidth, mAnchorY ); } glEnd(); } double ObjectSelector::getDrawUnitsPerObject() { int objectCount = mStorage->getStoredObjects( NULL ); double drawUnitsPerObject = mHeight / objectCount; if( drawUnitsPerObject > mWidth ) { drawUnitsPerObject = mWidth; } return drawUnitsPerObject; } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/PlotButton.h0000640000175000017500000000120510452713626025576 0ustar pabspabs/* * Modification History * * 2006-July-4 Jason Rohrer * Created. * * 2006-July-5 Jason Rohrer * Changed to subclass ButtonBase. */ #include "ButtonBase.h" /** * Button that initiates the planting action. */ class PlotButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ PlotButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); }; Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/ObjectStorage.h0000640000175000017500000000267310514123576026227 0ustar pabspabs/* * Modification History * * 2006-July-23 Jason Rohrer * Created. * * 2006-October-14 Jason Rohrer * Added virtual destructor. */ #ifndef OBJECT_STORAGE_INCLUDED #define OBJECT_STORAGE_INCLUDED #include "../DrawableObject.h" /** * Interface for classes that can be the base for an ObjectSelector user * interface element. */ class ObjectStorage { public: /** * Gets the objects stored by this class. * * @param outObjects pointer to where an array of objects should * be returned, or NULL to just return an object count. * Array must be destroyed by caller, but objects are managed by * this class. * * @return the number of objects stored. */ virtual int getStoredObjects( DrawableObject ***outObjects ) = 0; /** * Gets the index number of the selected object. * * @return the selected index, or -1 if no object selected. */ virtual int getSelectedObjectIndex() = 0; /** * Sets the index number of the selected object. * * @param inIndex the index to select, * or -1 to select no object selected. */ virtual void setSelectedObjectIndex( int inIndex ) = 0; // ensure proper destruction of subclasses virtual ~ObjectStorage() { } }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/EatButton.h0000640000175000017500000000135110500307010025347 0ustar pabspabs/* * Modification History * * 2006-July-6 Jason Rohrer * Created. */ #include "ButtonBase.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" /** * Button that initiates the planting action. */ class EatButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ EatButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); virtual ~EatButton(); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); protected: SingleTextureGL *mMouthTexture; }; Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/EatButton.cpp0000640000175000017500000001001410542007740025713 0ustar pabspabs/* * Modification History * * 2006-July-6 Jason Rohrer * Created. */ #include "EatButton.h" #include "../glCommon.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include EatButton::EatButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight, true ) { // construct mouth texture // a blurry white circle with a mouth cut out // black border int textureSize = 32; double halfTextureSize = 0.5 * textureSize; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // start all pixels as black and transparent for( p=0; p 0 ) { angle = acos( xRelative / pixelRadius ); } else { angle = 0; } if( yRelative < 0 ) { angle = 2 * M_PI - angle; } if( pixelRadius <= radius ) { // don't set alpha inside mouth if( angle > halfMouthAngle && angle < 2 * M_PI - halfMouthAngle ) { alphaValue = 1; } } channels[3][ pixelIndex ] = alphaValue; // black where transparent channels[0][ pixelIndex ] = alphaValue; channels[1][ pixelIndex ] = alphaValue; channels[2][ pixelIndex ] = alphaValue; } } // blur alpha int blurRadius = 1; BoxBlurFilter blur( blurRadius ); // blur all channels // all are identical, so blur one and copy textureImage->filter( &blur, 0 ); memcpy( channels[1], channels[0], pixelsPerChannel * sizeof( double ) ); memcpy( channels[2], channels[0], pixelsPerChannel * sizeof( double ) ); memcpy( channels[3], channels[0], pixelsPerChannel * sizeof( double ) ); mMouthTexture = new SingleTextureGL( textureImage, // no wrap false ); delete textureImage; } EatButton::~EatButton() { delete mMouthTexture; } void EatButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { // ofset of eating mouth and target from center double offset = inRadius / 3; double drawRadius = inRadius - offset; Vector3D targetCenter( inCenter ); targetCenter.mX += offset; if( mSelectedObject != NULL ) { // draw selected object mSelectedObject->draw( &targetCenter, drawRadius ); } Vector3D mouthCenter( inCenter ); mouthCenter.mX -= offset; // radius of eat icon is 0.666 of texture radius drawRadius = drawRadius / 0.6666; glColor4f( 1, 1, 1, inAlpha ); drawTextureQuad( mMouthTexture, &mouthCenter, drawRadius ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/TextBlockGL.cpp0000640000175000017500000000707210544377024026151 0ustar pabspabs/* * Modification History * * 2006-December-19 Jason Rohrer * Created. */ #include "TextBlockGL.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/SimpleVector.h" TextBlockGL::TextBlockGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, char *inString, TextGL *inText, int inMaxCharactersPerLine ) : LabelGL( inAnchorX, inAnchorY, inWidth, inHeight, inString, inText ), mMaxCharactersPerLine( inMaxCharactersPerLine ) { } void TextBlockGL::fireRedraw() { double charWidth = mWidth / mMaxCharactersPerLine; int numChars = strlen( mString ); // first, split into lines SimpleVector lines; double widestLineWidthFraction = 0; int charsLeft = numChars; int nextChar = 0; while( charsLeft > 0 ) { while( nextChar < numChars - 1 && mString[ nextChar ] == ' ' ) { // skip spaces at start of line nextChar ++; charsLeft --; } // skip chars we have done char *lineString = stringDuplicate( &( mString[ nextChar ] ) ); // truncate at end of line char moreCharsAfterLine = false; if( mMaxCharactersPerLine < strlen( lineString ) ) { lineString[ mMaxCharactersPerLine ] = '\0'; moreCharsAfterLine = true; } if( moreCharsAfterLine ) { // truncate at first space before end of line char foundSpace = false; for( int i=strlen(lineString)-1; i>=0 && !foundSpace; i-- ) { if( lineString[i] == ' ' ) { lineString[i] = '\0'; foundSpace = true; } } } int charsOnLine = strlen( lineString ); lines.push_back( lineString ); double lineWidth = mText->measureTextWidth( lineString ); if( lineWidth > widestLineWidthFraction ) { widestLineWidthFraction = lineWidth; } nextChar += charsOnLine; charsLeft -= charsOnLine; } int numLines = lines.size(); double lineHeight = mHeight / numLines; // each line includes between-line spacing double charHeightFraction = 0.65; double charHeight = charHeightFraction * lineHeight; // preserve character 1:1 aspect ratio if( charHeight < charWidth ) { charWidth = charHeight; } else if( charWidth < charHeight ) { charHeight = charWidth; lineHeight = charHeight / charHeightFraction; } double widestLineWidth = widestLineWidthFraction * charHeight; // can scale everything up a bit if we have room double scaleFactorA = mWidth / widestLineWidth; double scaleFactorB = ( mHeight / numLines ) / lineHeight; // use whatever is smaller double scaleFactor = scaleFactorA; if( scaleFactorB < scaleFactorA ) { scaleFactor = scaleFactorB; } charWidth *= scaleFactor; charHeight *= scaleFactor; lineHeight *= scaleFactor; double topY = mAnchorY + mHeight; for( int i=0; idrawText( lineString, mAnchorX, topY - charHeight, lineWidth, charHeight ); delete [] lineString; topY -= lineHeight; } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/PlantButton.cpp0000640000175000017500000000222510507250155026266 0ustar pabspabs/* * Modification History * * 2006-July-4 Jason Rohrer * Created. * * 2006-July-5 Jason Rohrer * Changed to subclass ButtonBase. */ #include "PlantButton.h" #include "../features.h" #include PlantButton::PlantButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight ) { } void PlantButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { if( mSelectedObject != NULL ) { // draw selected object, which is a leaf with it's stem at the // given position, and its length at the given scale double leafLength = 2 * inRadius; Vector3D stemStart( inCenter ); stemStart.mY -= inRadius; if( Features::drawNicePlantLeaves ) { // white to let texture color show through glColor4f( 1, 1, 1, inAlpha ); } else { // green to color the simple quad lines glColor4f( 0, 1, 0, inAlpha ); } mSelectedObject->draw( &stemStart, leafLength ); } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/QuitButton.cpp0000640000175000017500000000114610500347230026125 0ustar pabspabs/* * Modification History * * 2006-July-10 Jason Rohrer * Created. */ #include "QuitButton.h" #include "../glCommon.h" #include QuitButton::QuitButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight ) { } void QuitButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { // draw a red x glColor4f( 1, 0, 0, inAlpha ); Angle3D angle( 0, 0, M_PI / 4 ); drawBlurPlus( inCenter, inRadius, &angle ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/MateButton.h0000640000175000017500000000177610500320447025550 0ustar pabspabs/* * Modification History * * 2006-August-14 Jason Rohrer * Created. */ #include "ButtonBase.h" /** * Mate button. */ class MateButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ MateButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); /** * Sets a second selected object that this button should apply to. * * @param inObject the object that is selected, or NULL for none. * Destroyed by caller after a different object is set with * a call to setSelectedObject. */ virtual void setSecondSelectedObject( DrawableObject *inObject ); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); protected: DrawableObject *mSecondSelectedObject; }; Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/PlantButton.h0000640000175000017500000000122010452713625025732 0ustar pabspabs/* * Modification History * * 2006-July-4 Jason Rohrer * Created. * * 2006-July-5 Jason Rohrer * Changed to subclass ButtonBase. */ #include "ButtonBase.h" /** * Button that initiates the planting action. */ class PlantButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ PlantButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); }; Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/PoisonButton.cpp0000640000175000017500000000105510503764755026473 0ustar pabspabs/* * Modification History * * 2006-September-19 Jason Rohrer * Created. */ #include "PoisonButton.h" #include "../glCommon.h" #include PoisonButton::PoisonButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight ) { } void PoisonButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { glColor4f( 0, 0, 0, inAlpha ); drawBlurCircle( inCenter, inRadius ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/TexturedPanel.h0000640000175000017500000000172710541767344026266 0ustar pabspabs/* * Modification History * * 2006-December-18 Jason Rohrer * Created. */ #ifndef TEXTURED_PANEL_INCLUDED #define TEXTURED_PANEL_INCLUDED #include "minorGems/graphics/openGL/gui/GUIPanelGL.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include "minorGems/math/geometry/Vector3D.h" /** * Extension of GUIPanelGL that has a texture. */ class TexturedPanel : public GUIPanelGL { public: /** * Same as constructor for GUIPanelGL, except: * * @param inVertical true if this is a vertical panel, or false * if horizontal. */ TexturedPanel( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Color *inColor, char inVertical ); virtual ~TexturedPanel(); // override fireRedraw() in GUIPanelGL virtual void fireRedraw(); protected: char mVertical; SingleTextureGL *mBackgroundTexture; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/WaterButton.cpp0000640000175000017500000000123110500274504026263 0ustar pabspabs/* * Modification History * * 2006-July-5 Jason Rohrer * Created. */ #include "WaterButton.h" #include "../glCommon.h" #include WaterButton::WaterButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight ), mFullStatus( false ) { } void WaterButton::setFull( char inFullStatus ) { mFullStatus = inFullStatus; } void WaterButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { glColor4f( 0, 0, 1, inAlpha ); drawBlurCircle( inCenter, inRadius ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/NextTutorialButton.h0000640000175000017500000000140310542024543027313 0ustar pabspabs/* * Modification History * * 2006-December-19 Jason Rohrer * Created. */ #ifndef NEXT_TUTORIAL_BUTTON_INCLUDED #define NEXT_TUTORIAL_BUTTON_INCLUDED #include "RestartButton.h" /** * Button that moves on to the next tutorial page. * * Inherits from RestartButton to reuse the green arrow. */ class NextTutorialButton : public RestartButton { public: /** * See parameter description from BuuttonGL. */ NextTutorialButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/HarvestButton.h0000640000175000017500000000117310500301450026256 0ustar pabspabs/* * Modification History * * 2006-July-23 Jason Rohrer * Created. */ #include "ButtonBase.h" /** * Button that initiates the planting action. */ class HarvestButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ HarvestButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); }; Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/DiscardButton.cpp0000640000175000017500000000200710542600332026552 0ustar pabspabs/* * Modification History * * 2006-August-15 Jason Rohrer * Created. */ #include "DiscardButton.h" #include "../glCommon.h" #include DiscardButton::DiscardButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight, true ) { } void DiscardButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { if( mSelectedObject != NULL ) { // draw selected object mSelectedObject->draw( inCenter, inRadius ); } // draw a white x with a black shadow Angle3D angle( 0, 0, M_PI / 4 ); glColor4f( 0, 0, 0, inAlpha ); Vector3D shadowCenter( inCenter ); //shadowCenter.mX -= 0.1 * inRadius; //shadowCenter.mY -= 0.1 * inRadius; drawBlurPlus( &shadowCenter, 0.9 * inRadius, &angle ); glColor4f( 1, 1, 1, inAlpha ); drawBlurPlus( inCenter, 0.8 * inRadius, &angle ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/PoisonButton.h0000640000175000017500000000113710503764755026141 0ustar pabspabs/* * Modification History * * 2006-September-19 Jason Rohrer * Created. */ #include "ButtonBase.h" /** * Button that initiates the planting action. */ class PoisonButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ PoisonButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); }; Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/PauseButton.cpp0000640000175000017500000000613510540313065026266 0ustar pabspabs/* * Modification History * * 2006-December-14 Jason Rohrer * Created. */ #include "PauseButton.h" #include "../glCommon.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include PauseButton::PauseButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : ButtonBase( inAnchorX, inAnchorY, inWidth, inHeight ) { // construct pause texture // two blurry white vertical bars // black border int textureSize = 32; double halfTextureSize = 0.5 * textureSize; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // start all pixels as black and transparent for( p=0; p= xEnd - barWidth ) { // in one of the two filled bars int pixelIndex = y * textureSize + x; // white channels[0][ pixelIndex ] = 1; channels[1][ pixelIndex ] = 1; channels[2][ pixelIndex ] = 1; // opaque channels[3][ pixelIndex ] = 1; } } } // blur alpha int blurRadius = 1; BoxBlurFilter blur( blurRadius ); // blur all channels textureImage->filter( &blur, 3 ); // rgb are identical, so blur one and copy textureImage->filter( &blur, 0 ); memcpy( channels[1], channels[0], pixelsPerChannel * sizeof( double ) ); memcpy( channels[2], channels[0], pixelsPerChannel * sizeof( double ) ); mPauseTexture = new SingleTextureGL( textureImage, // no wrap false ); delete textureImage; } PauseButton::~PauseButton() { delete mPauseTexture; } void PauseButton::drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) { // draw a yellow pause symbol glColor4f( 1, 1, 0, inAlpha ); // compensate for small radius of texture double drawRadius = inRadius / 0.6666; drawTextureQuad( mPauseTexture, inCenter, drawRadius ); } Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/ButtonBase.h0000640000175000017500000000455410542012011025520 0ustar pabspabs/* * Modification History * * 2006-July-5 Jason Rohrer * Created. */ #ifndef BUTTON_BASE_INCLUDED #define BUTTON_BASE_INCLUDED #include "../DrawableObject.h" #include "minorGems/graphics/openGL/gui/ButtonGL.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include "minorGems/math/geometry/Vector3D.h" /** * Base class for all game buttons. * * Handles drawing button background and switching between pressed * and unpressed, as well as enabled and disabled, states. */ class ButtonBase : public ButtonGL { public: virtual ~ButtonBase(); /** * Sets the selected object that this button should apply to. * * @param inObject the object that is selected, or NULL for none. * Destroyed by caller after a different object is set with * a call to setSelectedObject. */ virtual void setSelectedObject( DrawableObject *inObject ); // implements the ButtonGL interface virtual void drawPressed(); virtual void drawUnpressed(); /** * Draw the icon on this button. * * @param inCenter the center of the icon. * Destroyed by caller. * @param inRadius the radius of the icon. * @param inAlpha the alpha factor for the icon. */ virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ) = 0; protected: /** * See parameter description from ButtonGL. * * Only should be called by subclass constructor. * * Additional parameter: * * @param inFadeLeft true to fade on left side, or false * to fade on bottom. Defaults to false. */ ButtonBase( double inAnchorX, double inAnchorY, double inWidth, double inHeight, char inFadeLeft = false ); /** * Draws this button with a given background color. * * @param inRed, inGreen, inBlue the background color. */ void drawWithColor( float inRed, float inGreen, float inBlue ); char mFadeLeft; Vector3D mIconCenter; double mIconRadius; DrawableObject *mSelectedObject; }; #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/WaterButton.h0000640000175000017500000000147510452717544025756 0ustar pabspabs/* * Modification History * * 2006-July-5 Jason Rohrer * Created. */ #include "ButtonBase.h" /** * Button that initiates the planting action. */ class WaterButton : public ButtonBase { public: /** * See parameter description from BuuttonGL. */ WaterButton( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); /** * Sets the full status of this button. * * @param inFullStatus true to set to full. */ void setFull( char inFullStatus ); // implements the ButtonBase interface virtual void drawIcon( Vector3D *inCenter, double inRadius, double inAlpha ); protected: char mFullStatus; }; Cultivation_9+dfsg1_UnixSource/game2/gameSource/userInterface/BorderPanel.h0000640000175000017500000000277210544401065025664 0ustar pabspabs/* * Modification History * * 2006-December-27 Jason Rohrer * Created. */ #ifndef BORDER_PANEL_INCLUDED #define BORDER_PANEL_INCLUDED #include "minorGems/graphics/openGL/gui/GUIPanelGL.h" #include /** * A panel with a gray line border that fades near the top. * * @author Jason Rohrer */ class BorderPanel : public GUIPanelGL { public: /** * Constructs a panel. * * Parameters are same as for GUIPanelGL. */ BorderPanel( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Color *inColor ); virtual ~BorderPanel(); // override fireRedraw() in GUIPanelGL virtual void fireRedraw(); }; inline BorderPanel::BorderPanel( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Color *inColor ) : GUIPanelGL( inAnchorX, inAnchorY, inWidth, inHeight, inColor ) { } inline BorderPanel::~BorderPanel() { } inline void BorderPanel::fireRedraw() { // superclass redraw first GUIPanelGL::fireRedraw(); // draw our border on top glBegin( GL_LINE_LOOP ); { glColor4f( 0.7, 0.7, 0.7, 1 ); glVertex2d( mAnchorX, mAnchorY ); glVertex2d( mAnchorX + mWidth, mAnchorY ); glColor4f( 0.7, 0.7, 0.7, 0 ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glVertex2d( mAnchorX, mAnchorY + mHeight ); } glEnd(); } #endif Cultivation_9+dfsg1_UnixSource/game2/gameSource/Plant.cpp0000640000175000017500000006204510507000567022301 0ustar pabspabs/* * Modification History * * 2006-July-2 Jason Rohrer * Created. * * 2006-September-25 Jason Rohrer * Fixed fruit highlighting bug. */ #include "Plant.h" #include "features.h" #include "glCommon.h" #include #include #include "minorGems/util/random/StdRandomSource.h" #include "World.h" extern StdRandomSource globalRandomSource; extern World *globalWorld; #include "minorGems/graphics/filters/BoxBlurFilter.h" Plant::Plant( double inSoilType, Seeds *inSeeds ) : mSeeds( inSeeds ), mLeaf( &( inSeeds->mGenetics ) ), mFlower( &( inSeeds->mGenetics ) ), mLeafTerminiiSet( false ), mTimeSinceLastFlower( 0 ), mHighlightRipeFruit( false ), mWaterStatus( 0.0 ), mJustWatered( false ), mPoisonStatus( 0.0 ), mPoisoned( false ), mGrowth( 0 ), mTimeSinceLastFruit( 0 ), // 30 seconds mTimeBetweenFruits( 30 ), mWaterConsumptionRate( 0.10 ), mGrowthRate( 0.05 ), mCurrentSoilType( inSoilType ), mStartZAngle( globalRandomSource.getRandomBoundedDouble( 0, 2 * M_PI ) ) { // water consumption based on leaf area // An area fraction of 0.25 results in the "standard" consumption rate // Larger areas increase the rate, smaller areas decrease it double leafAreaFraction = mLeaf.getLeafAreaFraction(); double adjustedFraction = ( leafAreaFraction + 0.75 ); // raise to 4th power to increase effect mWaterConsumptionRate *= pow( adjustedFraction, 4 ); // generate texture for leaf joint cap // a blurry circle of outer leaf color int textureSize = 32; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; int pixelsPerChannel = textureSize * textureSize; int i; int p; for( i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // start all pixels as outer leaf color with alpha of zero double leafGreen = mSeeds.mGenetics.getParameter( outerLeafGreen ); for( p=0; pfilter( &blur, 3 ); mJointCapTexture = new SingleTextureGL( textureImage, // no wrap false ); delete textureImage; } Plant::~Plant() { delete mJointCapTexture; int numFlowers = mFlowerTerminusIndicies.size(); int f; for( f=0; fisRipe() ) { return true; } } return false; } void Plant::highlightFirstRipeFruit( char inHighlight ) { mHighlightRipeFruit = inHighlight; } Fruit *Plant::harvest( Vector3D *outFruitPosition ) { // harvest first fruit that is ripe int numFruit = mFruit.size(); for( int f=0; fisRipe() ) { int terminusIndex = *( mFruitTerminusIndices.getElement( f ) ); Vector3D *terminus = *( mLeafTerminii.getElement( terminusIndex ) ); mFruit.deleteElement( f ); mFruitTerminusIndices.deleteElement( f ); delete *( mFruitAngles.getElement( f ) ); mFruitAngles.deleteElement( f ); outFruitPosition->setCoordinates( terminus ); return thisFruit; } } return NULL; } Fruit *Plant::peekAtRipeFruit() { int numFruit = mFruit.size(); for( int f=0; fisRipe() ) { return thisFruit; } } return NULL; } double Plant::getWaterStatus() { if( mGrowth < 1 ) { return mWaterStatus; } else { // done growing // no longer needs to be watered return 1; } } char Plant::isFullGrown() { if( mGrowth < 1 ) { return false; } else { return true; } } void Plant::passTime( double inTimeDeltaInSeconds ) { // become poisoned gradually if( mPoisoned ) { // 1 second to shrivel mPoisonStatus += inTimeDeltaInSeconds; if( mPoisonStatus >= 1 ) { mPoisonStatus = 1; } // do nothing else return; } // become watered gradually if just watered if( mJustWatered ) { // take 1 second to fill up mWaterStatus += inTimeDeltaInSeconds; if( mWaterStatus >= 1 ) { mWaterStatus = 1; mJustWatered = false; } } // growth rate reduced as soil gets further from our ideal soil type. // if completely opposite (disance between ideal and actual is 1), // then growth goes to half it's normal rate. double localGrowthRate = mGrowthRate * ( 1 - 0.5 * fabs( mCurrentSoilType - mSeeds.mIdealSoilType ) ); if( mWaterStatus > 0.0 && mGrowth < 1 ) { // growing mGrowth += inTimeDeltaInSeconds * localGrowthRate; // don't consume water if just watered to avoid // race condition if( !mJustWatered ) { // consume water mWaterStatus -= inTimeDeltaInSeconds * mWaterConsumptionRate; } if( mWaterStatus < 0 ) { mWaterStatus = 0; } if( mGrowth >= 1.0 ) { mGrowth = 1.0; // done growing // remove all extra water mWaterStatus = 0; } } else if( mGrowth >= 1.0 ) { // done growing mTimeSinceLastFlower += inTimeDeltaInSeconds; // advance each flower int numFlowers = mFlowerTerminusIndicies.size(); // bloom rate from leaf area // larger leaves results in faster bloom double bloomRate = mWaterConsumptionRate; int f; for( int f=0; f 4 ) { stage = 4; } *( mFlowerStages.getElement( f ) ) = stage; if( stage == 4 ) { // start growing fruit Seeds *offspringSeeds; Plant *closestPlant = globalWorld->getClosestPlant( this ); if( closestPlant != NULL ) { // cross offspringSeeds = new Seeds( &( mSeeds ), &( closestPlant->mSeeds ) ); } else { // duplicate self offspringSeeds = new Seeds( &mSeeds ); } // ripen rate depends on water consumption Fruit *newFruit = new Fruit( &( mSeeds.mGenetics ), offspringSeeds, mWaterConsumptionRate ); delete offspringSeeds; mFruit.push_back( newFruit ); mFruitTerminusIndices.push_back( *( mFlowerTerminusIndicies.getElement( f ) ) ); Angle3D *flowerAngle = *( mFlowerAngles.getElement( f ) ); mFruitAngles.push_back( new Angle3D( flowerAngle ) ); // remove flower mFlowerTerminusIndicies.deleteElement( f ); mFlowerStages.deleteElement( f ); mFlowerAngles.deleteElement( f ); delete flowerAngle; // reset boundary numFlowers = mFlowerTerminusIndicies.size(); // step f back so we don't miss one f--; } } int numFruit = mFruit.size(); for( f=0; fpassTime( inTimeDeltaInSeconds ); if( thisFruit->isRotten() ) { // remove it delete thisFruit; delete *( mFruitAngles.getElement( f ) ); mFruit.deleteElement( f ); mFruitTerminusIndices.deleteElement( f ); mFruitAngles.deleteElement( f ); numFruit = mFruit.size(); // back up in loop f--; } } /* if( mRipeFruit == NULL ) { // ripening fruit mTimeSinceLastFruit += inTimeDeltaInSeconds; if( mTimeSinceLastFruit >= mTimeBetweenFruits ) { // fruit ripe Seeds *offspringSeeds; Plant *closestPlant = globalWorld->getClosestPlant( this ); if( closestPlant != NULL ) { // cross offspringSeeds = new Seeds( &( mSeeds ), &( closestPlant->mSeeds ) ); } else { // duplicate self offspringSeeds = new Seeds( &mSeeds ); } mRipeFruit = new Fruit( &( mSeeds.mFruitNutrition ), offspringSeeds ); delete offspringSeeds; } } */ } } void Plant::draw( Vector3D *inPosition, double inScale, double inMaxZ, double inMinZ ) { if( mPoisoned && mPoisonStatus >= 1) { // draw nothing return; } double drawScale = inScale; if( mPoisoned ) { // shrink with poisoning drawScale *= ( 1 - mPoisonStatus ); } double radius = drawScale * ( mGrowth * 0.8 + 0.2 ); // leaves become black with poisoning // (shades of white to allow texture color to dominate) Color leafColor( 1 - mPoisonStatus, 1 - mPoisonStatus, 1 - mPoisonStatus, 1 ); if( ! Features::drawNicePlantLeaves ) { // set color to shades of green green for leaves if we're drawing // simple boxes, since there's no texture color leafColor.setValues( 0, 1 - mPoisonStatus, 0, 1 ); } Angle3D zeroAngle( 0, 0, 0 ); PlantGenetics *genetics = &( mSeeds.mGenetics ); int maxNumJoints = (int)( genetics->getParameter( jointCount ) ); double growthFactor = mGrowth * 0.8 + 0.2; int numFullJoints = (int)( growthFactor * maxNumJoints ); double partialJoint = growthFactor * maxNumJoints - numFullJoints; int numLeavesPerJoint = (int)( genetics->getParameter( leavesPerJoint ) ); Angle3D angleIncrement( 0, 0, 2 * M_PI / numLeavesPerJoint ); Angle3D startAngle( 0, 0, mStartZAngle ); double currentScale = 1; double scaleDecrement = currentScale / ( maxNumJoints + 1 ); Vector3D leafPosition( inPosition ); Vector3D positionIncrement( 0, 0, -0.5 ); Vector3D leafWalkerTerminus; SimpleVector thisLayerLeafTerminii; for( int j=0; j= inMaxZ ) { // draw this joint for( int g=0; genable(); glBegin( GL_QUADS ); { double capRadius = currentScale * radius * 0.1; double capZ = leafPosition.mZ; glTexCoord2f( 0, 0 ); glVertex3d( leafPosition.mX - capRadius, leafPosition.mY - capRadius, capZ ); glTexCoord2f( 1, 0 ); glVertex3d( leafPosition.mX + capRadius, leafPosition.mY - capRadius, capZ ); glTexCoord2f( 1, 1 ); glVertex3d( leafPosition.mX + capRadius, leafPosition.mY + capRadius, capZ ); glTexCoord2f( 0, 1 ); glVertex3d( leafPosition.mX - capRadius, leafPosition.mY + capRadius, capZ ); } glEnd(); mJointCapTexture->disable(); } Angle3D angleToNextJoint( &angleIncrement ); angleToNextJoint.scale( 0.5 ); currentAngle.add( &angleToNextJoint ); // start next joint at our current angle startAngle.setComponents( ¤tAngle ); currentScale -= scaleDecrement; leafPosition.add( &positionIncrement ); } if( partialJoint > 0 ) { Angle3D currentAngle( &startAngle ); // darker as growing completes // lower leaves are darker double colorScaleFactor = (double)(numFullJoints+1) / (double)maxNumJoints; // min scaling of 0.5 colorScaleFactor = colorScaleFactor * 0.5 + 0.5; // scale factor comes into effect as partial joint reaches 1 colorScaleFactor = (1 - partialJoint) + colorScaleFactor * partialJoint; Color thisLevelColor; thisLevelColor.setValues( &leafColor ); thisLevelColor.weightColor( colorScaleFactor ); double zValue = leafPosition.mZ; if( zValue <= inMaxZ && zValue >= inMaxZ ) { // draw this joint for( int g=0; genable(); glBegin( GL_QUADS ); { double capRadius = currentScale * radius * 0.1; double capZ = leafPosition.mZ; glTexCoord2f( 0, 0 ); glVertex3d( leafPosition.mX - capRadius, leafPosition.mY - capRadius, capZ ); glTexCoord2f( 1, 0 ); glVertex3d( leafPosition.mX + capRadius, leafPosition.mY - capRadius, capZ ); glTexCoord2f( 1, 1 ); glVertex3d( leafPosition.mX + capRadius, leafPosition.mY + capRadius, capZ ); glTexCoord2f( 0, 1 ); glVertex3d( leafPosition.mX - capRadius, leafPosition.mY + capRadius, capZ ); } glEnd(); mJointCapTexture->disable(); } } int numTerminii = thisLayerLeafTerminii.size(); int t; if( mGrowth >= 1 ) { // NOTE: // This method of collecting all leaf terminii for the plant ASSUMES // that each terminus is at a unique location // This seems like a safe assumption, given the way leaves are // arranged now, but it is not safe in the general case. // If two terminii are at the same location, the terminus collection // would finish before collecting all terminii if( !mLeafTerminiiSet ) { // not done collecting leaf terminii for full-growth plant int numExisting = mLeafTerminii.size(); char collision = false; for( int t=0; tequals( newTerminus ) ) { same = true; collision = true; } } if( !same ) { // add to list of all terminii mLeafTerminii.push_back( new Vector3D( newTerminus ) ); } } if( collision ) { // we are back to drawing a layer that we've already drawn // before // so we're not gathering new leaf terminii anymore mLeafTerminiiSet = true; } } else { // don't try adding flowers if we already have more than // numTerminii // flowers int numTotalTerminii = mLeafTerminii.size(); int numFlowers = mFlowerTerminusIndicies.size(); int numFruit = mFruitTerminusIndices.size(); if( numFlowers < numTotalTerminii && mTimeSinceLastFlower >= genetics->getParameter( timeBetweenFlowers ) ) { // new flower // pick random, unflowered, unfruited terminus int numTries = 0; char found = false; int foundIndex = -1; while( ! found && numTries < 100 ) { foundIndex = globalRandomSource.getRandomBoundedInt( 0, numTotalTerminii - 1 ); found = true; int f; for( f=0; fmZ; if( zValue <= inMaxZ && zValue >= inMaxZ ) { Angle3D *flowerAngle = *( mFlowerAngles.getElement( f ) ); double flowerStage = *( mFlowerStages.getElement( f ) ); mFlower.draw( terminus, flowerAngle, drawScale, flowerStage ); } } } // draw fruit int numFruit = mFruit.size(); for( int f=0; fmZ; if( zValue <= inMaxZ && zValue >= inMaxZ ) { Angle3D *fruitAngle = *( mFruitAngles.getElement( f ) ); Fruit *thisFruit = *( mFruit.getElement( f ) ); double fruitScale = drawScale * 0.2; thisFruit->draw( terminus, fruitAngle, fruitScale ); if( mHighlightRipeFruit && thisFruit->isRipe() ) { // make sure this is the fruit that we will harvest // next // (the z-range drawing can screw us // up here, since we might draw fruits out-of-order) // thus, the first-drawn ripe fruit is not necessarily // the fruit that will be next harvested Fruit *fruitNextHarvested = peekAtRipeFruit(); if( thisFruit == fruitNextHarvested ) { // this fruit will be harvested next glColor4f( 1, 1, 1, 0.25 ); // highlight brightens only glBlendFunc( GL_SRC_ALPHA, GL_ONE ); drawBlurCircle( terminus, fruitScale ); // back to normal blend function glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); // only highlight one mHighlightRipeFruit = false; } } } } } // delete this layer's terminus points for( t=0; t #include #include "minorGems/util/random/StdRandomSource.h" #include "minorGems/graphics/Image.h" #include "minorGems/graphics/filters/SeamlessFilter.h" extern StdRandomSource globalRandomSource; extern SoundEffectsBank *globalSoundEffectsBank; extern World *globalWorld; // #include "minorGems/io/file/FileOutputStream.h" // #include "minorGems/graphics/converters/TGAImageConverter.h" World::World( Vector3D *inCornerA, Vector3D *inCornerB ) : mCornerA( inCornerA ), mCornerB( inCornerB ), mSoilMap( inCornerA, inCornerB ), mCloudPosition( 0 ), mCloudMotionRate( 0.005 ), mGardenerVelocity( 10 ), mGardenerRotationVelocity( 3 ), mPortalPosition( 0, 0, 0 ), mPortal( NULL ), mMinPlantingDistance( 5 ), mMaxDistanceThatCountsAsClose( 10 ), mGardenerZ( -2.5 ), mHighlightPosition( NULL ) { // for testing // mPortal = new Portal(); // mPortal->upgrade(); // generate cloud texture int textureSize = 64; int numPixels = textureSize * textureSize; Image *textureImage = new Image( textureSize, textureSize, 4, false ); double *channels[4]; for( int i=0; i<4; i++ ) { channels[i] = textureImage->getChannel( i ); } // white // leave alpha uninitialized int p; for( p=0; p maxValue ) { maxValue = value; } } } // now scale by max and min double range = maxValue - minValue; for( p=0; p 1 ) { value = 1; } else if( value < 0 ) { value = 0; } channels[3][p] = value; } /* File outFileA( NULL, "clouds.tga" ); FileOutputStream *outStreamA = new FileOutputStream( &outFileA ); TGAImageConverter converter; converter.formatImage( textureImage, outStreamA ); delete outStreamA; */ // make seamless SeamlessFilter seamless( 2 ); textureImage->filter( &seamless, 3 ); /* File outFileB( NULL, "clouds_seamless.tga" ); FileOutputStream *outStreamB = new FileOutputStream( &outFileB ); converter.formatImage( textureImage, outStreamB ); delete outStreamB; */ // wrap mCloudTexture = new SingleTextureGL( textureImage, true ); delete textureImage; } World::~World() { int i; int numPlants = mPlants.size(); for( i=0; i=0; i-- ) { removeGardener( i ); } int numFlyingObjects = mFlyingObjects.size(); for( i=0; itrackOtherGardener( inGardener ); inGardener->trackOtherGardener( other ); } mGardeners.push_back( inGardener ); mGardenerPositions.push_back( new Vector3D( inPosition ) ); Angle3D *rotation; if( inRotation == NULL ) { rotation = new Angle3D( 0, 0, 0 ); } else { rotation = new Angle3D( inRotation ); } mGardenerRotations.push_back( rotation ); mGardenerRotationsComplete.push_back( false ); mGardenerDestinationForRotation.push_back( new Vector3D( inPosition ) ); mGardenerPlotsCornerA.push_back( NULL ); mGardenerPlotsCornerB.push_back( NULL ); mGardenerTargetOfPregnancy.push_back( false ); numGardeners = mGardeners.size(); setNumGardeners( numGardeners ); } void World::removeGardener( Gardener *inGardener ) { int i = mGardeners.getElementIndex( inGardener ); if( i != -1 ) { removeGardener( i ); } } void World::ignoreGardener( Gardener *inGardener ) { int i = mGardeners.getElementIndex( inGardener ); if( i != -1 ) { ignoreGardener( i ); } } void World::ignoreGardener( int inGardenerIndex ) { int i = inGardenerIndex; Gardener *gardener = *( mGardeners.getElement( i ) ); // remove any flying objects that are heading toward this gardener // since there will no longer be a destination for them int j=0; while( j < mFlyingObjects.size() ) { FlyingObject *object = *( mFlyingObjects.getElement( j ) ); if( object->mDestinationGardener == gardener ) { delete object; mFlyingObjects.deleteElement( j ); } else { j++; } } // all others should untrack this gardener int numGardeners = mGardeners.size(); for( j=0; juntrackOtherGardener( gardener ); // also, remove this gardener as a parent from any baby offspring Gardener *parentOfOther = other->getParentToFollow(); if( parentOfOther == gardener ) { // this is our offspring and it's still following us // sorry, goodbye other->forgetParent(); } // tell others to stop following us Gardener *leaderOfOther = other->getLeader(); if( leaderOfOther == gardener ) { other->setLeader( NULL ); } } } // tell our leader to drop us Gardener *ourLeader = gardener->getLeader(); if( ourLeader != NULL ) { ourLeader->dropFollower( gardener ); } // tell our parent to drop us Gardener *ourParent = gardener->getParentToFollow(); if( ourParent != NULL ) { ourParent->dropOutsideOffspring( gardener ); } // remove as plant tender int numPlants = mPlants.size(); for( j=0; jgetMusicPart(); } *outNumGardeners = numGardeners; return returnArray; } double *World::getAllGardenerMusicVolumeModifiers( int *outNumGardeners ) { int numGardeners = mGardenerPositions.size(); double *returnArray = new double[ numGardeners ]; for( int i=0; igetLife(); } *outNumGardeners = numGardeners; return returnArray; } Vector3D *World::getPlantPosition( Plant *inPlant ) { int index = mPlants.getElementIndex( inPlant ); if( index != -1 ) { return new Vector3D( *( mPlantPositions.getElement( index ) ) ); } else { return NULL; } } void World::setGardenerPlot( Gardener *inGardener, Vector3D *inCornerA, Vector3D *inCornerB ) { int index = mGardeners.getElementIndex( inGardener ); if( index != -1 ) { // delete any old corner values Vector3D *cornerA, *cornerB; cornerA = *( mGardenerPlotsCornerA.getElement( index ) ); cornerB = *( mGardenerPlotsCornerB.getElement( index ) ); if( cornerA != NULL ) { delete cornerA; } if( cornerB != NULL ) { delete cornerB; } if( inCornerA != NULL && inCornerB != NULL ) { // set new values *( mGardenerPlotsCornerA.getElement( index ) ) = new Vector3D( inCornerA ); *( mGardenerPlotsCornerB.getElement( index ) ) = new Vector3D( inCornerB ); } else { // leave NULL values *( mGardenerPlotsCornerA.getElement( index ) ) = NULL; *( mGardenerPlotsCornerB.getElement( index ) ) = NULL; } } } char World::isInPlot( Gardener *inGardener ) { int index = mGardeners.getElementIndex( inGardener ); if( index != -1 ) { Vector3D *position = *( mGardenerPositions.getElement( index ) ); return isInPlot( inGardener, position ); } else { return false; } } char World::isInPlot( Gardener *inGardener, Vector3D *inPosition ) { if( inGardener->isDead() ) { return false; } int index = mGardeners.getElementIndex( inGardener ); if( index != -1 ) { Vector3D *cornerA, *cornerB; cornerA = *( mGardenerPlotsCornerA.getElement( index ) ); cornerB = *( mGardenerPlotsCornerB.getElement( index ) ); if( cornerA != NULL && cornerB != NULL ) { return isInRectangle( inPosition, cornerA, cornerB ); } else { // no plot set return false; } } else { // not our gardener return false; } } char World::isInWater( Gardener *inGardener ) { int index = mGardeners.getElementIndex( inGardener ); if( index != -1 ) { Vector3D *position = *( mGardenerPositions.getElement( index ) ); return ! mSoilMap.isInBounds( position ); } else { // not our gardener return false; } } char World::isInWater( Vector3D *inPosition ) { return ! mSoilMap.isInBounds( inPosition ); } Plant *World::getClosestPlotPlant( Gardener *inGardener, char inGetEvenIfTooFar, char inGetOnlyRipe ) { int index = mGardeners.getElementIndex( inGardener ); if( index == -1 || inGardener->isDead() ) { // we do not manage this gardener or it's already dead return NULL; } Vector3D *position = *( mGardenerPositions.getElement( index ) ); Vector3D *cornerA = *( mGardenerPlotsCornerA.getElement( index ) ); Vector3D *cornerB = *( mGardenerPlotsCornerB.getElement( index ) ); if( cornerA == NULL || cornerB == NULL ) { // no plot for this gardener return NULL; } // else gardener has a plot // check each plant to find closest plant in gardener's plot double closestDistance; if( ! inGetEvenIfTooFar ) { // only consider plants that are close to inGardener closestDistance = mMaxDistanceThatCountsAsClose; } else { // consider all plants closestDistance = DBL_MAX; } Plant *closestPlant = NULL; int numPlants = mPlants.size(); for( int i=0; igetPoisonStatus() == 0 ) { Vector3D *plantPosition = *( mPlantPositions.getElement( i ) ); // ignore those outside of plot if( isInRectangle( plantPosition, cornerA, cornerB ) ) { double distance = position->getDistance( plantPosition ); // ignore those that are to far away to count as close if( distance <= closestDistance ) { // ignore all but ripe plants if caller requests it if( !inGetOnlyRipe || plant->isRipe() ) { closestDistance = distance; closestPlant = plant; } } } } } return closestPlant; } SimpleVector *World::getPlotPlants( Gardener *inGardener ) { SimpleVector *resultVector = new SimpleVector; int index = mGardeners.getElementIndex( inGardener ); if( index == -1 || inGardener->isDead() ) { // we do not manage this gardener or it's already dead // empty vector return resultVector; } Vector3D *cornerA = *( mGardenerPlotsCornerA.getElement( index ) ); Vector3D *cornerB = *( mGardenerPlotsCornerB.getElement( index ) ); if( cornerA == NULL || cornerB == NULL ) { // no plot for this gardener // return empty vector return resultVector; } // else gardener has a plot int numPlants = mPlants.size(); for( int i=0; igetPoisonStatus() == 0 ) { Vector3D *plantPosition = *( mPlantPositions.getElement( i ) ); // ignore those outside of plot if( isInRectangle( plantPosition, cornerA, cornerB ) ) { resultVector->push_back( plant ); } } } return resultVector; } Plant *World::getTendedPlant( Gardener *inGardener ) { if( inGardener->isDead() ) { return NULL; } int numPlants = mLastPlantTender.size(); for( int i=0; igetPoisonStatus() == 0 ) { return plant; } } } return NULL; } /** * Special FlyingObject implementation for fruit exchange (or harvest). * * When fruit reaches destination, it is added to storage of recieving * gardener. */ class FruitFlyingObject : public FlyingObject { public: /** * Same as constructor for FlyingObject, except: * * @param inGiver the gardener that is giving inDestinationGardener * fruit. NULL if there is no giver (harvest case). */ FruitFlyingObject( Gardener *inGiver, DrawableObject *inObject, char inDestroyObject, Gardener *inDestinationGardener, Vector3D *inStartPosition ) : FlyingObject( inObject, inDestroyObject, inDestinationGardener, inStartPosition ), mGiver( inGiver ) { } virtual void reachedDestination() { // no longer need to destroy object, since we're passing // it to destination gardener mDestroyObject = false; mDestinationGardener->storeItem( (Fruit*) mObject ); if( mGiver != NULL ) { // make sure giver still in world Vector3D *giverPosition = globalWorld->getGardenerPosition( mGiver ); if( giverPosition != NULL ) { mDestinationGardener->getFriendly( mGiver ); globalWorld->flyEmotionIcon( mDestinationGardener, mGiver, new LikeIcon() ); delete giverPosition; } } } protected: Gardener *mGiver; }; void World::harvestPlant( Gardener *inGardener, Plant *inPlant ) { Vector3D *plantPosition = getPlantPosition( inPlant ); Vector3D *gardenerPosition = getGardenerPosition( inGardener ); if( plantPosition != NULL && gardenerPosition != NULL ) { if( isInPlot( inGardener, plantPosition ) ) { double distance = gardenerPosition->getDistance( plantPosition ); if( distance <= mMaxDistanceThatCountsAsClose ) { // anger other gardeners (if this plant in their plots) // with this action angerOthers( inGardener, plantPosition ); // count harvesting as tending int index = mPlants.getElementIndex( inPlant ); *( mLastPlantTender.getElement( index ) ) = inGardener; Vector3D fruitPosition; Fruit *fruit = inPlant->harvest( &fruitPosition ); if( fruit != NULL ) { // true in case it doesn't reach its destination char destroyObject = true; FruitFlyingObject *flyingObject = new FruitFlyingObject( NULL, fruit, destroyObject, inGardener, &fruitPosition ); mFlyingObjects.push_back( flyingObject ); } } } } if( plantPosition != NULL ) { delete plantPosition; } if( gardenerPosition != NULL ) { delete gardenerPosition; } } void World::dumpWater( Gardener *inGardener ) { Plant *plant = getClosestPlotPlant( inGardener ); if( plant != NULL ) { plant->water(); int index = mPlants.getElementIndex( plant ); *( mLastPlantTender.getElement( index ) ) = inGardener; } } void World::dumpPoison( Gardener *inGardener ) { Vector3D *position = getGardenerPosition( inGardener ); if( position != NULL ) { Plant *plant = getClosestPlant( position ); if( plant != NULL ) { plant->poison(); // FIXME: flying object? Vector3D *plantPosition = getPlantPosition( plant ); angerOthers( inGardener, plantPosition ); delete plantPosition; } } delete position; } void World::getGardenerPlot( Gardener *inGardener, Vector3D **inCornerA, Vector3D **inCornerB ) { // default to returning NULL *inCornerA = NULL; *inCornerB = NULL; if( inGardener->isDead() ) { // ignore plots of dead gardeners return; } int index = mGardeners.getElementIndex( inGardener ); if( index != -1 ) { Vector3D *cornerA, *cornerB; cornerA = *( mGardenerPlotsCornerA.getElement( index ) ); cornerB = *( mGardenerPlotsCornerB.getElement( index ) ); if( cornerA != NULL ) { *inCornerA = new Vector3D( cornerA ); } if( cornerB != NULL ) { *inCornerB = new Vector3D( cornerB ); } } } double World::getPlotArea( Gardener *inGardener ) { Vector3D *cornerA, *cornerB; getGardenerPlot( inGardener, &cornerA, &cornerB ); double area = 0; if( cornerA != NULL ) { area = getRectangleArea( cornerA, cornerB ); delete cornerA; delete cornerB; } return area; } double World::getPlotIntersectionArea( Gardener *inGardenerA, Gardener *inGardenerB ) { Vector3D *cornerFirstA, *cornerFirstB; getGardenerPlot( inGardenerA, &cornerFirstA, &cornerFirstB ); if( cornerFirstA == NULL ) { // A has no plot, no intersection return 0; } Vector3D *cornerSecondA, *cornerSecondB; getGardenerPlot( inGardenerB, &cornerSecondA, &cornerSecondB ); if( cornerSecondA == NULL ) { delete cornerFirstA; delete cornerFirstB; return 0; } Vector3D *intersectionA, *intersectionB; getRectangleIntersection( cornerFirstA, cornerFirstB, cornerSecondA, cornerSecondB, &intersectionA, &intersectionB ); delete cornerFirstA; delete cornerFirstB; delete cornerSecondA; delete cornerSecondB; if( intersectionA == NULL ) { return 0; } double area = getRectangleArea( intersectionA, intersectionB ); delete intersectionA; delete intersectionB; return area; } Gardener *World::getClosestGardener( Gardener *inGardener ) { int index = mGardeners.getElementIndex( inGardener ); if( index == -1 ) { return NULL; } else { Vector3D *position = *( mGardenerPositions.getElement( index ) ); Gardener *closestGardener = NULL; double minDistance = DBL_MAX; int numGardeners = mGardeners.size(); for( int i=0; iisDead() && otherGardener->getParentToFollow() != inGardener ) { Vector3D *otherPosition = *( mGardenerPositions.getElement( i ) ); double distance = position->getDistance( otherPosition ); if( distance < minDistance ) { minDistance = distance; closestGardener = otherGardener; } } } return closestGardener; } } double World::getGardenerDistance( Gardener *inGardenerA, Gardener *inGardenerB ) { Vector3D *positionA = getGardenerPosition( inGardenerA ); Vector3D *positionB = getGardenerPosition( inGardenerB ); double distance = positionA->getDistance( positionB ); delete positionA; delete positionB; return distance; } Plant *World::getClosestPlant( Plant *inPlant ) { int index = mPlants.getElementIndex( inPlant ); if( index == -1 ) { return NULL; } else { Vector3D *position = *( mPlantPositions.getElement( index ) ); return getClosestPlant( position, inPlant ); } } Plant *World::getClosestPlant( Vector3D *inPosition, Plant *inPlantToIgnore, char inGetEvenIfTooFar, char inGetEvenIfPoisoned ) { Plant *closestPlant = NULL; double minDistance = DBL_MAX; if( ! inGetEvenIfTooFar ) { // reduce starting min distance to ignore those that are // farther away minDistance = mMaxDistanceThatCountsAsClose; } int numPlants = mPlants.size(); for( int i=0; igetPoisonStatus() == 0 ) ) { Vector3D *otherPosition = *( mPlantPositions.getElement( i ) ); double distance = inPosition->getDistance( otherPosition ); if( distance <= minDistance ) { minDistance = distance; closestPlant = otherPlant; } } } return closestPlant; } char World::canPlant( Vector3D *inPosition ) { if( ! mSoilMap.isInBounds( inPosition ) ) { // can't plant in water return false; } // do not ignore poisoned plants, // since we can't plant too close to them Plant *plant = getClosestPlant( inPosition, NULL, true, true ); if( plant == NULL ) { return true; } else { Vector3D *position = getPlantPosition( plant ); double distance = position->getDistance( inPosition ); delete position; if( distance >= mMinPlantingDistance ) { return true; } } return false; } Vector3D *World::getClosestWater( Gardener *inGardener ) { Vector3D *position = getGardenerPosition( inGardener ); Vector3D *returnValue = mSoilMap.getClosestBoundaryPoint( position ); delete position; return returnValue; } Vector3D *World::getClosestLand( Vector3D *inPosition ) { if( mSoilMap.isInBounds( inPosition ) ) { // this is a land point return new Vector3D( inPosition ); } else { // closest land to a water point return mSoilMap.getClosestBoundaryPoint( inPosition ); } } Vector3D *World::getRandomLandPoint() { double x = globalRandomSource.getRandomBoundedDouble( mCornerA.mX, mCornerB.mX ); double y = globalRandomSource.getRandomBoundedDouble( mCornerA.mY, mCornerB.mY ); Vector3D *returnValue = new Vector3D( x, y, 0 ); if( isInWater( returnValue ) ) { // find a land point near this water point Vector3D *newReturnValue = mSoilMap.getClosestBoundaryPoint( returnValue ); delete returnValue; returnValue = newReturnValue; } // else we picked a land point return returnValue; } Vector3D *World::getPlotCenter( Gardener *inGardener ) { Vector3D *a, *b; getGardenerPlot( inGardener, &a, &b ); Vector3D *returnValue = NULL; if( a != NULL && b != NULL ) { returnValue = getRectangleCenter( a, b ); delete a; delete b; } return returnValue; } void World::removePlant( Plant *inPlant ) { int index = mPlants.getElementIndex( inPlant ); if( index != -1 ) { mPlants.deleteElement( index ); delete *( mPlantPositions.getElement( index ) ); mPlantPositions.deleteElement( index ); mLastPlantTender.deleteElement( index ); delete inPlant; } // else this plant not managed by this world } void World::giveFruit( Gardener *inGiver, Gardener *inReceiver, Fruit *inFruit ) { Vector3D *startPosition = getGardenerPosition( inGiver ); // true in case it doesn't reach its destination char destroyObject = true; FruitFlyingObject *flyingObject = new FruitFlyingObject( inGiver, inFruit, destroyObject, inReceiver, startPosition ); delete startPosition; mFlyingObjects.push_back( flyingObject ); } /** * Special FlyingObject implementation for mating. * * When object reaches destination, that gardener becomes pregnant. */ class MatingFlyingObject : public FlyingObject { public: /** * Same as constructor for FlyingObject, except: * * @param inSourceGardener the source of the offspring object. */ MatingFlyingObject( Gardener *inSourceGardener, DrawableObject *inObject, char inDestroyObject, Gardener *inDestinationGardener, Vector3D *inStartPosition ) : FlyingObject( inObject, inDestroyObject, inDestinationGardener, inStartPosition ), mSourceGardener( inSourceGardener ) { } virtual void reachedDestination() { // no longer need to destroy object, since we're passing // it to destination gardener mDestroyObject = false; mDestinationGardener->setPregnant( (Gardener*) mObject ); // no longer target of pregnancy---actually pregnant globalWorld->cancelTargetOfPregnancy( mDestinationGardener ); // check if other gardeners should get mad at source gardener // first, make sure source gardener still exists Vector3D *sourceGardenerPosition = globalWorld->getGardenerPosition( mSourceGardener ); if( sourceGardenerPosition != NULL ) { // source gardener still in world int numGardeners; Gardener **allGardeners = globalWorld->getAllGardeners( &numGardeners ); for( int g=0; gisDead() ) { // another gardener (a third party to this mating) // who is its most liked? Gardener *mostLiked = gardener->getMostLikedGardener(); if( mostLiked == mDestinationGardener ) { // mSourceGardener mated with gardener's most liked // this makes gardener angry gardener->getAngry( mSourceGardener ); Vector3D *gardenerPosition = globalWorld->getGardenerPosition( gardener ); globalWorld->flyEmotionIcon( gardener, mSourceGardener, new DislikeIcon(), // normal size icon // for angry false ); delete gardenerPosition; } } } delete [] allGardeners; delete sourceGardenerPosition; } } protected: Gardener *mSourceGardener; }; void World::mateGardeners( Gardener *inParentA, Gardener *inParentB ) { Gardener *offspringOfA = new Gardener( inParentA, inParentB ); Gardener *offspringOfB = new Gardener( inParentB, inParentA ); Vector3D *startPositionOffspringOfA = getGardenerPosition( inParentB ); Vector3D *startPositionOffspringOfB = getGardenerPosition( inParentA ); // true in case it doesn't reach its destination char destroyObject = true; MatingFlyingObject *flyingObjectToA = new MatingFlyingObject( inParentB, offspringOfA, destroyObject, inParentA, startPositionOffspringOfA ); delete startPositionOffspringOfA; mFlyingObjects.push_back( flyingObjectToA ); MatingFlyingObject *flyingObjectToB = new MatingFlyingObject( inParentA, offspringOfB, destroyObject, inParentB, startPositionOffspringOfB ); delete startPositionOffspringOfB; mFlyingObjects.push_back( flyingObjectToB ); // track that these are targets int indexA = mGardeners.getElementIndex( inParentA ); int indexB = mGardeners.getElementIndex( inParentB ); *( mGardenerTargetOfPregnancy.getElement( indexA ) ) = true; *( mGardenerTargetOfPregnancy.getElement( indexB ) ) = true; } char World::isTargetOfPregnancy( Gardener *inGardener ) { int index = mGardeners.getElementIndex( inGardener ); return *( mGardenerTargetOfPregnancy.getElement( index ) ); } void World::cancelTargetOfPregnancy( Gardener *inGardener ) { int index = mGardeners.getElementIndex( inGardener ); *( mGardenerTargetOfPregnancy.getElement( index ) ) = false; } Gardener *World::getNextUserControllableGardener( Gardener *inGardenerToSkip ) { int i; // move each moving gardener int numGardeners = mGardeners.size(); for( i=0; iisDead() && gardener != inGardenerToSkip ) { if( gardener->mUserCanControl ) { return gardener; } } } return NULL; } // for testing // double timeSinceLastPortalUpgrade = 0; void World::passTime( double inTimeDeltaInSeconds ) { int i; Vector3D upVector( 0, 1, 0 ); // move each moving gardener int numGardeners = mGardeners.size(); for( i=0; iisFrozen() && ( !gardener->isDead() || gardener->isGhost() ) ) { Vector3D *position = *( mGardenerPositions.getElement( i ) ); Vector3D *desiredPosition = gardener->getDesiredPosition(); // check the destination that our rotation was set for Vector3D *lastDesiredPosition = *( mGardenerDestinationForRotation.getElement( i ) ); if( ! lastDesiredPosition->equals( desiredPosition ) ) { // new destination // our rotation not complete // we need to rotate again *( mGardenerRotationsComplete.getElement( i ) ) = false; } if( ! position->equals( desiredPosition ) ) { // not there yet // turn desired position into a scaled motion vector // vector representing the full path from current position to // desired position Vector3D travelVector( desiredPosition ); travelVector.subtract( position ); Vector3D motionVector( travelVector ); motionVector.normalize(); Angle3D *currentAngle = *( mGardenerRotations.getElement( i ) ); // don't worry about angle once we are done rotating // and have started moving char rotationAlreadyComplete = *( mGardenerRotationsComplete.getElement( i ) ); Angle3D *desiredAngle = upVector.getZAngleTo( &motionVector ); motionVector.scale( inTimeDeltaInSeconds * mGardenerVelocity ); if( motionVector.getLength() > travelVector.getLength() ) { // overshooting // truncate motionVector.setCoordinates( &travelVector ); } if( !rotationAlreadyComplete && currentAngle->mZ != desiredAngle->mZ ) { // rotate more Angle3D rotation( desiredAngle ); rotation.subtract( currentAngle ); if( fabs( rotation.mZ ) > M_PI ) { // more than 180 // rotate in other direction instead (shorter) if( rotation.mZ > 0 ) { rotation.mZ -= 2 * M_PI; } else if( rotation.mZ < 0 ) { rotation.mZ += 2 * M_PI; } } double maxRotationThisStep = inTimeDeltaInSeconds * mGardenerRotationVelocity; if( rotation.mZ < 0 ) { maxRotationThisStep = -maxRotationThisStep; } if( fabs( rotation.mZ ) > fabs( maxRotationThisStep ) ) { // truncate rotation.mZ = maxRotationThisStep; } currentAngle->add( &rotation ); if( fabs( currentAngle->mZ ) > M_PI ) { if( currentAngle->mZ < 0 ) { currentAngle->mZ += 2 * M_PI; } else if( currentAngle->mZ > 0 ) { currentAngle->mZ -= 2 * M_PI; } } } if( currentAngle->mZ == desiredAngle->mZ || rotationAlreadyComplete ) { // facing right direction // move // first flag rotation as complete // thus, we avoid correcting our rotation once // we have started moving // This avoids problems with round-off errors // toward the end of our trip when our remaining motion // vector is very short *( mGardenerRotationsComplete.getElement( i ) ) = true; // remember the destination that this rotation // was set for (incase the destination changes mid-move) lastDesiredPosition->setCoordinates( desiredPosition ); // skip this for now if( false ) { // make sure move doesn't take us out of bounds Vector3D newPosition( position ); newPosition.add( &motionVector ); if( mSoilMap.isInBounds( &newPosition ) ) { // move there position->add( &motionVector ); } else { // switch new position to closest in-bound position Vector3D *closePoint = mSoilMap.getClosestBoundaryPoint( &newPosition ); // gardener->setDesiredPosition( closePoint ); position->setCoordinates( closePoint ); delete closePoint; } } else { // allowed to move anywhere position->add( &motionVector ); } } delete desiredAngle; gardener->setMoving( true ); } else { gardener->setMoving( false ); // once we reach our destination, we prepare for the next // rotation *( mGardenerRotationsComplete.getElement( i ) ) = false; } delete desiredPosition; // set emotions based on closest gardener // first, set default gardener->setEmotionDisplay( 0 ); Gardener *closest = getClosestGardener( gardener ); if( closest != NULL ) { double likeMetric = gardener->getLikeMetric( closest ); // convert to -1, 1 range likeMetric = (likeMetric - 0.5 ) * 2; // adjust according to distance double distance = getGardenerDistance( gardener, closest ); double maxDistance = 20; if( distance > maxDistance ) { likeMetric = 0; } else { likeMetric *= ( maxDistance - distance ) / maxDistance; } gardener->setEmotionDisplay( likeMetric ); } gardener->passTime( inTimeDeltaInSeconds ); // check if gardener passing through portal if( !gardener->isDead() && mPortal != NULL && mPortal->isOpen() && position->getDistance( &mPortalPosition ) <= mMinPlantingDistance ) { // tell gardener to stop moving gardener->setDesiredPosition( position ); // match drawing z position for gardeners Vector3D startPosition( position ); startPosition.mZ = mGardenerZ; mPortal->sendGardener( gardener, &startPosition, *( mGardenerRotations.getElement( i ) ) ); } // check if closest plant is poisoned // in other words, gardener standing in poison Plant *plant = getClosestPlant( position, NULL, true, true ); if( plant != NULL && plant->getPoisonStatus() >= 1 ) { Vector3D *plantPosition = getPlantPosition( plant ); if( plantPosition->getDistance( position ) <= mMinPlantingDistance ) { // gardener standing in poison circle gardener->poisonFruit(); } delete plantPosition; } } } int numPlants = mPlants.size(); for( i=0; ipassTime( inTimeDeltaInSeconds ); if( mPortal != NULL && plant->getPoisonStatus() >= 1 ) { // check if this poisoned plant should destroy the portal Vector3D *plantPosition = *( mPlantPositions.getElement( i ) ); if( plantPosition->getDistance( &mPortalPosition ) <= mMinPlantingDistance ) { // portal center in poison delete mPortal; mPortal = NULL; } } } // move flying objects double flyingObjectVelocity = 1.5 * mGardenerVelocity; i=0; while( i < mFlyingObjects.size() ) { FlyingObject *object = *( mFlyingObjects.getElement( i ) ); Vector3D *motionDirection = getGardenerPosition( object->mDestinationGardener ); // consider gardener height motionDirection->mZ = mGardenerZ; double distanceToDestination = motionDirection->getDistance( object->mCurrentPosition ); motionDirection->subtract( object->mCurrentPosition ); motionDirection->normalize(); motionDirection->scale( flyingObjectVelocity * inTimeDeltaInSeconds ); if( motionDirection->getLength() >= distanceToDestination ) { // this last hop takes us there // call destination hook object->reachedDestination(); // get rid of this flying object delete object; mFlyingObjects.deleteElement( i ); // don't increase i } else { // this move will not pass destination object->mCurrentPosition->add( motionDirection ); // move on to next index i i++; } delete motionDirection; } // move clouds mCloudPosition += mCloudMotionRate * inTimeDeltaInSeconds; if( mPortal != NULL ) { mPortal->passTime( inTimeDeltaInSeconds ); /* // for testing timeSinceLastPortalUpgrade += inTimeDeltaInSeconds; if( timeSinceLastPortalUpgrade >= 10 ) { mPortal->upgrade(); timeSinceLastPortalUpgrade = 0; } */ } } void World::draw() { // draw soil map Vector3D dummy( 0, 0, 0 ); mSoilMap.draw( &dummy, 1 ); /* // draw some grid lines for motion reference glLineWidth( 2 ); glColor4f( 1, 0.5, 0, 0.25 ); double xStart, xEnd, yStart, yEnd; xStart = mCornerA.mX; yStart = mCornerA.mY; xEnd = mCornerB.mX; yEnd = mCornerB.mY; for( double x=xStart; x<=xEnd; x+=10 ) { glBegin( GL_LINES ); { glVertex2d( x, yStart ); glVertex2d( x, yEnd ); } glEnd(); } for( double y=yStart; y<=yEnd; y+=10 ) { glBegin( GL_LINES ); { glVertex2d( xStart, y ); glVertex2d( xEnd, y ); } glEnd(); } */ // draw any visible plots glLineWidth( 1 ); int numGardeners = mGardeners.size(); int i; for( i=0; iisPlotHidden() && ! gardener->isDead() ) { Vector3D *cornerA, *cornerB; cornerA = *( mGardenerPlotsCornerA.getElement( i ) ); cornerB = *( mGardenerPlotsCornerB.getElement( i ) ); if( cornerA != NULL && cornerB != NULL ) { Color *color = gardener->getColor(); // draw a box for plot glBegin( GL_LINE_LOOP ); { glColor4f( color->r, color->g, color->b, 1 ); glVertex2d( cornerA->mX, cornerA->mY ); glVertex2d( cornerA->mX, cornerB->mY ); glVertex2d( cornerB->mX, cornerB->mY ); glVertex2d( cornerB->mX, cornerA->mY ); } glEnd(); glBegin( GL_QUADS ); { glColor4f( color->r, color->g, color->b, 0.1 ); glVertex2d( cornerA->mX, cornerA->mY ); glVertex2d( cornerA->mX, cornerB->mY ); glVertex2d( cornerB->mX, cornerB->mY ); glVertex2d( cornerB->mX, cornerA->mY ); } glEnd(); delete color; } } } // draw plants int numPlants = mPlants.size(); // set color of ground according to water status Color dryColor( 0, 0, 0, 0.3 ); Color wetColor( 0, 0, 1, 0.5 ); // factor in poison status Color poisonedColor( 0, 0, 0, 0.9 ); for( i=0; igetWaterStatus(); if( plant->isFullGrown() ) { // don't show water for full grown plants waterLevel = 0; } Color *blendedColor = Color::linearSum( &wetColor, &dryColor, waterLevel ); double poisonLevel = plant->getPoisonStatus(); Color *blendedColor2 = Color::linearSum( &poisonedColor, blendedColor, poisonLevel ); delete blendedColor; setGLColor( blendedColor2 ); delete blendedColor2; drawBlurCircle( position, mMinPlantingDistance ); } // draw plant parts at various z depths for proper overlapping // (cannot use z-buffering here because plant leaves use transparent // textures) for( double z=0.5; z>-5; z-=0.5 ) { for( i=0; idraw( position, 8, z, z-0.499 ); } } // finally, draw brown cicles over plants that are out of water for( i=0; igetWaterStatus(); // factor in poison level double poisonLevel = plant->getPoisonStatus(); // don't draw at all for full grown plants // or for plants that have more than 50% water // or for fully-poisoned plants if( ! plant->isFullGrown() && waterLevel < 0.5 && poisonLevel < 1 ) { // becomes visible as plant needs water glColor4f( 0.5, 0.5, 0.1, ( 0.5 - waterLevel ) * 2 * 0.75 ); // gets bigger as plant needs more water double radius = mMinPlantingDistance * ( 0.5 - waterLevel ); // shrinks with poisoning radius *= ( 1 - poisonLevel ); drawBlurCircle( position, radius ); } } // draw portal // draw gardeners above everything else for( i=0; iisFrozen() && ! gardener->isDead() ) { Vector3D drawPosition( *( mGardenerPositions.getElement( i ) ) ); drawPosition.mZ = mGardenerZ; gardener->draw( &drawPosition, *( mGardenerRotations.getElement( i ) ), 4 ); } } if( mPortal != NULL ) { // draw all layers of portal, except part above clouds mPortal->draw( &mPortalPosition, 8, 1, -4.9 ); } // flying objects int numFlyingObjects = mFlyingObjects.size(); for( i=0; imObject; double scale = 1; if( flyingObject->isLarge() ) { scale = 3; } drawObject->draw( flyingObject->mCurrentPosition, scale ); } // last, draw highlight if( mHighlightPosition != NULL ) { glColor4f( 1, 0, 0, 0.25 ); // highlight brightens only //glBlendFunc( GL_SRC_ALPHA, GL_ONE ); drawBlurCircle( mHighlightPosition, mMinPlantingDistance ); // back to normal blend function //glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } if( Features::drawClouds ) { // last, cover with clouds // stretch twice as large as underlying world // put above everything double height = -5; // multiple layers to disguise tiling but still give some detail int numLayers = 2; double textureMappingRadii[2]; textureMappingRadii[0] = 7; textureMappingRadii[1] = 3; double textureAlphas[2]; textureAlphas[0] = 0.125; textureAlphas[1] = 0.25; mCloudTexture->enable(); for( i=0; idisable(); } if( mPortal != NULL ) { // draw portal portions above clouds mPortal->draw( &mPortalPosition, 8, -4.91, -30 ); } } char World::isInRectangle( Vector3D *inPosition, Vector3D *inCornerA, Vector3D *inCornerB ) { if( // x between a and b ( inPosition->mX <= inCornerA->mX && inPosition->mX >= inCornerB->mX || inPosition->mX >= inCornerA->mX && inPosition->mX <= inCornerB->mX ) && // y between a and b ( inPosition->mY <= inCornerA->mY && inPosition->mY >= inCornerB->mY || inPosition->mY >= inCornerA->mY && inPosition->mY <= inCornerB->mY ) ) { return true; } else { // outside return false; } } char World::getRangeIntersection( double inStartA, double inEndA, double inStartB, double inEndB, double *outStart, double *outEnd ) { // make sure start <= end for each range if( inStartA > inEndA ) { // swap double temp = inStartA; inStartA = inEndA; inEndA = temp; } if( inStartB > inEndB ) { // swap double temp = inStartB; inStartB = inEndB; inEndB = temp; } // pick biggest start if( inStartA > inStartB ) { *outStart = inStartA; } else { *outStart = inStartB; } // pick smallest end if( inEndA < inEndB ) { *outEnd = inEndA; } else { *outEnd = inEndB; } if( *outStart < *outEnd ) { return true; } else { // no intersection return false; } } void World::getRectangleIntersection( Vector3D *inFirstCornerA, Vector3D *inFirstCornerB, Vector3D *inSecondCornerA, Vector3D *inSecondCornerB, Vector3D **outCornerA, Vector3D **outCornerB ) { *outCornerA = NULL; *outCornerB = NULL; // first, find x intersection double firstXStart = inFirstCornerA->mX; double firstXEnd = inFirstCornerB->mX; double secondXStart = inSecondCornerA->mX; double secondXEnd = inSecondCornerB->mX; double xStart, xEnd; char xIntersects = getRangeIntersection( firstXStart, firstXEnd, secondXStart, secondXEnd, &xStart, &xEnd ); if( ! xIntersects ) { return; } // else x intersects // get y intersection double firstYStart = inFirstCornerA->mY; double firstYEnd = inFirstCornerB->mY; double secondYStart = inSecondCornerA->mY; double secondYEnd = inSecondCornerB->mY; double yStart, yEnd; char yIntersects = getRangeIntersection( firstYStart, firstYEnd, secondYStart, secondYEnd, &yStart, &yEnd ); if( ! yIntersects ) { return; } // both x and y intersect *outCornerA = new Vector3D( xStart, yStart, 0 ); *outCornerB = new Vector3D( xEnd, yEnd, 0 ); } double World::getRectangleArea( Vector3D *inCornerA, Vector3D *inCornerB ) { return fabs( inCornerA->mX - inCornerB->mX ) * fabs( inCornerA->mY - inCornerB->mY ); } Vector3D *World::getRectangleCenter( Vector3D *inCornerA, Vector3D *inCornerB ) { return new Vector3D( 0.5 * ( inCornerA->mX + inCornerB->mX ), 0.5 * ( inCornerA->mY + inCornerB->mY ), 0 ); } void World::angerOthers( Gardener *inGardener, Vector3D *inPosition ) { // first check if this is in any other gardener's plot int numGardeners = mGardeners.size(); for( int i=0; igetAngry( inGardener ); flyEmotionIcon( otherGardener, inGardener, new DislikeIcon() ); } } } } void World::flyEmotionIcon( Gardener *inSource, Gardener *inTarget, DrawableObject *inIcon, char inLarge ) { Vector3D *startPosition = getGardenerPosition( inSource ); FlyingObject *object = new FlyingObject( inIcon, true, inTarget, startPosition, inLarge ); delete startPosition; mFlyingObjects.push_back( object ); } void World::setHighlightPosition( Vector3D *inPosition ) { if( inPosition == NULL ) { if( mHighlightPosition != NULL ) { delete mHighlightPosition; } mHighlightPosition = NULL; } else { if( mHighlightPosition != NULL ) { mHighlightPosition->setCoordinates( inPosition ); } else { mHighlightPosition = new Vector3D( inPosition ); } } } void World::augmentPortal( Vector3D *inPosition, Gardener *inAugmenter ) { if( mPortal != NULL ) { if( inPosition->getDistance( &mPortalPosition ) > mMinPlantingDistance ) { // too far away to augment existing portal } else { // close enough to augment mPortal->upgrade( inAugmenter ); } } if( mPortal == NULL ) { // open a new portal and augment it mPortal = new Portal(); mPortalPosition.setCoordinates( inPosition ); mPortal->upgrade( inAugmenter ); } } char World::isPortalOpen() { if( mPortal == NULL ) { return false; } else { return mPortal->isOpen(); } } char World::isPortalClosed() { if( mPortal == NULL ) { return false; } else { return mPortal->isClosed(); } } Cultivation_9+dfsg1_UnixSource/game2/gameSource/FlyingObject.h0000640000175000017500000000356310544255127023254 0ustar pabspabs/* * Modification History * * 2006-July-24 Jason Rohrer * Created. */ #ifndef FLYING_OBJECT_INCLUDED #define FLYING_OBJECT_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "DrawableObject.h" #include "Gardener.h" /** * Wrapper class for objects that fly toward a gardener. * * @author Jason Rohrer */ class FlyingObject { public: /** * Constructs a wrapper. * * @param inObject the object to draw. * @param inDestroyObject true if this class should handle * destruction of inObject. * @param inDestinationGardener where the object is flying to. * Destroyed by caller after this class destroyed. * @param inStartPosition the start position of the object. * Destroyed by caller. * @param inLarge set to true to make icon larger than normal. * Defaults to false. */ FlyingObject( DrawableObject *inObject, char inDestroyObject, Gardener *inDestinationGardener, Vector3D *inStartPosition, char inLarge = false ); virtual ~FlyingObject(); /** * Hook to call when this object reaches its destination. * * Should be overridden by sub classes to implement custom * destination behavior. */ virtual void reachedDestination(); DrawableObject *mObject; Gardener *mDestinationGardener; Vector3D *mCurrentPosition; /** * Gets whether icon should be drawn large. * * @return true if icon should be drawn large. */ char isLarge() { return mLarge; } protected: char mDestroyObject; char mLarge; }; #endif Cultivation_9+dfsg1_UnixSource/game2/Makefile.MacOSX0000640000175000017500000000165610450236507021161 0ustar pabspabs# # Modification History # # 2006-June-27 Jason Rohrer # Created. Adapted from Transcend project. # ## # The common MacOSX portion of Makefiles. # Should not be made manually---used by configure to build Makefiles. ## # __mac__ to trigger certain mac-specific coping code # paths to GL and GLUT headers PLATFORM_COMPILE_FLAGS = -DBSD -D__mac__ -I/System/Library/Frameworks/OpenGL.framework/Headers -I/System/Library/Frameworks/GLUT.framework/Headers # various frameworks to support OpenGL, GLUT, and portaudio PLATFORM_LINK_FLAGS = -framework Cocoa -framework OpenGL -framework GLUT -framework CoreAudio -framework AudioToolbox ${PORT_AUDIO_PATH}/lib/libportaudio.a # Nothing special for OS X here GXX = g++ LINK_FLAGS = ## # Platform-specific minorGems file path prefixes ## PLATFORM = Linux PLATFORM_PATH = linux TIME_PLATFORM = Unix TIME_PLATFORM_PATH = unix DIRECTORY_PLATFORM = Unix DIRECTORY_PLATFORM_PATH = unix Cultivation_9+dfsg1_UnixSource/game2/documentation/0000750000175000017500000000000011401021171021252 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/documentation/changeLog.txt0000640000175000017500000000666711401020574023730 0ustar pabspabsVersion 9 2010-May-31 --Improved plot dragging behavior when player drops second corner of plot on top of one of the GUI panels. --Fixed bugs that let other gardeners interact with player's gardener after it was dead. --Locked max frame rate down to 80fps to reduce CPU usage on fast computers. --Added command-line switches for configure script (to skip interactive menu) by Jose Jorge. --Added Portuguese translation by Jose Jorge. --Fixed random number generation on 64-bit platforms (thanks to Kevin Fan). --Got working against latest minorGems. --Fixed string warnings thrown by recent GCC versions. Version 8 2007-August-9 --Fixed linear segmentation of soul trails in larger immortal images. Soul trails are now smooth curves in all image sizes. --Included French translation in distribution. --Added sound effects for many in-game actions. Version 7 2006-December-27 --Fixed missing tool-tips after restart, and associated restart crash. --Changed to mouse dragging for plot selection. --Added a pause button. --Improved font. --Improved button graphics. --Improved appearance of user interface bars. --Gardeners now get angry if someone mates with a gardener that they like a lot. --Added a tutorial. Version 6 2006-November-26 --Fixed some compiler warnings. --Set a limit of 10 stored items. --Added poisoning of fruit when standing on poisoned ground. --Added a Lead button. --Fixed destination position of flying objects, like harvested fruit, to match gardener height. --Added gates to immortality. --Added tool-tips for buttons. Version 5 2006-October-13 --Fixed a potential crash at startup. Version 4 2006-October-10 --Fixed a crash on exit. --Reduced music sample rate from 44100 to 11025 Hz to improve performance. --Fixed a crash when an orphaned baby finishes growing. --Improved selection of least/most-liked gardeners when there is a tie. --Added a limit on plot size for AI-controlled gardeners. Version 3 2006-October-6 --Increased contrast of clouds to make view less hazy. --Fixed bug that caused highlighted fruit highlighting to differ from harvested fruit. --Added a features.txt file for disabling graphical details on slower systems. --Rewrote AI system using probabilistic behavior selection. --Added genetic influences in behavior selection. --Added plot abandoning behavior. --Improved music pitches. --Fixed crash when a partially-grown baby's parent dies. --Fixed a music loudness bug when the gardener population shrinks. --Made water pickup and dumping smoother. --Fixed an eye drawing bug. --Turned on compiler optimizations and turned off debugging for builds. Version 2 2006-September-20 --Added genetically-generated music for each gardener. --Added clouds. --Fixed bug with plant overlapping. --Added poison action. --Fixed a bug that sometimes caused gardeners to rotate randomly at the end of a move. --Fixed a bug that caused the island shape to be the same after pressing the restart button. --Improved flower colors. Version 1 2006-September-13 --Fixed bug where AI tries to mate with an already-pregnant partner. --Fixed a bug where mating can be triggered multiple times while the mating cells are in flight, resulting in a machine-gun stream of mating cells. --Fixed bug causing flower petals to be too narrow. --Improved leaf textures to remove pixelation. Version 0 2006-September-9 --Initial release (Submitted to IGF)Cultivation_9+dfsg1_UnixSource/game2/documentation/igf/0000750000175000017500000000000011401021140022013 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/documentation/igf/submission.txt0000640000175000017500000001607110502516653025000 0ustar pabspabsJason Rohrer 28 93 Elm St. Short: Cultivation explores the social interactions within a gardening community. You lead one family of gardeners, starting with a single individual, and wise choices can keep your genetic line from extinction. While breeding plants, eating, and mating, your actions impact your neighbors, and the social balance sways between conflict and compromise. Credits: All parts of this game were conceived, designed, programmed, tested, and packaged by Jason Rohrer. Platforms: Windows 95/98/NT/2000/XP Mac OS X 10.2 and higher Linux Basic Requirements: --OpenGL Support --Mouse Minimum Tested System: --200 MHz CPU --Any 3D Card that accelerates OpenGL (test system: Voodoo3) Recommended System: --400 MHz CPU or faster --A fast 3D card that accelerates OpenGL Dev Time: 3 Months Number of developers: 1 part time (15 or so hours per week, plus a few late nights) 1 total Bio: Jason Rohrer is an independent programmer, scientist, artist, and activist. He holds two degrees in computer science from Cornell University. As a peer-to-peer networking developer working in open-source, he has created several popular programs over the past five years, including konspire2b and MUTE. His first video game, Transcend, was entered into the 2005 IGF and included on the Moondance Independent Games compilation. Cultivation is his second video game project. long description: Genres: --Social simulation --Cross-breeding sandbox --Real-time strategy Overview of gameplay The core mechanic in Cultivation is land allocation. Each gardener picks a plot of land, and a gardener can only plant, tend, and harvest from its chosen plot. Plots can overlap, putting portions of land and established plants in contention, and this is the main source of conflict in Cultivation. Gardeners can resize or move their plots at will, so land squabbles can result in compromise or revenge. Within this framework, a variety of play styles are possible, including cooperative, competitive, and antagonistic. Each choice has consequences within the social network---you can become well-liked or widely-hated. Additional gameplay elements include planting, watering, and harvesting; cross-breeding plants to isolate desirable traits; eating to maintain a balance of nutrients; gift-giving to bolster friendships; mating to pass on genetic heritage; and caring for offspring. Other features All graphics in Cultivation are procedurally generated at playtime. Plants and gardeners are constructed using genetic mappings and algorithms that emulate biological growth patterns. Thus, each plant and gardener is unique, and mating produces offspring that are genetic and visual combinations of the parents. Plant growth parameters and gardener behavior are also mapped from genetics and passed on to offspring. Each game of Cultivation looks unique and presents new strategic challenges. Extra text: Right before I designed Cultivation, I read Raph Koster's "Theory of Fun" book. I was inspired by his ideas about how games can become art. According to Koster, a work in any medium becomes art when it ceases to have one "right answer" or obvious interpretation. For games to become art, they must give players multiple viable play choices and not specify that only one choice is right. After I had some colorful human interactions in the real-world community of my town, I became interested in the balance between self-interest and compromise. Sometimes both opposing sides need to "give a little" to reach a common ground. This seemed like the perfect kind of problem---certainly with no right answer. How could I abstract these complex interactions into a game? A community of gardeners, with land disputes, seemed like an appropriate metaphor that still retained the important elements of self-interest and compromise. Submitted at 12:42 AM EST (Sept 9, 2006) Entry Number:152 Credit Card Transaction Reference:VPFE0B4D6C18 Please upload your file in the following directory!: /152/ Hostname: upload.minnickweb.com User: igfcontest Password: igfftpentry Response letter: If you're getting this message, that's because you've entered the main competition for the 2007 Independent Games Festival (http://www.igf.com). My name's Simon Carless, and I'm the Chairman for the IGF at the CMP Game Group, and I just wanted to send a quick extra email out to welcome you to the IGF and clarify a couple of points that have come up during the submission process: - Again, thanks to all that entered. We have a record amount of entries (again!) this year, and many of the 141 entered games seem to be exceptionally high quality, so basically - you guys rock! The full list of entries is up now on the IGF site: http://www.igf.com/php-bin/entries2007.php - tell us if you see any issues with how your game is listed. In addition, here's our final judge list for this year: http://www.igf.com/judges.html - we've added a lot of new and adorable industry judges to round out our roster, which is great news for everyone. - If you'd like to upload updates to your game and it was submitted via FTP, then you can upload a new version at any time, and the judges will be instructed to get the latest version when they start judging. However, we can't guarantee exactly when judges will get the .EXE, obviously - but they haven't yet, for what it's worth, so updates in the next few days will probably be grabbed by them. It's possible that updates uploaded in 6 weeks time will be grabbed too, of course - just upload your game updates into your numbered directory, and please put the date of the new version in the filename (eg game091405.exe), so it's obvious. - As with last year, we are going to _try_ to get anonymous feedback from the judges back to all contestants, even those who don't make it as finalists. Obviously, we can't guarantee that all judges will have time to write feedback as well as rate the game, and the judges will be anonymous, but hopefully we will be able to communicate some feedback to you, and if it's nice (heh!), you will be able to use it in promotional material for your game - eg. 'I loved this game! 2007 IGF Judge'. Last year, we got about 80-90% of the games receiving some amount of feedback. - For those who don't know, December 9th 2006 is the date which the finalists are announced on, so watch the website then - we will also start emailing you, because all finalists win either one Giga pass or two Classic passes to GDC 2076, which is in San Jose from March 5th-9th 2007, and finalists are allowed to make one final game patch update, and also upload a public demo version (if they can/want) for the Audience Award If there are any problems at this stage of the judging, then please get back to me and my colleague Stephanie Tang (stang@cmp.com) and we'll try to resolve it for you. I'll be away at the Tokyo Game Show from this Thursday (14th), however, so replies may be a little slow during that time. And again - thanks for entering the Independent Games Festival and helping make it the leading indie games festival it is today! Regards, Simon Carless [Chairman, IGF.] scarless@cmp.com 415-947-6145Cultivation_9+dfsg1_UnixSource/game2/documentation/notes/0000750000175000017500000000000011401021140022376 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/documentation/notes/notes.txt0000640000175000017500000024600710656652123024327 0ustar pabspabs July 9, 2006 Done with main coding for first iteration. Problems: X-If two plants are right next to each other, and one is ripe, it can be impossible (or difficult) to position yourself to eat it. Eat button does not become enabled if non-ripe plant is slightly closer. The goal here is to produce release-ready software in each iteration. Thus, before this iteration is complete, we still need: X-A quit button. X-Tweak parameters to make this iteration more fun. x-How-to-play documentation. X-A release-building script. July 10, 2006 In order to make this iteration "fun," there needs to be some kind of challenge that increases as time goes on. Right now, you must simply learn the basic mechanics of the game, and after that, you can play mechanically forever (very boring). What can make the game get harder and harder? One idea: energy usage rate increases as you age. The goal, then, is to stay alive for as long as possible. Added this, and it certainly makes it more fun. The behavior of plants and water does not make sense. Plants should turn brown when they are not growing because of a lack of water. July 13, 2006 Fixed watering plants issue. Added time display. Almost done with release building script. Source build works (and compiles). Still need to test building on Mac and Win32. July 15, 2006 Got builds working for MacOSX, Win32, and source distribution. Tagging all files as game2_iteration_1 July 23, 2006 Starting on Iteration 2. Got fruit storage and eating from storage working. Got UI working for fruit storage. July 24, 2006 Added gift button. To code fruit "flying" from one gardener to another (and also like/dislike icons), started work on FlyingObject class. Envision World managing a series of flying object animations across multiple passTime calls. July 25, 2006 Got flying objects and AI working for fruit exchange. Got flying emotion icons working. July 26, 2006 Got actions based on emotions working (encroachment and gift giving). Pretty much done with Iteration 2. Todo: X-Update How To Play document. X-A bit more testing and tweaking. (none needed) July 27, 2006 Should be no changes for build process. Tagging all files as game2_iteration_2 Now onto iteration 3. So far, interactions with other gardeners do not have consequences. For example, you can sit by yourself, working in your own plot, and "do well" in the game, living for five minutes or more. Being a loner should be a possible strategy, since I want to build a game where there is no one "right answer." However, there should be some trade-offs involved in picking the loner approach. Right now, it might be the sole optimal strategy. There are two places to go from here: 1. Add offspring into the picture, with friendships leading to mating, and each offspring representing another chance for the player. 2. Add variable fruit nutrients (some simple plant genetics, soil types, etc.), which will make friendship useful for facilitating trade. I think option 2 is better, since it will let me explore the core social gameplay mechanic without it being obscured by offspring. The game should be "fun" and interesting in its "single round" incarnation without offspring. Got variable soil done. Started work on seeds. August 3, 2006 Did more work on seeds and plant genetics. Crashes when fruit harvested. Autust 6, 2006 Fixed crash. Got nutrients working. Nearly done with Iteration 3. Still to do: --Tweak a bit X-Make all plots visible --Update how-to documentation. August 7, 2006 Simplified nutrition: --Each fruit now only high in one nutrient (and low in others). Thus, fruits nutrients can be distinguished easily (it is hard for us to see yellow as "high" in both red and green, or light pink to be very high in red and pretty high in both blue and green---we do not understand color mixing on a subconscious level). --Got rid of energy bar. Now all nutrient bars drop together. --Got rid of energy consumption rate increase with aging. Now life time is a fixed length, and low nutrient levels simply accellerate aging. These changes simplify the gameplay and make the game easier to understand without changing fundamental dynamics. These changes amount to getting rid of unessessary, tedious details. Next: Simplify soil parameters (go from continuous to binary). Did this. Again: much better. Next: X-Update AI to deal with nutrition and better deal with soil types. X-Take nutrition into account when giving gifts. X-Some computer-controlled gardeners should take gifting initiative. August 8, 2006 IGF deadline in one month. Forget about AI paying attention to soil types for now. Note that if a gardener is very busy watering (for example, if its plot is far away from the water source), it never has the opportunity to give a gift. The AI code has hard-coded prioritization: make sure we're not hungry, then make sure we have 3 plants, then make sure they are watered, and *then* give gifts or take revenge. In future, this should be rewritten to have dynamic prioritization. Thus, given our current state, we compute a priority score for each possible action and then pursue the action with the highest score. For example, if we *really* like a neighbor, we may put off watering our plants to give a gift. The genetics of an individual can also play a role here. Done with iteration 3. Tagging all files as game2_iteration_3 Still nothing forcing gardeners to interact. Nutrition doesn't feel like enough. There is also no space constraint that makes you *want* to exapand into other gardeners' plots. Right now, plants can be placed very close together. Solution 1: --Add constraint so that plants cannot be planted close together. --After a plant is grown in soil, soil remains infertile --What would balance this out? --Do we want the game to eventually end because of scarce soil resources? --It would certainly force gardeners to interact and fight (over last pieces of fertile soil) but only eventually. Solution 2: --Plants are permanent (in other words, trees). --They cannot be planted close together. --They must be watered until they reach maturity --Then they produce fruit periodically. With solution 2, if you plant a bunch of trees of the same type in your plot, you will eventually need to expand your plot to make room for trees of other types. Also, you could try to "take over" the established trees of other gardeners. New gardeners coming to island will need to fight for empty space to plant trees or try to take over existing trees. Eventually, could add an action for cutting down a tree to make space. Maybe this could tie into the idea of "making a boat" to send offspring to central server. What if land forever infertile after you cut down a tree? Then there is balance between farming and boat-building needs. But this doesn't help us make room for growing new plant varieties. Maybe soil infertile for a short period after cutting down a tree. In next iteration, add basic Solution 2. Another issue: game screen is very chaotic with too many gardeners moving around. It is impossible to follow. Should probably reduce size of island and reduce to 4 other gardeners. August 11, 2006 Reduced num gardeners and island size. Made plants permanent and added a minimum planting distance. Started work on Genetics. Next: test genetics by re-implementing plant parameters using Genetics class. August 13, 2006 Got genetics working for plants. Started work on pregnancy --- maybe done. Working on mating. Idea: --Mating handled by world (mateGardeners) --Flying objects in both directions. --When they reach their destinations, they trigger pregnancy. Hey... would be "cute" if after birth, a baby gardener followed you around for a while as it grew. During that time, half of all food eaten given to baby. Think about this. August 14, 2006 Got flying objects for mating and mating button. Next: X-visual representation of pregnancy progress X-Limits on mating based on social factors X-AI-triggered mating August 15, 2006 Next: X-Upon death, player takes over next offspring. X-Finish discard button implementation. August 20, 2006 Tagging all files as game2_iteration_4 Next iteration: General tweaking, improving graphics, etc. Game should be playable throughout, so no need for iterative process. Also need to be thinking about a name so that a SF website can be set up. Working on graphics for growing plants. Space for plants: --Leaf color --Leaf shape. --Leaf size. --Number of "joints" on main stem axis --Number of branches at each joint --Length of branches (each branch has leaf at end) (Upper branches are shorter than lower) --Fraction of branches that bear fruit (Fruit appears at end of branches) --Fruit nutrition (color) --Fruit shape --Fruit size The hard part here is the "shape" for leaves and fruit. Could be arbitrary polygons, but cannot be filled properly unless convex. Single triangles seem too crude. What about textured quads? Each plant could have parameters that specify (somehow?) which points on a small texture map are filled and which are transparent. ooooooooooo oooooxooooo oxooxxxooxo oxxxoxoxxxo oooxxxxxooo ooooxxxoooo oooooxooooo So, each leaf or fruit would be represented by one "particle" polygon (or a pair of polygons---see subreal source). Use quads for now. Okay... now how do we fill the textures? Maybe with pixel-laying "walkers" that spawn other walkers? Look at substitution systems on page 402 of _A New Kind of Science_ Need to simplify so that it can work iteratively on pixel level. Idea: --Start out with a line of pixels through center of image. --Mark the line with "special" pixels every n pixels. a = starting angle; b = angle increment l = starting length dl = length increment While l >= 1 pixel: { --At each special pixel, make pixel not special and then draw a line at angle a for l pixels --Mark new line with new speical pixels every n pixels a = a + b l = l - dl } Got version of this done with GL line drawing. Seems too slow (each leaf has lots of lines). Also working on version with textures for each leaf. Much faster, and may even look better. For some reason, the rotation is screwed up in the texture version. August 21, 2006 Fixed problem with rotation of texture quads. Got IFS leaf generation in place. Works well with hand-selected parameters, but generally does not produce very interesting leaves with random parameters. Also, IFS can only mimic certain natural forms (like maple leaves and ferns). Something like an elm leaf (oval with teeth) would not be possible. Another idea: Start with seed pixels (in gene-parameterized locations) and "grow" from them, filling in neighboring pixels, until a solid leaf results. Various growing rules could result in a variety of small-scale leaf fetures (for example, does a pixel become filled if it has exactly one filled neighbor, which would result in "branchy" leaves, or when it has at least one filled neighbor, which would result in "blobby" leaves). We could also specify, with genes, that the filling rule changes after a certain number of steps. August 22, 2006 Cellular leaf growth seems promising. Changes from Wolfram: --Look at all pixels in a circular neighborhood when counting filled neighbors. This reduces the "grid effect." Random seed pixels not very interesting, since CA pattern dominates appearance. More interesting idea: A "walker" that lays seed pixels while previously-laid pixels have already started growing. Walker could spawn (branch) other walkers periodically. Tips will have less growing time. Thus, we could get maple-leaf-like shapes, as well as lots of other shapes. Walkers could always start from center of image and move outward. Leaf becomes "finished" as soon as a filled pixel hits an edge. August 23, 2006 Working on cell walkers. Looking great! Lots of beautiful forms (like spirals, tendrils, etc.) Upon a "branch", a walker spawns one other walker. Could add parameter to control how many other walkers are spawned at each branch. To do: X-Tweak parameter range so that interesting shapes are generated most of the time. X-Integrate with cellular growth to "fill in" skeletons created by walkers. August 24, 2006 Getting some very beautiful leaf shapes. Commit now, and then tweak some more. Tweaked some more, found that original walker params are best, and that simplest post-walker cell expansion is best (if one or more neighbors are filled, become filled). Lots of leaf shape variety (due to walker paths). Very nice. To do: X-Clean up code. X-Improve color gradients (should leaf colors be genetic?) x-Map current hand-coded parameter range into plant genes. August 27, 2006 Got genes-to-leaf-shape mapping working. Strange problem: Same seeds (and genes) sometimes generate different leaf shapes. Doesn't happen with test program. Doesn't happen when LeakTracer or ElectricFence turned on. Have a hard-coded random seed parameter that causes it to happen. First walker sometimes goes 46 steps, and sometimes goes 40 steps. When it goes 46 steps, a total of 12 walkers are present at end. For 40 steps, 14 walkers are present. Idea: must have something to do with data from uninitialized memory. Both LeakTrace and EFence init all memory with constant values, so they would cause it not to happen. Some place in walker code, we are relying on uninitialized memory. Fixed it! Two parameters were not being set in spawned walkers: mSpawnIntervalFactor and mSpawnDouble Setting them fixes the variable leaf shape problem, but also might make leaf shapes less interesting (particularly the mSpawnDouble one, since those that spawn double now produce a ton of walkers and a very crowded leaf pattern that dies out quickly before expanding far). Can tweak this later. Tweaked a bit (and also fixed win32 bug) by putting lower limit on mSpawnInterval of 2 (it used to sometimes go down to zero). Still to do for plant graphics: X-Turn z-buffering on so that nearby plants overlap correctly. X-Make start angle for joints random instead of zero (right now, each plant of same type starts out at same angle, so they look "regular") --Other genetic parameters (z height, for example)? X-Some kind of central stems (right now, plants are hollow)? X-Make leaf shape meaningful (small leaf area consume less water to grow but produces fewer fruit...). Need to measure leaf area after growing it with walkers to do this. --Flowers (on stems between leaves?) --Fruit August 28, 2006 Z buffering won't work for plant parts, since leaves use transparent textures (so sorting is necessary anyway). So, implemented a hack sorting mechanism instead (draw lowest layer of all plants, then next layer, etc.) Working on plant joint cap to hide hollow center. Looking better. Bugs: X-Leaf color "jumps" suddenly as soon as partial joint is done." X-Segfaults upon exit after fruit has appeared (segfault in Genetics destructor). Plant joint cap looks fine. Ideas for flowers: --Radial array of petals. --Each petal a single triangle with one vertex at flower center. --Positions of other vertices based on genetics --Color of vertices based on genetics --3 to 8 petals (genetic) --Circular center part of flower can turn into fruit Flower opening: --Actually, "center" vertex should be sitting on border of flower center part. --Other two vertices for petal can morph from flower center to their positions. --While morphing, then can change from a greenish color to their genetic color --This won't work: center petal vertex will still be on border of center part, instead of behind center part, after petal fully opens---won't look good. --Instead: have petals grow out from behind center part. Bud forming: --Can simply increase in size, from zero, with all petals "hidden" behind center part. --After bud reaches full size, flower opens Somehow, flower should turn into a fruit. What will fruit look like? What shape? Made from polygons? Maybe just a shaded circle? Made with some kind of walkers (like leaves)? If fruit is irregular shape, how to make it look "right" coming out of a flower that is facing straight up at the camera? Leaf area should dictate speed of fruit production. August 29, 2006 Got genetics-based flower shape and gradual flower growth working. To do for flowers: X-Flower placement (thinking about placing on each leaf's "first walker" terminal spot). X-Stagger multiple flower growths on same plant (how? timing? some kind of genetic parameter that controls time spacing of new flower buds) --Fruit from flowers... how? August 31, 2006 Possible name: Seeds (still available on SF). Think about it for a day or so. Worked on textures for petals instead of pure polygons. Got flowers placed on leaf terminii. Problem with petal textures: Currently using circular texture (same as for flower center). But petals are triangular, so circular texture is cut off and distorted. Outer tip has smooth edge, but side edges are sharp lines. Better: A texture containing a triangular image with one triangle point near the center of the bottom edge and the other to points near the opposite corners (I use "near" here because texture should be blurred, and we need to leave room for that). Then we can anchor our triangular polygon at the center of the bottom edge and at the two opposite corners. How to fill such a texture: Set all pixels to white. Alpha set by walking through from top and "shrinking" filled line length a bit at each row (set shrink factor so that we hit zero near the bottom center point) September 1, 2006 Got better texture in place for petals: Looks great. Problem: many petals too narrow. Gene mapping makes this likely. New gene: Petal angle. Petal point A and B angles now relative to the main petal angle. Did this. Much better. Another name: Isolation Idea for fruit shapes: Combine circle with a sine-based function to determine the radius of the circle a various points around the circle. Got fruit shapes, and fruit growth mostly working. September 2, 2006 To do: X-Fix so that green is not a nutrient color (blends in with leaves and unripe fruit colors) Yellow should be a nutrient color instead (or another color?) X-Make fruit drawing rotation-aware X-Re-implement fruit harvesting (old code handles only one ripe fruit) (maybe use flying objects?) X-Map fruit progress rate from genetics X-Figure out what to do once fruit reaches full rot. X-Get rid of flowers after fruit grows. X-Tie fruit ripening time to leaf size (big leaves -> fast ripening) September 3, 2006 Names: Seeds Tendril Tendrils Isolation Calyx (too hard to pronounce) Cultivate Culture Propagate Encroachment Ephemeral Entwine Epiphany Equilibrium Equinox Esoteric Esoterra Essence Etiquette Eventually Exotic Extinct Fabric Fertile Flower Inorganic Artificial Simulated Unnatural Stem Branch Branches Radiate Raise Harvest Reciprocity Symbiotic Synthetic Synthesis Sympathy Empathy Tendrils and Thorns Thread Roots Tension Plant Transplant Ultraviolet Unison Idea for gardeners: collections of hollow boxes. Twist to show anger, expand to show friendship. Hollow box texture. Another idea: Overlapping scales. Stack of polygons (rectangles?) at various rotations and scales. Problem: can't see most of gardener this way (interesting genetic variety?) Scale idea best. First, lay down a blurry circle of a given color to represent gardener. Then have a walker, with genetic path parameters, that starts at the center of the gardener and lays scales as it goes. Walker "bounces" off the edge of the underlying circle. Walker dies after laying a set number of scales. Scales can change size and color (based on genetics) as walker goes along. Finally, top gardener off with cartoon eyes and three colored spheres to show nutrients. Got this working, pretty much. Have scales rotating in unison... looks good. To do: --Switch from hard-coded parameters to genetic parameters --Graphics for eyes (gentic eye shapes?) --Graphic for nutrients --Graphic for carrying water --Graphic for pregnancy --Make gardener motion rotation-aware Think about representing emotions with scales and eyes Currently leaning toward the following name: Cultivate or Cultivation September 4, 2006 Settled on "Cultivation" as name. Registering with SourceForge. Public description: Cultivation is a video game about the social and biological interactions within a gardening community. The resources necessary for genetic propagation are tight, and relationships can become tense. Cultivation explores the balances of self-interest, the common good, conflict, and compromise. Too long (296 char, limit 255) New: Cultivation is a game about the interactions within a gardening community. The resources needed for genetic propagation are tight, and relations can become tense. Cultivation explores self-interest, the common good, conflict, and compromise. Full description: Cultivation is a video game. I am developing it using platform-independent C++. Graphics are output through the OpenGL library. System requirements to play Cultivation are quite reasonable: a 3D card that supports OpenGL and a 200 MHz CPU. Cultivation development is nearly complete. I have been storing the source code in a temporary CVS location: http://hcsoftware.cvs.sf.net/hcsoftware/tempProjects/game2/ Why is Cultivation unique? This is a game about a community of gardeners. It is both a social and biological simulation. You control one gardener in the game, and the other community members are controlled by the computer according to their genetic parameters. You and the other gardeners plant seeds, care for the resulting plants, and harvest fruit. Plants can be selectively bred to isolate desirable traits. Land space is limited and must be divided among the gardeners. Gardeners can claim overlapping plots of land. In these conflicts, social factors like compromise and revenge come into play. Social tensions can be eased, and friendships built, by giving gifts. Friendships can culminate in mating and the production of offspring. Your gardener's lifetime is limited, but your can continue playing by controlling your offspring after your gardener dies. Cultivation is also graphically unique. All graphics are procedurally generated, and no binary texturemaps or other bitmaps are shipped with the game. Plant leaves and flowers, as well as the gardeners themselves, come in a virtually limitless variety of appearances, governed by their underlying genetics. Growth patterns and behavior are also controlled by genetics, so each new game presents slightly different strategic challenges. One side effect of procedural generation is that the Cultivation executable is incredibly small. All of that variety fits easily onto a single floppy disk without compression. Why did I develop Cultivation? I was trying to devise a game where there was more than one "right answer" or winning strategy. I wanted to explore conflicts that are more subtle than the violent conflicts portrayed in most modern games. I think that Cultivation succeeds in this direction. Your behavior in the game is up to you. Will your survive through hard work, planting and growing your own fruit, or will you simply free-load and steal from others? What will you do when a neighboring gardener encroaches on your plants and steals your fruit? Will you back off, giving up the plants and moving elsewhere, or fight back by stealing in return? Selected Public Domain license (gulp!) Submitted to SF September 4, 2006 at 11:25AM. Check status here: https://sourceforge.net/my/myprojects.php No email notice given (why not??) Back to work. Better for gardeners: --Rows of scales (walker goes back and forth across gardener base laying scales) --Only differences between gardeners are in scale rotations and colors? Did this. Looks pretty good, but looks very regular (rows). Maybe should modulate rows with sine waves... Did this, looks better. Sine params can be genetic. Added a bone overlay and a skin overlay. Looking rather animal. Bone shape/layout genetic... working on this. Got this all geneticized. Time to work on eyes. Got eyes looking pretty good (with emotion display) Working on gardener rotation. Still need to add rotation support to World Got this working. September 6, 2006 Todo: Graphics for: X-Water carrying X-Nutrition X-Pregnancy X-Aging (idea--- organs turn black...) Next: After birth, offspring should follow you and glean nutrients (every time you eat, you give half to offspring). Offspring doesn't age, but keeps growing. Got following working. Next: Nutrient sharing for following offspring (and see FIXME in Gardener). Did this. Seems to work. Still need to test a bit more. Also, noticed a Segfault on exit after taking over an offspring. September 7, 2006 Tested nutrient sharing, and tweaked nutrient rates more. Testing for leaks. Found a leak in rare circumstance (seems to happen when someone mates with you after death... the offspring is never destroyed... why not? Hard to reproduce.) Following offspring: X-Shouldn't be able to give gift to them or mate with them. Next: better plant water status Got it. Next: Better island graphics. idea: island radius (circle) modulated by landscape function Simplest collision detection: if gardener's next timestep goes outside of boundary, abort that step and truncate gardener's desired position. Making progress here. September 8, 2006 Actually, limiting gardeners to boundary of island in this way won't work, since gardeners get stuck on peninsulas when they try to walk "across" an inlet. This is a pain for human-controlled gardeners, and it won't work at all for AI-controlled gardeners. No time (nor desire) to implement path- finding. Two ideas: 1) Gardeners can walk anywhere, but know to plot/plant only on land. 2) Simple "route around" obstacle avoidance in World::passTime. Limit: spend at most 10 minutes on 2) before giving up and reverting to 1. Deadline 6:08 am. Okay... got 2) somewhat working. Works pretty well most of the time, but still gets stuck sometimes. That will never be okay for AI. So, revert to 1. Excellent self-control (not going off on this tangent too far). Anyway, it helped to test that the boundary functions are actually working. To do for island graphics: X-Change to use seed based on time for island shape and terrain. X-Add getInBounds function to soil map for testing boundary. X-Blur the soil map graphic a bit (right now, it has linear interp artifacts) X-Add getClosestBoundary function to aid GardenerAI in fetching water X-Change so that water can be fetched from anywhere along boundary. X-Add a grit overlay (multi-pass, to avoid seeing tiling?). Checked for memory leaks. Next: polish UI. --Highlight targest of actions when mouse hovers over button. First, work on water button. Finished with: --Plant button --Water button --Harvest button Next: --Eat button (how?) Pac-man icon (the only thing I can think of) Eat button looking good. Gift button? Done (arrow with object to give, plus recipient highlight). Next: panel color Done. Next: Mating button Done Next: Remove clock Next: X-Let "ghost" walk around after death. X-Make discard button nicer X-Make quit button nicer. Better: Start with several seeds, each one is used up when planted. Got everything but AI tweaks in place. Time to work on submission. Got submission in. Got builds made for all platforms and posted to web. Whew! Tag as cultivation_0 September 12, 2006 When testing on a fast computer, found these bugs: Mating, when triggered by AI: --Multiple offspring flyingobjects go at once. Mater is trying to mate over and over as long as isPregnant is not set on the recipient. What happens when all of those offspring objects arrive? Who deletes them? Maybe this was the mysterious memory bug. Easy fix (same as for gift giving): timeSinceLastMating... mate at most once every 5 seconds. --As soon as an AI's pregnancy is finished, it goes back to mate with you again and *succeeds*, replacing (??how??) your existing offspring. Everything else is very smooth and nice. Should fix these pregnancy bugs, though, 'cause they are showstoppers... Looking at GardenerAI, see that we never check if our mating partner is pregnant. Fixed this. Fixed the multiple "machine gun" mating bug too. Test this---if it works, should build and post an update ASAP. Note: the packaging tool for Windows is the FreeExtractor Wizard. Seem to be ready to release v1. Don't tag yet. Test extensively on a fast computer first... look for other bugs like this. Built tentative v1 releases already for Windows and Unix, though. Posted to NCN server. September 13, 2006 Other tiny bugs, might as well fix: X-Flower petals can still be too narrow. X-Leaves look pixelated on larger screens. Blur all channels?? Build a new tentative v1 release. Tag as cultivation_1 September 14, 2006 Starting work on next version. Idea: There needs to be more of a consequence to getting another gardener angry. Something that goes along with the idea of "everybody loses" Current idea: destroy neighbors plants, rendering the land around the plant unusable forever. After enough fighting, entire island runs out of plantable land, and everyone dies. Also, need to work on music. Simplest idea: A music manager that recieves a "song" from each gardener. The song specifies notes to play in a given order on a fixed time grid. Suppose the songs are 20 seconds long, and we want a time grid with 4 ticks per second. Each song is then made up of 80 "notes". A note has a pitch and a duration, and can also specify silence. Copied sound framework from Transcend. Got it to compile and link into Cultivation framework. Now need to work on MusicPlayer. September 17, 2006 Got stereo music placement working. Got songs fading with age. Next: need to derive songs from genetics. Twins should play same song, offspring song should be combination of parent songs. How? September 18, 2006 One idea is for each gardener to have two music parts, one for when standing still (slower notes) and one for when moving (faster, shorter notes). If we can guarantee that, in the wave table, the shorter notes have higher indecies in the note length vector, then we can simply specify a "length factor" when we construct each music part. We could also think about switching from lower to higher notes, maybe when carrying water? So, we could have four music parts, selected by the binary states of two factors: --moving? --carrying water? Melody encoded with genetics: Each gene value selects a pitch from the note table. Genetic cross would involve selecting each of these four for the offspring from a parent. Also, I'm feeling like the music is sounding very Transcend-like. How can I make it different and more interesting? Don't worry about this for now. What about reverse notes? Some kind of genetic flag? Maybe one per note. Thus, music encoded by 8 gene vectors (two per part, one encoding melody, another flagging reversed notes). Actually, why not just have a single "chance of reverse note" flag per gardener that applies to all melodies? Problem with all of this: no mixing or crossing of melodies. Maybe each melody should have two gene parts, one for the first half, and one for the second half. Better: part A and part B, with a third gene vector indicating how they switch back and forth. BAABB, something like this. Maybe the melody parts should be short, only a few notes long, and then we can let the part selector gene arrange them. Even better: set of MelodyPool genes with more than two melody segments, maybe A, B, C, D, E, and F. Each segment 4 notes long. Then, we have four different part selectors for the four different states. We can flip high/low and fast/slow flags when generating the melodies for each state (to select a pitch range and note length). Part selection genes should be long enough so that they can cover the full song length with the short notes. I.e., if song is 20 seconds long, and short note is 1/8 second, and each part has 4 notes, part selector should specify a series of at least 40 parts. Maybe only have one part selector that is used over and over to generate each of the four "songs." Actually, most pleasant music seems to be made up of 0.5 and 0.25 notes, but we can tweak this later. So, in terms of new genes, we have: 6 melody parts, each a vector of 4 doubles (4 notes) 1 arrangement gene, with enough doubles to select parts to cover entire song 1 gene specifying chance of reverse note. For now, let's make songs 20 seconds long, parts 4 notes long, and short notes 0.25 seconds. Thus, arrangement gene needs to be at least 20 parts long. Make it 25 to be safe. Made changes to genetics. Change MusicPart so that it accepts a melody vector and fast/slow, high/low, and reverse note flags. Almost done implementing this. See FIXME in Gardener.cpp Got this all working, sounds good. Some tweaks: Having 6 melodies to pick from makes pretty complicated songs. Maybe song complexity could vary by gardener. Nice to hook this into one of the other parameters. Maybe eye size: gardeners with big eyes and big pupils have more complicated songs. Did this, sounds good: more complexity variety. Next: quick addtion: clouds Got them working. What about pixelating them for an interesting effect? Looks cool, but should I jump on this bandwagon? More testing needed. See test code in World.cpp. September 19, 2006 No, do not jump on the pixelation bandwagon. X Need to check for memory leaks. Next: implement plant destruction. idea: --black circle flies from gardener to plant. --Plant turns black over 1 second --Plant shrinks to nothing, except for its circle (speed up drawing) Got working w/out flying object. Integrate into AI first and test that. Actually, since AI walks to plant before poisoning it, no need for flying object. Taking revenge by poisoning last tended plant of enemy. To do: X-Fix so that harvesting counts as tending X-Make sure that AI actually initiates poisoning if angry enough X-Fix AI planting spot choice bug. X-Add not to how-to. X-Add note to changelog. Almost ready for another release. But first: X-Make sure music loudness is correct no matter how many gardeners are present. X-Fix "rotate on arrival" motion bug. X-Thread safety issue: MusicPlayer is running (on behalf of SoundPlayer) in a separate thread. What happens when it accesses the global world? What if a gardener dies and is removed while MusicPlayer is accessing that gardeners MusicPart? X-Fix silent "gap" at end of song. Add a global lock that can synchronize between the two threads. In the GL thread, we only need to lock around the main passTime call, since that is where our state changes. In the MusicPlayer thread, we lock around the entire getMoreMusic function. Did this. Seems to make music less smooth on slower platforms. How did this work with Transcend? I guess I didn't think about it. Last step for Version 3 (Slamdance) is to redo the AI. Idea for AI: Priority-driven. Gives each possible action a weight depending on urgency, then picks highest weighted action and does that. The current AI uses hand-coded priorities. For example, if there is ripe fruit, we always harvest before doing anything else, no matter how angry we are or how much we want to mate. Thus, we can get "locked" into a harvest cycle (between two plants that ripen at the right rate) and never do anything else. If we have plenty of fruit in storage, harvest should not be such a priority. When I say "possible actions" above, I don't mean specific actions, but general categories of actions. No stored fruit and fruit is ready to harvest? Then harvesting should be a priority. The various thresholds should be set with genetics (how much stored fruit is enough, how likely we are to become revengeful, etc.). Read for a Version 2 test release (try on a fast computer)? Seems to be there. Testing on slow (but 3d-card-equipped) PC, noticed bugs: X-Changing destination mid-move causes rotation to be way off. X-Music skips a lot. Fixed rotation bug. Improved music issue by reducing amount of code in GL thread that is locked. Testing again, noticed bug: X-Flowers/fruit don't shrink when poisoned. Fixed scaling, but positions of fruit/flowers still don't move toward center along with leaf terminii. Also, flowers and fruit do not turn black. This might be okay, actually, 'cause it might look like plant leaves are "dropping out from underneat" fruit/flowers. Might be an interesting effect. Anyway, at least now they shrink into nothing instead of hanging there in full view and then suddenly disappearing. Tested on faster computer. Looks good---abstract and odd. Fixed bug causing island shape to be the same after restart. Fixed bug that forced one of flower petal colors to be same as center color. Try Version 2 release again. September 20, 2006 Fixed some MacOSX compile warnings. Testing on Mac (233 MHz), the sound skips a lot. Tried disabling locks, and it doesn't help. Reducing number of gardeners eliminates skipping. So, it's a CPU speed issue. Same on PC. Go with this for v2 release (if it doesn't skip on library computer). Added a proper icon for Mac version. Test windows version today, then post to SourceForge if it works. Testing on library computer. Noticed two issues (to resolve in v3): X-Clouds just make everything hazier---they are hard to see. Should increase cloud alpha contrast so there are more "fully transparent" spots. X-Fruit highlighting sometimes incorrect when there are multiple ripe fruits on a plant (harvested fruit differs from highlighted fruit... why?) Looking at source, couldn't figure it out. X-Two gardeners, with overlapping plots, often get in a "mime loop" where they both execute exactly the same actions at the same time. This should be fixed when the AI is re-written. X-Idea for AI: probabalistic. Each possible activity is given a weight, depending on urgency, and those weights are normalized so that they sum to 1. The normalized weights are used like a probability distribution, and a task is selected at random using this distribution. --Anger should propagate: if you anger gardener A, and gardener B's most- liked gardener is A, then B should also get angry. Maybe it should work the opposite way (if B least likes A, then B should get happy when A gets angry). Social network. X-Idea for slower computers (and to smooth testing on my computer): make each fancier graphic feature optional. Could have a features.ini file with a switch for each option (like "drawClouds", etc.). --You can get stuck carrying water if your plants are full-grown and you have no seeds left. --Switch to 1/f melodies --Question: Are clouds an improvement, or should they be dropped? They distract from the main view, certainly. Still, v2 is ready to go. Update HTML, then tag all as cultivation_2 September 25, 2006 Wasted day. What a mess. Tried to improve cloud look... part-way through, but got stuck dealing with linear interpolation artifacts. Thought they were random generator artifacts, spent tons of time testing various generators. Added cosine interpolation to landscape.cpp from here http://freespace.virgin.net/hugo.elias/models/m_perlin.htm Need to finish adding it, see FIXMEs. Need to tear out test generator code... Need to work on cloud stuff more in World.cpp September 26, 2006 Optimizing landscape code. Having some trouble, but using macros, was able to reduce running time by 18%. Are multiplies slower than addition? Not on a PPC. For all int operations tested (+, *, >>, and /), I saw about 20,000,000 ops per second. Divide and mod were slightly slower, more like 12-15 million ops per second, but no twice as slow or anything. So, forget about avoiding division. Now, what about doubles? All operations on doubles and floats were comperable in speed to operations on ints (how could this be true?) One slower operation is conversion from an int to a double via a (double) cast. Hmmm... maybe we're actually measuring loop overhead instead of operation times. Unrolling the loop 10 times, I see more of a difference, but still not a huge difference. Okay, enough distraction. Finished tweaking clouds. September 27, 2006 Start work on features.ini file. Should probably be called features.txt so that windows users can edit it easily. September 28, 2006 Added more switches. Works well (and increases the frame rate on my slow system). Ready to work on new AI. For now, implement only the behaviors that are present in the current AI, but with the new probabalistic model. Possible tasks: --Water (the driest plant) --Harvest (one piece of fruit) --Eat (one piece of fruit) --Planting (one new plant) --Expand Plot (for more space) --Capture neighbor's plant --Poison neighbor's plant --Give gift (one piece of fruit) --Mate --Rest (sit idle for some fixed time) Have state variable tracking current task. That task remains set until task complete (or until task fails) During each pass time step, we --continue executing the current task --complete the current task --pick a new task and start executing it Thus, we can have a switch statement that selects custom code for each selected task (to water, we must first walk to water, then fetch water, then walk to the dry plant, then dump water). To pick next task, we compute weights for each possible task (using various factors---for example, if we have no fruit stored, Eat should be weighted zero). Then we normalize the weights so that they sum to 1. Then we pick a [0..1] random variable and use it to pick a task (walking through the tasks, summing the weights, unil the sum surpases the value of our random variable). Started work on GardenerAI2.h and .cpp Got two tasks done. Need to add the rest. Also, we can tweak each task weight with a genetic weight modifier. September 29, 2006 Got all AI tasks implemented. Seems to work (and perhaps better than old AI). There are two switch blocks. The first is used to select behaviors with a weighting scheme. The second is used to carry out the chosen behavior. At this point, the second block and be left alone. The first block can be tweaked in the following ways: X-Genetics should modify the weights for each task (we might be able to do this elegantly with an array of gene names, so that we can modify weights with a simple loop). Perhaps genetic weights can range from 0.5 to 2, so we can, in the extremes, cut chance of behavior in half or double chance of behavior. October 2, 2006 Got genetic behavior modifiers in place and working. Rather elegant. Nice. Right now, only user's gardener can back off and compromise. Other gardeners can only expand their plots. Single gentic parameter: overlap_tolerance If low, gardener is likely to give up and pick a new plot to avoid overlap. If high, gardener is likely to stick with overlap and fight it out. But how will this parameter interact with the "capture plant" behavior, when a gardener intentionally expands its plot into another's space? Maybe overlap tolerance should specify how much overlap can be tolerated before giving up... in terms of a percentage of gardener's plants that are co-owned by others. Better: in terms of percentage of land that is co-owned by others. Thus, a gardener might expand its plot to take revenge, but after several expansions, give up and pick a new plot. First, should overhaul the plot selection algorithm. Instead of trying to be close to water and far away from gardeners, gardener can pick + repick until it finds a plot that meets its overlap tolerance. Should have another genetic parameter that controls its desire for water proximity. Actually, should drop water proximity as a criteria for now. And, we can simply just pick any land-centered plot in the plot selection phase. We can always decide drop it in the behavior selection phase if it doesn't meet overlap criteria. overlapTolerance can map to [0,1], since it is a fraction of our plot that overlaps with others. Need: algorithm for computing intersection of rectangles. Get area of intersection, compare to area of our plot. What if more than one plot intersects ours? Computing the true overlap fraction would be difficult. Instead, just sum the separate overlap areas. Find fraction of this sum over our plot area. Cap fraction at 1. So, overlapTolerance should map from [0,1.5] instead of [0,1] so that it is possible for some gardeners to tolerate 100% overlap. How much above 1 it goes determines chance of 100% tolerance ( [0,2] means 50% of gardeners are 100% tolerant, [0,1.5] means 33% are. What percent should be 100% tolerant? how about 25% of them. So it should map to [0, 1.33]. Testing abandoning behavior. It needs tweaking. Since gardeners don't take overlap into account when picking a plot, they often abandon their new plot shortly after picking it and wander around, establishing plot after plot. Maybe they should only consider abandoning in the middle of fighting, or if they are making some other gardener angry. Maybe they can check if they are have least-liked status with any gardener, and then check if their overlap fraction with that gardener. They consider abandoning if they overlap too much with a gardener that is angry at them. Try this. Also, gardeners pick resting too much, even when there is work to be done. This makes for a boring game. Turn resting off, except when there is no other task (make it the default). Found (whew!) a deadlock: Music thread locks globalLock while SoundPlayer's mLock is locked. World thread tries to manipulate SoundPlayer (thus trying to lock mLock) through a call to setNumGardeners while it has globalLock locked. Only happens when numGardeners changes, so pretty rare, but it freezes the game when it happens. Amazing to find it. Fixed it by putting a separate lock in SoundPlayer that protects the MusicPlayer (so that mLock doesn't need to be set when SoundPlayer calls the MusicPlayer). Verified that fix actually solved problem by inserting a sleep that makes deadlock happen consistently with old code. New code doesn't lock even with this sleep in place. Bug: gardeners can still give to you when you are dead (in ghost mode). They may also react to you in other ways, too (like react to the position of your now-invisible plot). Added more checks to ignore dead gardeners---they should fix this problem. Last thing to add: 1/f music Actually, too difficult to add this in the current implementation. So, skip it. Still, should make sure we have a proper set of notes in place. Existing pitches copied from level 001 of Transcend. Probably should pick something different. Next: X-Make rest time genetic (right now, it's hard coded) (instead, reduced chance of resting behavior) X-Anger propagation? Maybe not---perhaps it's too complicated for player to understand. X-Stuck-carrying-water bug (should be able to dump water back in ocean) X-1/f music October 3, 2006 Pitches and frequencies explained: http://www.jhu.edu/~signals/listen/music1.html Optimization: --Improved inner loop of MusicNoteWaveTable (startup) Looking at calls to blendNoise1d During startup (with profiler), it is called 341378 times (getSoilCondition is called 4096 times). What about during first 30 seconds? getSoilCondition (and thus blendNoise1d) is called only sporadically after that. Don't worry about it. Seems like checking water boundary is called a lot. May be some optimizations possible in the hot spots. Let it run for a while and profile it. After running for a while, getClosestBoundaryPoint was only called 38 times. At 2.63 ms per call, we could call it 380 times per second, so it is probably not hurting our frame rate. Giving up on profiling main game session. Turning debugging off cut executable size by 75% or so. Dumping water not smooth. Fixed this. Final stuff for v3: X-Better music pitches (two octaves of c-major scale) X-Turn on profiling and test X-Optimize a bit using profiler X-Turn on full optimizations for final compile X-Dumping water not smooth. Why? X-Update build scripts to copy features.txt X-Update how_to_play to mention features.txt Ready for v3 test builds. October 4, 2006 Tested on a "fast" computer. Problems: X-Rapid-fire giving of multiple fruits. Idea: insert a 0.25 sec delay between all AI tasks. Problem: this still doesn't deal with additional gifts while first gift is in-transit. Gift criteria (to avoid back-n-forth giving) requires that they have 2 less fruit than us, but while our gifts are in transit, they may still have less fruit than us. Maybe test for in-transit fruits? Re-Implemented simple last-gift timer instead. X-Music volume rises as gardeners die off (gets very loud when you're the last gardener). X-Instructions: not clear that you only need to water plants until they are done growing. X-Crash (when lots of partially-grown offspring were around) Maybe due to an offspring's parent dying before the offspring was full-grown. Looked in code---indeed, we don't deal with parentToFollow issues when we remove a gardener. Fixed this (and tested it, was able to trigger crash first with old code on linux. Same situation did not trigger a crash in new code). X-Yikes... some gardeners' eyes are flickering and sometimes completely invisible. Saw it happen twice on optimized linux build. Still trying to catch it with debugger. Not seeing it at all in build with debugging on and opt off. X-If a gardener has no seeds left (and no living plants), it stalls, doing nothing. Perhaps it should try to take over other gardener's plants. Actually, it will, if it is angry enough at the other gardener. However, it will leave "friend" gardener's plants alone and sit-n-starve. This is okay for now. Leave it. X-Water fill-up can be jerky if water dumped before fill-up animation done. X-When approaching another gardener to give a gift or mate, gardeners should stop moving once they get close enough to complete their task. Right now, if they pick no other task, they keep their last desired position set, so they end up right on top of you (if you are standing still) which looks odd. For rapid-fire issue, try inserting a 1/2 second pause after each AI action. Didn't work, see above. Caught disappearing eyes with debugger. RGB Color: 230,17,162 0.9019, 0.0666, 0.6353 Yes! Fixed it. Ready for v3a test build. October 5, 2006 Testing 3a on mac, found that features.txt didn't work. Fixed it. Finished final v3 builds. Put on 3 floppy disks. Ready to mail to Slamdance. Mailed it on October 5, 2006. October 6, 2006 Final testing good. Still one crash on win32. Should tag as v3, but no time tonight. October 9, 2006 Tagging all files as cultivation_3 Found source of Mac crash on exit: Accessing globalSoundPlayer after it has been destroyed (through setNumGardeners when World is being destroyed). Fixed it. Still hearing "clicking" in music on MacOSX. With a single gardener, there is no clicking. With two, there is more clicking. With more, there is even more. Thus, it is a performance issue. Looking at Transcend docs, realized that sample rate for final game was NOT 44100 Hz, but instead 11025 Hz. Yikes! Time to turn down the sample rate. October 10, 2006 Last night, caught a crash on both Mac and PC. In SimpleVector:getElementIndex somehow called from Gardener:passTime (on Mac, debugging info not enabled, on PC, quit debugger by accident before doing a backtrace [due to the broken keyboard]). Today, recompiled Mac version with all debugging on and caught it (caught it on PC too): --We tell our parent to drop us as an offspring when we finish growing even if our parent is NULL. Fixed this. This is a pretty serious crash. Should release a new version soon. Feedback from Alex: too frustrating, because whenever you plant, some other gardener almost always expands its plot into yours. They are too agressive. Also, some gardener almost always ends up expanding its plot to contain the whole island. Not fair, since human player cannot do the same. X-Some limit on size of plot? --Some way to back off without abandoning entire plot? --Chance at beginning of game to pick how many other gardeners you want to start out with? Looking at getLeastLikedGardener, if we like all gardeners the same, then player's gardener (which is first on our list) will always be returned. Not fair. Fixed this. Added a plot dimension limit of 40. This works better. Should get a new release out ASAP because of the fixed crashing bugs. Ready to test v4. Testing on a fast computer: works great, no crashes, even after several generations. So, ready to release v4. Updating website stuff. After posting release, tag files as cultivation_4 October 12, 2006 Alex says play is much better. Played for many generations with no crash. Saw a crash after dying as the last one living. Need to figure out why. Promotion (sites that have reviewed/described Transcend): X http://www.happypenguin.org/ http://www.giantrobeast.com/strafingrun/minireview2.html --only about shmups http://indiegamedev.tucows.com/blog/_archives/2005/6/13/937737.html --No post for more that 1 year http://shootthecore.moonpod.com/Links/Pcshmups/Database/T.html --Only about shmups X http://www.gametunnel.com/articles.php?id=346 X http://www.4colorrebellion.com/indie-games/ Other sites that do reviews, etc: X http://www.gamesareart.com/ X http://jayisgames.com/ X http://www.bytten.com/ X http://www.arsecast.com/ http://forums.indiegamer.com/ X http://www.madmonkey.net/ X http://www.gamehippo.com/ General software sites: X http://freshmeat.net project ID 61939 Mini description: conflict and cooperation in a gardening community One-line description: A unique game that explores conflict and cooperation in a gardening community. Short description: Cultivation explores the social interactions within a gardening community. You lead one family of gardeners, starting with a single individual, and wise choices can keep your genetic line from extinction. While breeding plants, eating, and mating, your actions impact your neighbors, and the social balance sways between conflict and compromise. Extra paragraph for short description: Cultivation features dynamic graphics that are procedurally-generated using genetic representations and cross-breeding. In other words, game objects are "grown" in real-time instead of being hand-painted or hard-coded. Each plant and gardener in the game is unique in terms of both its appearance and behavior. October 13, 2006 Bug report from LinuxGameTome: I like the game. It gives me a segmentation fault, but works fine when run in gdb. In gdb it runs with >300 fps. Without gdb it should run even faster. Might that be the reason for the segmenatation fault? Athlon 2500+ ATI 9600XT Debian Sarge kernel 2.6.8 Here is the backtrace: #0 0x08055647 in World::getAllGardenerPositions () (gdb) bt #0 0x08055647 in World::getAllGardenerPositions () #1 0x08074abe in MusicPlayer::getMoreMusic () #2 0x0807666a in SoundPlayer::getSamples () #3 0x08076a2f in portaudioCallback () #4 0x0807a170 in Pa_CallConvertInt16 (past=0x8336700, nativeInputBuffer=0x0, nativeOutputBuffer=0x8336810) at pa_common/pa_lib.c:645 #5 0x0807af69 in Pa_AudioThreadProc (past=0x8336700) at pa_unix_oss/pa_unix.c:650 #6 0x40266b63 in start_thread () from /lib/tls/libpthread.so.0 #7 0x4042f18a in clone () from /lib/tls/libc.so.6 (gdb) Found and fixed this. "Bill Meltsner" liked the game, but always saw a poisoned island as the end result. My ideas to fix this: How many times did you play through to see that poisoned result? If it really happens every single time, and there's nothing the player can do to prevent it, then there does need to be some adjustment. I don't think that fading poison will work, because I really wanted conflict to have some serious consequences. The problem is that the conflict can happen between AI gardeners, which are out of the player's control. What I would like to think about are more ways that the player can influence the behavior of the others... to sooth conflict somehow in order to prevent disaster. Right now, you can see two gardeners fighting, but you can't really do anything about it. Even if you are nice to them, the result will be them liking you more, but they will still hate each other and fight. It would be interesting if there was something you could do, involving some kind of self- sacrifice, that would encourage them to stop fighting. The fighting is caused by their overlapping plots, so what if you could create a "tempting" new plot for one of them on the other side of the island and give it as a gift? Maybe it would need to have at least as many plants in it as their existing plot for them to consider moving. But that would take some work on your part to construct (planting, watering, etc.), so it would be a nice self-sacrifice. I guess this could be easily accomplished (on my end, as the dev) with a "swap plot" button. It only is enabled when your plot contains more plants than the other gardener (the gardener you are standing near). When you press it, you take control of the other's plot, and the other switches to controling your plot. At that point, you would control the "conflicted" plot, and you could back off, or move away, or whatever to end the conflict. Hmm... maybe the sacrifice should be even bigger. Maybe you need to have twice the plants of the other gardener to make a swap happen... so you get a raw deal, but you sooth the conflict, which might save the island in the long run. It should be a tough choice to make. Should release v5 now so that people get this important bug fix. Done. Posting to Freshmeat. Tagging all as cultivation_5 October 14, 2006 problems posted by Anonymous @ 62.254.128.7 on Oct 13 2006 5:28 PM I'm getting reems of warnings like g++ -Wall -DLINUX -O9 -I../.. -I../../minorGems/sound/portaudio/pa_common -c -o sound/MusicPart.o sound/MusicPart.cpp g++ -Wall -DLINUX -O9 -I../.. -I../../minorGems/sound/portaudio/pa_common -c -o sound/MusicPlayer.o sound/MusicPlayer.cpp ../../minorGems/graphics/ChannelFilter.h:18: warning: ?class ChannelFilter? has virtual functions but non-virtual destructor sound/../userInterface/ObjectStorage.h:22: warning: ?class ObjectStorage? has virtual functions but non-virtual destructor and one final error of cp: cannot stat `game2/gameSource/Cultivation': No such file or directory Run Cultivation to play. Fixed the warnings, there must be some other error preventing compilation. Ideas from "Bill Meltsner" : Usually, I try to back off to avoid conflict - I go to another part of the island and begin happily (albeit reluctantly) farming there. Eventually, however, the island gets so crowded that there is no place to go where I don't overlap - and then civilization begins collapsing. WE'RE ALL DOOMED! Certainly avoiding breeding like rabbits is a good strategy to help alleviate overcrowding, but then that has to be balanced with keeping your line alive - plus the AI gardeners appear to have a propensity for having babies. I agree that fading poison is probably a bad idea - I was just throwing it out there as an idea. I do like the idea of a "swap plot" command - however, as you mentioned, the issue of how much self-sacrifice is worth helping to cease the conflict is tough to balance. I'd like to see that command in-game - it'd be interesting to see how much longer society could continue with that possibility. My main problem with that idea is that it doesn't seem too realistic (and of course nutrients named after colors is totally lifelike!) - one idea I had was a "Make Peace" button (or something along those lines). Basically, if you're near two gardeners who have a negative relationship with each other (overlapping plots being the most likely cause), the button is enabled - you then basically tell the two gardeners to calm down, it's not that bad, sharing is caring, etc. Depending on how much each gardener likes you, they start liking each other more - basically, the more they like you, the more they take to heart your sermon on brotherly love. There could, however, be a "point of no return" (perhaps a few notches above "POISON ALL THEIR PLANTS BWAHAHAHAHAHA") that, once two gardeners reach it, there's nothing you can do to save their relationship. In fact, attempting to mediate the conflict results in them both disliking you more, as well as each other, causing the poisoning to happen that much faster. Also, if they don't like you enough, the same thing happens - if they don't have much respect for you, they'll just get angry at you for trying to stop them from fighting. October 17, 2006 Coverage: http://pcburn.com/article.php?sid=1839 "Unique games have a tendency to wash up on the Linux shores every so often. Cultivation is one of the most unique to show up in quite a while." http://jayisgames.com/archives/2006/10/link_dump.php Considering it for a review, along with a bunch of other games (waiting for comments to decide what to review). Watch for comments here: http://forums.indiegamer.com/showthread.php?t=8745 Thinking about adding some kind of overall, island-wide goal. If you reach the goal before the game ends, you win. For example, the goal might be to build a temple. It might take several generations to do it. Cooperation might help with the goal, but will be necessary in general for basic survival through the project. So, what would be an interesting and fitting goal? Something different and unique. Building a temple probably isn't interesting enough. Also, keep in mind that we should avoid anything that only has one right answer. It should be possible to complete the goal without cooperating, maybe in a different way. Multiple paths to reach the goal. The interesting question then becomes: which path will you choose? Feedback from John Fink: "John Fink" > A pause button. Maybe there's one already and I'm missing it. > > Option to turn off poison, or some method to reclaim poisoned land. > > A bigger map! Or options to control the number of initial gardeners. More from JF: --Storage area is a pain to manage (my idea: limit size, older items fall off when it gets full) --Gardeners could work together to clean up poison. October 22, 2006 Idea for an end-game goal: portal. You have a mark, your offspring have the same mark. Only you can build the portal. Poison destroys the portal... need to rebuild elsewhere. Some challenge to build the portal over several generations... not sure what. Then, the final challenge is to lead some gardeners into the portal. Add a "follow" button that becomes active if a gardener likes you enough. You must lead them into the portal without stepping in by accident yourself. Take as many as you want with you to be immortalized in the world of the higher power. Last, you step into the portal, and it closes, stranding any other gardeners that you leave behind. They are immortalized in an output PNG file, maybe a combined picture of their souls (not a picture of them directly, more abstract, which is sized depending on how many are saved. This is the reward for the player, sort of a badge of completion. Still many questions, but very interesting. Draws connection between life in simulation and life in the "higher world", which in this case is our physical world outside of the game. Being immortalized just means being captured in a work of art in the higher world. October 26, 2006 More ideas about portal: Wherever you die first will start the first block of the portal. Need to die there N times (N=4, maybe?) to finish the portal. Poison destroys the portal, and dying somewhere else starts a new portal and destroys the current one. Portal grows very high into the sky, above the clouds, nearly touching the screen. When it is complete, it animates somehow to show that it is complete. After portal is complete, you can use the follow button to lead gardeners into it (only those that like you a lot will follow you). When you pass through the portal, it closes. Those that you save are immortalized in a souvenier image (and maybe an mp3 file?) The image should show the abstract souls of the gardeners and not the gardeners directly. Image size dictated by how many you chose to save. Hmm... maybe the follow button can also be used to prevent a pair of gardeners from fighting over and over. By getting a gardener to follow you, you can distract it from fighting (and feed it to keep it alive, or whatever). Thus, we might be able to get away without a "swap plot" feature, or whatever. Also, should there be a puzzle involved in building the portal? Maybe a breeding puzzle... Like feeding it a gardener of a particular color makes the portal grow faster... No... think of puzzles that occur in chess and go. Maybe a flipping puzzle of some kind. Think of a randomly-generated lock. Portal lock has multiple blocks to it. Some are lit up, some are not. Dying in a particular block causes blocks to lite up and nearby blocks flip state. Goal is to get them all lit up. To generate a puzzle, work backward from unlocked state: X XXXX XX XXXXX X XXXX OO XXXXX O XXOO OO XXOXX O XOOO OO OOOXX O XOOO XO OOOOO O OXOO OO OOOOO For now, just implement a simple portal. Passing idea: suicide by eating a poisoned piece of fruit. Work on portal: See Portal.cpp. Idea: When player's gardener dies, call World::augmentPortal (checks for poison-free ground, if not on top of existing portal, destroys old portal (if present), starts new one if on existing portal, upgrades it). Draw portal z levels along with plant z levels, and also interleave with gardener z level. When plant poisoned, check if portal nearby, destroy portal. Got part of portal drawing working. See FIXMEs in World.cpp Only seeing one level... why? October 27, 2006 Fixed portal drawing. Got basic portal agmentation working. Got poisoned fruit working (to support suicide---avoid waiting around to die). October 30, 2006 Fixed portal poisoning. Started work on following button and following genetics. Still need to implement action handling for click of follow button. AI: similar to following a parent. Should following be permanent? Should we allow user to break off following? Why not? Following button could show a red "x" over it when standing next to a gardener that is already following us. October 31, 2006 Worked on following... got it working pretty much. Still testing. Watch for FIXMES: X-fast pregnancy for testing X-low following threshold November 8, 2006 Worked on portal graphics. Got some pretty results. Each layer in the portal should take its parameters from the gardener that augments it. Layers should be extremely variable in appearance. Need two states for each layer: active, and inactive. Layers remain inactive until final layer is augmented, then they all become active. November 13, 2006 Improved portal layer graphics with glyphs. Got rise-up animation working for portal passage. There are a some FIXMEs in place for testing. November 19, 2006 Got soul trails looking very good. Still a crashing bug, see FIXM November 20, 2006 Working on PNG encoding. No available implementation besides libpng and zlib, which are huge and cumbersome.... ugg. Looking at spec, just need signature, header, one IDAT chunk, and footer. The hard part is the IDAT chunk, since the image data is supposed to be compressed in there using Deflate. Looking at the Image Library from here: http://www.colosseumbuilders.com/sourcecode.htm Downloaded to here: /data5/jcr13/downloads/imagelib November 21, 2006 Got complete-hack-of-a PNG implementation written, mostly from scratch, including the checksum code from the PNG and ZLIB specs. It writes only uncompressed deflate blocks (allowed according to deflate spec). Would be nice if it could output compressed blocks, but that is for another release. A 2-gardener soul trail image is about 19KiB, which is not terrible. Idea: eventually consider using real zlib implementation... only about 1000 KiB of code X-Next: used the nice "draw blur circle" code from soul trails to draw the portal glyphs. Consider ways to make leaves less blocky, too. November 22, 2006 Leaves, exported to GIMP, look fine and not pixelated. Maybe just look bad on 16-bit Voodo3 card. Try it on Nate's Got smoother glyphs working... they look great. Sped up death after eating poisoned fruit. No reason to keep player waiting. X-Noticed that some gardeners were giving the gift of poisoned fruit. Odd, because they should be ignoring poisoned fruit when selecting a gift. However, fruit-counting function was not ignoring poisoned fruit. Fixed this. During testing, watch for gifts of poisoned fruit. We should also disable the gift button for poisoned fruit on our end. Fixed it too. Pretty much ready for a test release of v6. Still working on doc changes. November 23, 2006 Got all doc changes in place. Reviews on GameTunnel suggested that tool tips would be helpful. Added them (complete with my own hand-painted font texture). With tool-tips in place, decided to remove rant about instructions from guide. Checked for leaks. At this point, we have dealt with every problem that the critics have pointed out. Good. Still need to do something about language selection. Idea: language.txt file. Did it, and checked for leaks. When trying to open my own gate, found the "die away from gate and gate starts from scratch" rule annoying. Changed it. November 24, 2006 After many tries, finally was able to generate a large-ish soul trail image for the logo filling, but it was HARD. Too hard. Maybe need to tweak it a bit. Fewer portal levels? More controllable offspring (2 instead of 1 per mating)? AI jitters before giving gift if multiple gardeners tied for most liked. Problem: getMostLikedGardener returns one at random to break ties. --Randomness good to spread gifts/hate out instead of focusing on one gardener (often the player's gardener). --GardenerAI is stateless. It picks the "gift" action, but then keeps asking for the mostLiked as it executes that action. Safe, because the first mostLiked may die and be removed before the gift action finishes. Thus, stateless AI is "safe" Solution: keep one marked as mostLiked after we pick it the first time, but set it back to NULL if the like metrics change. Thus, in the case of a tie, we pick one most-liked at random and keep returning it on calls to getMostLikedGardener until something changes. Fixed this November 25, 2006 Finished code for feeding followers. Ready for final play-testing. November 26, 2006 Checked "feeding followers" code for memory leaks. Done building test v6. Still need to test all of them (especially the source distribution). Tool tips not working on mac. Fixed it. Noticed glyph_0.tga still being output. Fixed it. Outside offspring of followers not being fed. Tested on a fast computer... everything looks good. Putting latest HTML online. Summary of changes: The game now has an eventual "win" condition: open the gate to immortality and lead your fellow gardeners through it. The Lead button, used in achieving the goal, can also be used to quell a turf war. In-game tool-tips have been added to explain the buttons. The storage area has been limited to 10 items: older items are dropped once the limit is reached. Okay, ready to tag as cultivation_6 Promotion: X-Freshmeat X-Email Jay Bibby (JayIsGames) X-Mad Monkey post X-Upload to IGF FTP To do: X-Fix storage area to add a size limit X-Portal building X-Follow button (call it "Lead" button in docs" X-Portal graphics X-Abstract art output from saved gardeners. X-Portal should close after user-controled gardener passes through it X-Sometimes gardeners give to babies that are following others (this is okay for now... they are real and out in the world, after all they sometimes get mated with too, which is funny). X-Portal glyph graphics: rotate instead of deltaDeltaX and deltaDeltaY X-Update how-to-play X-Spell check how-to-play X-make new graphical version of instructions. X-proofread how-to-play X-proofread guide X-New screenshot X-Tool tips X-Should not crash if font.tga can't be read X-Build files need to copy font and language files. X-Generate large soul trails for a better logo X-Fix jerky AI behavior before giving gift. X-Baby jump to desired position when born X-White line above "R" in font TGA. X-Gardeners that are following never eat (should eat our gifts... actually, we should share all the food we eat with them, just like offspring). Close to finishing this... tracking them, just need to feed them. See FIXME in Gardener.cpp X-Fix tooltip for dropping a follower X-Update docs about feeding followers. X-Enable code optimizations and turn off debugging. X-Pass-through feeding of the babies of followers. Running to do (must): X-Speed up gardener generation. X-Better indication of plant needing water X-Better island graphics X-Better buttons (and target highlighting) X-Seed drawing (color of blue is off) X-Tweak AI X-Profile and enable optimized compile X-A graphical how-to-play document (with screenshots as examples)? (later, online) November 30, 2006 Ideas coming out of a review by (and email discussion with) Alex Snyder http://evolutionlive.blogspot.com/2006/11/review-of-cultivation.html Some kind of visual representation for pollination? My own idea: --Eating a poisoned fruit could kill your followers, too. Could be a way to quell over-population. Then again, you can limit the effects of over-population already by leading others. Running to do (if time) --Pause button --Fruit shadows --Fruit flying object scale X-Leak when mating happens with a dead gardener? December 14, 2006 Feedback from Tim W: >>>> Cultivation's not really my type of game, though I did enjoy playing it. I think the reason why not many people are giving feedback is because the instructions can be a little complicated (even though it's quite easy to learn). Perhaps a tutorial would help? Most people also judge game by their screenshots, it might be a good idea to touch up the sidebars a bit, since it appears throughout the game. Also, the window size should be at fullscreen or a larger size with an option to change accessible and noted clearly at the start of the game. I got this error while playing as well: http://static.flickr.com/144/319337796_b565086009_o.jpg >>>>> He's hitting the reset button during the crash... need to fix this, certainly. Feedback from IGF judge: >>>> Could use more player feedback as to age and status of player I love the idea of this game! The idea of being a gardener (of some sort) and competing for resources seems like good fun. And I saw glimpses of that when playing. Too bad the game is extremely confusing. The art is not great, but worse than that, it's bad to the point where I can't understand what the different icons are even supposed to be. Too bad there's no way to pause. Why can't I click and drag a box again? That seems like the obvious interface, every casual gamer knows how to do that, but I suspect you didn't do that because of a technical shortcoming. >>>> December 17, 2006 Checked for memory leaks. December 18, 2006 Finished tool tips for object storage. Checked for leaks. Added shadow to discard button's x. Improved seed icons. Added pixelated background texture to buttons. Still not happy with it. Started work on background textures for panels. Idea: Panel fades towards top (done), but button backgrounds fade toward bottom (not done yet). December 19, 2006 Got sidebars looking much better. Good enough for now. Checked for leaks. Now deriving seed width from the number of leaves per joint. Narrow seeds produce plants with fewer leaves per joint. Ideas for tutorial: Various events in game trigger display of particular tutorial pages. Once a given page has been shown, it will never be shown again (even if same event happens again). Tutorial pages shown in a panel that has two buttons ("ok" and "stop tutorial). Game paused until tutorial window dismissed? Actually, maybe not (if window can fit at top of screen nicely). Tutorial panel can be added to side panel when needed, and removed when dismissed. December 21, 2006 Trying to make a trailer: To convert a series of numbered files to an mpeg1 video: ( 30 fps, min 500 kbits/sec) /data5/jcr13/downloads/ffmpeg/ffmpeg -r 30 -i test%d.png -f mpeg1video -vcodec mpeg1video -r 30 -b 500k test.mpeg To concat multiple MPEGs together: cat *.mpg | /data5/jcr13/downloads/ffmpeg/ffmpeg -i - -f mpeg1video -vcodec mpeg1video -r 30 -b 500k together.mpeg To grab the contents of the GL screen: unsigned char *outputImage = new unsigned char[ w * h * 3 ]; glReadPixels( 0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, outputImage ); Idea: --"v" key enables video grab. --Window resizes (temporarily) to 640x480 --Frame rate fixed to 30FPS (fake in-game time set to 1/30 sec per frame) --Grab 300 frames to numbered PNG files (10 seconds worth) --Go back to old window size Got trailer frame capture working. At 4:15am, finally finished trailer. December 22, 2006 Need to remove video-generating code while somehow leaving it in place for future use. Did this. Fixed button issues and added background for storage area. Checked for leaks. Tutorial framework working well. December 23, 2006 Just got word that trailer needs to be 720x486, not 640x480. Converted game video clips with scaling and cropping. Working on generating new titles (not using Adobe Premiere, because it doesn't seem to handle the odd aspect ratio for titles). Instead, should generate a sequence of jpegs for each title: If title.jpg is the full-sized title and back.jpg is a black background image, we can use a sequence of commands like: composite -geometry 210 -gravity center title.jpg back.jpg title_1.jpg composite -geometry 220 -gravity center title.jpg back.jpg title_2.jpg composite -geometry 230 -gravity center title.jpg back.jpg title_3.jpg Actually, can use percentages in geometry. That would work better, given that the title images will be different sizes vary from 75 to 100 percent, for example December 24, 2006 composite -geometry 50% -gravity center title_0.jpg title_background.jpg title_0_0.jpg composite -geometry 50.1% -gravity center title_0.jpg title_background.jpg title_0_1.jpg composite -geometry 50.2% -gravity center title_0.jpg title_background.jpg title_0_2.jpg composite -geometry 100% -gravity center title_0.jpg title_background.jpg title_0_3.jpg Problem: placement and scaling using composite is too jerky from frame-to- frame. Wrote custom program for smooth placement and scaling. Works well. Idea to save space: Generate links for all jpeg files in frame order (interleaving titles and scenes). Then call ffmpeg once to generate full movie. Problem: title frames and scene frames have different aspect ratios. We're using /data5/jcr13/downloads/ffmpeg/ffmpeg -r 30 -i frames/frame_%d.jpg -f mpeg1video -vcodec mpeg1video -r 30 -b 1864k -s 720x538 -cropbottom 26 -croptop 26 cultivation_720_486.mpg ...with sizing and cropping to convert 640x480 scene frames without distortion to 720x486. The problem is that the interleaved title frames are already 720x486, so they are getting distorted. Also, scene frames are drawn with a weird wrap-around effect because they are smaller. ffmpeg must be computing frame sizing based on the first frame instead of frame-by-frame. Thus, it is assuming that all frames are the same size. Want text to be as sharp as possible (thus, we should avoid generating 640x480 text frames and then allowing ffmpeg to scale them). Instead, should blow up and crop scene frames ahead of time? What about space? December 25, 2006 Got 720x486 trailers generated. Then read about square pixels and 4:3 aspect ratios... ack! Generated one more with letterboxes and non-square pixels for a 4:3 aspect ratio. Okay, back to the tutorial. Got most of tutorial written. Decided that we need to pause game while tutorial screens showing (so that the player doesn't waste life time while reading). Need to tweak behavior a bit here (tool tips should show during pause, tutorial-induced pause should not reduce frame rate). See FIXME. December 26, 2006 Tutorial's test for offspring isn't working. See FIXMEs in game.cpp Fixed this. When writing about emotions in tutorial, though of a new gameplay element: Gardeners should get very angry (max angry) if you mate with their most liked. Implemented this and tested it. Nice twist to add, and easy to add. Good. Checked for memory leaks. Got variable-width font working. Looks great. Got larger window as default. Added border to tutorial. Checked for leaks. Turned off debugging. Turned on optimization. Fixed some problems with tutorials. In testing, it seems that the "VERY angry" "you stole my mate" behavior is too powerful. Poison poison everywhere. It is also impossible to avoid. Thus, I need to trim it down to balance the game (hey---they game was hard enough without that bit, eh?) So, instead of VERY angry, they should just get angry. Did this. Found and fixed a crash with hover object for the selector (if hover object drops out from underneath us). Ready to build v7. So, this mostly amounts to polish (including some of my own): x-Tool tips vanish after restart x-Crash on restart x-Pause button x-Drag to select plot X-Update docs about dragging X-Add lowercase to the font X-Tool tips for storage X-Improved icons (especially seeds). X-Better looking sidebars overall X-Seed width should depend on another plant parameter (growth rate?) X-Tutorial X-Background for storage area X-Reset button not faded properly X-Polish the font X-Note in docs about in-game tutorial X-Font kerning? X-Tutorial border? X-Larger default screen size (with option to change) (glutReshapeWindow, glutFullscreen) X-Proofread tutorial text. Tagging all as cultivation_7 July 30, 2007 Working on a few polish points before FuturePlay deadline. Trying to fix segmented soul trails. In doing this, and trying to fix the random seed for repeat testing, I noticed that the landscape was seeded independently using the current time. I think that I fixed this (by using a seed generated by globalRandomSource instead), but I need to make sure it is still generating interesting landscapes in the final release. Fixed the segmented trails. Looks much better. August 7, 2007 Working on adding sounds to interface cued by various actions. Got sound effects bank in place. Just need to fill it. Put rattling sound in place for planting seed. Ideas for other sounds: --Pick up water: rising sine blip --Dump water: falling sine blip --Poison: low sawtooth blip --Pick fruit: pop sound (like finger popping lips) --Eat fruit: crunch sound --Give fruit: high ping sound --Mate: (?) maybe similar pop sound August 9, 2007 Got all sound effects in place. A nice improvement. In the future, it might be nice to add effects for lead/stop-lead and also for the gate rings and gate opening... also for rising up. To do: X-Make sure each new game using a different landscape. X-Add to read-me about switching language files. X-Add basic sound effects. X-Make sure French translation included in build (looks like it will be). Tagging all as cultivation_8 Cultivation_9+dfsg1_UnixSource/game2/documentation/futurePlay/0000750000175000017500000000000011462022731023424 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/documentation/futurePlay/Makefile0000640000175000017500000000047510713651552025102 0ustar pabspabs# # Generic: # # Map all .cpp C++ and C files into .o object files # # $@ represents the name.o file # $< represents the name.cpp file # %.eps: %.sk sk2ps $< $@ %.dvi: %.tex latex $< %.ps: %.dvi dvips -Ppdf -t letter -o $@ $< %.pdf: %.ps ps2pdf $< all: poster.pdf clean: rm *.pdf *.ps *.dvi *.log *.aux Cultivation_9+dfsg1_UnixSource/game2/documentation/futurePlay/poster.tex0000640000175000017500000000551510713656665025512 0ustar pabspabs\documentclass[12pt]{article} \usepackage{fullpage,times} \makeatletter \newcommand\TitleSize{\@setfontsize\TitleSize{100}{100}} \newcommand\WebAddressSize{\@setfontsize\WebAddressSize{40}{40}} \newcommand\BylineSize{\@setfontsize\BylineSize{20}{20}} \newcommand\SectionHeadingSize{\@setfontsize\SectionHeadingSize{30}{30}} \newcommand\BodySize{\@setfontsize\BodySize{14}{14}} \makeatother \pagestyle{empty} %\textwidth 7.5in \textheight 9.5in %\oddsidemargin -0.5in \begin{document} \begin{center} {\TitleSize Cultivation}\\ \vspace{0.25in} {\BylineSize a video game by Jason Rohrer} \end{center} %\noindent {\SectionHeadingSize Conflict vs. compromise} {\BodySize Cultivation explores the social interactions within a gardening community. You lead one family of gardeners, starting with a single individual, and wise choices can keep your genetic line from extinction. While breeding plants, eating, and mating, your actions impact your neighbors, and the social balance sways between conflict and compromise. In Cultivation, there is no shooting, but there are plenty of angry looks. } \vfill \noindent {\SectionHeadingSize Game mechanics as metaphor } \vspace{0.05in} {\BodySize One part of Cultivation's thesis might be summarized as follows: {\it Conflict can cost both sides more than either stands to gain by winning.} In a novel or film, we could state this thesis outright or tell a story that demonstrates our point (the novel and film {\it The House of Sand and Fog} comes to mind as a work with a similar thesis). In a game, we could also issue a bald thesis statement or tell an a story with a moral, but we have one additional tool for communication: the mechanics of gameplay themselves. We can design mechanics that lead the player down a particular intellectual path. There is no right way to play Cultivation, just as there is no right way to interpret the series of events that emerge from a given play through the game. This subtlety actually separates Cultivation somewhat from the Serious Games movement: Cultivation is not just a ``game with a message,'' but a game that demands interpretation from the player. } \vfill \noindent {\SectionHeadingSize Procedurally-generated content} \vspace{0.05in} {\BodySize All of the graphics, sounds, melodies, and other content in Cultivation are 100\% procedurally generated at playtime. In other words, there are no hand-painted texture maps---instead, each object has a uniquely ``grown'' appearance. Every time you play, Cultivation generates fresh visuals, music, and behaviors. Even though Cultivation is a game with a virtually limitless pallete of visual variety, it still fits easily on a single floppy disk. } \vfill \begin{center} {\BylineSize Free download for Windows, Mac, and Linux:}\\ \vspace{0.125in} {\WebAddressSize http://cultivation.sf.net} \end{center} \end{document}Cultivation_9+dfsg1_UnixSource/game2/documentation/gameDesign/0000750000175000017500000000000011401021120023307 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/documentation/gameDesign/design.tex0000640000175000017500000006050310466155064025340 0ustar pabspabs\documentclass[12pt]{article} \usepackage{epsfig,times,fullpage} % syntax: %\insertfigure{width}{file_name}{caption}{label} \newcommand{\insertfigure}[4] { \begin{figure*}[tb] \begin{center} \fbox{ \begin{minipage}{#1} \includegraphics[width=\textwidth]{#2} \caption{#3} \label{#4} \end{minipage} } \end{center} \end{figure*} } \begin{document} \title{Game 2 Design Document} \author{Jason Rohrer} \maketitle \begin{abstract} \end{abstract} \section{Game Goals} The goal of this game project is to help the player explore: \begin{enumerate} \item {\bf The relationship between self-interest and community building}; and \item {\bf The role of games in society}. \end{enumerate} \section{Game Topic} The topic of this game can be distilled best into the following phrase: {\bf Community Gardening} \section{Game Goals in Detail} \subsection{Self-interest and community building} Within this general goal, I want to explore: \begin{itemize} \item Community building vs. self-interest; \item Compromise vs. victory; \item Coexistence vs. conquest; and \item All of the trade-offs inherent in these dualities. \end{itemize} For example, conquest gets you all of the resources and all of the power, but achieving it results in loss of life on both sides along with lingering bitterness on the conquered side. Conquest can lead to a lack of long-term stability. On the other hand, coexistence involves less resources for your side, plus ongoing squabbles over border lines. \subsection{The role of games in society} Can we somehow ask questions about the medium itself, particularly in relation to the first game goal? Modern art asks ``What is art?'' and pushes at the boundaries of art itself. As a result, however, most ``modern art'' pieces are not very enjoyable for most viewers. Perhaps these viewers are not interested in exploring the boundary between ``not art'' and ``art,'' but are instead wanting to view pieces that are dead-center in the ``art'' region. Only a select few (myself included) seem to appreciate modern art. Most of these fans are artists themselves. Thus, we might conclude that only those who work in a given field are really interested in exploring work that pushes the boundaries of that field. Perhaps those that work in art are so familiar with ``dead-center'' on the art spectrum that they are bored with it. The boundaries become more interesting to people who are bored with the center. With modern art ``appreciation'' problems in mind, we can come to the following realization: {\em If we try to make a game that pushes similar boundaries (``Is it a game?''), it will likely appeal to very few people, though a select few will love it.} So, we should probably avoid using a game to ask questions like ``What is a game?'' Instead, we can think about the general role of games in society: \begin{itemize} \item They do not result in ``real world'' gains or losses; \item They seem to ``waste time'' and maybe even ``waste resources;'' \item They are fun; \item They may (or may not) teach us something useful about the real world; and \item They can be social devices (for example, to build friendships). \end{itemize} More generally, we could explore the role of recreation in society. Too much, of course, would lead to societal collapse. Not enough, on the other hand, might harm mental health. What about the role of games in mitigating conflict (which relates to our first game goal). We can think about ``World Cup'' instead of ``World War.'' Within this general goal, I want to explore: \begin{itemize} \item What is more fun, a game or real life? \item What is a proper balance between work and play? \item How do work and play relate to self-interest and community building? \end{itemize} ``I don't like his politics, but he's an okay guy---we bowl together every Thursday.'' \section{Game Topic in Detail} When considering the topic of ``community gardening'' here, I am not going into details about how a particular topic feature can fit into a game, but instead fleshing out the topic itself. The game mechanics can try to represent various facets of the topic. Thus, the discussion below describes such a community as it might exist and function in the real world, not as it might be represented by a game. \subsection{The community} Consider a community of neighboring gardeners with no officially-enforced property ownership. This would be an environment with co-existence, cooperation, shifting boundaries, and many distinct individuals operating near each other. In such an environment, each gardener would pick a plot of land to cultivate, and there would be nothing stopping multiple gardeners from picking the same plot or selecting plots that overlap. Nothing would stop one gardener from harvesting fruit from the plants cultivated by another (in other words, stealing). Different plots in such an environment might be good for growing certain plants and bad for growing others. A wet plot might be good for rice and a dry plot good for garlic. A loamy plot might be good for melons and a sandy plot good for parsnips. A shady plot might be good for lettuce while a sunny plot good for tomatoes. \subsection{Trade} Of course, gardeners cannot live on rice (or whatever their ``easiest'' crop is) alone, as each crop provides different necessary nutrients. Furthermore, each gardener will likely grow more of a particular crop than is necessary for solo consumption. Excess crops of a particular type, if stored for too long, will rot and become useless. Also, plant seeds do not just come out of thin air: they must either be harvested from mature plants or obtained from other gardeners. Thus, in such an environment, trade between gardeners can be beneficial. Pollination is an important factor in such an environment. Pollen from a neighbor's plants may cross with a gardener's plants and produce unexpected offspring plants (perhaps even plants that are no longer well-suited to the gardener's soil conditions). Alien pollination is also a way to produce new (and perhaps useful) plant varieties, however. \subsection{Gardener biology} Of course, gardeners in this environment are living creatures themselves and subject to the various biological constraints. Gardeners, as mentioned before, must eat to survive. Various actions consume various amounts of energy---sustaining hard work requires more food consumption than sustaining light work or idleness. Furthermore, even with sufficient and nutritionally-comprehensive food consumption, gardeners have limited life spans. Thus, like all biological creatures, they are driven to produce offspring. More offspring increases the chances of gene survival but involves a greater food burden. \subsection{Recreation} Gardeners in such a community would do other things besides gardening. We can think about the kind of recreation that they would engage in. Some of that recreation would certainly involve other gardeners from the community. This recreation would take time away from their gardening chores. In addition, it might consume various resources that could otherwise be put toward gardening and food production. At the very least, recreation would consume energy that could otherwise be devoted to gardening, but it might require other resources as well. Corn kernels might be dried and saved for use as game counters (perhaps for the game mancala), particular land areas might be converted from gardens into play fields, and special crops might be grown just for the purpose of making recreation equipment (for example, rubber trees). \subsection{Social interactions} Gardeners in such a community would compete for limited resources. Land itself is the most obvious resource---as mentioned earlier, certain land areas are better-suited for growing particular types of crops. Gardeners would be jealous of the land used by their neighbors. The borders between two gardens, in particular, would be in contention. Gardeners would ``push'' against each other, trying to expand their gardens into their neighbors' space slowly over time. There would also be contention over ownership of the plants themselves. Who owns a plant that is growing right on the border between two gardens, and who is entitled to harvest from it? What happens when one gardener ``sneaks'' into another gardener's plot and harvests from it? These contentions would have emotional results: a given gardener would like some neighbors and dislike others. ``Tit for tat'' strategies might develop, where gardeners would expand into the plots of (or steal from) gardeners that have bothered them in the past. Grudges would develop, and these grudges might span generations. Trade would also figure into the social and emotional space. Gardeners would grant more favorable trading terms to the gardeners that they like, and may even give extra produce away for free to their friends. A gift of produce might be used to express goodwill or cement a friendship. Friendships could be bolstered, and tensions eased, through recreation with neighboring gardeners. \section{General Design Ideas} \subsection{Idea: A game within a game} The sub-game is potentially more ``fun'' than ``real life'' in the main game. ``Real life'' in the main game continues around you while you engage in the sub-game (thus, while playing, you waste both time and resources). Friendships grow when you play subgame with your neighbors. \subsection{Mitigating development risk} Though the second goal depends on the first goal, the first goal depends very little on the second goal. In other words, we can explore community issues in an abstract setting that does not include recreation at all. Furthermore, it is easy to ``tack'' recreation onto an existing game that already satisfies the first goal. Thus, to mitigate risk, the first goal is the main priority, and the second goal can be dealt with in a later mega-iteration {\it after} a polished game satisfying the first goal is complete. \subsection{Agile design} I am using agile techniques for the design as well as the development. Thus, I will not completely design the game, up front, in great detail. During each iteration, I will create a detailed design for that iteration. Thus, subsequent sections of this document will focus on design iterations. However, I already have a high-level sketch of the overall game in my head, and I will put it down on paper in the next section. \section{Overall Vision for game} Note that I am not including the game elements that satisfy the second goal in this vision statement. The playfield is a 2d map of limited size (maybe an island) and varied terrain. Each gardener picks a plot by drawing a single rectangle on the map. A gardener's rectangle can only be seen by that gardener---it is invisible to the other gardeners. Thus, the rectangle represents what the gardener {\it thinks} she owns, not what she actually owns. Gardeners can redraw their rectangles at any time. Gardeners can only plant and harvest from inside their rectangles. Gardeners start with a limited number of seeds. They can plant these in their rectangle and then care for the plants as they grow. There is a water source somewhere on the map, and they must haul water to their plants to keep them from dying. Eventually, plants bear fruit that can be harvested. Fruit can be eaten at any time to gain energy, and the seeds can be saved for later planting. Harvested fruit that is kept too long without being eaten eventually rots. Planting in another gardener's rectangle, or harvesting fruit from plants growing in another gardener's rectangle, makes that gardener mad at you. Of course, you cannot see the other gardener's rectangle, but you can guess where it is by looking at the location of the gardener's plants. Gardeners that become mad at you may purposely expand their rectangles into your space to ``steal'' fruit from your plants or plant seeds on your land. The main play mechanic here is deciding where to put your rectangle in the first place and when to expand or contract it. Should you fight with your neighbor, stand your ground, and keep pushing for more land, or should you back off a bit, maybe sacrificing a few of your plants in the process, to appease your neighbor? Giving another gardener extra produce will make that gardener like you, and friendly gardeners are more likely to give you produce in the future or deal more fairly with you when it comes to garden border squabbles. Various activities, such as moving around, take energy, and more energy can be obtained by eating produce that contains a balance of particular nutrients. I am envisioning three nutrient types, Red, Green, and Blue. We can display our nutrient stockpiles (from what we have eaten) with three bars. Whenever there is at least one unit in each bar, one unit of each nutrient is combined to increase our energy bar by one unit. The energy bar can be white (the combination of Red, Green, and Blue, of course). Different plants produce different amounts and combinations of nutrients in their fruits, and the color of the fruit indicates which nutrients it contains. A yellow fruit would contain Red and Green but no blue. (Think of the fabled ``white tomato'' that contains a perfect balance of all three nutrients). Different sections of land are particularly suited to different types of plants. Thus, certain plants thrive and produce a lot of fruit in one area while struggling and producing little fruit in another area. A life bar slowly decreases over time (and never increases), representing the fact that each gardener has a limited lifetime. If the energy bar hits zero at any point, the life bar decreases faster (and various activities that would normally consume energy faster consume the life bar faster). Plant manifestations are controlled by underlying genetics, and nearby plants can cross-pollinate to produce new varieties. If a given community member likes you enough, you can mate with him and produce two offspring, one for each of you to take car of. A newborn offspring is completely helpless for a while and need to be fed a balance of all nutrients for it to advance to adult status. Once it becomes an adult, an offspring ``moves out'' into the world to become another gardener and live under its own control. Once your lifebar hits zero, you die. Your control switches to your oldest adult offspring. If you have no living adult offspring when you die, the game is over (your genetic line is finished). How a neighbor reacts to you is controlled by its underlying genes (a series of parameters). Your genes change over time according to your behavior---in other words, the system search for genes that would best describe the actions that you take. A cross of your genes and your mate's genes are passed on to offspring. Sample genes: \begin{itemize} \item lifespan \item amount bothered by invasion \item amount bothered by theft \item tendency to invade \item tendency to steal \item dislike's effect on invasion tendency \item dislike's effect on stealing tendency \end{itemize} Both offspring and plant seeds can be sent through some kind of portal into an online archive. These ``user creations'' will be downloaded into other player's game worlds. Thus, the desire to ``spread your genes'' can be carried farther than a single computer. The tangible goal for the player is to survive as long as possible. A viable strategy for achieving this goal will be slightly different for each game, and may vary throughout a particular game, due to: \begin{itemize} \item different terrains \item different neighbor parameters (and thus behaviors) \end{itemize} Thus, each game offers a new challenge, and something new to learn, because each game is slightly different. The hardness of a given game can be determined by: \begin{itemize} \item the total available land area \item the number of other gardeners present \item the number of seeds you start with \end{itemize} There are two possible tracks here: \begin{enumerate} \item We could set up artificial challenges for the player (such as, ``survive for at least 20 minutes in this level, and then you can move on to the next, harder level'') that increase in difficulty \item We could somehow build a system that results in gameplay that automatically becomes more difficult over time (through a gradual, and natural, increase in population, for example). \end{enumerate} The second option sounds more interesting, but we could fall back on the first option if we cannot get the second option tuned properly. Also, the second option may annoy advanced players because they cannot jump right to a ``hard'' challenge when they start a new game. For these players, we could imagine a feature that lets the world start and advance through time without them for a while---then they could jump in and compete with already-established gardeners. \section{Iteration 1} Features in this iteration: \begin{itemize} \item gardeners, with one gardener controlled by player \item movement \item plot rectangles \item planting \item hauling water \item harvesting and eating for energy \item simple like/dislike metric for all pairs of gardeners \end{itemize} Not in this iteration: actions based on like/dislike, nutrition, food storage after harvest (eat immediately on harvest), seeds (click to plant, no limit on planting), multiple fruits per plant. \subsection{I/O} \subsubsection{Input} Mouse interface with only single-button clicking (and no dragging). \paragraph{One-click actions} \begin{itemize} \item Click empty space to move. \item Click ripe plant to harvest it. \item Click neighbor to display like/dislike for you. \item Click ``plant'' button when inside plot to start a plant in current location. \item Click ``grab water'' when standing on water source to fetch a unit of water. \item Click ``dump water'' button when standing on a plant to water it. \end{itemize} \paragraph{Two-click actions} \begin{itemize} \item None. \end{itemize} \paragraph{Three-click actions} \begin{itemize} \item Click ``plot'' button, then click map to specify one corner of plot. Click map again to specify second corner of plot. \end{itemize} \subsubsection{Output} Entire map is displayed in one window with a black background. Water source is a blue triangle. Player's gardener is a solid white square. Other gardeners are different color squares. Player's plot is a hollow white rectangle. Plants are hollow green triangles when growing and solid green triangles when ready to harvest. Hollow in an unripe triangle is filled with blue to indicate water status. Bright blue means full water, black means about to die. Player's energy is a bar. Like/dislike metric is printed to standard out. \subsection{Game structure} Other gardeners pick reasonably-sized plots at random. They plant in these plots at random. They water their plants using a greedy method (most dry plant first). Their dislike grows one unit every time a gardener plants in their plot. Their dislike grows two units every time a gardener harvests from a plant in their plot. \subsection{Program structure} All gardeners (including player's gardener) are represented in the same way as an object with state. Gardener state includes energy count, a flag for carrying water, and a possible destination (if moving). World state includes a list of gardener objects and a world position for each gardener. World state also contains plot coordinates for each gardener and coordinates for each plant object. Plant objects track growth progress and water status of each plant. A table structure can track like/dislike metrics for all pairs of gardeners. An AI controller objects direct computer-controlled gardeners Objects for this iteration: \begin{itemize} \item World \item Gardener \item Plant \item FriendshipTracker \item GardenerAI \end{itemize} \section{Iteration 2} Start with Iteration 1 as a base. Features added in this iteration: \begin{itemize} \item Storage of harvested produce. \item Stored produce eventually rots. \item Gifts of harvested produce. \item Graphical representation of reactions to encroachment (directed anger) or good works (directed gratitude). \item Actions directed by emotions (gifts and counter-encroachment). \end{itemize} \subsection{I/O} \subsubsection{Input} \paragraph{New One-click actions} \begin{itemize} \item Click harvest button to harvest plant. \item Click stored fruit to select it. \item Click gift button to give selected stored fruit to closest neighbor. \item Click eat button to eat selected stored fruit. \end{itemize} \paragraph{New Two-click actions} \begin{itemize} \item None. \end{itemize} \paragraph{New Three-click actions} \begin{itemize} \item None. \end{itemize} \subsubsection{Output} Multi-item grid display in sidebar of stored fruits. Rotting fruit triangles get shorter and shorter. During a gift action, a small fruit icon ``flies'' from the giver to the receiver. Reactions to good or bad acts are displayed with green ``+'' or red ``x'' icons flying from the reactor to the actor. \subsection{Program structure} Harvested fruit stored in the Gardener object. \section{Iteration 3} Start with Iteration 2 as a base. Features added in this iteration: \begin{itemize} \item Variable soil types around island. \item Soil type at a given spot is along a spectrum from green to brown. \item Plant parameter indicating preferred soil type. \item Maturation time extended when plant placed in less-than-ideal soil. \item Plant seeds (each gardener starts out with a supply of seeds). \item Three nutrient types (Red, Green, and Blue). \item One unit of each nutrient needed to produce one unit of energy. \item Plant parameters indicating nutrient profile of plant's fruit. \item Eating a fruit leaves behind an unlimited supply of seeds for that plant. \item Parameters in seeds of a fruit are the result of a cross between the parent plant and the closest other plant. \end{itemize} \subsection{I/O} \subsubsection{Input} \paragraph{New One-click actions} \begin{itemize} \item Click stored fruit to select it. \item Click eat button to eat selected fruit. \item Click stored seeds to select them. \item Click plant button to plant a seed from the selected seeds. \item Click gift button to give fruit or seeds to closest neighbor. \end{itemize} \paragraph{New Two-click actions} \begin{itemize} \item None. \end{itemize} \paragraph{New Three-click actions} \begin{itemize} \item None. \end{itemize} \subsubsection{Output} Soil type is indicated by color of map. Fruit color indicates a fruit's nutritional profile. Seed color indicates a seed's preferred soil type. New multi-bar display on each gardener. Horizontal white bar displays energy. Vertical red, green, and blue bars display current nutrient levels. \subsection{Program structure} New objects for this iteration: \begin{itemize} \item SoilMap \item Seed \end{itemize} \section{Iteration 4} Start with Iteration 3 as a base. Features added in this iteration: \begin{itemize} \item Reduce size of island and number of gardeners (to make game easier to understand). \item Make plants permanent (watered until maturity, then bearing fruit periodically after that). \item Prevent plants from being planted close together (with graphic to show planting radius). \item Parameterize gardener behavior with genetics. \item Offspring gardeners that are cross of parents. \item Can only mate with gardeners that like you. \item Each gardener gets one offsping as result of mating. \item Offspring start out immature and immobile. Must be carried by parent (pregnant). \item Carrying offspring slows gardener down and consumes nutrients faster. \item An imbalance of nutrients prolongs pregnancy. \item After offspring ``born,'' it enters world as a computer-controlled gardener. \item Upon death, player can take over control of oldest living offspring and play on. \end{itemize} Not in this iteration: ``learning'' genetics from a player's actions. Player's gardener starts out with random genetics just like other gardeners. \subsection{I/O} \subsubsection{Input} \paragraph{New One-click actions} \begin{itemize} \item Click mate button to mate with closest other gardener. \end{itemize} \paragraph{New Two-click actions} \begin{itemize} \item None. \end{itemize} \paragraph{New Three-click actions} \begin{itemize} \item None. \end{itemize} \subsubsection{Output} ``Circle'' around plant when close to it showing nearby unplantable area. Pregnancy represented by growing ``bulge'' on bottom of gardener. \subsection{Program structure} New objects for this iteration: \begin{itemize} \item None. \end{itemize} \end{document}Cultivation_9+dfsg1_UnixSource/game2/documentation/gameDesign/Makefile0000640000175000017500000000044210450246221024766 0ustar pabspabs# # Modification History # # 2006-June-27 Jason Rohrer # Created. # all: dvi ps pdf dvi: design.dvi ps: design.ps pdf: design.pdf design.dvi: design.tex latex design.tex design.ps: design.dvi dvips -t letter -o design.ps design.dvi design.pdf: design.ps ps2pdf design.ps Cultivation_9+dfsg1_UnixSource/game2/documentation/how_to_play.txt0000640000175000017500000002477111077641026024373 0ustar pabspabsCultivation http://cultivation.sf.net by Jason Rohrer http://hcsoftware.sf.net/jason-rohrer Introduction This game is about a community of gardeners raising food for themselves in a shared space. There is no "right" way to play Cultivation---it is a dynamic system that you can interact with in a variety of ways. In fact, you may notice that two runs through Cultivation can be quite different. This flexibility is balanced with one hard limit: the game ends when your genetic line becomes extinct. Thus, an implicit goal is to keep your line alive for as long as possible, which will give you more time to pursue whatever goals you set for yourself. Your eventual goal in the game, should you chose to pursue it, is to open a gate to immortality and lead some of your fellow gardeners through it. Achieving this goal wins the game and gives you a reward. This rather brief document should get you started playing Cultivation. You can also learn to play by going through the in-game tutorial. Cultivation is Free I have placed Cultivation, and the underlying source code, in the public domain. You are free to do whatever you want with it. However, I spent many months working on it, and my family and I do need to eat. If you got some enjoyment out of Cultivation, and you can afford it, please stuff a few dollars in an envelope and send it to: Jason Rohrer 93 Elm St. Potsdam, NY 13676 You can also donate online, and read about my family's simple lifestyle (my wife, child, and I live on less than $10,000 per year), here: http://hcsoftware.sf.net/jason-rohrer/supportMyWork.php Translations Cultivation was originally released in English, my native language. However, it uses a translation engine when rendering text. The upshot is that it is very easy to translate Cultivation into other languages. Cultivation ships with both English and French language files. The default is English. To switch to French, open the file "language.txt" in a text editor, delete the word English, and type the word French. To create your own translation, you can start with "English.txt" or "French.txt" as a template. Drop your new language file into the "languages" folder, and of course edit "language.txt" to select your language. System Speed Issues Cultivation should be playable on a variety of systems. It has been played on a 166 MHz machine with a 3D card, as well as a 250 MHz machine with no 3D card, during testing. On these "slow" systems, the game was playable, but certainly not smooth. A 400 MHz machine with a 3D card should produce a rather smooth play experience. If you have a slower system or no 3D card, the frame rate in Cultivation may be choppy. Play will become smoother if you disable some of the more complicated graphical elements. Take a look at the file "features.txt" in your Cultivation folder. Changing a "1" to a "0" will switch a feature off. You must quit and re-launch Cultivation for your changes to take effect. Controls This game is played entirely with a single mouse button (the left mouse button, if you happen to have more than one). The keyboard is never used in the game. The most basic control is a single click on the play surface to move your gardener. All other actions are controlled with the buttons displayed at the bottom and left side of the screen. Buttons are enabled based on the actions that are possible at your gardener's current location. For example, after clicking the Set Plot button, you can select a plot of land by clicking and dragging a rectangular area on the play surface. On the bottom bar, from left to right, you will see the following buttons (each button icon is described in brackets). Note that when you start the game, many of these buttons will be disabled, so you will not be able to see all of the icons: --Plant [Greenish leaf shape of the plant that will result] --Set Plot [White square] --Water [Blue circle] --Harvest Fruit [Image of fruit that will be picked] --Mate [Image of two gardeners together] --Lead [Image of one gardener following another] --Poison [Black circle] --Pause [Two vertical, yellow lines] --Quit [Red x] The left side bar shows your gardener's stored items. There are two buttons at the top, above the storage area: --Give Gift of Selected Fruit [Fruit with white arrow] --Eat Selected Fruit [Fruit with white chomper] Below the storage area, at the bottom of the left sidebar, there is one button: --Discard Selected Item [White x over the item] All gardeners start the game with three seeds in their storage areas. To find your starting seeds, look above the discard button in your storage area: you will see three items featuring inverted brown triangles. As you harvest fruit and eat it, you can obtain more seeds. Nutrient Usage There are three nutrients in the game, Red, Yellow, and Purple. Standing still consumes the least amount of nutrients. Moving around consumes more, and so does holding water. Moving while carrying water consumes the greatest amount of nutrients. Being pregnant also increases nutrient usage. When your gardener exhausts its supply of a given nutrient, it ages faster. The speed of aging increases as additional nutrients are exhausted. Eating fruit replenishes exhausted nutrients. Fruit is colored according to its highest nutrient. Gate to Immortality The gate has five rings that must each be opened before the gate itself opens. Rings are opened by the death of the gardener that you are controlling. The first time your gardener dies, you will see the first ring appear at the location of its death. To open additional rings, you must make sure you move your next gardener back to the gate right before it dies. If your next gardener dies away from the partially-opened gate, an additional ring will not open. After the fifth ring is opened, the gate itself will open. At that point, any gardener that moves into the center of the gate will pass into limbo and await immortality. To finish the job, you must pass your gardener through the gate too, which will cause the gate to close. Your gardener, and whatever other gardeners passed through the open gate, will become immortal. What about the other gardeners who did not pass through the gate? They're stuck on the island. Play Hints The descriptions given above should be enough to get you started. If you are stuck or confused, here are some hints: --Draw a plot for yourself, plant some plants in the plot, keep them watered until they are done growing, harvest fruit when it becomes ripe (after the flowers die back) and then eat the resulting fruit to gain more nutrients. --When you click the Plot button, a cross-hair is shown as you move your mouse around the map. Click once on the map to set the first corner of your plot, and click a second time to set the opposite corner of your plot. --Pick up water by moving into the water and clicking the Water button. Dump water on a plant by moving near the plant and clicking the Water button again. --A tan circle over a plant means it needs more water to finish growing. After a plant is done growing, you no longer need to water it. --You can only plant, water, and harvest in your own plot. You can draw a new plot for yourself at any time. Your plot can intersect with the plots of others (intersecting plots are the main sources of conflict). --You can only plant if you have seeds selected in the storage area. The Plant button shows the leaf shape of the plant that will grow from the selected seed. --Dark seeds (with white backgrounds) prefer dark soil. Light seeds (with black backgrounds) prefer light soil. The color under a seed indicates the color of the fruit that will result. --Give gifts of fruit to other gardeners. If they like you, they will give gifts back. If they like you a lot, you may be able to mate with them. If they adore you, they may even let you lead them. --Pregnancy progress stalls if you are low on any nutrient. --When a baby is following you, it shares all of the food you eat. The baby stops growing when it is low on any nutrient. --Any gardeners that you are leading also share all of the food you eat. --Fruit rots eventually, so harvest it and use it quickly. Rotting fruit turns white and shrinks. --Keep an eye on the buttons that are enabled. The state of the buttons tells you what actions are available in a given situation. For example, to tell when fruit is ripe on a nearby plant, watch for the harvest button to become enabled. --Watch for flying emotions. If you're doing something to please or anger a neighbor, the neighbor will let you know. Anger is displayed with a red "x" symbol, and pleasure is displayed with a green "+" symbol. --If you mate with a gardener that another gardener likes a lot, you will make that other gardener angry. Chose a mate with care. --Click the Poison button when near a plant to poison it. The plant will die, and the soil around it will become forever unusable. Poisoned soil turns black. --Fighting gardeners can ruin the island for everyone. You can use the Lead button to quell a fight. --You can't plant anything in the water. --When you die, you take over control of your oldest living descendent. When you run out of descendents (in other words, when your genetic line is extinct), the game is over. --After the game is over, you can still move around the play surface to watch what happens with the other gardeners. Clicking the Restart button will start a new game. --To successfully open the gate, you must survive through at least five generations and make sure that each of your gardeners dies where the first one died. Each death will open a new ring of the gate. --Only the gardener you are controlling can open a gate ring by dying. Deaths on the part of other gardeners, even your own offspring that you are not yet controlling, has no effect on the gate. --If you walk across poisoned ground, some of the fruit you are carrying will become poisoned. Eating poisoned fruit can offer you a quick-and-painless death (which might be convenient if you are trying to open the next ring of the gate). --A gate is destroyed if the ground under it becomes poisoned. A new gate cannot be started on ground that is already poisoned. --A gate can be opened over the water. --What does immortality mean? After you successfully close a gate, take a look in your Cultivation folder. You'll be rewarded with some soul trails. Cultivation_9+dfsg1_UnixSource/game2/build/0000750000175000017500000000000011401021117017500 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/build/unix2dosScript0000750000175000017500000000013710455474111022405 0ustar pabspabs cat "$1" | ./unix2dos > tempUNIX2DOS.txt cp -f tempUNIX2DOS.txt "$1" rm -f tempUNIX2DOS.txtCultivation_9+dfsg1_UnixSource/game2/build/unix2dos.c0000640000175000017500000000035310455474111021437 0ustar pabspabs#include #include int main(void) { while(1) { int c = getchar(); if(c == EOF) exit(0); if(c == '\n') { putchar(015); /* ^M */ putchar(012); /* ^J */ } else { putchar(c); } } exit(0); } Cultivation_9+dfsg1_UnixSource/game2/build/makeDistributions0000750000175000017500000000221610531421650023140 0ustar pabspabs#!/bin/sh # # Modification History # # 2006-July-13 Jason Rohrer # Copied from Transcend build. # if [ $# -lt 2 ] ; then echo "Usage: $0 release_name unix_platform_name" exit 1 fi rm -rf unix rm -rf windows mkdir windows mkdir unix # work on unix tree first mkdir unix/Cultivation mkdir unix/Cultivation/languages cp ../documentation/how_to_*.txt unix/Cultivation/ cp ../gameSource/features.txt unix/Cultivation/ cp ../gameSource/language.txt unix/Cultivation/ cp ../gameSource/font.tga unix/Cultivation/ cp ../gameSource/languages/*.txt unix/Cultivation/languages # duplicate unix tree so far to make windows tree cp -r unix/Cultivation windows/ cp ../gameSource/Cultivation unix/Cultivation/ cp win32/Cultivation.exe win32/*.dll windows/Cultivation/ cd unix tar cf "Cultivation_$1_$2.tar" Cultivation gzip "Cultivation_$1_$2.tar" # compile unix2dos cd .. g++ -o unix2dos unix2dos.c cp unix2dos windows cp unix2dosScript windows cd windows for file in Cultivation/*.txt do ./unix2dosScript "$file" done for file in Cultivation/languages/*.txt do ./unix2dosScript "$file" done zip -r "Cultivation_$1_Windows.zip" Cultivation Cultivation_9+dfsg1_UnixSource/game2/build/source/0000750000175000017500000000000011401021117021000 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/build/source/runToBuild0000750000175000017500000000125310532332134023026 0ustar pabspabs#!/bin/bash # # Modification History # # 2006-July-13 Jason Rohrer # Copied from Transcend. # # 2006-November-26 Jason Rohrer # Added tool-tip stuff. # cd game2 chmod u+x ./configure ./configure echo "Building portaudio..." cd ../minorGems/sound/portaudio chmod u+x ./configure ./configure make cd ../../../game2 echo "Building Cultivation..." cd gameSource make cd .. cd .. cp game2/gameSource/Cultivation ./Cultivation cp game2/documentation/how_to_*.txt . cp game2/gameSource/features.txt . cp game2/gameSource/language.txt . cp game2/gameSource/font.tga . mkdir ./languages cp game2/gameSource/languages/*.txt ./languages echo "Run Cultivation to play." Cultivation_9+dfsg1_UnixSource/game2/build/source/exportSrc0000750000175000017500000000036310455475205022744 0ustar pabspabscvs -z3 -d:ext:jcr13@minorgems.cvs.sourceforge.net:/cvsroot/minorgems export -r HEAD minorGems cvs -z3 -d:ext:jcr13@hcsoftware.cvs.sourceforge.net:/cvsroot/hcsoftware export -r HEAD tempProjects/game2 mv tempProjects/game2 . rm -r tempProjectsCultivation_9+dfsg1_UnixSource/game2/build/source/cleanSrc0000750000175000017500000000032310544404552022475 0ustar pabspabsrm -r minorGems/ai rm -r minorGems/bench rm -r minorGems/doc rm -r minorGems/examples rm -r minorGems/temp rm -r game2/gameSource/videoCapture rm -r game2/documentation/slamdance rm -r game2/documentation/html Cultivation_9+dfsg1_UnixSource/game2/build/makeDistributionMacOSX0000750000175000017500000000166510531421650023777 0ustar pabspabs#!/bin/sh # # Modification History # # 2006-July-13 Jason Rohrer # Copied from Transcend build. # if [ $# -lt 2 ] ; then echo "Usage: $0 release_name unix_platform_name" exit 1 fi rm -rf mac mkdir mac mkdir mac/Cultivation mkdir mac/Cultivation/languages cp -r macOSX/game2.app mac/Cultivation/Cultivation.app cp ../gameSource/Cultivation mac/Cultivation/Cultivation.app/Contents/MacOS cp ../documentation/how_to_*.txt mac/Cultivation cp ../gameSource/features.txt mac/Cultivation cp ../gameSource/language.txt mac/Cultivation cp ../gameSource/font.tga mac/Cultivation cp ../gameSource/languages/*.txt mac/Cultivation/languages rm -r mac/Cultivation/Cultivation.app/CVS rm -r mac/Cultivation/Cultivation.app/Contents/CVS rm -r mac/Cultivation/Cultivation.app/Contents/MacOS/CVS rm -r mac/Cultivation/Cultivation.app/Contents/Resources/CVS cd mac tar cf "Cultivation_$1_$2.tar" Cultivation gzip "Cultivation_$1_$2.tar" Cultivation_9+dfsg1_UnixSource/game2/build/win32/0000750000175000017500000000000011462022731020454 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/build/win32/iconSource_256.png0000640000175000017500000000342210532326267023701 0ustar pabspabs‰PNG  IHDR D¤ŠÆPLTE2£$t&^"$"æææF>šÚ 2âBB6v‚t¤¾ž*4œ(€nBÐ )®‹'cvG*†Zv¤¦¤g¨F zîBšÔ J@Ç#\jkPaE:::|’.Bzw×2[A—^²:*ŠVšÚn&`,B>'v73«]qé9î:¿#bªŽ.žnRžBRWB†nzN2ž!¼KKf‚rÊ>B]RY×1¢ÚzåMsK^6žr–:y«j¦Ö &~7§BSy:øU^šÇêîê[KiÃ6*‡;b¬b6Jx¢ÊfÊâ6e§N2¶&%tO:V&I~zÊn666'j=2žb í.˜hrzªªªuŠu!liFZ "ödæ2ÕÖÔ|ÞU?¶P¬,~~Š.æ í6°cS:¶VN:"ÈÊÊ8ÔDšìX&€NJ®”ÎRÂB–r(†0b×U~† ^þ^8\Jòòòn¦ˆ‡iÃz7£5Êæ^z&2ÊþJž0ööö&~vnb>N†F’æNFä'€RªBB^¢f¸J"T=,’%:FZ>n†RÇ5Rš–nžnS„ÒfXš"i‚ž*R ^Â:nrb¯3B†"@ä0*Š‚ræ^ ô*l0²ÖBO•YB‚bQSLÀ"’—‘ÚbJž†îG6¶B¿÷J>›IÛBú>B¨(²òjUªNI¶*^š&80hL.žbb’r6x"Òø@Jï5¹º»3ª~ÆN#wb×emT˜NJºR '»e±ے΂.–6bŽUgÛ2NÎF$x’FRýýýZ®Z,.6¢,2®zînb^*\B–ôLjöNMç7nnn8VXVVVXœh„x‚f†.f>’R:6ZL„jŠøV+Œ+ kWbtF¦FÐKHúTV¢r¦>Fw}‹bKGDˆH pHYs  ÒÝ~ütIMEÖ ;x0™x˜IDATxœc˜]1v11v7oI=„(œ¥·y⿉‚Ldøžy^¾©‡® ËÎ3ý¾ÎýÐK/BK^ˆš*Ûu!+г».ã–¨S´o¾ïüóOø–ü”¹n‡¤@ïæu“K¡'ôr„0æ\0g4|BûŸ\ž² ÛÏ'në¿qª¸ÃË[ÙŸQT£ül‡öǽùë^$+«ŸTòØ7Tæz DÁÍën%±æâjs«ÜÝÝÏ“L9( b~éÓu°‚O“þXsó`‹?6Ö¸ši3£X7%å®Í½ü ~›A x½+ç3þðg¯îÝ×ÏíœkñÇÊg­cwðµS÷'èyš„>67ŸüènÊ=Ù½ß÷:] ¶X½îÉ&†M*Þ¡b€ ®ÿì̘#{oÑÎc>3öï¿8{öêɦ @i½ãÀå.»ë/f]ȘiÃ-¾vmÚþ³[}2æ2ƒä7‰¾t»Tðìgå™õë¹W®ýóêlÐE«µò ‘¹‘»¦°Z_JŸÄpÓä£ÀÁœ«Â«*Ž4þ äªcMõzøµÍíß$e™S²ë@–nzÀ¥ètËVã{±cáC8ÿ2ñ~6ÃM¿SŒ±GízŸqkwYZšÅV |¡}“áý,†˜ôKó™Y€ò¹b^qKç.Ó¸øßñJž|ƒØ.û&×S~Ù 1‚¿NhžoxÿöüÇoç,ûÇéNç•5yÂ3Î?¡¾'†!ÆóWÉ–Ô‡S€fÖYZlû°ÜòÛ<áiá±EN\ÚÓT ÊÔ0ì(ã+W¬—.Xxµ[8Is…ȬþÄëÀ¸°{&ª­ÙÀúpÊyã΄o•/¯ø=ݺ-Ì?‰Éû€r0º•üœÿÃ^NîRï’„§ ¯nm*çíᔊ'˜=òõ|ûò{Û•L§ªÏÓ~+)Hbãé)Лx_çiSëÓdz¶ôÙ;W5{[kíÉ“Bþ;\”!iòrúÔ’Y±æ"[Œ­ûæë7ý?órž¦&› а‚˜›‚¿~þaŽtp¨e™CZÚˆÙ;t‡ 8ƒAòPEå‰-ªªµ+˜fÝp–öàô–á‹AÊ817¯Ëô÷Gµ,`^ÁÄt†ÛãÌÑË1(YOÏîrú‹œ;ú½wxWþÜñì2FæÕ»Üq]ÊÅäÀžÿìéÈŽÁÌÝ“ôôôb€`s6BA:üËíK]ŽIEND®B`‚Cultivation_9+dfsg1_UnixSource/game2/build/win32/Cultivation.ico0000640000175000017500000000427610532326266023472 0ustar pabspabs ¨( @í Žªb"ÀL±et$5ÇR¤tôZFzÃiÔÖÕ#Ç@Uø::Â^B§7J÷¿fÒ„:²^J\8nž.BªR:–r@T.,¬Pž6^¨gN§e˜.WRB2ædÊÊȆfVŠ*UŽbi"nÚšš^'‹å †~6b1×Y£2#¿: »º¹^b¾j„L’x$B\*—A[;‡*>úBNªUn¦^,¢6wzBBâ2r’bhœX=T"äF¶?¢^B Fbw#jò²ööö6Ãiæ..6’bòòò%’,¢VêîênîzVøŠžJæææžRî8x<BÖ²†BnÊz&z>7v'¬b.’|KÐ@øÒJEaP–šRK¼>Êr7çMXìš":NÐBbÚS6BRºJOt%>¦rV¶:r–BRΔ‚Š*b†Z=j'brn†*9éqzÚ¢Gî†Wk í &¶25£7¤¦¤KsM‚v6–.€'fÊ¢U×b^þ^"†B^ærLh00†(8&2ÛgN˜TScSQÇ®) 0žJÂ(¨BnžnÖ¦RFÏ& uŠuLô–Z®Z:: R*nnn^B<^&Y•OF¦F^æÊ›>R’>v~&~& šXFŒF‘—’6âÊ ÔN€&K[VVV®2‡ˆNæ’xJ+Œ+œ4NöjÛI5ïJýýýTúHFhõ"x6Û!ž2NÆ~&V:Ê2Š~~>F666FÎNJ¸ftb*¶IDÔ8»' 3¯R]Bj«yyS]«3~I°6bž.B¶6†n>"$"×0l*×2šž‚b‚B¦n,`&F†NGvc*žUÞ|‚Î’,þ‚x„>bnil!‚fKª3kj\vZXV8zrhmeBîzZF:ååååååååååRRRyyåååååååååååååååååååRÕ•ÉÙ¤U¡OKK¡O•yååååååååååååååR&Õ?..hŒ¸«èŒ«‡xŒ­Õååååååååååååå¼É]¸¸rœñ"º$ºŒìÕåååååååååå弎Ƈˆ"œ33"€“©$h«]²ååååååå弎Æx±tÃtJ©©Q«jŒ¤RååååååÕ?£ª€½tÖËSJ+{Ÿº2=Ýݤ/¹ååååRÀ1Î8{¦8ä€ÖS}ǵ‚ž¦††d%(Œ¤×ååååOol+6°0¢ŸžïÁÌ‚…MáŸ9†%àÂ/¹åååסxJ±}ëëí97’–æ; ŠÛj%à™hÙåå&­ìXÝ®P¶D-p–æ›9_¥mŸÞË¿(«¡×¹]j®D¶¶¶¶-ïïËg_°ÓŠHßÝ"t€¸èÉåÆªÖ ¶D¶¶¶Iéé°ž<¥é0!‹ßçÏätˆBO¹ŽÆˆÝ-®¬›-¶¶›@gÅ[Ì!pïs‹ßWä*Jˆ O?BJÔqqqâ|LîgÅ#¾kï‹^®|§}{ÑYy¼YÑ*»qqqq笖Vãfnáá`sP¶¶¶}{1F×]Ñ e„»qqq,”~ZÌãv#Nc¶¶¶¶¶-2è1y¼UB*e´Ð"AšÏ'ÚÊ[C [~IP L¶¶PÍKå&­x‡êe>´+ÜÚÊ[—\¨¾aÊ^®ÃâçDPËB?å&É.€êÐe>)ÊÊá\TEÅqqqq,|2ÍɹRÉx¸ä*Ð ÄÇE¯¾ »qqqqÃËŒ¡Õ¹RUjä½½˜‰Ä wNwÌŠ` „G,q,Ø«j¡åå/Ž«‡J*‘<[wu‰³·µÅ`[aEÊHiÑ1ÀåååU¸½½`[[…ƒ 5>)¯cu‰)ÄloYåååå¼Éͪ½`Å‚ µµð´e zz½BÉååååå×­ŒX±‘‘*Çb· È +ÐÐ*ä4j]ÉååååååR׎Íx±ˆ+b :z*êÐ**‡¸è²&ååååååååR¼ÉUBjˆ+´:z*Ðê½*½äˆXƲ&ååååååååååR&É]ÍèÎÈÒJ4J±Jˆ4ªx]É&ååååååååååååå¹&?1Î1jB.jª.ìUŽ×&¹åååååååååååååååRÀKÀŽ¡U]²­Õ¼Råååååååååååååååååå¹&&¼¼&RååååååååååCultivation_9+dfsg1_UnixSource/game2/build/win32/iconSource.png0000640000175000017500000000455110532326266023310 0ustar pabspabs‰PNG  IHDR üí£bKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEÖ 9.mS:öIDATxœu–Ë[冿۹ÚçØ>_ÆsMH¹KAˆªÁU„?€JeGשXV¨•*6H¬ªªH´‹VŠD7Ý „J…B!Aa$™ÌŒgÆc}lŸãsùî]q)ä÷¼ô~úô> îwP@Ì!aÈäZ`­ 2cH  ‘V†æŽVÐP Hø£ä¾é@C35«“Ò3³1.úJ$Ìlö‘•CxäÉÜçÓ–f%YTUQSÜŠüóc$É‘ïÀ=º]ÿù¡¾ß "™). ‘VƒýtСÃu>^“YCUÍK@£û@Æ;#ØõÎ|ŽK¡YøçGåã92D~È9CvÔuÖ }ªLû¬{2Û:[ôOÈ9ѹ@##%Þ©n;«7½Ó×£f02ë‘ØVÝRÎT"ì*€°L‘٘ȜÈÄшT§f#L6òô’iS ô}âÈœ¯gµ>/ÿÌjß%þžÌ2âÇrÃ>Âjk@9P+%H\Š-¹Ph|ÂHÕK”éíŸÊyKKû;¨™ïÀl~埽â¬iÖûÍåÜ ÆÌ –Í=®´ÑD…0âÐS’Ã<âØQŠiDs䌜5MÊ 6y³N…#ÓobбQÛr^-ذš¦Q¬ÂöêB©Ï¦B¸$¯I¨s UÓ ¥*—'I;£ÅÖl¥¹Ì•Ì”¤‰µÐGÆmw]?¬¦·-€¨‘9ÇåC{yÃY½iµzȞٲ䨊k8k-D¤%É5×™B f³J¬y«ÜKˆÆÞÌdLr;Såù’¶ÓéW|“ ²° çÈßjß¶;»š§ÈÕ¶á‰Â‹kÜY'Q6Ö4/ñºC(Ì ¸ã©‰¿ïæp!— #jvª²A&̆Í|^ëš –½t‡G§µp$™±³ºeµúÈœŸ‰¥(g¾Y:LGa/§rúDÉ=ZƒJƒƒÖhÀ‡ÍÜW8zó³è½¿‡áNZÚ´ uæ´ÑDfcY«R@…ì¹³² d†ÌM\¥›“¤ÌEdÑ…sµî>wÎi‰üw'Ü2lnäãýéÕ?÷æ]vï=sÁÆG*Ì»ÙMÄ\‘J¨¨O ¡Ø™h^K‰Õ–Ø¥#Ø\HìiÔÙ’™éT•³ ªS¹Â+VúHÀ‡”Næ S>x?‡ßþÛ .]º´¼¶òë?þ^¯ìòÉqr‚HÌ —sˆ%DÄY–F‰™š÷9ƒ,( Gæ¹åëIçn#kZh±”.¹eÇ‚‘äæ±_þáÒåË—ƒÁÒÒò‹/þêâÅ‹y‘7 »èì²áa¶e€„ÙˆIYºk | ¸>Š?ýw‹|›í¹àØ1x®– ½#”ˆGzëfÍ— ýÈÇ£…vù¥—^zá…¢(ªÕjFc<‹gEÆó½:©Æ`@¦U~¸ëcÿƒ~ñ—8ê -õ‚]ßLŸñ.<Œíf ¹Wø“ÚŒÖ3cQRâO{›wïœ?waqqñ^KJ©kׯ'nnW8$«=CZ–Í@kéþø ÿôxºÏµÔ­Áé“?¹øÔooß¹ §óÕ”:³± ‹,™¦Ý1wÖKo^~íÎæ)¥ÖZ)ucãÆ[ïÿ­òŒåŸÄm˜uNu³nÝÙÁFÀòëxÑ]§f—1!\\\¼téÒ³Ï>{ýÖî(;êEwÜæ¨‚xMç_LÒI§::•Û7_yõ7Î<¶ØY%á»wÿï+Ò<Í<a¡`C‹b€p ¶^ûÓëW?¹zíÚµF£ñüóÏ?ùä“c{_ÝvbYï4%×ã¾W ô´œ4Ìú7{ï¼3Mƒ|êÊ„"3¥ý˜…ˆh eækQ‚Ø•´Õj¾üòËœsŒ1!0ŸÏGIÏ,ÙÒÚlÂt·cÏÏî"Ñþ {‘43œ‰ÊŒ¸U6u)›hÅ”b˜GeI-Ť©d™MD\‰ ýç»o3Æ,˺—®µ¾òÑG£I´Ò,fyññb¸Óm2;–ÌÂåÔ¥Ô™g…ÚÙÞ‹op™j• ºŠz´¿4Á¾´’a'"õôÖÞé.__Y·,‹1öÁ¾þÆ_[:mÇnÎZØV•Œ¦…ù‚hn1—Oиësæ3öèW3¤uäYy¯2¿ýD±wV¢¥%³&=Hº¡Ù Á©èmñÖ^½Ò@)Œ°pO®«iÁ@™Z«©%[¡ôL÷•„õ6js^ž¡è¡çn<2¥½|sË»(ۮɬ¦¥A€FZ82oAÙn5KÇJAh-[ñPç<ÆE,ØœL2Zòf§öòsŸµ“¾m‰ˆŽQe:'’ƒc/¾½•Ö×´l)êM¾žLM=6:!³‰æ;ÑÝ´·,ZgíA<ò-N«3£ê@×ú`•ƒýÃÁÇ… M0ïI\Ör¼7ìD“ƒmTù2} ï> äñ{«‰ø@+¬%P”t(f9MVÙçh&R޽F÷1“W§Áæ!ßçÚœÝb0MÛ^¯ìGi’M •ö›ì³µÚ?’tOý_ˆøæwF_c6©Î®Ÿ Cêuì¶¥fãt“.oülÅx ôñ,FlK¤¡S*Î.%§N¤BÁéܺzPÃå«ù~%¾uŽ>­øið½Ñ¿‡àŽH:E€±-ªTL#CÇPˆC ME 4P€e|X¥¹ ¥½³ÙP›ç½ã||N±&Pæ7±_WtO]€2´2Tá°i ¨DFAkc:¦3cïÐùRÙ™-%Qr@L%v»îõ[ËiÜFæ™é•‹ÅþIM Œï $üù…’Y3RËm¹G®@r#ÛN‰Oª’EÛ¨,±0†ä°rZäûµbäÝ'èð9zØÔƒÜßxÉZŒË1wßFÖÌYQZvéš”E&b‰,#ß>ÏãÓbZ’ôÇ“îøº? ‘AR@œ¤€2°±ë«BˆÄ”9ÑßÏÝÿ fÓ ,&IEND®B`‚Cultivation_9+dfsg1_UnixSource/game2/build/macOSX/0000750000175000017500000000000011401021117020632 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/build/macOSX/icon128_color.png0000640000175000017500000003465710504225627023760 0ustar pabspabs‰PNG  IHDR€€L\öœbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEÖ  0„Ã#Ì IDATxœì½y¯dÇqè¹sj½KoìæNʤ)YDÛ`û=Œ¾Ø|¦`þxÏ›ägÓÖb‰¤(Q\{¿[U%·ˆ˜?ª»I˲%¶I¶f†ÂEáâÖ©sâ—[æE…¯äÉ =éøÿ»|à ËWž°|à ËWž°|à ËWž°|à ËWž°|à ËWž°|à ËWž°|à ËWž°ü€þ¿¥ÎaŸô üNy¤JÀÿô/Påáç”þðGØ,€ÚDн6õ·Ý-¤úPõQ)Uòi  € Š¿Î/Iþ è=îõþð*¢ üpD#£ÉUÏHò‰ÁQREP£èA‘ö—E@PT%PjžØƒ~JþìõΔþo"¨Õ Š ¨h2RA“Ádüdà#(‚’¡Xƒ@ŒŠ ‹Ç ø À^P€êƒ±üPï䔼¢$ÊÊ %  ‚¦ É€Ä b(TQÀ0*Ä*û=ž_  TÝ(¥'e‘ž8DÞ«•Œ¢WòbfÕÎ*š ²f‘<21šLa2]OMDR[î;DŸP’@§ì”½ÖÔ¨"ŠQ5{k†ì•œŠ%U1¿}ùâåÀÃI@!SSl[ÌœÝ25W7ècH£rd”Ää‹™õ¶Íl vB+õb¦l(ò…‡&¬%y² dxœË´P± ¤b@­²Óý´¨ ÔÔ€X6ø)ëK’'‘íD¡7Ýd»hÕ.ksuÛ½xË®·ùÞ*Ÿ´sÝ"à˜yòÔMH”AÁmÜÁhæ ˦uë…TB ZC>9Ì÷À"hõ<ÍösB¹QªŠ¢EÅUp2´Å,¨Ùi u·ª»µ–FÊ\ËLkPvJµŠ”¿¤Iðå ú@ãTÐF´©>p%M¶‹3ÓìÀõþòEóTïŽÄ-­]ìÈOXŠUrIR3¢Mu4‘?Êþpª#æS/É 7vY¨I¦a tuW;j0Ÿ,¸d;7¦“²Yæ“#ž–u{ )km´¶R*¡hmdIüÛ î‹’/€2RýDû&¡Ð&r#¹hVv6P;š®GŠ*ÉìüñÖ;GÓt…ê2iEÀ¨"ª £© dš”)h¸ª ¨™À*T™,LV D³p\' ;ˆu»ÎηùôÐt½LÛ:¬¸?¬Š  €`ïź/R9ä ð0¶2© )`2™ˆ6¡Éöà~÷ìMwxA>£ÍU2k©Ómý*šP«dØÍ+(`Õ¢’Šj»ÄZQ­~UÍ\e"ΈˆR¨ö\Î…|6A"NjWÅ®·äÀš¶W-€ÉnTÛxû©t›©mëP@IÅú/!Xûâ<Ò~F›ÐDt‘츷õnqÚ>óA¸z&µF«R)ôvÔMÔ*yB«`Ô.€Å¨#Oµn˜AL`ô„Á°]UÓ1£‘JP‘ÚÚ^Oh”‹1ÀˆI’´h«í@3€Èyr9\9-ç‡ãûZÏI@A Å(`ú½S)_ TD䇮ND?’ë÷Þ¶ä·RH"ËØU'Én½¥f ŸM£èÀ "α(t.¦™S5]¥ÀHH( €¢ëT VB´h;¥ <ŠTŠR¬&S·ŠAÈ* [Àà/±iN¦[Q¢/žËö¸n‘`TÏÅÕ_Ðløb(*R%Ñõäò=ù†­é¶vvnæÄS&WdÍPÐŽ¦(Lˆ ¨ °‘=>(a[±ñâY\Q ¢0ª*ŠÈèÉ´ •P«Qy¢¼Tv‡ªìx`-L^*šŒ6׋5÷bgv5Ðí¤É1  ˆƒ¹#ü"ü¢/€ Éèr…žÂ†üÝH~kÚY\ØùÆ./Ðõ £–ÊI%bš’Ä,… ¢i =5N}UcEZ=¬Y3 ¨ª¨2’ `:Ad@ŠH²JѳrIâ jᤪ;êΕ˜ùÆV¨½¯›KJRZ@ ¨R⟷|Žè¡ÏCù¡ö/¨Ùß‘ßÙÕ©]^ß“ì|cÚí„TH˜Á2¹˜´feU²`\CƓΠ:€¤ó5bPÚ[ÙŽ€@ * UA,"!YP‰*YyBIbİ7âbâV¦™oÑbÈíÜúRç…|­ƒK¶rŸÊ¡°QþüÇëçrÅ}Þxïnî#¬„n$¿£°#¿±óS³w+ô_ ´o RF›ÑF4‰Üˆn ß›fkægnyæÖ§4Û`¨&·îýz´¶@–êJ]fã…Ehö‹8l Z#Ô4j ÎKk(VåŠa²T k(‘UrZ(½˜" ãU÷EUPÍ,A„üÄ”ÕÌ¥Á")™ÆØ™qëó:våôÀt*²ê¿ÈƒøÏA럒ÏcàÞø”cߤ½ý1Í–š3w|'ß1‹ tƒÄlÂè×½é&jbÓ€™ iê¼D©Ì¬Â€ÙZpD„¢ L”P m4à b/F¡ ±Xafkž˜ Ð5ˆhTYHêÀ–-RSxB$$Ïd•#rš×¡É§®œ'F©Ù)‰X4Eÿ £Mä&ô;ò;j.Ì⾿t»{ú¦;ºO~TÍD5£Jª»Ü¶Øâ¬øÖ;i8jRs-ÔŒìckû™7Kš9æ¬xßÍ…jFÌR),*1( €@Û:BÔ ¹—!%UDU­€ˆ PaE«*ùdgª¢<Ùº™ñˆuƒR ¹ÁÞ·릖3‡j‘½–öóµBŸ¤ú0Ç0¢ëÉï(l©=·‹SpæÎì|8!E3Kn•ò…”SmÖa–ì¢ó³¥%ÄëÄe4Éc%°6wa;oñPV”%Zëg#@Ó©‚1(- Y dòÅÍ54ÈsœJ¾(Ó˜“2HE­Š^©AàU•Õ.Јª/À"ÑHìý±–³ï hDÓ—«6uc”08üוöHþ+øAžÇFrÓC—kÛs»<³‹3ÀIj!—ÐfÓdtÕÎÔt‹8[Ϭ!£…HNM#« 39è2(ë¤@‚Á¢aÈBZ\(±z[#DÀyhi¶4!¨Óª›<´É %õ)M5s&‘ ¨*¼÷mT$É~š´OÇpe#¥HL©ÐF*¸õNrD˜yµ&‘ð9ö=6}dúë£Ì>º‘Ü`fçvqîÖgîà´#†Ñ´‘BS•™œ´‹à ÙêçÃâ26&è€ÑxcŒ‘¢,€··´€$ìµ0T‚BPA `ƒ ©'^]‡þÀ,ÝlfL–ÚˆëJئ©Íã(nª%–¢D8¡&VDPE0¡*g,Û œµF›p9ªfÞ*/ò)(ÏA@ò“€k¹¬ÀûI0PØPsagvÔöîhgB¤Ádaañ¦³µÙ5ý,˜Fídƒ»à3TÅGDH;Óušjt@ F‚k­³Úµ4ó&Ì©»bgç¦÷Æztʾª(´ÉHd%$$ÑHÅ"ÈB"Ä‚5›*žŒ¡Pc¾R*]`÷ì†BÃÓ$©`u*¿[G_(€=…}Ò-£Íä ;jÏÜÑ]pÝt$?¸åpAT©jÆ`| ÖŠ¥ra’5,^Å£%kÉ PV-v‚¬s:EW´4¤aïØ[€ )JŒP²ÏÖ•&ÐÌÛ6ÔY-† ^}¤[! -¶n}N³ÆØ€>ê@}|ŸÀí×G5E²¹‰Üˆagº š¨PlÈä+#0F²@}L6 „ÕÓ´+jrñX„Îf[*=5³‹’€d…ˆÔØÖ¨…š@!R&ÈÞND›ÆÔ5ˆØŒz/`ÊŠ‘ œë¤ ‘K† ¬•I';ß%b³)´‹¹æ¢Øá¼âbnÛ¶ nt6ADæ2xÞ è ÕŸüÑp¹^thR5Ÿô„}I¨0MA›÷3m2݈&C-Y™É15löeBV)HÖ‘7€Z{b¬LäØIµÆøZ”,…!Vé§I1^ÒÌÉÜcèpDÛo Y&(ž/:@RÜ„Õ “cœ€Œ*a®¬9‹fÝìøýÛ|÷ƒØoãÇγæAuÔz·lºÃµ»¼Œ—4_©UMºãó™HÞÐl—êVWÑ\FB@ ¢ È_&€q/ñ¾£ MD;ÚÕEsý®mÁ&À¬¥ª)•šê uˆVÕ©z- ’çŽ$ú†]ž°Æš Ç1–œ°ì¨Ï9q4°ZMeéÃáÂ!t ءsŒøÒîî†ä"4Y3UcAAlMX[‹Ê„â@´Ôš™§^ÆSù8é­šïÆ‹‹x¾­Ã(ãVÑëÚÐwþFgÿÈëq›·TûA•)Zq[»¼†öYt؈Qxü$Ýg ’º@ü°æ^Ðx>{ñh_úŒ¶¸õD!*Z¤T#èäj%ÞµehËLl°B$€±Ä’z ÌI㎧MKNRÅqC ƒÌ¬;0·’ €S;ƒY-³s‘CIzS²A6Åz$µ%¨*B%(V¡Œ5ŸÝ/ÞJ»wâøqÚž¦~[?ýdqd€„8¸ÛÛæãÐ|Ø„ç–Ò‚jçVÙt(¹ÑÚ ìòBò\Ù=ê&~ìRÁcÌ€}ƒ¸"òƒXÌjFjGžöí ÊÔT;W©Šu'RQ T`€Ú”¼Ú’ÒvÈë¤*ª#ä Ä‘kVkMƒ~éf šËÅ'_­ˆ¦%CH'¦b;¹ÖHO£T7+i–\[]A“š2ÏÒHAÎX§ZNo ÷~>žýxÚü*ÆmÑÿ šU…<Õkj×^QwètvEb§L2­J.¡ÉŠòÉ®ÇËé# ÈÔŽÝÓwÃ¥ 0sD4¢U4‹fA'Ô('”QM‡´d2(i°¼Š¹!€XÅÑT5V!PQœ4îTKÁÛ®ñ-yRëÁvjf@+ ˆ“Õ^ØÄݤÇES´ÅÌSi8$/¡øP“ 5–|z§¿û³áÞ?»_漫¿S_mÛ]Z_ZvËQÒæü.zPL>oê®U‘Ov…ì71ì[ä?»<UBEªh*šì7þh[.ŠJEWEJå‰U´`M@ÀÌ« ‘¡cèH‰8È^3³ÖZ«€hev‚!Ø™¹Î›êm5Ep«Y€_ca,§¶ŸyœWùb#¥ %¡L¬æù`»Ú 1Éæ|ûìÉîäïß~ãìÃ3àµ[)÷.Ÿ€âΨѺ9qŠ (·?^ †¢0Ñ$­Ï‚{áŒXÙÍ‹]&¯hж¨ ¦A³@;«v±# ¬ë¶“Ûz±¨¢hQaÑ40-«¹4tóùz"¢Æ^jP$$Q,0J0‹Æ%SÁNYJÑÀbª°V±ÀZ¶§ÓùÛSÿ^þ=µo­½~ýúŸýÙŸ}ï{ß{çwúqøû·ßÎgçÔ]E›ílPçÐgÉIK#¹}h…¾ œFb4iÏ€Swd–‰üdºèŽÙ*"HE;G·"Ó"µh:"[V7‚gl{ ÷M›‹âpưÈî0‡ëyUMwk–’ð$ŲR!ÈŠæ’…–¼hÇn49Kô¥JË>“„‹fªù¼ì~vï¦|þ{iþøÿøOþäO^{íµ+W®\\\Üî¿7‚œ [šnëP{«LŸ¬Ã%Ÿ €>,» ~Øp˜¨L7RH¦-h3y%§¦% LhÁtD-¯ LX… {®«Zƒfëë³± ¦jA=bnÊhs´… Il/0²Y¡xÀ…¯W F’ÞIZŽMHNSÍj#,Ðyš%©¹ø°¦_Õt_z†ø;2„ðÒK/}÷»ß}饗›¦yõÕW_ùð_Onžd:µ«Kä-OPÎBÙ´’⃭mçŒ~FÛ=©"4É´ÑÌ":F+Ê#ð¯`Ø9‡€¢Z’–AK„QÁ64ÌŽŽÎ‚!+•tgc$L¶Ôâ(qbb ˆ •PÝ´&:·¾ó­¯ß¸þÌÝ»wô£ݺuk³ÙüûÇ»|ùò+¯¼ò½ï}÷ûßÿþ+¯¼ÒuÝ£ß_Y_é Èx6‘M‹h|>y*ßE£bôqÓAŸÀƒGõÁž[TP%+äÈ+5LŽˆÈ££™6„TP KFž@*”<LÊÉ“¯ª&çX@¹#¶xpc#œÊPjÇ÷4E•KŠ+rÁ“ÐY«~sž¬Ø¸¬&¼>£ñÃeHYýµ¯ÿ÷ÿþ¿½øâ ÿò/?zÿý÷OOOcŒ9çù|~tttíÚµçŸþÕW_}ýõ׿ño¬×k¢öC¼„z墆ËB6Qsæ>Ü( Š± æaˆú؇Áûm·ü`Û­)Ô­ƒ³ 4¢gãÅÌÑ¢Y‚5H^A¢rî¥öÂYùœõ\\˜ƒ·ŒE¢XÜ Õ ™l¸$ [¬sQOÕuf°6TƒË3CKm+6豑™Ï0Á‹—&[eñDëõÁ /¼ðõ¯ý…^øæ7¿õË_þòÖí[ý®GÄÂÑÑÑիמ~úÆ+¯¼rýúõÙl"²g ªªZcÍ[ð[K®£àݺÚÕh—;/í7û})qÀƒ¯Ñ‹>J¸r1{ùp.gg†™9¹yqÇêŽÑΑ¬*‚2JÖ2èøN·8ß—:)ö⪞y\,p1ÓÙ g­ŠÚ ” Y¨3i#[å‘m³iF²²«&»z¦ã ›•ŸÍÔ§uN¡¡”qbSkA£„`afsoŒ !Îçó«W¯~ó›ß¼¸¸Øn·µÖ¦i‹Åµk×V«UÛ¶ÿþ¡EdœÆ\’ëÔ•‹9ùM2M²ó1}©É85ª„°¯… š.Û¶°[·Ó¡] ?·òàL” Néð3îß•éDÒ—AêN´ˆ Ø,©mauD—×Ü‚¬Tó\r2\Lbž¸_ò鱉+nnŒ½¡Ã’çC…â Ùì}1sÅ¥-©9ÓPÄBŽ%°]ÍçóKÛé$Ç8æœÀ{|||éÒ%ˆ1†@UùM%>2A»ÝnÆI'³"€Àƒ/›¥VA š¬å±õÿ™ã€Oö4# -›u¼}ÅŸ6Gc¸ZɃ™ƒ=¨fHà„‚o¤ÿ_üK=ÿenñpRsÏexàŠE °ïutr™6Wuz ¯^¡M–Á‚MÆML…(,!=›)]IzeƒË$Qk ª@¦5®mdŽÁ¡›U`1“_^ñã2L÷ONN®_¿þH³"òh¼‹ü‡^|­u³Ùܺs+qA3ã©åÉñØhs} Ç»¸‹¬´˜ÇK}öHXÌ'¹ PIsjÎÝ¡ø#F ¦q¦µèXq s¿Æ§‹(w^N?̱ÿÍG•ª³–AâÇ{0œÑNÌõ5 &+fað¹›+LÜ›]ÇÞv"!sÔÕlrž ÎçÎ5ÞI•15>Þ˜ÝûÅætûÑ{ï½÷ÒK/íí;|j€ÿÆûso"»Ýî׿þõ¯?|¿4M'¢¾™ô ÷krQŸ¹ŸîŸÊtÄÒjý­×øò™¨8ED0mWÇö™]{cò‡lfhI’¨Z5ßÒô†œÿ0ßùy¼‹sþω*h5ij6›öÖmÛüÒµöòØúèÔµùò|¼ZìAä"C3@Už2ÙJ*5µ©´ŒÅã¤jP­w]z6~xysþ«÷Þzë­—_~ù•W^AÄÿHãðpíÝÏ f¾yóæO~úÓ›''æZ³~Ùqs,Ê Ô’€m¸¼Éw‹––¿û½"ˆ„Ö˜¸ƒâ«é4Ô- ›ÈU@£¢¦¦Ü¹ùÎ{£u‹ëhu¦ñ¦«ÕÍûÈ'.ºÀÅ¥6ŒSg|#VUC±H’]š|IÎí±¿ôÔüÝwîýâÝøá/­×ë«W¯þÆ÷îÕ½§òéŸ÷ïßÿñü£Ÿýëàãâk ÿ27ÔmÃ;•4·,þx¢YÁ0Ácôªxã7þæþöÇ·~ÌÏðâÕC{ŒÚeªÖ´€j52¥“uÙ^°äèq”ùXéhñZVÃc“ÏgéÎÚÌwd*’¨erÜk¼%ÃÇ%oßiyéÒ¥o|ãßøÆ7žþùœó­[·>¸{ów>81'íò¥f¸,ÖÓVºl C¬B‚š§)öƒäBV•ã aisWŠé4=õ´†ÞþðÍÿ±¹}ûÖŸ|ó›/¿ôòz½öÞcˆhïŒö}ÿÖ[oýä§?ùÁ?øé½ŸÄgúõ·WþJ*ë€RÔ̶ԈÖeÎó¼=¨»Vªªü¾©ÖßÇlÌRöRœT[Îçéþ<è ^Ðg‚ læIã--·ÅÆæè`Õ÷ý4MÿÉÕqo|þò/ÿò¥—^ !ìso½ó‹Ï6㉙֘×g¦"U‘Ìä IhZ%…J–تÖfcò.JHI7swºªñ¸Á,Î:¸ú¼Ü…“ß}çÿü¿îýìÍŸþÑ×þø¹gŸ;88hÛ¶mÛã4MwîÜyó7ß¾ù‹w.~ÎOOËï4tceW¶<qêl'äw’cí3÷Ì£J©ûò$}Vgôñ‚€ Ô­å-Ê2‰$â‚6¢+0iÝõÍ O?w4»´ÝnßyçÍfSëoqñàààÏÿüϾÿýïûÛß>>>ÞÇD7nܸ~õÚÜÜ»ëãåÖ"lƒÿèPcÖõÈ͆¹±fN\(°¸RMÔ"°ŽTÙ±:J–6 ìBQM e6Ÿ]©/b éâWý›ïþó;üüðàx½Ÿ¿øâ‹1ƾïUµišÙlæœsÎ!"ü;g|š¦iš"'Z€9¾€àpt°:‘ƒÓzÊ#pv¬Þ`çÒŸÔ~•Ošr·Éë;JïÔõE<Úˆ@{ÑÑiPqIln8¶hHƒB4ì ”x¤9`D@EE-T¦™––Ùõh—c>#Ð}{è—`ÄÝž6{e¯ 6œZ¨–:­N•ÜÒðT{٠ðÿè^×mÛì³?÷Úÿ÷’R:=?Ò@]g՛ѻ°Óù7;ñ»& Ig®´xÙL‡É×| H>9“K@·²ÏFŸ‰E K;)1 PÝJJ„VËL*²ˆð¤ À4“SQHªDAA°n;‰Kt¡n’=ˆQvú Eö™K‘ Å=ÙJ*VÙî+0`‘¼*©ŽÄ˜ÖO:Ý:¿yûöí×^{íQØùi;ó(åòÆJ)wïÞýðƒ‡˜ÃêJ@mc5ÍXq¬¹äÉfÍ­ÈÚ.l]©â;ŽnLGgì¥.usLU)6®Øf¾4ˆŸLohìTªË!Å4Ätúí<Æ3?f5êÖ¨8*"¢«Z´¤µ³KOÆ’)”î^êß~)Ÿ=8YHì—Õ˜õPû ¤ìÈ.²]f `”­ ¤Õ¢ ‘û÷ï¾÷öÛoík_{á…)ýÓû’bggg¿úÕ¯Þûð#!š¯ü|>t‹í¶Ú1'æòýƒ‹Ú´VÕJÇÎÔe-Ž H]•µž;:¹ ÛÎ^¹[Žï[Š3@b“dÖ¦¦ »~Ú^Ä-×<¯µp¾_Õ *™foNP¢rbeàÑ6ä@ãÖ1Ýe·ÖÒªì _FM>}(6{‰”P.V’™|g—§ôÔ@h¨ îÐߺõÑOùãgòìz½><<|tGC~ÿf?ÁH)}üñÇÿëŸþé£ÛwšõÑzÖÍçÅ…¶=L’v&ošþþ2ÁÂ=›j f³°Ý¼€&T™•6óÁX “f³‚£A6[#\x–Ál|`ô¹ºSè7À7)CæJj’ÚŽP2ª›K ,S+i†ÆCnѨmJýt}ðKŸZÔ€ørz(‰ÉÓ¹æF¢0p`Ó±?ncöÆûÿëà‡‡«ÕêOÿôO‹Åo^ëSæèÓÚÿÁð“7¯Ϯ6óKPtÅãh† ëd Zj’ YE*ÎÀÌ,½Hj)·53Í&©‘yàƒ!Í#F±”YÚ¢-Ò8ËwünΖÑ6M¾Oã`9’C¨ŠÑ‚N¤è©ð$<¢[ÓY­€ôÁ9à€(½oõq›s?±BV¦9`Äùd—lçRws¾ãÛ§9\žô©öb8ýá[?˜usïýk¯½¶Z­þ½Ùy4üSJï¿ÿþßþÝßþõþæ^:]?¿ZÞ(í:5Õ´Õh 8[øGlMɰ%Sв5¶Çyï5@:€’‹eè¶çÅPPdŸ v¤à5Ù:5£ÏãÎc[–J‡H½uÕÌNŶ±v T ¢!- *<æé#P!åâJ9¹ÌÃìaè«ð¸[ ÀÿþCDAT SÑñÇÐóàe"ÓwíRÊæþæìÖEÖ2DÔ4ÍÞõ‘½ ¤ª°ÙlÞ|óÍÿñ?þçÿüûüâÖMw°¾úüÕõ³D‹ä…,xSš&Î=xCHµMÆÖ¥sšÕHë‘4à°Ä ôxtº „-š6)VQÛyyûÚq˜OQK5Bs¢̹¼|{û퇗7|)©ÛJì1Yc—Ô ¡|jÊùBÒAÝÞÈ·¯Ç›7d\+دÀj¾LOƒW1û$­$…”|±óL­C…²Ûìî||róƒ[îÇQDöÑ@Î9¥Ô÷ý»ï¾ûÿø?øÁÿú¯üî¯G럿ôÒ³O/ܼñœ4Õæ®íׯY" Ø1Rn‰lÈvh}p¶£¸ bÍá=ZßÇfÔ’ÉF²EÙVcëÝã)ºÔŽb1‰J+˜§øÜÉùw>8}zS—,;|öcîÞ峯mÙK´ä­²ãÑ×þ€·7jEÒRk Ü€X÷Øw›ª§J€VòbRPÝ‚Š™YÓ‰ÖŽÇ}w=»îœþøöîÃÿû£ëoüãóϽy9²XæƒÏv“jvu2 p•î¼xvv°‘C¹5žÊ˜õ\àåûé&Ås·œnIM½ð܃V|pØ.{Uûàœã/q‹Ò^ö *1 í¤Õ+; äV`º@v†"hÕ΋=ŠíBçWçðÔŠoÍÊmWzÞ Oq"ìj¾¼Ò{lÄãÑ–1¥³"Ĉ H¹³•ÌØÙ83cc;Ÿë"–u¯+…Ð1¦ ¸:õ3líÂØh5›yZN­ò.K›yµíkÁ|F9š"­a×Í ­©ÙèÒB‡pîa{ɘsYÞÒëCùW°16[$„*Nª…Ú(;Ø¿þ ØüWŽ*øä(ÍUbrvÝSSs#ScÈ7vžýñàˆ‚¯ÍàÀ=Õ4ëC½:“HiSjU àXTPD7<»«@gˆcÈ‘(ÍZjLFÛw”ƒuáâ¹ÛÛ+wóáý¼ìË‚Éόڹ¿dV-ŒdÏc Jî ¶H˜jßR#vVøȨ_Š&4#díP/Ø"Ü9¢ûäàƒÊ;åKvö¬E¤rNZÉÆ®¤9@+5(}\Óÿ¹ø”°Wö(`°<™ŽüÑ® ´^3¥or‹ãÜÖ€Á´—}MZ¤pÌ¥–\±H{F³A#oRê\?kv«æŒ««Ö‹v6˜ïnÜÝ]»giuQæ\!3ïÚcßy.QÇ©N1'ƒµ£¾ºfô³¢ç!Ý꘹ï¡Üá “+c3 ?as&œaÍó¨ó÷k?êö†9ýãξ@è.:€¹ê¤Yƒ¸ÇëÆý ù|({¨Äi^ÔÍ•2³î`tv‹®G R2Teà¨qàxÞD´uS¨PŠªÆJcñC‹ÿ+¹“O-ÚZ‚R†R)ˆ æ«§=ýrXŸ¼µó¡$‹vlò²Šç®¬žfÙçTÇÕÁOæb“•0e˜ âbwÃïVéBÉÚy@4ÑçI.á  ƒ¡‡‹÷æjÙV:ŽØ[è¥Ó.ã/¿47?ÌÓY[:opä'âù3éfy;×4ÑR)e¯ì@̧¡Äûá ß„5Ý\p‚£ÉK š=¯<:$ÞÔhÄÍØ6ˆ–¤˜h†ƒhÁÎ)¥\¯l–á/—‹O{+ V³ñvV™IÍc#ƒ5¢Å²1Ý{ï;Y”XsÂÒkÞÊÍÊüÛÙ⇿oϦZªêà  Uó|ýjøñû²kj5O´TZ*`¯â>ë9¼/î=ÊPÅû]¨ÈnºŦsŸ¯÷íôJbc÷×vºœuÍÊɃˆ6¼ýÓÛñæjùög'•©r~ÝoëÑn*çÝ\ÓrW«T¦ aßË4Rc^šacÿ5Ö[_ÒØ §Î®cx»ž¯`Rö\ýÙkð‡ž`ºž§å«týRòTÆÙnáƒ8ewßuÿÈýgIªqRˆŒì‚•9 ©U¼âÄ^Œ“?½ñ’U*É׃šd «”Ñç´w¦|#¦å((ëZµ( ÷¦lçT52«ÍT1öyIñÌópkè/}}:aZÙ~ÕÊ@ý²òG¹z1¸Žò2Œ§Óñݳ²ù»týéâ%o'’ü®ú÷^ÿÎW`w/@£âH Ä©IHA9€Ö:‰ýAº}39³Ó7èÎ4¯…RYWéBe`•‹ ¢[(°ÉW$q"å0¯÷Lí¥ƒ¼Îñ"ﺲ''æÀ¥0–H•„‡ c=¾ãt9ÍË:]<Éë×Òÿ“”ßñøLÇ Éü»ßÂWHSÅÑŒ¢Ù$FŽ 1–«ó¡™s7C{ˆæÂNÏò’ÇSF,¦)e-e£è%<-€‡ â¹#éòÉk^о~¶„K»¦aD*[ÏÑénzlé²’4åþHù¥ò‰–¿WÞGõŠ_0Úug|•8ÛÛ—#€ÊŒ“AË®¥›ˆYʤl§*¯MxGÕ›²ê¹KˆÚ PâÞ"¹DUçŠ ¤E‰û°Û°i L­© U™;[ÖÒÿD VŠº¹Úx8V9<zŠøtŽâ„w>§ŸÅ™]¾NžðçI˜¨b@•œ(q´àP§Ï’¾P}&ýJeKvT¼Æ1Ko´SGÛŒeØãíBR«Å!¨ÛiÑ2-ëŸÿ¾ôE:åNypŠZ?yʼ=V9F™+>KÜ“žxIx DwùÐX§HŒ6£c² X²²¨2`D3 HhµË¬Ül°{Y¸’~¢lL•È 䈼ˆ—âQiT-¬ŠdYeÂ×2Õ\KÞ“ÑðhðÜÿœÚxŸ¢Lº:Ûy!ø ‰3ÑÎ0!TÌf7S£ 䊂ª±4hy¤è•\@•( 5O4S‹DÅpDÍæ«Ý2ÿW¾r¦üG>H Fa×d÷¡Ë~7€Èb2PF%V¸Í\1Üä‘<T€BT¤d‘ä9¢F”Dݧ¦îo£ô;j|Ž~¾ð?+e¤Äxû‡Ý+àÑ]W, è  a@I" %,ú׈ÿ¾þgv‡ ‚€îä‘¿ÑŒ†··\PUe¹}°ýÆûßäÛàvÕÃý%Š?8߸ÿ÷ùFOÆÿ?< ðÀ< ðÀ< ðÀ< ðÀ< ðÀ< ðÀ< ðÀ< ðÀ< ðÀ< ðÀ< ðÀ< ðÀü;¡{aËbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEÖ  4~ËskÊIDATxœíiwÚVÇÿ˜U,€¶ lÇ8IÓôôû†´išÄNÁÄ€Ù#±±˜çÅsæVȤ=•X¤ÿ9zÛ9p5¿™;w›ëZ, 8²­6ýmV6—€Íå`s9Ø\6—€Íå`s9Ø\6—€Íå`s9Ø\6—€Íå`s9Ø\6—€Íå`s9Ø\6—gÓ_ÀL-‹•Ï:¹\®•Ï>k¯X,x||Äãã#æó9f³ÙÒ3ŸÏÙïõ ¸\.àààn·géq»Ýì÷ûÄΰX,0ŸÏ1ŸÏ1N¡i4MÃx<Æh4‚ªªPUãñ“ÉÓé³ÙŒA@Æ÷x<ðz½ðù|à8Ç! "Àï÷Ãï÷ÃëõÂívÃívï ; yú|>Çd2aÆî÷ûP²,³GQ ŒF#ŒÇchš†étŠù|Îp»Ýðz½ðûýƒ‡ÃàyÑh”=<Ï#‰0(|>‹» ÃN@†'O‡eÝnív­V ­V N½^ï‰ñÿIÐC‹ÅH$ Š"DQ„ ˆÇãˆF£…B,2ì";~6›AUUôû}t»]4 ÔëuÔj5<<< Ùl¢Óé@–eôû}¨ªÊŒNÝÄä|>8ŽC$A4E"‘@2™ÄÑÑ$IÂññ1R©âñ8"‘8ŽƒÇãÙ)¶2üx<†¢(h·Û¨Õjøòå *• îïïñððÀ OÞnôôïVeÿúÈ@Q@8::B&“A6›Å³gÏ IAÏó „m—k›OÏçshš†Á`€v»jµŠR©„R©„r¹ŒZ­†V«EQ00™LX¶¿ÊÐ?ÒT£ç 4:ðù|ƒày¢(B’$är9äóyäóy¤Ói‚€p8 ¿ß·ÛýŸ¾“ÿZ[ Àb±Àl6Ãh4B¯×C­VÃíí-þúë/‹ET*4›M|ýú•^oôuMú7讇@8<}ú„R©„F£EQ if³Ù¿óÿDÆd‘~F EG£EA·Û]…œŸŸC’$D£Q­ë¶Jô:îîîpuu…>àêê wwwèt:‡˜N§O²xà¿7ü*ÑgA QÆt:Åx<Æ`0`󔟜œœ ‘H°q[´ß„úûv»ÛÛ[¼ÿïß¿Çõõ5ªÕ*z½Æã1Æéµ‰fÔé'§ƒúý>†Ã!ËSA`yÁ6hãß‚Æö­V 777x÷î~ÿýw\__£^¯³¿)¯ÿ–h"‰Dßq4-@ÃR‚$™L²9ƒMk£ß`>Ÿ/ÿ·ß~ÃÛ·o™ñƒ&“ÉVxý:­‹ãñ˜%‹ôP;\.DQD0ÜxN°11Ñívq{{‹wïÞáíÛ·¸ººB­VÃ`0Xzi¤m2¾^«¢Ád2¢(KÑK?ÛxppŽã6::؋œɲ,ãîîþùçRØß5ã“VA0›Í0 P¯×ár¹Ød’Ïçc+‹~¿có–@Yó`0@­VÃÕÕþøãfü~¿étºdìm7¼^Æ.¯úý>êõ:<›Vƒl~ÀëõnËx||„ªªh6›¸¹¹Á‡ðùóç½0¾^úh@дÀÄó<‚ÁàÒ«e)Ð4 ½^år?~ÄÕÕîïï¡(ÊÞŸ´ EQpP(Äö@Àò|À2h~ŸBÿõõ5>}ú„»»;ôz=6ÔÓÿý>HÞîîîØ^ƒÃÃC67`uW`”õ·Ûm”J%\__£T*¡Óé°!“Dï¡Óé T*±=‡‡‡lªØÊ®ÀŒáïóçÏ(‹h4‡l^_ÿ÷û$cW0›Í0Ñh4P,‘J¥ Š"xžgI¡UQÀ’GïýårÅbµZm/ûýu2¶‘¢V«¡X,¢\.£Ýn[ MÀ˜—J%T*t:½í÷×Iß>Ê:*• J¥ÒÚ‘™2jh·ÛE¥RA¹\^úí$cWP.—Q©TÐívŸ8†™2š§FV*T«UȲüdŽß. £ÍˆV«UT*æ´pd¶L€X¯×qv»ÍVËôg'éÛ;ŸÏÙRøýý=êõ:s@¿àC[·­lÜ.Hï$¨Õjèv»–%ƒ¦ ïãÚí6Ûº­ªêRx³+úöÓ²x§ÓÁÃÃÚí¶e9’©Ð¢O«ÕB³Ù„,Ë–&8»"J”eYF³ÙD«ÕÂ`0°d4`ú‡Žk©¶«÷“ôï¢e§ÓA«ÕZ™(›!SÐ4 Š¢°S;Ž÷¯—> t:¥­pfÊôÛ¢dYF¯×cÛ»ìîõëDÉà`0@¯×ƒ,Ël#¬™ïÌTèÐmvÂÿS»:_@çw€ét UUÙmêÏ( ( TU5=4 ÚîMçó­œßÞU‘ãŒF# ¨ªjúPÐô€ŽkÛyÞÿGEŽ£o;ÛÐÑnUU—qÒïý-ã¤Ðº÷f†LN§SV¡Ãéÿ¿/ÊŒïÍL™hO<‰rÆÿ?&*~e¬pb–L ¦ÆXµ´¹¢n€œfg§‚IŽáÿöfGж–FÙvíü¦Pª²µOU5­Õ ²ª¥)K¬Qc}_ä4^¯×’šƒ¦Y…<Ò)Ø]*ž¸)‘ãß›™2-бgŽãX½<ÀaYú÷ò­÷f†L€ŽASa$ÇðßÕп·Àãñ€ã8„ÃavúÕàÛr¹\ðz½¬P5ÕÚI¼^/8ŽÏóK…V‹úŸÏǪ”sgºã˜ÚPqe:¯§ÙáÿÒ¿ÇÃêG£QVDjgˆF£ˆÅb, 8†_-—Ëż?‹-UÝ9€ÿý~?xžG"‘@4…ßïwæÖˆÞ•£çyÞ’÷e*>ŸÑh¢("‘H  9Ý€NÆð …ØÍ$ÑhÔ’Ó¦N{½^„Ãaˆ¢ˆd2éD5Ò{2™„(Š‡Ã–ŒœL€¨GGGH$à8ΙÂÓÉŽãØM$‚ <‰–fÉTW<88@ @<‡$I8::b¡Í®†7Š’¿h4Êî"ŠÇã–U 3õô;>>F&“aÕ²õ…샾½4\™LÇÇÇ–:‰é¸Ýn„B!¤R)d³Y¤Óé• Ž] зSŸ(§Óid³Y¤R)„B!Ë–ÐM1”àÄãqd³Yär9ÖH;¯ès¤T*…\.‡l6‹xÏ®y¥ƒ£ÆÂQû*z‰Dù|…Bù|~#ÞX‘‡!I …»Z¢€¾0Ò¾DUý~,ÃÉÉ ^¼xB¡I’‡7’[Z.^ÿr¹;¯¿UK_Db×!X×ïg2\^^âåË—ÈårˆÅb›"·üº''™LâüüŠ¢°›¾g³Ù“úÁ» Á*ãÓHèââ¯^½Âùù9»AlSë#– _$’$ £ÑèÉò~¿ÿä–-`7NC8m'ã üüóϸ¼¼d¡“Ûå6ri”~Šøää“É„ÝüM·q®º8jÛ£Á:ã‡Ãafü_~ù?ýôNNN¶b]dcׯéŠNOO—N»\®µ÷n+«ŒO;|Èø¿þú+Þ¼yƒÓÓSK|¾¥^IË ¢(.Ý«çñxàñxÖÞºM]‚Ñ𴹓vCé=ÿÍ›78??‡(ŠlY|ÓÚøÝ¥´}\E¶xD(oÞ¼éh°Êëi/d,C:F¡PÀëׯñúõkœžžB„­¹6ذݰÉdn·>Ÿ¡P‘Hápø›·‡o"¬óz¯×˶uœœàòò¯^½Âåå¥s{ø÷D‘@v8‚¶GóFé=œ}‘ :±K7ŽŽŽÉdÍfñìÙ3H’AÀó CFBundleInfoDictionaryVersion 6.0 CFBundleIdentifier org.cultivation CFBundleDevelopmentRegion English CFBundleExecutable Cultivation CFBundleIconFile Cultivation.icns CFBundleName Cultivation CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion 0 CFBundleShortVersionString 0 CFBundleGetInfoString Cultivation, Version 0 CFBundleLongVersionString 0 NSHumanReadableCopyright Public Domain, 2006 LSRequiresCarbon CSResourcesFileMapped Cultivation_9+dfsg1_UnixSource/game2/build/macOSX/game2.app/Contents/Resources/0000750000175000017500000000000011401021117026153 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/build/macOSX/game2.app/Contents/Resources/Cultivation.icns0000640000175000017500000011313110504230156031343 0ustar pabspabsicns–Yit32VIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿއ…怂‚€Ú€€ƒ€ƒØ€ …€€Ö)    Ó*    Ñ %)#€ ## Í/ ,4.(#!!"%#$# €Ç %0:81+&##%&$€ ())% Ã9 !&+4<<60-++,(%&,(" #*--)!#% À= ',07=?;50,+,)$(,*))((,-+&#!&-&!$&" ¾, *259?D?:5/-,)()('-0.+-+'!#$'€*,,)! ½  '%$!!,47;@DC<852.)+€, 142/-+)*+((*++€* % " ¼6 ''(#!&.6:<@EE?:782+.01355331-,02/--+*&')%"€#& º4 $&%"")07:<@EI@;86301123544561/24452,,+*'€$ %&"·  $%" ").5:<@HKB:74€1$213432474245430.010)&'$'," ´  #%&$""$)-4:>CJMD:64€2%1/13213554320-+.110-.*$'+$#  ±+ "$)**(&%*-05:>GLNH7642241./101320.++/11-.0,&%'$$'°8  $#%*0440,/1235:CJOOJ<520232/124684/221/.-/10,(%&'$"'+$ °M !"%+.2<@AA=5233;GLPPLA5201224788;<<;741012330,(,-*%'))&$# ¯* $&%')**7DOXVNF@71AKRTQKF7325327;=€?HJIC87767642.0.+')-,&  ®N $((),1+&?O\gibYOFHLS\ZTOK;469778=ADEKW^__VF<;987654321022*$$# ®; $&(*/2/0F\lwzti^WXXchbWSM?8:<>>?@EFGWiw~}n_TH?<<:;€9865531/-&! ¬& $'(+-/11:L`xŽ‘†wljiimqi]WQE<>@CFIOazŒ—‘…xj]SJBA?@=<:9€8 741+%")( ª  &,/1/@Pd€•”Šƒ|vuuxwog_UJ>?€@(BEFETj€˜¯ž—‹xg\TSQPRRPNIFDA=963,&'&  ©S (11001/2CRby…‰‡€€{vvsnfZM@AA@@BCCIYq†Ÿ§™Ÿƒohb`abafeb\YVSOIB=6/)&! ©S " )--,,/08GUaquˆ†ˆ‡ˆ€uwytk_RDBA@@A@@K[n…¦¢‘“‰xqnjpyznmmkib\QG@70*$#"¨  $%'+-24=ITcop{ˆˆ‰ƒ}xuvyvnbTI€B)CC?BLXh’’އ~w€€rkiw“¥–„‚…}}uo`OD<9/$$%§  #(*+,,-/4:@IUemlvƒŠ„xlh€m3lmfVMBAHJJGIOXfy||ƒƒyqqphbbs’±§‡wsrstsjVH=4,$"%¤Y #%')+..-2CMRJB9-&""%"£Z #$&)-33/2;AHR_hkpvwrhaZNGJ[r€„|bblqtob[[clpqvxqkc]SIDC=-%+/.'"!#&'¢B %% $)++.54017>EOZcfihcc`XOD=;Rs‹›’…€…Œ{g\Z_fimszwqkbSA1$ „$$# ¢A "" $+/-/1/.15;AIQX`c`\[ZRG@AI`{•±ªš›¤¦’|lZPUX]bpusleY@* ‡"! ¢? !" #*..1.,/37;@FKQTVUSRSJGHOYi}›»¼¹ÁÆÀ–ykYOMPSZ`fke]F)‰ -# ¢= '" "(+.21147;=AFJNQSPLJIHLS^jr–²¾ÉÙÖá„o[SLKNSTX`]N2ŠRq]" ¢< " !',.03658;?CEHMSXZUQLIPWez†}ƒ‘¦¹Ë×ÑÁ®—€o\PIJNQQVS@! Š.ˆÁ›Q¡= !"%,231348=@BFHKRYbie^YWboœ˜~|ˆž¶ÆÑÑÖÊ®–…iTLLTWTPJ3ŠX¶ìØ‹6 >  $%'.553449@BEGFIT_q~xmgfnx‡–Œvs~’¬ÃÉÏãî¢qYPTagc\L, ‰9Úþô´^" Ÿ@ !%&$+24668=CEGHFDOZgvytrswzƒ‰vv€ˆ£ÀÄÊßíÒµp_Z\fprjV7"-† 1xÄóÿÿØ/"! ŸA "%%#(/37:=BEFFECAJTamtvz€~}‚~ƒ–ª¸ËÜãÛ¾™‚m`boƒvY@UqZ)„>w»ìýÿÿî–9#$$ M $%''*059>ACFEDB?=EQajmsv}„‚~~ƒ~z€Ž£ÇÞãÞ¿‘km~šWHÁ¯}K+ )BcŒÀíý€ÿ ï?%'& [  $*+.3;>BDFGDA>:7:DVagmpw„~~ˆ|vyŠ¢¼ÓÛâÊ«™€sy‰ ¢‡VN‘ãíѬ‡mabi|”±Ùôÿÿýÿÿîœ; "'¡I !!$,/49@DFEED?5-'#$)2?LWbtƒ†€|‡ŒyvzŒ¦¶ÁÌÛÕ¿¥ˆzŠœŸ‡VY”êÿùéÛ˽¹ÁÎçöÿ ýÿÿì’5# ¡G %%&+/3:?BILMKB4(#2Hc}Œ€y€‚~vsy‰œ¨°½ÑÕű–€ˆ––bYŒäÿÿýþþöýýü†ÿ ߈,¡ #+247>BCNURF2! €# 9Zr}zyy}{x{‰–¡§±»¼¸°™†„›™pZÐÿý€ÿý…ÿ ûÐn%  %089;:,‰"2GI1Fl~}shm}ŠŠ‡‘™–•‚‚„Œ“œyV_›äü‚ÿýýƒÿè£N&172.+€) Ÿ */2345682)Š#b›ƒF0Pqvplo{ˆ‘І‘•œ“z|}}„†dGfªèûˆÿç³e1.;=5-+*(& ,-.//121&Š&6‘ÔÅ{?:^nqquz‚—•ŽŽŒ†ytvvrz„xRAf¨Üóý„ÿþß«m5.;?>70-)&  %(*+*,,)Š@c¾õï«\5Jgomns~‹——“Œƒxvmllmv‚lG>W½âòøûýüÿóÊ›a3+;A<8532,#   %'(*,)! ‡K âüýÔ‚@@^adflu€‹”€’+†€zkdejrzw_H;DcŠª»ÄÍż¤zR,)7=941.24*   ##%)-)4G9„CIŽÑöÿÿðL6VZZ`ekr}‘“‘Œ…€wgaclrussk[QA9=O_gpkbP7)(17740-,/.&   !""&+(!cšŒS*6^’Î÷€ÿ:õ¦P0FOQV[dmyƒ‡ŒŒ„xlc_`elilrohfaSE:4/-+)#'-263/-++..*#    !('&~ËØ±ƒYA34GCHOXbkt{~~qc_Y[cicenmiikig^PA6-,+0430,)(''+-(#Ÿ  '(,‡âûîÓ»¡‹“¡¸Òöÿýÿ:ô¦J(<:=GMUYXVUWQD9/*$#*($$)%!  ¢ #*#9}Ãîýÿýÿ@ýÿÿøØ›S-;Wjs|‚|m]OIFINQROID?<>BGLRRQPLB8.)+&*.*('%&(%¢Z   **!;s³ÚôýÿýÿÿýþïÉU-2Nbhs{€‰ˆ}o^VOMRTUTOJDA?@DHLMLI@6/*+-5OmrcJ0#(('£Y '*'!!2W¶×ßíððêѪH+0F[clrt~Œ‡}nd[VUXZ\WRNIEADHIGE>81+,-9W†–’ˆˆf2!& £€U $('&+''9TuŒ”ž›“{Y:%.?OW`nqpv‚‡‹ƒxne\Y\_c_c`ZTNGDECA<6,'(+UŽ¢¯©ž›“e)  §T "&)-5;5//5;AGIC:..6AIOU^glmpz…’†qi`]`bcirtldXMFC@=60(##@xŸ´Àû± ŠJ ©S  $'*19DGGD?:41248;72*"P‚¡¼ÎØÇ­£˜e©R#%'+17;@KPXWUNJIKHFDCDGLV]bjux‹”‰yj^Y[fruw}~zgQC<=;4,(!$W ¿ÔáÀ˜˜•b( ªR%'*18<=?AIQUVWSNKFB><>=@EJRZfjv†Ž|lbWRYh|‚~zxyiVIB;81,-'"N‡£¼Êκ¤˜‡O,( « %,29:D@FNRSOIEA<5/.078:@JQXcryqi]PRZiz{xojhj]RIA:2-.0,';xŸ­º¿¹­œp9# «P#+3798556=BEFGB>83)%#&*+.29CLW\^]\SMR\ejeb[VUSKIE>51122/,)F|𦬤™ƒJ# ¬" *2562-.16:<=<<:3-)'%'%!(06??;83=GJLJB@CGJKKLMH<4-.442.-&#.@GF3 ®M *--,'$'(.30*052.,)(%$-6<;<;973-,1;AA:7;BGJTX[WL=4((/12-$ ! ¯ #+%#$#(.-,./+*))'&5E€K.HA92.*('09758),-)  É4 "0>Pi’­˜‚wcRJWee[VVamos{vxufTJHH=/)# Å  (;TgxŸÀ·¤~sv~y€leaXi†‰‹~i_ddYKEA4'" Â; *27Qn¬ÆÇ²¡•Ž‘•†z‘‡tmgwŽ–˜Œqiu}iTWaR>5'  ¾>  /:CMOMb‚•¡´ËÒÁ®¢“’•‰z‡“ŽŒŠ„ˆ•—up‚–~pwr^K?6+ ¼A /N`fjjd_iŒ¤¯½ÎÞо¯–•‹ˆ‰ˆ‡–¢™“–’ƒpvx„Ž””ˆq_]\C*( »A  &^y€ˆ‘™§¬ž•¢«ºÇÙäìëçâßáæòþýüûúùùïáÔÌü¸¹¿ÍÞÝÙ×€ÕÌÊÒÜÛÚÕââÞØÒÊÁ¹Ÿyw{Q ¢* ",9Hay‡Ž’’•—­µ¦œ™”¦½ÍàîòôñëãÜßæôýüû€ú-éÛ˼¸´²³ºÄÏÐÓÓÏÎËËÈż¯¬§Ÿ™š¦³¼ÃÈȾ®’xs}d0¡) &0=Rdu„‰‘œš¨±¬§›‘¥¿ÒäôùóîçÚÓÖÝëúý€ü.÷æÖÀ¬¬¯ª«´¿ÆÈÈÇÅż¶©qc^XKGP^p†›²±–€rr|r?¡[ !*?Zeivz‚‰™©©ž §¦¡›¬Å×åðñéâÕÏÌÌÖâñþþüûøæÔ´•š¢œŸª¸ÀÄÁ¿½¹¶ªŒpV?/! "6Rysqx€ƒM ¢B #-Q}|pzŠ‘’±¯¡˜œ™˜˜£³Ç×àéëâÕÆÁ¿ÄÎÝêûüúú÷æÏ´’‹Ž’¢°·¾½¹´´°‘c=!„ 4T]\k{zvQ$ ¡A &5Zttoy’žš¤ š˜˜ ¯µ¾Ì×ÜàÞÔÅ»µ¹ÀÓáëöüùúùçÒ½›†‰”Ÿ««©µ½ºµ±–f3‡(?QgspgM) ¡@ /RoqiluŒ˜¥ž”ž¨«­´ÃËËÊÎÏÑÏÆ¸·¹¿ÈÕàëõüûûúæÖǯœ¡­µ®²¼Â½µ™j3ˆ 2=@WigV@' ¡> 2d‚rgnv‡œ©§¥¬¸¿ÄÉÓÕÏÉÈÇÃþµ³ºÁÇÑÜéóûûüüêÜξµ­§¦¤¨ºÆÇĬx9ŠRqd<9TcJ/'& ) #.Rpmho‚”›¢¬²±¹ÆÑÞããáÓÁ·¶´³±±®¯¯»ÉÙçñú€ûñäÕÅ´ª§¤¡¶ÅÊÆ™PŠ.ˆÁ›Z2B^N:4:4Ÿ> '1Gcqs}–¨©¥¬¯»ÊÓÜèìëáе  §«°­§¡›²Æ×åïùüûûõæÛÏ»®°®¨¡¯Àǵw-ŠX¶ìØ‹OF\SD>>. ž,  *:Qjz~„›±¯­­¬¿ÔÝãèéåÜͱ•™¤ª°«§¢£¶ÊÙãíöü€ûéÝÔÉÂÁ»³©¯º¿—S‰9Úþô´mR^bYP> žA"-C\o~~z©°³¶»ËÙäéìäÝÑŰœž¥§¦¦ª©­¸ÈÓâëóüûúüîßÒÎÌÌËÅ»¸º´‚F&-† 1xÄóÿÿ؆UZsoeM' B ",Ebs{|v† ®·ÀËÙâçæâÛÔʼ®¥¤£Ÿ›§«­µ¼ÂÜéòúûûüóãÖÑÇÅÍÒÉ»¸³vGVqZ)„>w»ìýÿÿî–UPwyxh9N #-Dfy{‚„‹ ¯½ÌØáååàÚÑÍ󥦫§¢™”§«²²¹ÑæñùùûüúæÛÕǼÉÊÀ²±ªkJÁ¯}K+ )BcŒÀíý€ÿ ïžS@g}€Jž$ #0Mdn{Œ’˜¬ÁÏÚâèéáØÌÀº©˜” ª¬« ‘’ €¥5°ÃÝì÷üüýüëÞÔÈÂÇÁ¶°¶«hL‘ãíѬ‡nabi|”±ÙôÿÿýÿÿîK2YlsƒYŸJ $Gro`y“Ÿ«ÀÕáåæåßÔ®”vjfly„“œš‹…–¡™˜¥´ÎßêùþýýñÝÍÁÃÊËÉÄôuX”êÿùéÛ˽¹ÁÎçöÿ ýÿÿì”Fh‹œ¥¨®³·¾§l,Š#b›„N?Xt{™¸ÊÑÌÑÕÙÞÕÓÕ×ÝçîîíìéÉ„Xjªèûˆÿç³jJk ·«•ŽŠ…}X-Ÿ 2e“”š›œ¡¦¥‚CŠ&6‘ÔÅ{JLrr„±ÌØ×ÓÔÙÝØÒËÉËÑÖÕÒ×ÕÈ£sWk¨Üóý„ÿþß«sPh”´Á³ž•ŠzfJ-  (T{‡ŠŒ•—‹h'Š@c¾õï«dLuƒy¦ÅÖÙÕÕ×ÝÜÑŽÁ»¼º°°ª¥¢hMZ¾âòøûýüÿóÊœhRnŸ¶º¶®©¦‘tX=) $Cfz€†Œ”ŽqO ‡CK âüýÔ…Ra…‰¢ÁÐÖÓÓÔÝàÓĺ®¡¨¦—„|~‰“‰hEGe‹ª»ÄÍż¤~cSo¢¾¼®£œ¦¬‹hL3#Ÿ"5Xtx}Š™‹cCgšŒS*6^’Î÷€ÿ;õ¦V@^ov‚›¸ÈÈÏÚæßȯ¡ ž–puvn—‡naVNGDMSUZt”ª³«Ÿ˜Ž™™ŠsP, %@blhp……UAËØ±ƒYA34bvw€‡†zeem~Š€&Ž|k[axŒ™™…wnmaQe‰„hk‡ƒ€ytityuzQ ¡ 6KU[iƒ‰…gMj®íÿýÿÿýÿÿý€ÿ;øÌt6Fm‚‰’‰|kPPoz{…–žœ—|hbƒžŒ†pekhPVnnacx‰ƒ}~…{ikmpoT(¡ #/4>Zpw{[Ju½ò€ÿý…ÿ<øÜ“J=QuƒˆsbQYmyu{’¤‘‡‡xu”©¡‹ˆ•fMYZTYYKY^tŒ‡z|Š}`VfpkT)¡ $0LfcXu‹uPLÃîýÿýÿ@ýÿÿøØœYC^zІ„ˆnhdgw}tx–°£Š‚†‚‹˜¨¢…xˆ]SLBPfZGFOhˆ…}€…|V9T^N: ¢[  (Nk\QlŒŽoHIt³ÚôýÿýÿÿýþïÉZ?Ry˜žŠ„ysoot{xt€šµ«Š™œ¡§¨—†xTP]SMZk_:")AVas‡…ƒY8?@. ¡( "7MOMbƒ‘…fHB[¶×ßíððêѪH/=kŸœ–˜ˆt€w/uvƒ˜¬¯—‡š©ª §«ž•oY]][aldM$(>.$SjoiF6-! ¡[ )6AK^y‰†w_FHYw•ž›“bA$2f§¥–š¡˜ˆƒ€~…„…‹–¦¶ª£°»³£Ÿ”~qtyfV_cE! "3&'?Njb>*"¢Y*7Jcu‚‹˜«§ŒjRHJLPPKKLC@c’©¯©¡¡¦¤”„{sy„Š” ­ÇÊÆËÎÅ­’’{qxx`MUW3 )DOH. §S '>Ymy€¤¾Ë¿¦ŒsgULMO^uywŠš¤§¤£¡ œŠ|qg{‘¡°·µ¿ÖáåéèݼƒŒ†„mXKNN>"&5$ DG?0  ©S4Xu}ƒ£µÅÓËÀ¶¤œ‰ˆŽ˜§§Ÿ¤œ”‰}vv‡ž°¾ËÑÐÖåöú÷÷íŘ…€ˆ‘€pfQGHH'9`A3cdS;( ©S6_|¢ºÅÉÍÆº¬ ££œš’Š‹¡²ª¥žƒqv|z…£¼ÇÖâçêóýøðçàܹ nk_gx]G=) /@63]……\;'ªR 0Pj{‘§»ÀÁÁ¾²¤˜”‹‹‘‘––Ž—‚tnpy}zޝÉ×êøøûúíâÏÉ¿·©š‹v]B=_~jU? ,/2>v•Š€† ¬¤¢¦¢˜¦ºÂÉÊû¬‘tRKTR[hha_]K. ¯K &;P^ptmr…“œ‡lhc[G/8B>2"1^…ydz˜˜‘“¡£ ±ÃÏ××ÏÁ¸›{e^niWdvtf\ZJ' ±H )7G^rsv|ˆ˜ t_ZX?0?403@LXazncr~y—¢¬½ÌÙäåÛÍÁªuKUqkZe|„m[O9" µE $)Jkomz‘ udca; ,FA=Ojrpfkhqytoq{¢µÄÒßïïâÔǰxLFYbUWnyg_D·F !=dqhtˆ‘’ŠxljtU3IKHNIFZifYmwywyvˆ›´ÅÑÜêëßÒÁ®~Q9I^XMXXUT7 ·€B .M^dx{ƒ{hZft@3>DHF(=pmaeepvtzˆ—¯ÃËÐÚÝ×ȳnF/@]]QE<5/  ºA .4StrvveO@Wx}S+-4AO-:jlmdgqgbu—°¹ÁÈÌʽ®„K/$:]cJ/$ ¼?  4FSip]YZcqsfH9+8L<,Dfpids|i\s„{q…›®µ¶´©”pF6EZnpK ½< "/L^jztrqqaDBTZMQk€m^q„{v}|j]j‹›‰{{fTh~l]9 À9  07`}{utuxvgSN_kXJj}cRfˆpYMO`•†wn]rtUNM@. Ä6 )?U_lqln`KB@T_W[gj\NXotfQ6'F_}–ˆ~nUW^M8*  Æ6  ,:GTX[hO12=FGMdne]SN^dUPOQ`k~Š{mL82(%" Ë2  -6;9@H9&'6?56Rhd^UGQ\`mu}rd^]YitM/-)'&! Ê0$%%$#(.$ ?]THB>GS`ig_UJ?67P]A-+*& Í, %41'$,29;=;76873264)%  Ô  € '-220*%  Ö€    ))$ Ù‚  å„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤ÿþþéÿþýýþ‡ÿýû€ùûýåÿýûù÷÷ùû„ÿ úöôòññòô÷úÿþýüýþØÿþüúøöôòðñôøýÿÿüöñíêè€ç éêíò÷üûù÷öö÷ùüÕÿ þüù÷õóñïíëêêîôõôîéãÝÛÙØÙÚÜÞâåæéìî€íïòõùÔÿ þüøôñïíëêéçåß߀àÛÖÑËÉÆÇËÎÎÏÐÑÔ×ÛáâãåèëðôùûûüþÏÿ þúõïêéèæâÞÚÔÎÌÉú®¦¥®¸¹³´º»ÀÅËÓÖ×ÚÝàäêïôöøûÏÿ1þùóëäßÚ×ÐÁ¹»º··¸µ°¤š†„’ˆ ¦¥¨´ÁÅÇÊÎÓ×ÝåíðôøþÿþþÌÿ1öêÞÕÍÉÅ·ž§¡¢¢ ™‰€{xxukden{}}„–©««±¼ÂÊÒÙãêíñöùúûýÈÿ4úôîåÙκµ²§‰t‚Œ‰ƒynkmlje_^\Y][]jxƒˆŒ–¤®¹ÄÍÖÜáæìóô÷ûÄÿ9þùòìçáÛÕȽ°¦›—–~hlqrrj_WTVXUQVW\UHHGM[dejs€‰Ÿ¯·ÃÎÕÚâêîò÷ýÀÿ<ýùôîéáÙÕÏÉñ¤˜ˆ|z~q_\``YRHAHJD;EVgqz”¬µÊ×ßèñüºÿFýùôïéâÚÒȽ°™r`aadc[OB98:988;7/+(&# "#"%()*,15:EV\bt“´ÌÖáëøþýýþ¶ÿFü÷ñêãÚÐĸ­‹kYVQVWM?4.-/23697-# #+/4:FOV_`y™±Å×áì÷øùü¶ÿGýöíâÖË¿²¤–…v`SNLMKA4+('*+->@<3+!"',19BIP`w‡ŽªÈÔàíòõùý´ÿ"ý÷ëàÔŸ® ‘rbTIEDD?6-%" !"4DIE@<1$€€€#',8>>UuxtްÅÔâëïôû±ÿ&ýúöòìàÔĵ¨›ˆxlaRF=8741*#!:PZSNF<.$ƒ!/69H_idp“¬¾Öäêï÷¯ÿ*ýû÷óíèáÖÇ·¦“‚lYUQA4,*'&!.CWgeUNF7+& †$.4@OZWYs‹¥ÊÛâêóý­ÿ1þû÷òìæÞ×̼«˜oYJF=1'" !2HZok\[QB3-*#€‚$-2;HKNb}œºÐÚäíù­ÿOþùôíçß×ÌÀ®™€hXMD:.%&4GWgb`^YL>72+$%*15+$%*'##1BLQRT`cZOLF;1+#$)5GThzŽ›¨½Õàîý¬ÿ5þõëáÖʼ®›„lQA80)" $(.23/*##-=TVNT_gg[NKID=2%!(+,-'€€",þüøóìßÓĵ¥y`J>2)" +:HPOG=659?N^\SX`a]TE?B?80$$0CNQME<3)# '1>Mdtls½ÒìªÿSúõïéÚͼª–iS>1)!#/?S[RKD>:=EP\[\__YWNJHD:0( *;JXcWQJ;/(#!!$%&(*+.7AH\tyƒž®Åãú©ÿSúôìãÔ¯˜iZI3' *3AQTOJEBACDGPU_d`\UIILB2("$2FScbTXWD51,)%##(-/159=>ILHYr„•¢¡²Ôô©ÿWøîäØË¶£‰jPI<* "1:COKLJHFDFC>GT]a_^VGA@6*"(7IZocPOQIA=92-'(-/06>FKOYXPUco…š˜œÃîÿÿþþ¢ÿ[þüùóéÜÑ®—€]E9.$&7AKQJKNJJD=;:BLUZZVPE;60( +9H[e[QJD>ED:4.,(034;FNW\ggXQQSp”’вá÷úúüþ¡ÿ[üùõïãÖȹ¤ŠqU@3*!)9GYWKLNNI?3048840( /=INNHBBDJ\c[UW[VNJKGB<61,%  &,5Iaiigi›Ñäêð÷ý ÿCýõìäØÈµ¡{VJB0#(8IX`dZSF:430*"(;IMG@@DOZcje^^_VONMHD@7+! … !5S[\do’ÁÝéîõû¡ÿ@õëáÖŰ—qWJ>-(8DJPZZTG9330'!"3BLSIHMIC921.&#%+9FPVKCAJUOMUVOJC@FFB@<6& ‰ -&#@Xi~š¸Óåêñ÷þ ÿùìßѾ qTOG7)+&.029?<840.+''*7?IQUNIDEIJIJEGGDEE:686, ŠRq]).Phƒ¡³ÁÒäïöü ÿ=úêÙÉ· xZNC4$"&)/0/.,+*),6GINQQOKE;>CFHLJEBB@822-#Š.ˆÁ›R*?h}–¦­ºÚíôúžÿýú÷çÖ¯™{\G:,!€$#%&'',3EQJKMQQKF>>DDCKH@;8;61-( ŠX¶ìØ‹=,Tv‹›§¾ÜëòøÿþúöñäÒ½¨ŽqUB4+ €-  "&-@KEFJMPNF?;ECAA:32274/0) ‰9Úþô´^)Aiz‹¢Çáêðöýœÿýùóíâθ¡†iR@3+ (  %8ELPQIOPG?<<>?:2//.11-,' -† 1xÄóÿÿØ53Xjy”½Ûêð÷üœÿþøñêáÍ·£ƒeO@3*!€ € !.?N_WGKIEB>48:;=;5/140& 'SqZ)„>v»ìýÿÿî–<,Q`k®ÜíóøüÿNøðèÝ̶¢‚cL@2'# &4GXRD@>?D@715;IMB=>A9(0Á¯|K+ )BcŒÀíý€ÿ ïC,M^dmŸÝò÷ûýÿùïæÚ˶}bQA1$! €   )9GHBBGJLH<17DQR€M%K?,2ãíѬ‡mabi|•±Ùôÿÿýÿÿîœ?,Qhnj•ÐòüþžÿJûðåØË´e\VD3&    &1;CN]eaWPGK[b[UTPNC.8”êÿùéÛ˽¹ÁÎçöÿ ýÿÿì’8-Vt|uŽÆó ÿ þòå×ɵ‡`YQB4'€7   +?]y|wlgiorsn\WTOG: 2Šäÿÿýþþöýýü†ÿ ߈//]zŠˆŽ½í¡ÿ ôæ×Ç´—wdO>4'€ €# (Ak˜š“„}|xwyvd]^ZM@'-uÐÿý€ÿý…ÿ ûÐn*5f“’‰°ãü ÿ÷èׯ´ŸƒfM;4, ‡  .MmšŽ‹…zvsqfbhdSG2#X¯ôŒÿ ýî®N+?ev‡‰›Ðö ÿüêØÆ®•v]J=7.  ˆ"  $-:HaŠ‘…„~zxuligcgcXR?#7†Ôüý†ÿýÿýýúÓ€:3DTcs||Áñ¡ÿíÚÉ®r]QJA3#‰"2FA:LKKGUvˆ‚{lcimkmk`^VRLE/!N›äü‚ÿýýƒÿ è£S2?FI\r‡‡ˆ¸ì¡ÿóàϳre\WL8&Š#b›‚B&Dhnh^`p~}zj_ddhpj[UPMGD=(&]ªèûˆÿç³f7:AP\fibF)-R޽âòøûýüÿóÊ›a2&5BCDTbn·ÌÜêõ¡ÿñÝʪˆqcRA2'  ‡BK âüýÔ„Qb˜žsfadlnd]ZUIB:38MkybD0,<`Šª»ÄÍż¤zS('((9L^`aˆ±ÉÛèóýŸÿýòÝÊ®‰j\M9-&2G9„CHŽÑöÿÿðbh¡¢”…m\RZf_ZUMGB<8=Qs†…vm[E>2-5I]fpldR5!"(8IU\f†§ÆÚèñúÿ þûøñÜȳ‹hZN=/'bšŒS*5^’Î÷€ÿ!õ§fk¤¶©šy^OTWSSPNMH??ERhyomqcQNJ;.€%')'"'6CHRf¤ÊÜçðùÿ üùôîÛDzn^TE4("}ËØ±ƒYA34EObu¸ÖêúþŸÿûõîéÝÆ£eWQD6)8݇ÿý‚ÿ)è©P>_w{m_WR`mvu‹•‹zˆ’„mXKHHEEB?=1' € .BMWem–Èßíú ÿý÷ðêãЪ~o\F90_®íÿýÿÿýÿÿý€ÿ;øÌt8Kirj^\Z_n{t{Œ˜˜’…~…ŠhNHKF>>DB90' )?Vbkv•Ãàìø¡ÿùòëæÖ¼§—ƒhQC6%(q½ò€ÿý…ÿ<øÜ’E5fupkdghlrtv|‘£¡–ŽŽ‹‚‡|]FDHC9:IO?4*" $=Wio{—Âáìö¡ÿûôíèÚÈ´ž€f]W@-7}ÃîýÿýÿAýÿÿøØ›T2Aanqnmrytqoox’§›Š‘¢œˆkXFAFE?CQKD=3+& "7Wk|“­Íãìõý ÿ\ýöïêàн«†jibL6';s³ÚôýÿýÿÿýþïÉ[??=!&/BišÀÖâîôùü¡ÿEþûùøòáÑÀ«–|bPB7*&(.7BFKS\\THbƒyidksqhjyˆ‘•†qqr{‘–†‡…n\RWVZjpWHCFF. €%-7Hi›ÇÛèòøûþ¢ÿEþýþÿëØÆ°˜|i[NA2"%4CNSPNOSZ`^v‰wsqrsrrtƒ“š£„i]\f~…rs€xhY`oicjdXJFJL!€ )5@Mj Ëãí÷ý¨ÿSñÞ˱vlcUC4(6K`kmhffkjhgaY[fhoq|„…š¡ŠmTHEPj{qogfjkmklj_XIDKL -7BOt¬Ôêôú©ÿS÷äÒ¸“|xnYD7.%*?Wgjnmhe`ZUC88;EP_nxv‚–—sUD43@\~sh_bplc^eosgM>IQ(/6G[€´äïõú©ÿBýé×Ū—bK>6,$4FXeql]UK@0"#&+6CU[_i{zcP;+,9UtqhZUVb_WQWctpQK^\E:0! )3:DP_^TI>/(1?T`YVMHILIGHKLT\N?>GK- 9IW`€¤Ñð÷ûýªÿPõæØÌ¼ªˆjWIE>-$(,3==."!(/9GOG;4*#,4GUPHB:GQ[q™¿æõý¬ÿûìàÕɹhVJIE6*#%&&#€4 $&/8=:+  -4DKCA;5;FIHIND;77:EI<63, %8CLUhˆªØòú®ÿOòæÛÓÅ›n^RJI>1*# 5GJC=.+*!*5>=9<=8865-3740.+1BOG80'"$'-7?DQa}¤Çäóùþ­ÿ÷ëâÙγ•~cKLF92) €7Elux|hJ7$-/149>-  #+AIH3.,35=LWl‡£Äçïöúüþ®ÿLöîèáÓ±—€n\PKA4)  aŠ˜©²®˜}G "'+   &1CF?=ABEKay­Íåôøüþ°ÿ,úóíêÞÐÁ¯ž‡o[RI?1%! ,}•­½µ„nP " € !%0FYQLV^gvЧÁÙíúþ³ÿ#ý÷óðêÜÐö©‡i^VH8,& :‘’¨¼¥€mbN   '**7Mgfdr{®ÅÑãöý¶ÿ0ûøöõéÝÓÉ»šwfcTC9.&4‰Œ™¥œ•‡lD  €  .33;St…•°Ïáêñ÷ü¶ÿý€üBôêÞÕÈ®Ž|p]QJ<0%(bŠ‘”“ž¡r2   '7=>D[}˜¤¯»ËÝèîôøüºÿ#ûïæÜÔÅ®¢…gaXMD5'"!Bt…Ї~Œ‘W "€ 7CIHMe±¿ÏÙáèîó÷ûþ»ÿõëäÝÓÆ»¦‘j]YE3+''+Fazznp^3€ #*1+!€ 1AJJIVm›ÉÔÝåëñõùüþ¼ÿ=ùñêåÝÔÊÁ¸¥Š|kN=21/,)3CKF<.!(07=6+$'+(*49CJIJfŠ·Öäëñöûþ¿ÿ9üöðìèÞ×Ñʾ¯§ˆfVD;52..,,*'(*&$'8CLKA812351.2>@HC:EQ^wœÈäîôúÃÿ6þúöóñêãÝØÑÇÀ®šƒdM?9:9641/0/-/9M\bb]WLD==95=ELURPYo»ÜõüÇÿ3üúøøôîêçàÖÏDz‘xcLIFB><;955?Ncvwv}sbQDA??DIO\dq†¢ÁçùÉÿ2þýýþý÷òîêâÛÖÑÌ¿¯”uke\OINOIJZk|ŽŒ‰‰o]USUYXWXq‰µÌêúÏÿ,û÷ôóìçâÞÚÙØÊ­ž’Šxsv}vn}ŽšŸž›—–’‘Œ‹‡„œ´ÄÞîýÐÿýûúùöñîëçâàÝÖÍĽ€®¶­¨°º½··»ÀÆÌÒÐÈÆÃÄÁËÑÞçöùüÑÿþþÿþøôðíëêêëèãáßÙרÓÖÒÖßêñòòñìíæçñóïô÷øûþÕÿûøö€ôöøøõðëéæäãääæéìòøþÿÿû÷óñóôõ÷øúüþ×ÿþüûûüþÿÿþùõòðïîïðóöúƒÿüúøö÷ùûýäÿüúø÷÷øúý†ÿþüûüþçÿþýýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©ÿt8mk@    ",5>FLQUWXWUQLF>5,"  +9HWft€Š’™ž¡¢¡ž™’Š€tfWH9+ );OdyŒ«·ÁÉÏÔØÚÛÚØÔÏÉÁ·«ŒydO;)/F_y‘§ºÉÖßåêíðòôõõõôòðíêåßÖɺ§‘y_F/0Jg…¡¹ÌÛåíòöøùúûüýýýýýüûúùøöòíåÛ̹¡…gJ0 *Ef‡§ÁÖäîôøûüýýýýþþþþþþþþþýýýýüûøôîäÖÁ§‡fE*  :\¤ÂØèòøûüýþþþþþþÿÿÿÿÿÿÿÿÿþþþþþþýüûøòèØÂ¤\: *Jq˜ºÕçòùüýþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþýüùòçÕº˜qJ* 4X‚«ÌãñøüýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþýüøñãÌ«‚X4  >f‘¹ØëöûýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþýûöëØ¹‘f> "DqžÅàñùüþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþüùñàÅžqD" #Gv¦ÍæôûýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþýûôæÍ¦vG#  "GxªÑêöüþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþüöêѪxG"  DvªÒìøüþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþüøìÒªvD >p¦ÑëøüþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþüøëѦp>4fžÍêøýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþýøêÍžf4*X‘Äæ÷ýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþý÷æÄ‘X* J‚¹àôüþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþüôโJ :q«ØñûþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþûñØ«q:*[˜Ìëùýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþýùë̘[*F»ãöýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþýöã»F0f¤ÕñûþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþûñÕ¤f0I‡Âèøýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýøè‡I/g§ÙóüþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþüóÙ§g/F…ÂèùþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùèÂ…F )_¡ÖòüþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþüòÖ¡_) ;y¹åøýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýøå¹y;O‘ÌîûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþûîÌ‘O +d§ÛõýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýõÛ§d+ :yºæùþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþùæºy:HŒÉíûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþûíÉŒH "WÕóüþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþüóÕW"  ,f«ß÷ýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷ß«f, 5t·åùþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþùå·t5>€ÁêúþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúêÁ€>FŠÉîûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþûîÉŠFM“ÐñüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüñГMR™ÕóýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýóÕ™RVžØõýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýõØžV X¡ÛöýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöÛ¡X  X¢ÛöýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöÛ¢X  X¡ÛöýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýöÛ¡X VžØõýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýõØžVR™ÕóýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýóÕ™RM“ÐñüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüñГMFŠÉîûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþûîÉŠF>€ÁêúþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúêÁ€>5t·åùþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþùå·t5 ,f«ß÷ýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþý÷ß«f,  "WÕóüþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþüóÕW" HŒÉíûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþûíÉŒH:yºæùþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþùæºy: +d§ÛõýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýõÛ§d+ O‘ÌîûþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþûîÌ‘O;y¹åøýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýøå¹y; )_¡ÖòüþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþüòÖ¡_) F…ÂèùþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùèÂ…F/g§ÙóüþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþüóÙ§g/I‡Âèøýþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýøè‡I0f¤ÕñûþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþûñÕ¤f0F»ãöýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþýöã»F*[˜Ìëùýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþýùë̘[*:q«ØñûþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþûñØ«q: J‚¹àôüþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþüôโJ *X‘Äæ÷ýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþý÷æÄ‘X*4fžÍêøýþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþýøêÍžf4>p¦ÑëøüþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþüøëѦp> DvªÒìøüþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþüøìÒªvD  "GxªÑêöüþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþüöêѪxG"  #Gv¦ÍæôûýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþýûôæÍ¦vG# "DqžÅàñùüþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþüùñàÅžqD" >f‘¹ØëöûýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþýûöëØ¹‘f>  4X‚«ÌãñøüýþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþýüøñãÌ«‚X4 *Jq˜ºÕçòùüýþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþýüùòçÕº˜qJ* :\¤ÂØèòøûüýþþþþþþÿÿÿÿÿÿÿÿÿþþþþþþýüûøòèØÂ¤\:  *Ef‡§ÁÖäîôøûüýýýýþþþþþþþþþýýýýüûøôîäÖÁ§‡fE* 0Jg…¡¹ÌÛåíòöøùúûüýýýýýüûúùøöòíåÛ̹¡…gJ0/F_y‘§ºÉÖßåêíðòôõõõôòðíêåßÖɺ§‘y_F/);OdyŒ«·ÁÉÏÔØÚÛÚØÔÏÉÁ·«ŒydO;) +9HWft€Š’™ž¡¢¡ž™’Š€tfWH9+  ",5>FLQUWXWUQLF>5,"    Cultivation_9+dfsg1_UnixSource/game2/build/macOSX/game2.app/Contents/Resources/game2.rsrc0000640000175000017500000000552710455474111030071 0ustar pabspabs §§°;)This application needs at least a MacPlus"This application needs more memory!This application is out of memory*This application needs at least System 8.6 About this wxWindows Application}This application needs Appearance extension (built in with System 8) - this restriction will be relieved in the final release"ÿÿÿýAboutÉ-Dà01Æ#b""#b1Æ0ààðøü?þ?þ?þ?þ?þüøðàDDðˆp @@€€ðøøððàÀÀ€€D!@€@€@€@€!€Ààp8?€ÿÀÿÀÿÀÿÀÿÀÀ?àðø|>Dàð88< ngc†aÆ`æpv0<øà@ð?ø<<~ÿo†çÇcæá÷pþp~<<üð@D€€€€€€àððððPPPÀÀÀÀÀÀàð?ø?ø?ø?ø?ø?ø?ø?ø DÀ@@À1€Gþ€€þ€@À@ÀÀ€?€ÀÀÀÀ?€þÿÿÿÿÿþÿÀÿÀÿÀÿÀÿ€?€D€`Œ €€Àø€àðüÿÿÿÿÿÿÿÿÿÿÿÿøD@x`üqÎy†|~¸|0l0F00ÀxàüñþûÿÿÏÿÿÿ¾ÿüþxÿxïøÏø‡øøD>~þþ>6b`ÀÀ?ÿÿÿÿ÷óáàÀD€Àà€€€üü€€€àÀ€€ÀàðøÀ?þ?þ?þ?þÀøðàÀ€D€Àà€ˆŒ?þŒˆ€àÀ€€Ààð èÜ?þÿ?þÜ èðàÀ€D Ààp8?—ãá!ð1ø:|<<>??€ Dxpp`HàÀ€€H88xþüøpððéðÇà‡À„Œ>\<<8|üüD @øô *¨ð€8`€>|pàü?ööþüø?ðÀøà AÀ¢ð¥üKÿ×ÿÀñÿÿðàÿü€ÿÿÿ€ÿÿÿ€?ÿþ€ÿÿþ€?ÿúÀÿÿöÿÿî_ÿÿÞÿÿÿ¾Ïÿÿóÿþÿäÿýþá?ûüàO÷øàïðxßà?À€?€à?x><¸ð`AÀãðçüÏÿßÿÀÿÿÿðÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿüÿÿÿøÿÿÿðÿÿàÿÿÀÿÿ€ÿÿþüøð`@ ÀðëüŽÿ¯þ—þßþïú¿ç‹æÅÜ0Ø0`À Àðÿüÿÿÿÿÿÿÿÿÿÿÿÿÿþÿü?øðàÀ€ì oÀÞ½ÖfoÀÞ»îeffoÀâ;»fffnì,¥ffff`á¦ffffн½æfffmÀÿöffff\Àìßfffm ÿü ßffPßü]À ßm ÿÀ uÀ Ðßü mÀ ÿÀÎeÏüÝoÀõ÷ëñ+õøˆ‚VÒÒÒìñøùû^^ý‚ï°ìÒÒÒìñ÷õ­]33^‡^‚ÓÒÒÒÒÒëý‚33¬ÕÒëÒÒÒÒÒÒÒõˆ33_ïÒÒÒÒÒÒÒÒ€û^]]ÕÒÒÒÒÒÒÒÒ€öýÿÖìÒÒÒÒÒÒÒÒ€++õþ÷ôïëÒÒÒÒÒ€öùÿýþö+WþðëÒÒ€öVÿÿ÷~ªö÷Wóì€+Vÿÿ÷1͆ö÷ú+Vÿÿ÷õzë€öVÿÿ÷*«ì€+ÿÿ÷[«ðý÷š STR#RMENU^MBARjcarbvldes‚CURSšICN#Nics#Zics4fics8r€ÿÿ?ÿÿeÿÿm€ÿÿsÿÿ‰ ÿÿŸ ÿÿç ÿÿ/ ÿÿwÿÿ¿ÿÿÿÿOÿÿ—ÿÿßÿÿ'ÿÿoÿÿ·ÿÿÿÿÿGÿÿ€ÿÿ×€ÿÿÛ€ÿÿ€ÿÿ£Simple Alert MessagesCultivation_9+dfsg1_UnixSource/game2/build/macOSX/game2.app/Contents/MacOS/0000750000175000017500000000000011401021117025143 5ustar pabspabsCultivation_9+dfsg1_UnixSource/game2/build/macOSX/game2.app/Contents/MacOS/dummy.txt0000640000175000017500000000012510455474111027054 0ustar pabspabsA placeholder file so that "cvs export" will include this otherwise empty directory.Cultivation_9+dfsg1_UnixSource/game2/configure0000750000175000017500000000377210730301035020325 0ustar pabspabs#!/bin/bash # # Modification History # # 2006-June-27 Jason Rohrer # Copied/modified from Transcend project. Dropped a platforms that were only # nominally supported. # # 2007-Dec-11 Jose Jorge # Added platform switches which allow us to skip the interactive menu. # case $1 in "--linux") platformSelection=1 ;; "--macosx") platformSelection=2 ;; "--win32") platformSelection=3 ;; "--help") echo "Use the following switches to skip interactive mode:" echo "--linux" echo "--macosx" echo "--win32" exit ;; esac while [ -z "$platformSelection" ] do echo "select platform:" echo " 1 -- GNU/Linux" echo " 2 -- MacOSX" echo " 3 -- Win32 using MinGW" echo " q -- quit" echo "" echo -n "> " read platformSelection if [ "$platformSelection" = "q" ] then exit fi # use ASCII comparison. if [[ "$platformSelection" > "3" ]] then platformSelection="" fi if [[ "$platformSelection" < "1" ]] then platformSelection="" fi done # use partial makefiles from minorGems project makefileMinorGems="../minorGems/build/Makefile.minorGems" makefileMinorGemsTargets="../minorGems/build/Makefile.minorGems_targets" platformName="Generic" platformMakefile="generic" case "$platformSelection" in "1" ) platformName="GNU/Linux" platformMakefile="Makefile.GnuLinux" ;; "2" ) platformName="MacOSX" platformMakefile="Makefile.MacOSX" ;; "3" ) platformName="Win32 MinGW" platformMakefile="Makefile.MinGW" ;; esac rm -f Makefile.temp echo "# Auto-generated by game2/configure for the $platformName platform. Do not edit manually." > Makefile.temp rm -f gameSource/Makefile cat Makefile.temp $platformMakefile Makefile.common $makefileMinorGems gameSource/Makefile.all $makefileMinorGemsTargets > gameSource/Makefile rm Makefile.temp exit Cultivation_9+dfsg1_UnixSource/game2/Makefile.common0000640000175000017500000000254011377047544021362 0ustar pabspabs# # Modification History # # 2004-April-30 Jason Rohrer # Created. Modified from MUTE source. # # 2005-August-29 Jason Rohrer # Added optimization options. # # 2007-April-23 Jason Rohrer # Upgraded to latest minorGems dependency format. # ## # The common portion of all Makefiles. # Should not be made manually---used by configure to build Makefiles. ## EXE_LINKER = ${GXX} RANLIB = ranlib LIBRARY_LINKER = ar DEBUG_ON_FLAG = -g #-DDEBUG_MEMORY DEBUG_OFF_FLAG = DEBUG_FLAG = ${DEBUG_OFF_FLAG} PROFILE_ON_FLAG = -pg -a -DUSE_GPROF_THREADS PROFILE_OFF_FLAG = PROFILE_FLAG = ${PROFILE_OFF_FLAG} OPTIMIZE_ON_FLAG = -O9 OPTIMIZE_OFF_FLAG = OPTIMIZE_FLAG = ${OPTIMIZE_ON_FLAG} PORT_AUDIO_PATH = ${ROOT_PATH}/minorGems/sound/portaudio # common to all platforms SOCKET_UDP_PLATFORM_PATH = unix SOCKET_UDP_PLATFORM = Unix COMPILE_FLAGS = -Wall -Wwrite-strings -Wchar-subscripts -Wparentheses ${DEBUG_FLAG} ${PLATFORM_COMPILE_FLAGS} ${PROFILE_FLAG} ${OPTIMIZE_FLAG} -I${ROOT_PATH} -I${PORT_AUDIO_PATH}/pa_common COMPILE = ${GXX} ${COMPILE_FLAGS} -c EXE_LINK = ${EXE_LINKER} ${COMPILE_FLAGS} ${LINK_FLAGS} LIBRARY_LINK = ${LIBRARY_LINKER} cru # # Generic: # # Map all .cpp C++ and C files into .o object files # # $@ represents the name.o file # $< represents the name.cpp file # .cpp.o: ${COMPILE} -o $@ $< .c.o: ${COMPILE} -o $@ $< Cultivation_9+dfsg1_UnixSource/game2/Makefile.MinGW0000640000175000017500000000157410450236507021047 0ustar pabspabs# # Modification History # # 2003-November-2 Jason Rohrer # Created. # # 2003-November-10 Jason Rohrer # Removed pthread flag. # Changed LINUX flag to WIN_32 flag. # Added wsock32 library flag. # ## # The common MinGW (GNU for Win32) portion of Makefiles. # Should not be made manually---used by configure to build Makefiles. ## PLATFORM_COMPILE_FLAGS = -DWIN_32 # need various GL libraries, winmm, and portaudio # -mwindows tells mingw to hide the dos command window on launch PLATFORM_LINK_FLAGS = -lopengl32 -lglut32 -lglu32 -mwindows ${PORT_AUDIO_PATH}/lib/libportaudio.a -lwinmm # All platforms but OSX support g++ and need no linker hacks GXX = g++ LINK_FLAGS = ## # Platform-specific minorGems file path prefixes ## PLATFORM = Win32 PLATFORM_PATH = win32 TIME_PLATFORM = Win32 TIME_PLATFORM_PATH = win32 DIRECTORY_PLATFORM = Win32 DIRECTORY_PLATFORM_PATH = win32 Cultivation_9+dfsg1_UnixSource/minorGems/0000750000175000017500000000000011401021171017346 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/network/0000750000175000017500000000000011401021061021035 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/network/HostAddress.h0000640000175000017500000002204711331675571023466 0ustar pabspabs/* * Modification History * * 2001-January-10 Jason Rohrer * Created. * * 2001-December-12 Jason Rohrer * Made serializable. * Changed the constructor signature. * * 2001-December-14 Jason Rohrer * Added a method for getting the local address. * Fixed several typos. * Added functions for testing equality and making copies. * Fixed a comment. * * 2002-February-25 Jason Rohrer * Made destructor virtual. * * 2002-March-25 Jason Rohrer * Added function for conversion to numerical addresses. * Changed equality function to resolve both addresses to * their numerical form first. * Changed equality function to be localhost savvy. * * 2002-March-26 Jason Rohrer * Added some print statements for testing. * Removed testing print statements. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-April-8 Jason Rohrer * Added an isNumerical function because hostname lookups on * numerical addresses are slow on Win32. * * 2002-April-11 Jason Rohrer * Fixed a bug in equals when both lookups fail. * * 2002-July-23 Jason Rohrer * Fixed a bug in equals when our local lookup fails. * * 2010-February-1 Jason Rohrer * Fixed a bug in getNumericalLocalAddress when our local lookup fails. */ #include "minorGems/common.h" #ifndef HOST_ADDRESS_CLASS_INCLUDED #define HOST_ADDRESS_CLASS_INCLUDED #include #include #include "minorGems/io/Serializable.h" #include "minorGems/util/stringUtils.h" #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Address of a network host. * * @author Jason Rohrer */ class HostAddress : public Serializable { public: /** * Constructs an uninitialized address. * * Note that all other functions (besides deserialize and the * destructor) assume that the HostAddress is initialized. * * Useful prior to deserialization. */ HostAddress(); /** * Constructs an address. * * @param inAddressString a \0-terminated string containing the * host address. This string will be destroyed when this class * is destroyed, so it cannot be const. * @param inPort the port of the host. */ HostAddress( char *inAddressString, int inPort ); virtual ~HostAddress(); /** * Gets the address of the local host. * * @return the address of the local host. * Note that the mPort field in the returned HostAddress is * set to 0. * Must be destroyed by caller. */ static HostAddress *getLocalAddress(); /** * Gets the address of the local host in numerical format. * * @return the address of the local host. * Note that the mPort field in the returned HostAddress is * set to 0. * Must be destroyed by caller. */ static HostAddress *getNumericalLocalAddress(); /** * Gets a numerical version of this host address. * * For example, if we are using IPv4, this will generate a * HostAddress containing an aaa.bbb.ccc.ddd style address. * * @return a numerical version of this address, or NULL * if address resolution fails. * Must be destroyed by caller if non-NULL. */ HostAddress *getNumericalAddress(); /** * Gets whether this address is in numerical format. * For IpV4, this is xxx.xxx.xxx.xxx * * @return true if this address is numerical. */ char isNumerical(); /** * Gets whether another address is equivalent to this address. * * @param inAddress the address to compare to this address. * * @return true iff inAddress is equivalent to this address. */ char equals( HostAddress *inAddress ); /** * Makes a copy of this host address. * * @return the copy of this address. */ HostAddress *copy(); /** * Prints this address to standard out. */ void print(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); char *mAddressString; int mAddressLength; int mPort; }; inline HostAddress::HostAddress( char *inAddressString, int inPort ) : mAddressString( inAddressString ), mPort( inPort ) { mAddressLength = strlen( mAddressString ); } inline HostAddress::HostAddress() : mAddressString( NULL ) { mAddressLength = 0; mPort = 0; } inline HostAddress::~HostAddress() { if( mAddressString != NULL ) { delete [] mAddressString; } } inline HostAddress *HostAddress::getNumericalLocalAddress() { HostAddress *address = getLocalAddress(); if( address != NULL ) { HostAddress *numAddress = address->getNumericalAddress(); delete address; return numAddress; } return NULL; } inline char HostAddress::equals( HostAddress *inAddress ) { // if the port numbers are not equal, we can return // right away if( mPort != inAddress->mPort ) { return false; } // otherwise, the port numbers are equal, // so we need to compare the addresses // first, try to compare numercally looked-up addresses HostAddress *numericalThis = this->getNumericalAddress(); if( numericalThis != NULL ) { HostAddress *numericalOther = inAddress->getNumericalAddress(); if( numericalOther != NULL ) { char returnVal = false; // watch out for localhost loopbacks if( !strcmp( numericalThis->mAddressString, "127.0.0.1" ) ) { // this address is localhost // make sure other address is not our external local address HostAddress *localAddress = getNumericalLocalAddress(); if( localAddress != NULL ) { if( !strcmp( localAddress->mAddressString, numericalOther->mAddressString ) ) { returnVal = true; } delete localAddress; } else { // numerical lookup failed for one but not the // other, so assume addresses are different returnVal = false; } } else if( !strcmp( numericalOther->mAddressString, "127.0.0.1" ) ) { // other address is localhost // make sure this address is not our external local address HostAddress *localAddress = getNumericalLocalAddress(); if( localAddress != NULL ) { if( !strcmp( localAddress->mAddressString, numericalThis->mAddressString ) ) { returnVal = true; } delete localAddress; } else { // numerical lookup failed for one but not the // other, so assume addresses are different returnVal = false; } } // if numerical addresses are identical, // then hosts are equal if( !strcmp( numericalThis->mAddressString, numericalOther->mAddressString ) ) { returnVal = true; } delete numericalOther; delete numericalThis; return returnVal; } delete numericalThis; } // otherwise, if lookup fails, compare raw adddresses if( !strcmp( inAddress->mAddressString, mAddressString ) ) { return true; } else { return false; } } inline HostAddress *HostAddress::copy() { char *stringCopy = new char[ strlen( mAddressString ) + 1 ]; strcpy( stringCopy, mAddressString ); return new HostAddress( stringCopy, mPort ); } inline void HostAddress::print() { printf( "%s:%d", mAddressString, mPort ); } inline int HostAddress::serialize( OutputStream *inOutputStream ) { int numTransmitted = 0; numTransmitted += inOutputStream->writeLong( (long)mAddressLength ); numTransmitted +=inOutputStream->write( (unsigned char *)mAddressString, mAddressLength ); numTransmitted += inOutputStream->writeLong( (long)mPort ); return numTransmitted; } inline int HostAddress::deserialize( InputStream *inInputStream ) { int numTransmitted = 0; long readLong; numTransmitted += inInputStream->readLong( &readLong ); mAddressLength = (int)readLong; if( mAddressString != NULL ) { delete [] mAddressString; } mAddressString = new char[ mAddressLength ]; numTransmitted +=inInputStream->read( (unsigned char *)mAddressString, mAddressLength ); numTransmitted += inInputStream->readLong( &readLong ); mPort = readLong; return numTransmitted; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/unix/0000750000175000017500000000000011401021060022017 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/network/unix/SocketUDPUnix.cpp0000640000175000017500000001453710155050216025215 0ustar pabspabs/* * Modification History * * 2004-November-3 Jason Rohrer * Created. * * 2004-November-4 Jason Rohrer * Added code for win32 compatibility. * Fixed a memcpy bug. * * 2004-December-7 Jason Rohrer * Fixed a bug in the evaluation of wait return codes. */ /* * The following changes were needed for win32 compatibility: * -- Winsock.h included * -- socklen_t defined * -- closesocket instead of close * -- inet_aton does not exist on win32 (switched to the linux-obsolete * inet_addr) */ #include "minorGems/network/SocketUDP.h" #include "minorGems/network/NetworkFunctionLocks.h" #include "minorGems/util/stringUtils.h" #ifndef WIN_32 // standard unix includes #include #include #include #include #include #include #include #else // special includes for win32 #include // windows does not define socklen_t typedef int socklen_t; // windows does not have a close function for sockets #define close( inSocketID ) closesocket( inSocketID ) #endif #ifdef BSD // BSD does not define socklen_t typedef int socklen_t; #endif // prototypes /** * Waits on a socket for incoming data. * * @param inSocketID the socket to watch. * @param inMilliseconds the timeout. * * @return 1 if data is ready to be read with recvfrom, -2 if we * timeout, and -1 on an error. */ int waitForIncomingData( int inSocketID, long inMilliseconds ); SocketUDP::SocketUDP( unsigned short inReceivePort ) { int socketID; socketID = socket( AF_INET, SOCK_DGRAM, 0 ); // bind to receivePort struct sockaddr_in bindAddress; bindAddress.sin_family = AF_INET; bindAddress.sin_port = htons( inReceivePort ); bindAddress.sin_addr.s_addr = INADDR_ANY; bind( socketID, (struct sockaddr *)&bindAddress, sizeof(bindAddress) ); int *socketIDArray = new int[1]; socketIDArray[0] = socketID; mNativeObjectPointer = (void *)socketIDArray; } SocketUDP::~SocketUDP() { // unwrap our native object int *socketIDArray = (int *)( mNativeObjectPointer ); int socketID = socketIDArray[0]; close( socketID ); delete [] socketIDArray; } struct UDPAddress *SocketUDP::makeAddress( const char *inAddress, unsigned short inPort ) { // try converting it from aaa.bbb.ccc.ddd int convertedAddress = inet_addr( inAddress ); if( convertedAddress != -1 ) { struct UDPAddress *address = new struct UDPAddress; address->mIPAddress = convertedAddress; address->mPort = htons( inPort ); return address; } else { return NULL; } } char *SocketUDP::extractAddress( struct UDPAddress *inAddress, unsigned short *outPort ) { struct in_addr addressStructure; addressStructure.s_addr = inAddress->mIPAddress; NetworkFunctionLocks::mInet_ntoaLock.lock(); // returned string is statically allocated, copy it char *addressString = stringDuplicate( inet_ntoa( addressStructure) ); NetworkFunctionLocks::mInet_ntoaLock.unlock(); *outPort = ntohs( inAddress->mPort ); return addressString; } int SocketUDP::send( struct UDPAddress *inAddress, unsigned char *inData, unsigned long inNumBytes ) { // unwrap our native object int *socketIDArray = (int *)( mNativeObjectPointer ); int socketID = socketIDArray[0]; // pack our address into the required structure struct sockaddr_in toAddress; toAddress.sin_family = AF_INET; toAddress.sin_port = inAddress->mPort; toAddress.sin_addr.s_addr = inAddress->mIPAddress; return sendto( socketID, (char *)inData, inNumBytes, 0, (struct sockaddr *)( &toAddress ), sizeof( toAddress ) ); } int SocketUDP::receive( struct UDPAddress **outAddress, unsigned char **outData, long inTimeout ) { // unwrap our native object int *socketIDArray = (int *)( mNativeObjectPointer ); int socketID = socketIDArray[0]; if( inTimeout != -1 ) { int waitValue = waitForIncomingData( socketID, inTimeout ); // timed out or saw an error while waiting if( waitValue == -1 || waitValue == -2 ) { *outAddress = NULL; *outData = NULL; return waitValue; } // else we have data waiting } struct sockaddr_in fromAddress; int bufferSize = 10000; unsigned char *receiveBuffer = new unsigned char[ bufferSize ]; socklen_t fromAddressLength = sizeof( fromAddress ); int numReceived; numReceived = recvfrom( socketID, (char *)receiveBuffer, bufferSize, 0, (struct sockaddr *)( &fromAddress ), &fromAddressLength ); // if no error and no overflow if( numReceived >=0 && numReceived <= bufferSize ) { *outAddress = new struct UDPAddress; (*outAddress)->mPort = fromAddress.sin_port; (*outAddress)->mIPAddress = fromAddress.sin_addr.s_addr; *outData = new unsigned char[ numReceived ]; memcpy( (void *)( *outData ), (void *)receiveBuffer, numReceived ); } else { *outAddress = NULL; *outData = NULL; } delete [] receiveBuffer; return numReceived; } /* socket timing code adapted from gnut, by Josh Pieper */ /* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ int waitForIncomingData( int inSocketID, long inMilliseconds ) { fd_set fsr; struct timeval tv; int returnValue; FD_ZERO( &fsr ); FD_SET( inSocketID, &fsr ); tv.tv_sec = inMilliseconds / 1000; int remainder = inMilliseconds % 1000; tv.tv_usec = remainder * 1000; returnValue = select( inSocketID + 1, &fsr, NULL, NULL, &tv ); if( returnValue == 0 ) { return -2; } else if( returnValue == -1 ) { printf( "Selecting socket during receive failed.\n" ); return returnValue; } else { return returnValue; } } Cultivation_9+dfsg1_UnixSource/minorGems/network/web/0000750000175000017500000000000011401021061021612 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/network/web/WebClient.h0000640000175000017500000001731311067220046023661 0ustar pabspabs/* * Modification History * * 2002-May-5 Jason Rohrer * Created. * Added a utility function for receiving data. * * 2002-May-12 Jason Rohrer * Added support for fetching the final (after redirects) URL. * * 2002-May-26 Jason Rohrer * Added support for fetching mime types and content length. * Added a function for fetching MIME types alone. * * 2006-January-22 Jason Rohrer * Added support for timeouts. * * 2008-September-25 Jason Rohrer * Added POST. */ #include "minorGems/common.h" #ifndef WEB_CLIENT_INCLUDED #define WEB_CLIENT_INCLUDED #include "minorGems/network/HostAddress.h" #include "minorGems/network/Socket.h" #include "minorGems/network/SocketClient.h" #include "minorGems/network/SocketStream.h" #include #include /** * A class that implements a basic web client. * * @author Jason Rohrer. */ class WebClient { public: /** * Gets a web page. * * @param inURL the URL to get as a \0-terminated string. * Must be destroyed by caller if non-const. * @param outContentLength pointer to where the length of the content, * in bytes, should be returned. * Useful for binary content which cannot be reliably terminated * by \0. * @param outFinalURL pointer to where the actual * URL of the content (after following redirects) should be returned, * or NULL to ignore the final URL. Defaults to NULL. * The string returned in this location must be destroyed * by caller. * @param outMimeType pointer to where the MIME type * of the content should be returned, * or NULL to ignore the MIME type. Defaults to NULL. * The string returned in this location must be destroyed * by caller. * @param inTimeoutInMilliseconds the timeout value when making * the connection and again when reading data in milliseconds, * or -1 for no timeout. * Defaults to -1. * * @return the fetched web page as a \0-terminated string, * or NULL if fetching the page fails. * Must be destroyed by caller if non-NULL. */ static char *getWebPage( char *inURL, int *outContentLength, char **outFinalURL = NULL, char **outMimeType = NULL, long inTimeoutInMilliseconds = -1 ); /** * Fetches a web page via POST. * * @param inURL the URL to post to as a \0-terminated string. * Must be destroyed by caller if non-const. * @param inPostBody the body of the post as \0-terminated string. * Body must match MIME type application/x-www-form-urlencoded * Must be destroyed by caller if non-const. * @param outContentLength pointer to where the length of the content, * in bytes, should be returned. * Useful for binary content which cannot be reliably terminated * by \0. * @param outFinalURL pointer to where the actual * URL of the content (after following redirects) should be returned, * or NULL to ignore the final URL. Defaults to NULL. * The string returned in this location must be destroyed * by caller. * @param outMimeType pointer to where the MIME type * of the content should be returned, * or NULL to ignore the MIME type. Defaults to NULL. * The string returned in this location must be destroyed * by caller. * @param inTimeoutInMilliseconds the timeout value when making * the connection and again when reading data in milliseconds, * or -1 for no timeout. * Defaults to -1. * * @return the fetched web page as a \0-terminated string, * or NULL if fetching the page fails. * Must be destroyed by caller if non-NULL. */ static char *getWebPagePOST( char *inURL, char *inPostBody, int *outContentLength, char **outFinalURL = NULL, char **outMimeType = NULL, long inTimeoutInMilliseconds = -1 ); /** * Gets the MIME type for a web page without fetching the content. * * @param inURL the URL to get as a \0-terminated string. * Must be destroyed by caller if non-const. * * @return the fetched MIME type as a \0-terminated string, * or NULL if fetching the page fails. * Must be destroyed by caller if non-NULL. */ static char *getMimeType( char *inURL ); protected: /** * Receives data on a connection until the connection is closed. * * @param inSocketStream the stream to read from. * Must be destroyed by caller. * @param outContentLength pointer to where the length of the content, * in bytes, should be returned. * Useful for binary content which cannot be reliably terminated * by \0. * * @return the received data as a \0-terminated string. * Must be destroyed by caller. */ static char *receiveData( SocketStream *inSocketStream, int *outContentLength ); /** * Executes a web method. * * @param inMethod the method to execute (for example, GET or HEAD). * Must be destroyed by caller if non-const. * @param inURL the URL to get as a \0-terminated string. * Must be destroyed by caller if non-const. * @param inBody the request body as a \0-terminated string, or NULL * for bodyless requests (like GET or HEAD). * Body must match MIME type application/x-www-form-urlencoded * Must be destroyed by caller if non-const and not NULL. * @param outContentLength pointer to where the length of the content, * in bytes, should be returned. * Useful for binary content which cannot be reliably terminated * by \0. * @param outFinalURL pointer to where the actual * URL of the content (after following redirects) should be returned, * or NULL to ignore the final URL. Defaults to NULL. * The string returned in this location must be destroyed * by caller. * @param outMimeType pointer to where the MIME type * of the content should be returned, * or NULL to ignore the MIME type. Defaults to NULL. * The string returned in this location must be destroyed * by caller. * @param inTimeoutInMilliseconds the timeout value when making * the connection and again when reading data in milliseconds, * or -1 for no timeout. * Defaults to -1. * * @return the fetched web page as a \0-terminated string, * or NULL if fetching the page fails. * Must be destroyed by caller if non-NULL. */ static char *executeWebMethod( char *inMethod, char *inURL, char *inBody, int *outContentLength, char **outFinalURL = NULL, char **outMimeType = NULL, long inTimeoutInMilliseconds = -1 ); }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/web/WebRequest.h0000640000175000017500000000257411374544330024104 0ustar pabspabs#ifndef WEB_REQUEST_INCLUDED #define WEB_REQUEST_INCLUDED #include "minorGems/network/Socket.h" #include "minorGems/network/HostAddress.h" #include "minorGems/network/LookupThread.h" // a non-blocking web request class WebRequest { public: // inMethod = GET, POST, etc. // inURL the url to retrieve // inBody the body of the request, can be NULL // request body must be in application/x-www-form-urlencoded format WebRequest( const char *inMethod, const char *inURL, const char *inBody ); // if request is not complete, destruction cancels it ~WebRequest(); // take anoter non-blocking step // return 1 if request complete // return -1 if request hit an error // return 0 if request still in-progress int step(); // gets the response body as a \0-terminated string char *getResult(); protected: char mError; char *mURL; char *mRequest; int mRequestPosition; SimpleVector mResponse; char mResultReady; char *mResult; HostAddress *mSuppliedAddress; HostAddress *mNumericalAddress; LookupThread *mLookupThread; Socket *mSock; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/web/URLUtils.cpp0000640000175000017500000002200107550627305024024 0ustar pabspabs/* * Modification History * * 2002-May-10 Jason Rohrer * Created. * * 2002-May-11 Jason Rohrer * Added functions for hex encoding and decoding. * * 2002-May-12 Jason Rohrer * Added conversion for #, &, and ?. * Added conversion for CR and LF. * * 2002-August-1 Jason Rohrer * Added conversion for /, \, ., and :. * * 2002-September-12 Jason Rohrer * Added missing breaks. * * 2002-September-25 Jason Rohrer * Fixed a bug with the way + characters are handled. * Changed to trim the returned buffer. * * 2002-October-8 Jason Rohrer * Added functions for extracting query arguments. */ #include "URLUtils.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/SimpleVector.h" #include char *URLUtils::getRootServer( char *inURL ) { char *urlCopy = stringDuplicate( inURL ); char *endOfURLType = strstr( urlCopy, "://" ); char *returnString = NULL; if( endOfURLType != NULL ) { char *startOfRootServer = &( endOfURLType[ 3 ] ); char *endOfRootServer = strstr( startOfRootServer, "/" ); if( endOfRootServer != NULL ) { endOfRootServer[0] = '\0'; } returnString = stringDuplicate( startOfRootServer ); } delete [] urlCopy; return returnString; } char *URLUtils::getRootRelativePath( char *inURL ) { char *urlCopy = stringDuplicate( inURL ); char *endOfURLType = strstr( urlCopy, "://" ); char *returnString = NULL; if( endOfURLType != NULL ) { char *startOfRootServer = &( endOfURLType[ 3 ] ); char *endOfRootServer = strstr( startOfRootServer, "/" ); if( endOfRootServer == NULL ) { returnString = stringDuplicate( "/" ); } else { char *lastSlash = endOfRootServer; char *currentSlash = strstr( &( lastSlash[1] ), "/" ); while( currentSlash != NULL ) { lastSlash = currentSlash; currentSlash = strstr( &( lastSlash[1] ), "/" ); } // terminate string right after last slash lastSlash[1] = '\0'; returnString = stringDuplicate( endOfRootServer ); } } delete [] urlCopy; return returnString; } char *URLUtils::hexDecode( char *inString ) { // first run through string and replace any + characters with spaces char *workingString = stringDuplicate( inString ); char *plusLocation = strstr( workingString, "+" ); while( plusLocation != NULL ) { plusLocation[0] = ' '; plusLocation = strstr( plusLocation, "+" ); } int stringLength = strlen( workingString ); char *returnString = new char[ stringLength + 1 ]; int stringIndex = 0; int returnStringIndex = 0; while( stringIndex < stringLength + 1 ) { if( workingString[ stringIndex ] != '%' ) { // not a hex representation returnString[ returnStringIndex ] = workingString[ stringIndex ]; stringIndex++; returnStringIndex++; } else { // the start of hex char twoChars[2]; twoChars[0] = workingString[ stringIndex + 1 ]; twoChars[1]= workingString[ stringIndex + 2 ]; char summedChar = 0; for( int i=0; i<2; i++ ) { int shiftAmount = 4 * ( 1 - i ); switch( twoChars[i] ) { case '0': summedChar += 0x0 << shiftAmount; break; case '1': summedChar += 0x1 << shiftAmount; break; case '2': summedChar += 0x2 << shiftAmount; break; case '3': summedChar += 0x3 << shiftAmount; break; case '4': summedChar += 0x4 << shiftAmount; break; case '5': summedChar += 0x5 << shiftAmount; break; case '6': summedChar += 0x6 << shiftAmount; break; case '7': summedChar += 0x7 << shiftAmount; break; case '8': summedChar += 0x8 << shiftAmount; break; case '9': summedChar += 0x9 << shiftAmount; break; case 'A': summedChar += 0xA << shiftAmount; break; case 'B': summedChar += 0xB << shiftAmount; break; case 'C': summedChar += 0xC << shiftAmount; break; case 'D': summedChar += 0xD << shiftAmount; break; case 'E': summedChar += 0xE << shiftAmount; break; case 'F': summedChar += 0xF << shiftAmount; break; default: break; } } returnString[ returnStringIndex ] = summedChar; stringIndex += 3; returnStringIndex++; } } delete [] workingString; // trim the return string workingString = returnString; returnString = stringDuplicate( workingString ); delete [] workingString; return returnString; } char *URLUtils::hexEncode( char *inString ) { SimpleVector *returnStringVector = new SimpleVector(); int stringLength = strlen( inString ); int i; for( i=0; ipush_back( '%' ); returnStringVector->push_back( '0' ); returnStringVector->push_back( 'A' ); break; case '\r': returnStringVector->push_back( '%' ); returnStringVector->push_back( '0' ); returnStringVector->push_back( 'D' ); break; case '#': returnStringVector->push_back( '%' ); returnStringVector->push_back( '2' ); returnStringVector->push_back( '3' ); break; case '&': returnStringVector->push_back( '%' ); returnStringVector->push_back( '2' ); returnStringVector->push_back( '6' ); break; case '?': returnStringVector->push_back( '%' ); returnStringVector->push_back( '3' ); returnStringVector->push_back( 'F' ); break; case '\\': returnStringVector->push_back( '%' ); returnStringVector->push_back( '5' ); returnStringVector->push_back( 'C' ); break; case '/': returnStringVector->push_back( '%' ); returnStringVector->push_back( '2' ); returnStringVector->push_back( 'F' ); break; case ':': returnStringVector->push_back( '%' ); returnStringVector->push_back( '3' ); returnStringVector->push_back( 'A' ); break; case '.': returnStringVector->push_back( '%' ); returnStringVector->push_back( '2' ); returnStringVector->push_back( 'E' ); break; case '+': returnStringVector->push_back( '%' ); returnStringVector->push_back( '2' ); returnStringVector->push_back( 'B' ); break; case ' ': returnStringVector->push_back( '+' ); break; default: returnStringVector->push_back( inString[i] ); break; } } int numChars = returnStringVector->size(); char *returnString = new char[ numChars + 1 ]; for( i=0; igetElement( i ) ); } returnString[ numChars ] = '\0'; delete returnStringVector; return returnString; } char *URLUtils::extractArgument( char *inHaystack, char *inArgName ) { char *argNameWithEquals = new char[ strlen( inArgName ) + 2 ]; sprintf( argNameWithEquals, "%s%s", inArgName, "=" ); char *haystackCopy = stringDuplicate( inHaystack ); char *pointerToArgStart = strstr( haystackCopy, argNameWithEquals ); if( pointerToArgStart == NULL ) { delete [] haystackCopy; delete [] argNameWithEquals; return NULL; } else { char *pointerToArgEnd = strstr( pointerToArgStart, "&" ); if( pointerToArgEnd != NULL ) { // terminate string at arg end pointerToArgEnd[0] = '\0'; } // else entire remainder of string is argument char *pointerToArgValue = &( pointerToArgStart[ strlen( argNameWithEquals ) ] ); // trim string char *returnString = stringDuplicate( pointerToArgValue ); delete [] argNameWithEquals; delete [] haystackCopy; return returnString; } } char *URLUtils::extractArgumentRemoveHex( char *inHaystack, char *inArgName ) { char *extractedArg = extractArgument( inHaystack, inArgName ); if( extractedArg != NULL ) { char *convertedArg = URLUtils::hexDecode( extractedArg ); delete [] extractedArg; return convertedArg; } else { return NULL; } } Cultivation_9+dfsg1_UnixSource/minorGems/network/web/WebRequest.cpp0000640000175000017500000002460211374544330024433 0ustar pabspabs#include "WebRequest.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/StringBufferOutputStream.h" #include "minorGems/network/SocketClient.h" WebRequest::WebRequest( const char *inMethod, const char *inURL, const char *inBody ) : mError( false ), mURL( stringDuplicate( inURL ) ), mRequest( NULL ), mRequestPosition( -1 ), mResultReady( false ), mResult( NULL ), mSock( NULL ) { const char *startString = "http://"; char *urlCopy = stringDuplicate( inURL ); char *urlStart = stringLocateIgnoreCase( urlCopy, startString ); char *serverStart; if( urlStart == NULL ) { // no http:// at start of URL serverStart = urlCopy; } else { serverStart = &( urlStart[ strlen( startString ) ] ); } // find the first occurrence of "/", which is the end of the // server name char *serverNameCopy = stringDuplicate( serverStart ); char *serverEnd = strstr( serverNameCopy, "/" ); char *getPath = strstr( serverStart, "/" ); if( serverEnd == NULL ) { serverEnd = &( serverStart[ strlen( serverStart ) ] ); getPath = (char *)"/"; } // terminate the url here to extract the server name serverEnd[0] = '\0'; int portNumber = 80; // look for a port number char *colon = strstr( serverNameCopy, ":" ); if( colon != NULL ) { char *portNumberString = &( colon[1] ); int numRead = sscanf( portNumberString, "%d", & portNumber ); if( numRead != 1 ) { portNumber = 80; } // terminate the name here so port isn't taken as part // of the address colon[0] = '\0'; } mSuppliedAddress = new HostAddress( stringDuplicate( serverNameCopy ), portNumber ); mNumericalAddress = NULL; mLookupThread = NULL; // launch right into name lookup mLookupThread = new LookupThread( mSuppliedAddress ); mSock = NULL; // compose the request into a buffered stream StringBufferOutputStream tempStream; tempStream.writeString( inMethod ); tempStream.writeString( " " ); tempStream.writeString( getPath ); tempStream.writeString( " HTTP/1.0\r\n" ); tempStream.writeString( "Host: " ); tempStream.writeString( serverNameCopy ); tempStream.writeString( "\r\n" ); if( inBody != NULL ) { char *lengthString = autoSprintf( "Content-Length: %d\r\n", strlen( inBody ) ); tempStream.writeString( lengthString ); delete [] lengthString; tempStream.writeString( "Content-Type: application/x-www-form-urlencoded\r\n\r\n" ); tempStream.writeString( inBody ); } else { tempStream.writeString( "\r\n" ); } mRequest = tempStream.getString(); mRequestPosition = 0; delete [] serverNameCopy; delete [] urlCopy; } WebRequest::~WebRequest() { if( mLookupThread != NULL ) { // this might block delete mLookupThread; } delete mSuppliedAddress; if( mNumericalAddress != NULL ) { delete mNumericalAddress; } delete [] mURL; if( mSock != NULL ) { delete mSock; } if( mRequest != NULL ) { delete [] mRequest; } if( mResult != NULL ) { delete [] mResult; } } int WebRequest::step() { if( mError ) { return -1; } if( mSock == NULL ) { // we know mLookupThread is not NULL if we get here if( mLookupThread->isLookupDone() ) { mError = true; mNumericalAddress = mLookupThread->getResult(); if( mNumericalAddress != NULL ) { // use timeout of 0 for non-blocking // will be set to true if we time out while connecting char timedOut; mSock = SocketClient::connectToServer( mNumericalAddress, 0, &timedOut ); if( mSock != NULL ) { mError = false; } } if( mError ) { // lookup or socket construction failed if( mNumericalAddress == NULL ) { printf( "Error: " "WebRequest failed to lookup %s\n", mSuppliedAddress->mAddressString ); } else { printf( "Error: " "WebRequest failed to construct " "socket to %s:%d\n", mNumericalAddress->mAddressString, mNumericalAddress->mPort ); } return -1; } } else { // still looking up return 0; } } int connectStatus = mSock->isConnected(); if( connectStatus == 0 ) { // still trying to connect return 0; } else if( connectStatus < 0 ) { // failed to connect mError = true; printf( "Error: " "WebRequest failed to connect to %s:%d\n", mNumericalAddress->mAddressString, mNumericalAddress->mPort ); return -1; } else if( connectStatus == 1 ) { // connected if( mRequestPosition < (int)( strlen( mRequest ) ) ) { // try to send more char *remainingRequest = &( mRequest[ mRequestPosition ] ); int numSent = mSock->send( (unsigned char *)remainingRequest, strlen( remainingRequest ), // non-blocking false ); if( numSent == -1 ) { mError = true; printf( "Error: " "WebRequest failed to connect to " "send full request\n" ); return -1; } if( numSent == -2 ) { return 0; } mRequestPosition += numSent; // don't start looking for response in same step, // even if we just sent the entire request // in practice, it's never ready that fast return 0; } else if( mResultReady ) { return 1; } else { // done sending request // still receiving response long bufferLength = 5000; unsigned char *buffer = new unsigned char[ bufferLength ]; // non-blocking // keep reading as long as we get full buffers int numRead = bufferLength; while( numRead > 0 ) { numRead = mSock->receive( buffer, bufferLength, 0 ); if( numRead > 0 ) { for( int i=0; i /** * A class that can resolve file extensions to mime types. * * @author Jason Rohrer */ class MimeTyper { public: /** * Constructs a mime typer. * * @param inFileName the configuration file from, or * NULL to specify the default file name, "mime.ini". * File name is relative to the settings directory. * Defaults to NULL. * Must be destroyed by caller if non-NULL and non-const. */ MimeTyper( char *inConfigFileName = NULL ); ~MimeTyper(); /** * Gets a mime type string from a file extension string. * * @param inFileExtension a \0-terminated string containing * a file extension, including the '.' * Must be destroyed by caller if non-const. * * @return the mime type as a \0-terminated string, * or NULL if there is no match. * Must be destroyed by caller if non-NULL. */ char *getMimeType( char *inFileExtension ); /** * Gets a mime type string from a file name. * * @param inFileName a \0-terminated string containing * a file name with extension. * Must be destroyed by caller if non-const. * * @return the mime type as a \0-terminated string, * or NULL if there is no match. * Must be destroyed by caller if non-NULL. */ char *getFileNameMimeType( char *inFileName ); protected: // a string containing all types read from the configuration file char *mMimeTypesString; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/web/WebClient.cpp0000640000175000017500000002730411070507451024217 0ustar pabspabs/* * Modification History * * 2002-May-5 Jason Rohrer * Created. * Changed to default to http:// if no URL type specified. * Added support for the Host: header. * * 2002-May-7 Jason Rohrer * Added support for pages that have been moved permanently. * * 2002-May-12 Jason Rohrer * Added support for the Object Moved message type. * Added support for fetching the final (after redirects) URL. * Fixed a memory leak. * * 2002-May-26 Jason Rohrer * Added support for fetching mime types and content length. * Added a function for fetching MIME types alone. * Changed to use case-ignoring string comparison where appropriate. * * 2002-July-19 Jason Rohrer * Changed to deal with not found headers properly. * * 2002-August-5 Jason Rohrer * Fixed a typo. * * 2002-November-10 Jason Rohrer * Added a fix to deal with a slashdot bug. * * 2006-January-22 Jason Rohrer * Added support for timeouts. * * 2008-September-25 Jason Rohrer * Added POST. */ #include "WebClient.h" #include "minorGems/util/log/AppLog.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/SimpleVector.h" char *WebClient::getWebPage( char *inURL, int *outContentLength, char **outFinalURL, char **outMimeType, long inTimeoutInMilliseconds ) { return executeWebMethod( "GET", inURL, NULL, outContentLength, outFinalURL, outMimeType, inTimeoutInMilliseconds ); } char *WebClient::getWebPagePost( char *inURL, char *inPostBody, int *outContentLength, char **outFinalURL, char **outMimeType, long inTimeoutInMilliseconds ) { return executeWebMethod( "POST", inURL, inPostBody, outContentLength, outFinalURL, outMimeType, inTimeoutInMilliseconds ); } char *WebClient::getMimeType( char *inURL ) { int contentLength; char *mimeType; char *content = executeWebMethod( "HEAD", inURL, NULL, &contentLength, NULL, &mimeType ); if( content != NULL ) { delete [] content; } return mimeType; } char *WebClient::executeWebMethod( char *inMethod, char *inURL, char *inBody, int *outContentLength, char **outFinalURL, char **outMimeType, long inTimeoutInMilliseconds ) { char *returnString = NULL; char *startString = "http://"; char *urlCopy = stringDuplicate( inURL ); char *urlStart = stringLocateIgnoreCase( urlCopy, startString ); char *serverStart; if( urlStart == NULL ) { // no http:// at start of URL serverStart = urlCopy; } else { serverStart = &( urlStart[ strlen( startString ) ] ); } // find the first occurrence of "/", which is the end of the // server name char *serverNameCopy = stringDuplicate( serverStart ); char *serverEnd = strstr( serverNameCopy, "/" ); char *getPath = strstr( serverStart, "/" ); if( serverEnd == NULL ) { serverEnd = &( serverStart[ strlen( serverStart ) ] ); getPath = "/"; } // terminate the url here to extract the server name serverEnd[0] = '\0'; int portNumber = 80; // look for a port number char *colon = strstr( serverNameCopy, ":" ); if( colon != NULL ) { char *portNumberString = &( colon[1] ); int numRead = sscanf( portNumberString, "%d", & portNumber ); if( numRead != 1 ) { portNumber = 80; } // terminate the name here so port isn't taken as part // of the address colon[0] = '\0'; } HostAddress *host = new HostAddress( stringDuplicate( serverNameCopy ), portNumber ); // will be set to true if we time out while connecting char timedOut; Socket *sock = SocketClient::connectToServer( host, inTimeoutInMilliseconds, &timedOut ); char *finalURL = stringDuplicate( inURL ); char *mimeType = NULL; int receivedLength = 0; if( sock != NULL ) { SocketStream *stream = new SocketStream( sock ); // reuse the same timeout for read operations stream->setReadTimeout( inTimeoutInMilliseconds ); // method and trailing space need to be sent in the same // buffer to work around a bug in certain web servers char *methodWithSpace = new char[ strlen( inMethod ) + 2 ]; sprintf( methodWithSpace, "%s ", inMethod ); // send the request stream->writeString( methodWithSpace ); stream->writeString( getPath ); stream->writeString( " HTTP/1.0\r\n" ); stream->writeString( "Host: " ); stream->writeString( serverNameCopy ); stream->writeString( "\r\n" ); if( inBody != NULL ) { char *lengthString = autoSprintf( "Content-Length: %d\r\n", strlen( inBody ) ); stream->writeString( lengthString ); delete [] lengthString; stream->writeString( "Content-Type: application/x-www-form-urlencoded\r\n\r\n" ); stream->writeString( inBody ); } else { stream->writeString( "\r\n" ); } delete [] methodWithSpace; // the simplest thing to do is to read upto the // socket close first, then extract the content char *received = receiveData( stream, &receivedLength ); char *content = NULL; char notFound = false; if( stringLocateIgnoreCase( received, "404 Not Found" ) != NULL ) { notFound = true; } // watch for redirection headers if( stringLocateIgnoreCase( received, "302 Found" ) != NULL || stringLocateIgnoreCase( received, "301 Moved Permanently" ) != NULL || stringLocateIgnoreCase( received, "302 Object Moved" ) != NULL ) { // call ourself recursively to fetch the redirection char *locationTag = "Location: "; char *locationTagStart = stringLocateIgnoreCase( received, locationTag ); if( locationTagStart != NULL ) { char *locationStart = &( locationTagStart[ strlen( locationTag ) ] ); // replace next \r with \0 char *nextChar = locationStart; while( nextChar[0] != '\r' && nextChar[0] != '\0' ) { nextChar = &( nextChar[1] ); } nextChar[0] = '\0'; char *newFinalURL; content = getWebPage( locationStart, &receivedLength, &newFinalURL, &mimeType ); delete [] finalURL; finalURL = newFinalURL; if( content == NULL ) { // not found recursively notFound = true; } } } char *contentStartString = "\r\n\r\n"; char *contentTypeStartString = "Content-type:"; if( notFound ) { returnString = NULL; } else { if( content == NULL ) { // scan for content type char *contentTypeStartMarker = stringLocateIgnoreCase( received, contentTypeStartString ); if( contentTypeStartMarker != NULL ) { // skip marker char *contentTypeStart = &( contentTypeStartMarker[ strlen( contentTypeStartString ) ] ); // extract content type // make sure the buffer is big enough char *contentType = new char[ strlen( contentTypeStartMarker ) ]; int numRead = sscanf( contentTypeStart, "%s", contentType ); if( numRead == 1 ) { // trim mimeType = stringDuplicate( contentType ); } delete [] contentType; } // extract the content from what we've received char *contentStart = strstr( received, contentStartString ); if( contentStart != NULL ) { content = &( contentStart[ strlen( contentStartString ) ] ); receivedLength = receivedLength - strlen( contentStartString ) - ( (int)contentStart - (int)received ); returnString = new char[ receivedLength + 1 ]; returnString = (char*)memcpy( returnString, content, receivedLength ); returnString[ receivedLength ] = '\0'; } } else { // we already obtained our content recursively returnString = new char[ receivedLength + 1 ]; returnString = (char*)memcpy( returnString, content, receivedLength ); returnString[ receivedLength ] = '\0'; delete [] content; } } delete [] received; delete stream; delete sock; } delete host; delete [] serverNameCopy; delete [] urlCopy; if( outFinalURL != NULL ) { *outFinalURL = finalURL; } else { delete [] finalURL; } if( outMimeType != NULL ) { *outMimeType = mimeType; } else { if( mimeType != NULL ) { delete [] mimeType; } } *outContentLength = receivedLength; return returnString; } char *WebClient::receiveData( SocketStream *inSocketStream, int *outContentLength ) { SimpleVector *receivedVector = new SimpleVector(); char connectionBroken = false; long bufferLength = 5000; unsigned char *buffer = new unsigned char[ bufferLength ]; while( !connectionBroken ) { int numRead = inSocketStream->read( buffer, bufferLength ); if( numRead != bufferLength ) { connectionBroken = true; } if( numRead > 0 ) { for( int i=0; ipush_back( buffer[i] ); } } } delete [] buffer; // copy our vector into an array int receivedSize = receivedVector->size(); char *received = new char[ receivedSize + 1 ]; for( int i=0; igetElement( i ) ); } received[ receivedSize ] = '\0'; delete receivedVector; *outContentLength = receivedSize; return received; } Cultivation_9+dfsg1_UnixSource/minorGems/network/web/MimeTyper.cpp0000640000175000017500000000757607724666437024314 0ustar pabspabs/* * Modification History * * 2002-April-20 Jason Rohrer * Created. * * 2002-April-22 Jason Rohrer * Fixed a bug with the mime type string. * * 2002-April-30 Jason Rohrer * Removed an unused variable. * * 2002-September-17 Jason Rohrer * Moved mime.ini into settings directory. * * 2002-October-7 Jason Rohrer * Added a function for getting mime types from file names. * * 2003-September-1 Jason Rohrer * Copied into minorGems from the konspire2b project. */ #include "MimeTyper.h" #include "minorGems/util/stringUtils.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include #include MimeTyper::MimeTyper( char *inConfigFileName ) { File *configFile; char **pathSteps = new char*[1]; pathSteps[0] = "settings"; if( inConfigFileName == NULL ) { configFile = new File( new Path( pathSteps, 1, false ), "mime.ini" ); } else { configFile = new File( new Path( pathSteps, 1, false ), inConfigFileName ); } delete [] pathSteps; if( !configFile->exists() ) { char *name = configFile->getFileName(); printf( "Error: MIME config file %s does not exist.\n", name ); delete [] name; mMimeTypesString = new char[ 1 ]; mMimeTypesString[0] = '\0'; } else { int length = configFile->getLength(); mMimeTypesString = new char[ length + 1 ]; char *name = configFile->getFileName(); FileInputStream *inStream = new FileInputStream( configFile ); int numRead = inStream->read( (unsigned char *)mMimeTypesString, length ); if( numRead != length ) { printf( "Error reading from MIME config file %s.\n", name ); delete [] mMimeTypesString; mMimeTypesString = new char[ 1 ]; mMimeTypesString[0] = '\0'; } else { mMimeTypesString[ length ] = '\0'; } delete [] name; delete inStream; } delete configFile; } MimeTyper::~MimeTyper() { if( mMimeTypesString != NULL ) { delete [] mMimeTypesString; } } char *MimeTyper::getMimeType( char *inFileExtension ) { char *extensionMatch = strstr( mMimeTypesString, inFileExtension ); if( extensionMatch ) { // we should be able to scan two strings from // the position of the match // the first string should be the extension // the second string should be the mime type // make sure buffer is big enough char *buffer = new char[ strlen( mMimeTypesString ) + 1 ]; int numRead = sscanf( extensionMatch, "%s", buffer ); char *returnString = NULL; if( numRead == 1 ) { int numToSkip = strlen( buffer ); // skip first string token (extension) // and read next string token (mime type) numRead = sscanf( &( extensionMatch[ numToSkip ] ), "%s", buffer ); if( numRead == 1 ) { returnString = stringDuplicate( buffer ); } } delete [] buffer; return returnString; } else { return NULL; } } char *MimeTyper::getFileNameMimeType( char *inFileName ) { int fileNameLength = strlen( inFileName ); int lastPeriodIndex = -1; for( int i=0; i #include /** * A class that handles permissions for received connections. * * @author Jason Rohrer. */ class ConnectionPermissionHandler { public: /** * Constructs a handler. */ ConnectionPermissionHandler(); ~ConnectionPermissionHandler(); /** * Gets whether a connection is permitted. * * @param inAddress the address of the host connecting. * Must be destroyed by caller. * * @return true iff a connection is allowed. */ char isPermitted( HostAddress *inAddress ); private: SimpleVector *mPermittedAddresses; SimpleVector *mPermittedPatterns; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/web/server/RequestHandlingThread.h0000640000175000017500000000600307726131706027545 0ustar pabspabs/* * Modification History * * 2002-March-12 Jason Rohrer * Created. * * 2002-March-29 Jason Rohrer * Fixed include order. * * 2002-August-2 Jason Rohrer * Added use of ConnectionPermissionHandler. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #ifndef REQUEST_HANDLING_THREAD_INCLUDED #define REQUEST_HANDLING_THREAD_INCLUDED #include "PageGenerator.h" #include "ConnectionPermissionHandler.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include "minorGems/network/Socket.h" #include "minorGems/network/SocketStream.h" #include "minorGems/system/Thread.h" #include "minorGems/system/MutexLock.h" #include #include #include #define REQUEST_HANDLING_THREAD_BUFFER_SIZE 4096 /** * Request handler for WebServer. * * @author Jason Rohrer. */ class RequestHandlingThread : public Thread { public: /** * Construct a handler. * * @param inSocket the socket to send the requested * file trough. Is destroyed before this thread terminates. * @param inGenerator the class that will generate the * page content. * Is not destroyed by this class. * @param inConnectionPermissionHandler the class that will * grant connection permissions * Is not destroyed by this class. */ RequestHandlingThread( Socket *inSocket, PageGenerator *inGenerator, ConnectionPermissionHandler *inConnectionPermissionHandler); ~RequestHandlingThread(); /** * Returns true if this handler is done and ready to be destroyed. * * Synchronized, so may block. * * @return true if this handler is done and ready to be destroyed. */ char isDone(); /** * Gets a string representation of the current time. * * @return a timestamp string. Must be destroyed by caller. */ static char* getTimestamp(); // implements the Thread interface virtual void run(); private: Socket *mSocket; PageGenerator *mGenerator; ConnectionPermissionHandler *mConnectionPermissionHandler; MutexLock *mDoneLock; char mDone; /** * Sends an HTTP "not found" message with a "not found" web page. * * @param inStream the stream to send the not found page to. * @param inFileName the name of the requested file, * or NULL. */ void sendNotFoundPage( SocketStream *inStream, char *inFileName ); /** * Sends a "bad request" web page. * * @param inStream the stream to send the page to. */ void sendBadRequest( SocketStream *inStream ); }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/web/server/ConnectionPermissionHandler.cpp0000640000175000017500000000641007726131706031323 0ustar pabspabs/* * Modification History * * 2002-August-2 Jason Rohrer * Created. * Fixed a buffer size error. * * 2002-September-17 Jason Rohrer * Changed to use the SettingsManager. * * 2002-November-9 Jason Rohrer * Added support for matching address patterns. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #include "ConnectionPermissionHandler.h" #include "minorGems/io/file/File.h" #include "minorGems/util/SettingsManager.h" ConnectionPermissionHandler::ConnectionPermissionHandler() { SimpleVector *addressVector = SettingsManager::getSetting( "allowedWebHosts" ); int numAddresses = addressVector->size(); mPermittedAddresses = new SimpleVector(); mPermittedPatterns = new SimpleVector(); for( int i=0; igetElement( i ) ); char *starLocation = strstr( addressString, "*" ); if( starLocation == NULL ) { // an address mPermittedAddresses->push_back( new HostAddress( addressString, 0 ) ); } else { // an address pattern mPermittedPatterns->push_back( addressString ); } } delete addressVector; } ConnectionPermissionHandler::~ConnectionPermissionHandler() { int numAddresses = mPermittedAddresses->size(); int i; for( i=0; igetElement( i ) ); delete address; } int numPatterns = mPermittedPatterns->size(); for( i=0; igetElement( i ) ); delete [] pattern; } delete mPermittedAddresses; delete mPermittedPatterns; } char ConnectionPermissionHandler::isPermitted( HostAddress *inAddress ) { int numAddresses = mPermittedAddresses->size(); int i; for( i=0; igetElement( i ) ); if( address->equals( inAddress ) ) { return true; } } // didn't match any address exactly // check if it matches one of our patterns HostAddress* numericalAddress = inAddress->getNumericalAddress(); if( numericalAddress == NULL ) { return false; } char *addressString = numericalAddress->mAddressString; int numPatterns = mPermittedPatterns->size(); char foundMatch = false; for( i=0; igetElement( i ) ); int patternLength = strlen( pattern ); int addressLength = strlen( addressString ); char done = false; for( int j=0; jlock(); tempDone = mDone; mDoneLock->unlock(); return mDone; } // example HTTP request and response /* GET /images/title_homepage4.gif HTTP/1.0 HTTP/1.0 200 OK Date: Fri, 11 May 2001 18:05:08 GMT Server: GWS/1.10 Connection: close Expires: Sun, 17 Jan 2038 19:14:07 GMT Content-Length: 7963 Content-Type: image/gif Last-Modified: Tue, 21 Nov 2000 16:20:07 GMT GIF89a1s */ void RequestHandlingThread::run() { HostAddress *receivedAddress = mSocket->getRemoteHostAddress(); if( receivedAddress == NULL ) { printf( "Failed to obtain host address, so " "refusing web connection.\n" ); // refuse delete mSocket; // flag that we're done mDoneLock->lock(); mDone = true; mDoneLock->unlock(); return; } else if( ! mConnectionPermissionHandler->isPermitted( receivedAddress ) ) { printf( "Refusing web connection from: " ); receivedAddress->print(); printf( "\n" ); // not permitted delete mSocket; delete receivedAddress; // flag that we're done mDoneLock->lock(); mDone = true; mDoneLock->unlock(); return; } // else permitted delete receivedAddress; int maxLength = 5000; // first, receive the request and parse it SocketStream *sockStream = new SocketStream( mSocket ); int requestBufferLength = maxLength; char *requestBuffer = new char[requestBufferLength]; int requestBufferIndex = 0; // read until we see two \r\n 's in a row unsigned char *charRead = new unsigned char[1]; charRead[0] = 0; char requestDone = false; char error = false; // _we_ actually only care about the first line of // the request, but we need to read the entire request // to make the other host happy char firstLineDone = false; int numRead = 0; while( !requestDone && !error ) { while( charRead[0] != 13 && !error ) { numRead = sockStream->read( charRead, 1 ); if( !firstLineDone ) { if( numRead != 1 ) { error = true; sendBadRequest( sockStream ); } // read data into our buffer else if( requestBufferIndex < requestBufferLength ) { requestBuffer[ requestBufferIndex ] = charRead[0]; requestBufferIndex++; if( charRead[0] == 13 ) { firstLineDone = true; } } else { error = true; sendBadRequest( sockStream ); } } } if( !error ) { // look for rest of double \r\n // this will effectively skip other lines in the request, // since we don't care about them numRead = sockStream->read( charRead, 1 ); if( charRead[0] == 10 && numRead == 1 ) { numRead = sockStream->read( charRead, 1 ); if( charRead[0] == 13 && numRead == 1 ) { numRead = sockStream->read( charRead, 1 ); if( charRead[0] == 10 && numRead == 1 ) { requestDone = true; } } } if( numRead != 1 ) { error = true; sendBadRequest( sockStream ); } } } // \0 terminate the request buffer if( requestBufferIndex < requestBufferLength ) { requestBuffer[ requestBufferIndex ] = '\0'; } else { requestBuffer[ requestBufferLength - 1 ] = '\0'; } if( !error ) { // at this point, we have received the entire // request, and stored the most important part in // requestBuffer // if maxLength = 500, // formatString = "%499s" // used to limit length of scanned string char *formatString = new char[ 20 ]; sprintf( formatString, "%%%ds", maxLength - 1 ); // the second string scanned from the buffer should // be the file path requested char *filePathBuffer = new char[ maxLength ]; int numRead = sscanf( requestBuffer, formatString, filePathBuffer ); if( numRead != 1 || strcmp( filePathBuffer, "GET" ) != 0 ) { // an invalid request error = true; sendBadRequest( sockStream ); } else { // a proper GET request // skip the GET and read the file name numRead = sscanf( &( requestBuffer[3] ), formatString, filePathBuffer ); if( numRead != 1 ) { error = true; sendBadRequest( sockStream ); } } delete [] requestBuffer; delete [] charRead; delete [] formatString; if( !error ) { // now we have the requested file string sockStream->writeString( "HTTP/1.0 200 OK\r\n" ); char *mimeType = mGenerator->getMimeType( filePathBuffer ); sockStream->writeString( "Content-Type: " ); sockStream->writeString( mimeType ); sockStream->writeString( "\r\n" ); delete [] mimeType; // even if the client requests a keep-alive, we force a close sockStream->writeString( "Connection: close" ); // finish header sockStream->writeString( "\r\n\r\n" ); // pass it to our page generator, which will send the content mGenerator->generatePage( filePathBuffer, sockStream ); } delete [] filePathBuffer; } else { delete [] requestBuffer; delete [] charRead; } delete sockStream; delete mSocket; // flag that we're done mDoneLock->lock(); mDone = true; mDoneLock->unlock(); } void RequestHandlingThread::sendNotFoundPage( SocketStream *inStream, char *inFileName ) { // example "not found" response /* HTTP/1.0 404 Not Found Date: Fri, 11 May 2001 18:26:16 GMT Server: GWS/1.10 Connection: close Set-Cookie: PREF=ID=3300d4623bf73a57:TM=989605576:LM=989605576; domain=.google.com; path=/; expires=Sun, 17-Jan-2038 19:14:07 GMT Content-Length: 142 Content-Type: text/html Not Found

404 Not Found

The requested URL /fjfj was not found on this server. */ char *buffer = new char[500]; if( inFileName != NULL ) { sprintf( buffer, "HTTP/1.0 404 Not Found\r\n\r\n" "

404 Not Found

The requested file " "%s was not found\r\n", inFileName ); } else { sprintf( buffer, "HTTP/1.0 404 Not Found\r\n\r\n" "

404 Not Found

The requested " "file was not found\r\n" ); } inStream->write( (unsigned char *)buffer, strlen( buffer ) ); delete [] buffer; } void RequestHandlingThread::sendBadRequest( SocketStream *inStream ) { // exampl "bad request" response /* Bad Request

400 Bad Request

Your client has issued a malformed or illegal request. */ char *buffer = new char[500]; sprintf( buffer, "

400 Bad Request

" "Your client has issued a malformed or illegal request." "\r\n" ); inStream->write( (unsigned char *)buffer, strlen( buffer ) ); delete [] buffer; } char* RequestHandlingThread::getTimestamp() { char *stampBuffer = new char[99]; time_t t = time( NULL ); char *asciiTime = ctime( &t ); // this time string ends with a newline... // get rid of it asciiTime[ strlen(asciiTime) - 1 ] = '\0'; sprintf( stampBuffer, "[%s]", asciiTime ); // delete [] asciiTime; return stampBuffer; } Cultivation_9+dfsg1_UnixSource/minorGems/network/web/server/WebServer.cpp0000640000175000017500000000476107726131706025570 0ustar pabspabs/* * Modification History * * 2002-March-12 Jason Rohrer * Created. * * 2002-April-5 Jason Rohrer * Changed to extend StopSignalThread. * Added timeouts to socket accept to support checking for stop signal. * * 2002-August-2 Jason Rohrer * Added use of ConnectionPermissionHandler. * * 2002-August-6 Jason Rohrer * Changed member init order. * * 2002-September-17 Jason Rohrer * Removed argument to ConnectionPermissionHandler constructor. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #include "WebServer.h" #include "minorGems/util/log/AppLog.h" WebServer::WebServer( int inPort, PageGenerator *inGenerator ) : mPortNumber( inPort ), mMaxQueuedConnections( 100 ), mThreadHandler( new ThreadHandlingThread() ), mPageGenerator( inGenerator ), mConnectionPermissionHandler( new ConnectionPermissionHandler() ) { mServer = new SocketServer( mPortNumber, mMaxQueuedConnections ); this->start(); } WebServer::~WebServer() { stop(); join(); delete mServer; delete mThreadHandler; delete mPageGenerator; delete mConnectionPermissionHandler; } void WebServer::run() { char *logMessage = new char[100]; sprintf( logMessage, "Listening for connections on port %d\n", mPortNumber ); AppLog::info( "WebServer", logMessage ); delete [] logMessage; char acceptFailed = false; // main server loop while( !isStopped() && !acceptFailed ) { char timedOut = true; // 5 seconds long timeout = 5000; Socket *sock; AppLog::info( "WebServer", "Waiting for connection." ); while( timedOut && !isStopped() ) { sock = mServer->acceptConnection( timeout, &timedOut ); } if( sock != NULL ) { AppLog::info( "WebServer", "Connection received." ); RequestHandlingThread *thread = new RequestHandlingThread( sock, mPageGenerator, mConnectionPermissionHandler ); thread->start(); mThreadHandler->addThread( thread ); } else if( isStopped() ) { AppLog::info( "WebServer", "Received stop signal." ); } else { AppLog::error( "WebServer", "Accepting a connection failed." ); acceptFailed = true; } } } Cultivation_9+dfsg1_UnixSource/minorGems/network/web/server/ThreadHandlingThread.h0000640000175000017500000000256007726131706027330 0ustar pabspabs/* * Modification History * * 2002-March-12 Jason Rohrer * Created. * * 2002-April-5 Jason Rohrer * Changed to extend StopSignalThread. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #ifndef THREAD_HANDLING_THREAD_INCLUDED #define THREAD_HANDLING_THREAD_INCLUDED #include "RequestHandlingThread.h" #include "minorGems/system/StopSignalThread.h" #include "minorGems/system/MutexLock.h" #include "minorGems/util/SimpleVector.h" #include #include /** * Thread handler for the microWeb server. Runs periodically * and checks for finished threads that can be deleted. * * @author Jason Rohrer. */ class ThreadHandlingThread : public StopSignalThread { public: /** * Constructs and starts a handler. */ ThreadHandlingThread(); /** * Stops and destroys this handler. */ ~ThreadHandlingThread(); /** * Adds a thread to the set managed by this handler. * * @param inThread the thread to add. */ void addThread( RequestHandlingThread *inThread ); // implements the Thread interface virtual void run(); private: SimpleVector *mThreadVector; MutexLock *mLock; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/web/server/WebServer.h0000640000175000017500000000326407726131706025232 0ustar pabspabs/* * Modification History * * 2002-March-12 Jason Rohrer * Created. * * 2002-March-29 Jason Rohrer * Fixed include order. * * 2002-April-5 Jason Rohrer * Changed to extend StopSignalThread. * * 2002-August-2 Jason Rohrer * Added use of ConnectionPermissionHandler. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #ifndef WEB_SERVER_INCLUDED #define WEB_SERVER_INCLUDED #include "PageGenerator.h" #include "ConnectionPermissionHandler.h" #include "minorGems/network/Socket.h" #include "minorGems/network/SocketServer.h" #include "minorGems/network/SocketStream.h" #include "RequestHandlingThread.h" #include "ThreadHandlingThread.h" #include "minorGems/system/StopSignalThread.h" #include #include /** * A class that implements a basic web server. * * @author Jason Rohrer. */ class WebServer : public StopSignalThread { public: /** * Constructs an starts this server. * * @param inPort the port to listen on. * @param inGenerator the class to use for generating pages. * Will be destroyed when this class is destroyed. */ WebServer( int inPort, PageGenerator *inGenerator ); /** * Stops and destroys this server. */ ~WebServer(); // implements the Thread::run() interface void run(); private: int mPortNumber; int mMaxQueuedConnections; SocketServer *mServer; ThreadHandlingThread *mThreadHandler; PageGenerator *mPageGenerator; ConnectionPermissionHandler *mConnectionPermissionHandler; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/web/server/ThreadHandlingThread.cpp0000640000175000017500000000471507726131706027667 0ustar pabspabs/* * Modification History * * 2002-March-12 Jason Rohrer * Created. * * 2002-April-5 Jason Rohrer * Changed to extend StopSignalThread. * * 2002-August-6 Jason Rohrer * Changed member init order. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #include "ThreadHandlingThread.h" ThreadHandlingThread::ThreadHandlingThread() : mThreadVector( new SimpleVector() ), mLock( new MutexLock() ) { this->start(); } ThreadHandlingThread::~ThreadHandlingThread() { stop(); join(); mLock->lock(); int numThreads = mThreadVector->size(); // join each thread and delete it for( int i=0; igetElement( i ) ); thread->join(); delete thread; } delete mThreadVector; mLock->unlock(); delete mLock; } void ThreadHandlingThread::addThread( RequestHandlingThread *inThread ) { mLock->lock(); mThreadVector->push_back( inThread ); mLock->unlock(); } void ThreadHandlingThread::run() { while( !isStopped() ) { // sleep for 5 seconds sleep( 5000 ); // printf( "Thread handler looking for finished threads\n" ); // look for threads that need to be deleted mLock->lock(); char threadFound = true; // examine each thread while( threadFound ) { threadFound = false; int numThreads = mThreadVector->size(); for( int i=0; igetElement( i ) ); if( thread->isDone() ) { // join the thread before destroying it // to prevent memory leaks thread->join(); // remove the thread from the vector and delete it // printf( "deleting a thread\n" ); mThreadVector->deleteElement( i ); delete thread; threadFound = true; // jump out of the for loop, since our // vector size has changed i = numThreads; } } } mLock->unlock(); } } Cultivation_9+dfsg1_UnixSource/minorGems/network/SocketClient.h0000640000175000017500000000415411314457356023631 0ustar pabspabs/* * Modification History * * 2001-January-10 Jason Rohrer * Created. * * 2002-October-13 Jason Rohrer * Added support for timeout on connect. * * 2008-September-30 Jason Rohrer * Added support for non-blocking connect. * * 2009-December-23 Jason Rohrer * Added note about manditory outTimedOut flag. */ #include "minorGems/common.h" #ifndef SOCKET_CLIENT_CLASS_INCLUDED #define SOCKET_CLIENT_CLASS_INCLUDED #include "Socket.h" #include "HostAddress.h" #include /** * Class that can make connections to socket servers. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class SocketClient { public: /** * Connects to a server and returns a socket. * * NOTE: * If inAddress is not in numerical format (in other words, if it * requires a DNS lookup before connection), this function may block * even if non-blocking mode is specified. * Consider using LookupThread to lookup the address before * calling this function. * * @param inAddress the host to connect to. Must be destroyed * by caller. * @param inTimeoutInMilliseconds the timeout value for the connect * in milliseconds, or -1 for no timeout. Defaults to -1. * Set to 0 for non-blocking connect that returns a not-yet-connected * socket. * @param outTimedOut pointer to where the timeout flag should * be returned, or NULL for no timeout. If timeout used, will * be set to true if timeout happened, or false if it did not. * Defaults to NULL. * MUST pass a pointer here if you want to use time-outs or * non-blocking connects. * * @return a socket for the connection, or NULL if an error occurs. */ static Socket *connectToServer( HostAddress *inAddress, long inTimeoutInMilliseconds = -1, char *outTimedOut = NULL ); }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/SocketManager.h0000640000175000017500000000324010157336507023756 0ustar pabspabs/* * Modification History * * 2004-December-13 Jason Rohrer * Created. */ #ifndef SOCKET_MANAGER_INCLUDED #define SOCKET_MANAGER_INCLUDED #include "minorGems/network/Socket.h" #include "minorGems/system/MutexLock.h" #include "minorGems/util/SimpleVector.h" /** * Ensures proper destruction of static data items at program termination. */ class SocketManagerDataWrapper { public: SocketManagerDataWrapper(); ~SocketManagerDataWrapper(); MutexLock *mLock; SimpleVector *mSocketVector; }; /** * A class that ensures thread-safe socket shutdown and destruction. * * Useful if a thread needs to break a socket that another thread is using. */ class SocketManager { public: /** * Adds a socket to this manager. * * @param inSocket the socket to add. * Will be destroyed by this manager. */ static void addSocket( Socket *inSocket ); /** * Breaks the connection (both directions) associated with a socket. * * This call is safe even if inSocket has already been destroyed. * * @param inSocket the socket to break. */ static void breakConnection( Socket *inSocket ); /** * Destroys a socket and removes it from this manager. * * @param inSocket the socket to destroy. */ static void destroySocket( Socket *inSocket ); private: // allocated statically to ensure destruction on program termination static SocketManagerDataWrapper mDataWrapper; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/SocketManager.cpp0000640000175000017500000000375010157336507024317 0ustar pabspabs/* * Modification History * * 2004-December-13 Jason Rohrer * Created. */ #include "SocketManager.h" // static initialization SocketManagerDataWrapper SocketManager::mDataWrapper; SocketManagerDataWrapper::SocketManagerDataWrapper() : mLock( new MutexLock() ), mSocketVector( new SimpleVector() ) { } SocketManagerDataWrapper::~SocketManagerDataWrapper() { int numSockets = mSocketVector->size(); for( int i=0; igetElement( i ) ); } delete mSocketVector; delete mLock; } void SocketManager::addSocket( Socket *inSocket ) { MutexLock *lock = mDataWrapper.mLock; SimpleVector *socketVector = mDataWrapper.mSocketVector; lock->lock(); socketVector->push_back( inSocket ); lock->unlock(); } void SocketManager::breakConnection( Socket *inSocket ) { MutexLock *lock = mDataWrapper.mLock; SimpleVector *socketVector = mDataWrapper.mSocketVector; lock->lock(); int numSockets = socketVector->size(); char found = false; for( int i=0; igetElement( i ) ); if( currentSocket == inSocket ) { currentSocket->breakConnection(); found = true; } } lock->unlock(); } void SocketManager::destroySocket( Socket *inSocket ) { MutexLock *lock = mDataWrapper.mLock; SimpleVector *socketVector = mDataWrapper.mSocketVector; lock->lock(); int numSockets = socketVector->size(); char found = false; for( int i=0; igetElement( i ) ); if( currentSocket == inSocket ) { delete currentSocket; socketVector->deleteElement( i ); found = true; } } lock->unlock(); } Cultivation_9+dfsg1_UnixSource/minorGems/network/SocketUDP.h0000640000175000017500000001325410144135464023035 0ustar pabspabs/* * Modification History * * 2004-November-3 Jason Rohrer * Created. * * 2004-November-4 Jason Rohrer * Added default receive timeout (infinite). * * 2004-November-9 Jason Rohrer * Added functions for comparing and copying UDPAddresses. */ #ifndef SOCKET_UDP_INCLUDED #define SOCKET_UDP_INCLUDED /** * Structure representing a UDP endpoint. */ struct UDPAddress { // binary internet address in network byte order unsigned long mIPAddress; // port number in network byte order unsigned short mPort; }; /** * Network socket that can be used as an endpoint for sending and receiving * UDP packets (unreliable datagrams). * * Note: Implementation for the functions defined here is provided * separately for each platform (in the unix/ and win32/ subdirectories). * * Socket::initSocketFramework() must be called once before this class * is used. * * @author Jason Rohrer */ class SocketUDP { public: /** * Constructs a UDP socket and starts listening for incoming datagrams. * * @param inReceivePort the port to listen on, in platform-dependent * byte order. */ SocketUDP( unsigned short inReceivePort ); ~SocketUDP(); /** * Makes a UDPAddress structure. * * @param inAddress the IP address in ascii numbers-and-dots notation. * Must be destroyed by caller if non-const. * @param inPort the port number in platform-specific byte order. * * @return an address structure, or NULL if converting the address * fails. * Must be destroyed by caller if non-NULL. */ static struct UDPAddress *makeAddress( const char *inAddress, unsigned short inPort ); /** * Extracts address elements from a UDPAddress structure. * * @param inAddress the address structure. Must be destroyed by * caller. * @param outPort pointer to where the port number, in * platform-specific byte order, should be returned. * * @return the IP address in ascci numbers-and-dots notation. * Must be destroyed by caller. */ static char *extractAddress( struct UDPAddress *inAddress, unsigned short *outPort ); /** * Compares two UDP addresses. * * @param inFirst the first address. * @param inSecond the second address. * * @return true if the addresses are equal, or false if they are * different. */ static char compare( struct UDPAddress *inFirst, struct UDPAddress *inSecond ); /** * Makes a copy of a UDP address. * * @param inAddress the address to copy. * * @return a copy of the address. Must be destroyed by caller. */ static struct UDPAddress *copy( struct UDPAddress *inAddress ); /** * Sends a datagram through this socket. * * Note: the recommended maximum data length is 512 bytes * to ensure that the datagram can be routed without * fragmentation through all spec-compliant routers. * Most routers support larger datagrams, however. * * @param inAddress the address to send data through. Must be * destroyed by caller. * @param inData the data bytes to send. * @param inNumBytes the number of bytes to send. * * @return the number of bytes sent successfully, * or -1 for a socket error. */ int send( struct UDPAddress *inAddress, unsigned char *inData, unsigned long inNumBytes ); /** * Receives a datagram from this socket. * * @param outAddress pointer to where the address of the remote * host (the datagram sender) should be returned. * Will be set to NULL on socket error or timeout. * Must be destroyed by caller if non-NULL. * @param outData pointer to where the received data should be * returned. Will be set to NULL on socket error or timeout. * Must be destroyed by caller if non-NULL. * @param inTimeout the timeout for this receive operation in * milliseconds. Set to -1 for an infinite timeout. * -2 is returned from this call in the event of a timeout. * Defaults to -1. * * @return the number of bytes received successfully, * -1 for a socket error, or -2 for a timeout. */ int receive( struct UDPAddress **outAddress, unsigned char **outData, long inTimeout = -1 ); /** * Used by platform-specific implementations. */ void *mNativeObjectPointer; }; inline char SocketUDP::compare( struct UDPAddress *inFirst, struct UDPAddress *inSecond ) { if( inFirst->mIPAddress == inSecond->mIPAddress && inFirst->mPort == inSecond->mPort ) { return true; } else { return false; } } inline struct UDPAddress *SocketUDP::copy( struct UDPAddress *inAddress ) { struct UDPAddress *returnAddress = new struct UDPAddress; returnAddress->mIPAddress = inAddress->mIPAddress; returnAddress->mPort = inAddress->mPort; return returnAddress; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/socketTest.cpp0000640000175000017500000000426210157336507023723 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-28 Jason Rohrer * Added more output. * Added better error checking. * * 2001-December-14 Jason Rohrer * Added a test of HostAddress::getLocalAddress(). * * 2002-July-30 Jason Rohrer * Added a test of streams. * * 2002-July-31 Jason Rohrer * Changed to test multiple blocks. * * 2002-February-4 Jason Rohrer * Added test of getting local address from socket. */ #include "Socket.h" #include "SocketStream.h" #include "SocketServer.h" #include "SocketClient.h" #include "HostAddress.h" #include int main() { HostAddress *address = HostAddress::getLocalAddress(); printf( "Local address: " ); address->print(); printf( "\n" ); int port = 5158; SocketServer *server = new SocketServer( port, 100 ); printf( "Waiting for a connection on port %d\n", port ); Socket *receiveSocket = server->acceptConnection(); if( receiveSocket == NULL ) { return 1; } printf( "Connection received\n" ); HostAddress *localAddress = receiveSocket->getLocalHostAddress(); if( localAddress != NULL ) { printf( "Our local address (fetched from socket) is " ); localAddress->print(); printf( "\n" ); delete localAddress; } SocketStream *receiveStream = new SocketStream( receiveSocket ); receiveStream->setReadTimeout( 10000 ); int numBytes = 4000; int checksum = 0; unsigned char *buffer = new unsigned char[numBytes]; /* for( int i=0; iread( buffer, 1 ); checksum += buffer[ 0 ]; if( numRec != 1) { printf( "Faiedl to read.\n" ); return 1; } //sleep( 1 ); } */ int count = 0; while( true ) { int numRec = receiveStream->read( buffer, numBytes ); printf( "Received %d successfully,\tcount = %d\n", numBytes, count ); count++; } /* for( int i=0; icopy() ), mNumericalAddress( NULL ), mLookupDone( false ) { start(); } LookupThread::~LookupThread() { join(); delete mAddress; if( mNumericalAddress != NULL ) { delete mNumericalAddress; } } char LookupThread::isLookupDone() { mLock.lock(); char done = mLookupDone; mLock.unlock(); return done; } HostAddress *LookupThread::getResult() { mLock.lock(); HostAddress *result = NULL; if( mNumericalAddress != NULL ) { result = mNumericalAddress->copy(); } mLock.unlock(); return result; } void LookupThread::run() { HostAddress *numAddress = mAddress->getNumericalAddress(); mLock.lock(); mLookupDone = true; mNumericalAddress = numAddress; mLock.unlock(); setFinished(); } Cultivation_9+dfsg1_UnixSource/minorGems/network/upnp/0000750000175000017500000000000011462022731022033 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/network/upnp/portMapping.h0000640000175000017500000000206311373324323024511 0ustar pabspabs/* * Modification History * * 2010-March-23 Jason Rohrer * Created. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #ifndef PORT_MAPPING_INCLUDED #define PORT_MAPPING_INCLUDED /** * Maps a port on a UPNP Gateway Device (discovers it first). * * @param inPort the port to map. * @param inDescription the application description to log on the gateway. * @param inTimeoutMS timeout to use for UPNP discovery process. * @param outExternalIP pointer to where external IP of the gateway should * be returned. Populated with a newly-allocated string, or NULL on failure. * Destroyed by caller. * * @return 1 on success. */ int mapPort( int inPort, const char *inDescription, int inTimeoutMS, char **outExternalIP ); /** * Maps a port on a UPNP Gateway Device (discovers it first). * * @param inPort the port to close on the gateway. * @param inTimeoutMS timeout to use for UPNP discovery process. * * @return 1 on success. */ int unmapPort( int inPort, int inTimeoutMS ); #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/upnp/portMapping.cpp0000640000175000017500000001110011373324323025034 0ustar pabspabs/* * Modification History * * 2010-March-23 Jason Rohrer * Created. * * 2010-March-24 Jason Rohrer * Fixed a destruction bug when discovery fails. * * 2010-April-6 Jason Rohrer * Switched from printf to log. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "portMapping.h" #include #include #include "miniupnpc/miniupnpc.h" #include "miniupnpc/upnpcommands.h" #include "miniupnpc/upnperrors.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/log/AppLog.h" static int getIGDURL( int inTimeoutMS, struct UPNPUrls *urls, struct IGDdatas *data, char *lanIPAddr, int lanIPLength ) { AppLog::info( "Trying to do UPNP discover\n" ); struct UPNPDev *devList = upnpDiscover( inTimeoutMS, NULL, NULL, 0 ); if( devList == NULL ) { AppLog::error( "UPNP discovery failed\n" ); return -1; } AppLog::info( "Checking UPNP dev list for a Gateway\n" ); int result = UPNP_GetValidIGD( devList, urls, data, lanIPAddr, lanIPLength ); freeUPNPDevlist( devList ); if( result != 0 ) { if( result != 1 ) { AppLog::error( "No connected UPNP Gateway Device found\n" ); } } else { AppLog::error( "No UPNP Gateway Device found\n" ); } return result; } int mapPort( int inPort, const char *inDescription, int inTimeoutMS, char **outExternalIP ) { *outExternalIP = NULL; int returnVal = -1; struct UPNPUrls urls; struct IGDdatas data; char lanIPAddr[16]; int result = getIGDURL( inTimeoutMS, &urls, &data, lanIPAddr, 16 ); if( result > 0 ) { if( result == 1 ) { char externalIPAddress[16]; int result = UPNP_GetExternalIPAddress( urls.controlURL, data.servicetype, externalIPAddress ); if( result == UPNPCOMMAND_SUCCESS && externalIPAddress[0] != '\0' ) { *outExternalIP = stringDuplicate( externalIPAddress ); } else { AppLog::error( "GetExternalIPAddress failed.\n" ); *outExternalIP = NULL; } char *port = autoSprintf( "%d", inPort ); result = UPNP_AddPortMapping( urls.controlURL, data.servicetype, port, port, lanIPAddr, inDescription, "TCP", NULL ); if( result != UPNPCOMMAND_SUCCESS ) { AppLog::getLog()->logPrintf( Log::ERROR_LEVEL, "AddPortMapping(%s, %s, %s) failed with code " "%d (%s)\n", port, port, lanIPAddr, result, strupnperror(result) ); } else { AppLog::info( "AddPortMapping success\n" ); returnVal = 1; } delete [] port; } FreeUPNPUrls( &urls ); } return returnVal; } int unmapPort( int inPort, int inTimeoutMS ) { int returnVal = -1; struct UPNPUrls urls; struct IGDdatas data; char lanIPAddr[16]; int result = getIGDURL( inTimeoutMS, &urls, &data, lanIPAddr, 16 ); if( result > 0 ) { if( result == 1 ) { char *port = autoSprintf( "%d", inPort ); int result = UPNP_DeletePortMapping( urls.controlURL, data.servicetype, port, "TCP", NULL ); if( result != UPNPCOMMAND_SUCCESS ) { AppLog::getLog()->logPrintf( Log::ERROR_LEVEL, "DeletePortMapping(%s) failed with code " "%d (%s)\n", port, result, strupnperror(result) ); } else { AppLog::info( "DeletePortMapping success\n" ); returnVal = 1; } delete [] port; } FreeUPNPUrls( &urls ); } return returnVal; } Cultivation_9+dfsg1_UnixSource/minorGems/network/upnp/testMap.cpp0000640000175000017500000000151411352214515024157 0ustar pabspabs#include "portMapping.h" #include // false to unmap char map = true; int main() { if( map ) { char *externalIP; int result = mapPort( 7780, "test upnp", 2000, &externalIP ); if( result == 1 ) { printf( "mapping success\n" ); } else { printf( "mapping failure\n" ); } if( externalIP != NULL ) { printf( "External IP = %s\n", externalIP ); delete [] externalIP; } } else { int result = unmapPort( 7780, 2000 ); if( result == 1 ) { printf( "unmapping success\n" ); } else { printf( "unmapping failure\n" ); } } return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/0000750000175000017500000000000011401021060022547 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/MultiSourceDownloader.h0000640000175000017500000000546210150605366027243 0ustar pabspabs/* * Modification History * * 2004-November-21 Jason Rohrer * Created. * * 2004-November-23 Jason Rohrer * Fixed compile errors caused by multiple definitions. */ #ifndef MULTISOURCE_DOWNLOADER_INCLUDED #define MULTISOURCE_DOWNLOADER_INCLUDED /** * Abstract API for multi-source downloads. * * @author Jason Rohrer. */ extern int MULTISOURCE_DOWNLOAD_IN_PROGRESS; extern int MULTISOURCE_DOWNLOAD_FAILED; extern int MULTISOURCE_DOWNLOAD_CANCELED; /** * Gets a file from multiple sources. * * @param inFileDescriptor abstract pointer to a file descriptor that can * be used by inChunkGetter to identify a file. * Must be destroyed by caller. * @param inFileSize the size of the file in bytes. * @param inChunkSize the size of a download chunk in bytes. * @param inNumSources the number of file sources. * @param inFileSources an array of abstract pointers to file sources that * can be used by inChunkSize to connect to sources. * Array and elements must be destroyed by caller. * @param inChunkGetter pointer to the function that can be used to get a * chunk. * The function must return an array of chunk bytes (or NULL on failure) * and take the following arguments: * ( void *inFileSource, void *inFileDescriptor, * unsigned long inChunkNumber, unsigned long inChunkSize ). * @praram inDownloadProgressHandler pointer to the handler function for * download progress events. * This function must return true to continue the download (or false * to cancel) and take the following arguments: * (int inResultCode, unsigned long inTotalBytesReceived, * void *inExtraArgument ). * inResultCode will be set to one of MULTISOURCE_DOWNLOAD_IN_PROGRESS, * MULTISOURCE_DOWNLOAD_FAILED, or MULTISOURCE_DOWNLOAD_CANCELED. * Once a FAILED or CANCELED result code is passed to the handler, the * handler will never be called again, so it should destroy its extra * argument, if necessary. * @param inProgressHandlerExtraArgument pointer to an extra argument to be * passed in to the handler function each time it is called. * Must be destroyed by caller. * @param inDestinationPath the path to save the file to. * Must be destroyed by caller. */ void multiSourceGetFile( void *inFileDescriptor, unsigned long inFileSize, unsigned long inChunkSize, int inNumSources, void **inFileSources, unsigned char * (*inChunkGetter)( void *, void *, unsigned long, unsigned long ), char (*inDownloadProgressHandler)( int, unsigned long, void * ), void *inProgressHandlerExtraArgument, char *inDestinationPath ); #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/DuplicateMessageDetector.cpp0000640000175000017500000000464307716730656030234 0ustar pabspabs/* * Modification History * * 2003-August-7 Jason Rohrer * Created. * * 2003-August-14 Jason Rohrer * Changed to output message history to file. */ #include "DuplicateMessageDetector.h" #include "minorGems/util/stringUtils.h" #include #include DuplicateMessageDetector::DuplicateMessageDetector( int inMessageHistorySize ) : mMaxHistorySize( inMessageHistorySize ), mLock( new MutexLock() ), mSeenIDs( new SimpleVector() ), mTotalMessageCount( 0 ), mHistoryOutputFile( NULL ) { mHistoryOutputFile = fopen( "messageHistory.log", "w" ); } DuplicateMessageDetector::~DuplicateMessageDetector() { int numIDs = mSeenIDs->size(); for( int i=0; igetElement( i ) ); } delete mSeenIDs; delete mLock; fclose( mHistoryOutputFile ); } char DuplicateMessageDetector::checkIfMessageSeen( char *inMessageUniqueID ) { mLock->lock(); mTotalMessageCount++; int numIDs = mSeenIDs->size(); char matchSeen = false; for( int i=0; igetElement( i ) ); if( strcmp( otherID, inMessageUniqueID ) == 0 ) { // match // push the ID back to the end of the queue mSeenIDs->deleteElement( i ); mSeenIDs->push_back( otherID ); matchSeen = true; } } if( mHistoryOutputFile != NULL ) { fprintf( mHistoryOutputFile, "%d %d %s", mTotalMessageCount, (int)( time( NULL ) ), inMessageUniqueID ); } if( !matchSeen ) { // add the message mSeenIDs->push_back( stringDuplicate( inMessageUniqueID ) ); // make sure history not too long if( mSeenIDs->size() > mMaxHistorySize ) { delete [] *( mSeenIDs->getElement( 0 ) ); mSeenIDs->deleteElement( 0 ); } if( mHistoryOutputFile != NULL ) { fprintf( mHistoryOutputFile, "\n" ); } } else { // add duplicate tag if( mHistoryOutputFile != NULL ) { fprintf( mHistoryOutputFile, " D\n" ); } } if( mHistoryOutputFile != NULL ) { fflush( mHistoryOutputFile ); } mLock->unlock(); return matchSeen; } Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/protocolUtils.h0000640000175000017500000000370107721440564025634 0ustar pabspabs/* * Modification History * * 2003-August-12 Jason Rohrer * Created. * * 2003-August-22 Jason Rohrer * Added function for getting a token after reading. */ #ifndef P2P_PROTOCOL_UTILS_INCLUDED #define P2P_PROTOCOL_UTILS_INCLUDED #include "minorGems/io/InputStream.h" #include "minorGems/io/OutputStream.h" /** * A collection of common protocol processing functions. * * @author Jason Rohrer */ /** * Reads from a stream up to (and including) the * first occurence of a tag. * * @param inInputStream the stream to read from. * Must be destroyed by caller. * @param inTag the tag to look for. * Must be destroyed by caller if non-const. * @param inMaxCharsToRead the maximum number of characters to read. * * @return the read string, or NULL if reading fails. * NULL is also returned if the character count limit is * reached. * Must be destroyed by caller if non-NULL. */ char *readStreamUpToTag( InputStream *inInputStream, char *inTag, int inMaxCharsToRead ); /** * Reads from a stream up to (and including) the * first occurence of a tag and gets a specific token from the read data. * * Tokens are split by whitespace. * * @param inInputStream the stream to read from. * Must be destroyed by caller. * @param inTag the tag to look for. * Must be destroyed by caller if non-const. * @param inMaxCharsToRead the maximum number of characters to read. * @param inTokenNumber the token to get after reading up to inTag and * tokenizing the read data. 0 specifies the first token. * * @return the read token string, or NULL if reading or token extraction fails. * NULL is also returned if the character count limit is * reached. * Must be destroyed by caller if non-NULL. */ char *readStreamUpToTagAndGetToken( InputStream *inInputStream, char *inTag, int inMaxCharsToRead, int inTokenNumber ); #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/MultipleConnectionPreventer.cpp0000640000175000017500000000333307674671761031030 0ustar pabspabs/* * Modification History * * 2003-June-20 Jason Rohrer * Created. */ #include "MultipleConnectionPreventer.h" MultipleConnectionPreventer::MultipleConnectionPreventer() : mLock( new MutexLock() ), mConnections( new SimpleVector() ) { } MultipleConnectionPreventer::~MultipleConnectionPreventer() { mLock->lock(); int numConnections = mConnections->size(); for( int i=0; igetElement( i ) ); } delete mConnections; mLock->unlock(); delete mLock; } char MultipleConnectionPreventer::addConnection( HostAddress *inAddress ) { mLock->lock(); char connectionExists = false; int numConnections = mConnections->size(); for( int i=0; igetElement( i ) ); if( inAddress->equals( otherConnection ) ) { connectionExists = true; } } if( !connectionExists ) { mConnections->push_back( inAddress->copy() ); } mLock->unlock(); return !connectionExists; } void MultipleConnectionPreventer::connectionBroken( HostAddress *inAddress ) { mLock->lock(); char connectionFound = false; int numConnections = mConnections->size(); for( int i=0; igetElement( i ) ); if( inAddress->equals( otherConnection ) ) { connectionFound = true; delete otherConnection; mConnections->deleteElement( i ); } } mLock->unlock(); } Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/MessagePerSecondLimiter.h0000640000175000017500000000402307775323015027465 0ustar pabspabs/* * Modification History * * 2003-October-9 Jason Rohrer * Created. * * 2003-October-12 Jason Rohrer * Switched to a floating point limit. * * 2004-January-2 Jason Rohrer * Added seprate mutex for transmission function to prevent UI freeze. */ #ifndef MESSAGE_PER_SECOND_LIMITER_INCLUDED_H #define MESSAGE_PER_SECOND_LIMITER_INCLUDED_H #include "minorGems/system/MutexLock.h" /** * Class that limits the number of messages transmitted per second. * * @author Jason Rohrer */ class MessagePerSecondLimiter { public: /** * Constructs a limiter. * * @param inLimitPerSecond the maximum number of messages * transmitted per second, or -1 for no limit. * Defaults to -1. */ MessagePerSecondLimiter( double inLimitPerSecond = -1 ); ~MessagePerSecondLimiter(); /** * Sets the limit. * * Thread safe. * * @param inLimitPerSecond the maximum number of messages * transmitted per second, or -1 for no limit. */ void setLimit( double inLimitPerSecond ); /** * Gets the limit. * * Thread safe. * * @return the maximum number of messages * transmitted per second, or -1 if no limit set. */ double getLimit(); /** * Called by a message transmitter to indicate that a message * is about to be transmitted. Will block if messages * are being transmitted too frequently. * * Thread safe. */ void messageTransmitted(); protected: MutexLock *mLock; MutexLock *mTransmitLock; double mLimitPerSecond; unsigned long mMillisecondsBetweenMessages; unsigned long mSecondTimeOfLastMessage; unsigned long mMillisecondTimeOfLastMessage; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/DuplicateMessageDetector.h0000640000175000017500000000272507716716063027674 0ustar pabspabs/* * Modification History * * 2003-August-7 Jason Rohrer * Created. * * 2003-August-14 Jason Rohrer * Changed to output message history to file. */ #ifndef DUPLICATE_MESSAGE_DETECTOR_INCLUDED_H #define DUPLICATE_MESSAGE_DETECTOR_INCLUDED_H #include "minorGems/system/MutexLock.h" #include "minorGems/util/SimpleVector.h" #include /** * Class that detects duplicates of past messages so that they can be * discarded. * * @author Jason Rohrer */ class DuplicateMessageDetector { public: /** * Constructs a detector. * * @param inMessageHistorySize the number of message IDs to * maintain in our history. * Defaults to 1000. */ DuplicateMessageDetector( int inMessageHistorySize = 1000 ); ~DuplicateMessageDetector(); /** * Checks if a message has been seen in the past. * * @param inMessageUniqueID the unique ID for the message. * Must be destroyed by caller. * * @return true if the message has already been seen, or * false if the message is new. */ char checkIfMessageSeen( char *inMessageUniqueID ); protected: int mMaxHistorySize; MutexLock *mLock; SimpleVector *mSeenIDs; int mTotalMessageCount; FILE *mHistoryOutputFile; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/MessagePerSecondLimiter.cpp0000640000175000017500000000523110227735471030021 0ustar pabspabs/* * Modification History * * 2003-October-9 Jason Rohrer * Created. * * 2003-October-12 Jason Rohrer * Switched to a floating point limit. * * 2004-January-2 Jason Rohrer * Added seprate mutex for transmission function to prevent UI freeze. * * 2005-April-15 Jason Rohrer * Changed to use updated Thread interface. */ #include "MessagePerSecondLimiter.h" #include "minorGems/system/Time.h" #include "minorGems/system/Thread.h" MessagePerSecondLimiter::MessagePerSecondLimiter( double inLimitPerSecond ) : mLock( new MutexLock() ), mTransmitLock( new MutexLock() ), mLimitPerSecond( inLimitPerSecond ) { if( mLimitPerSecond != -1 ) { mMillisecondsBetweenMessages = (int)( 1000 / mLimitPerSecond ); } else { mMillisecondsBetweenMessages = 0; } Time::getCurrentTime( &mSecondTimeOfLastMessage, &mMillisecondTimeOfLastMessage ); } MessagePerSecondLimiter::~MessagePerSecondLimiter() { delete mLock; delete mTransmitLock; } void MessagePerSecondLimiter::setLimit( double inLimitPerSecond ) { mLock->lock(); mLimitPerSecond = inLimitPerSecond; if( mLimitPerSecond != -1 ) { mMillisecondsBetweenMessages = (int)( 1000 / mLimitPerSecond ); } else { mMillisecondsBetweenMessages = 0; } mLock->unlock(); } double MessagePerSecondLimiter::getLimit() { mLock->lock(); double limit = mLimitPerSecond; mLock->unlock(); return limit; } void MessagePerSecondLimiter::messageTransmitted() { // allow only one transmitter to report at a time mTransmitLock->lock(); // protect our variables (make sure settings functions are not // called while we touch the variables) mLock->lock(); unsigned long millisecondsSinceLastMessage = Time::getMillisecondsSince( mSecondTimeOfLastMessage, mMillisecondTimeOfLastMessage ); if( millisecondsSinceLastMessage < mMillisecondsBetweenMessages ) { // this message is coming too soon after last message // sleep unsigned long sleepTime = mMillisecondsBetweenMessages - millisecondsSinceLastMessage; // unlock main lock befor sleeping so that settings can be changed mLock->unlock(); Thread::staticSleep( sleepTime ); // relock mLock->lock(); } Time::getCurrentTime( &mSecondTimeOfLastMessage, &mMillisecondTimeOfLastMessage ); mLock->unlock(); mTransmitLock->unlock(); } Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/OutboundChannel.h0000640000175000017500000001150610157121106026025 0ustar pabspabs/* * Modification History * * 2003-June-22 Jason Rohrer * Copied from the konspire2b project and modified. * * 2003-July-27 Jason Rohrer * Added a setHost function. * * 2003-September-23 Jason Rohrer * Replaced sleep-waiting with semaphores. This results in a major speedup. * * 2003-October-9 Jason Rohrer * Added support for message limiters. * * 2004-December-12 Jason Rohrer * Added a queue size parameter. */ #ifndef OUTBOUND_CHANNEL_INCLUDED #define OUTBOUND_CHANNEL_INCLUDED #include "minorGems/network/HostAddress.h" #include "minorGems/io/OutputStream.h" #include "minorGems/system/MutexLock.h" #include "minorGems/system/Semaphore.h" #include "minorGems/system/Thread.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/network/p2pParts/MessagePerSecondLimiter.h" /** * A channel that can send messages to a receiving host. * * NOTE: * None of the member functions are safe to call if this class has been * destroyed. Since the application-specific channel manager class can * destroy a given instance of this class at any time, these member functions * should NEVER be called directly. * Instead, the appropriate channel manager functions should be called. * * @author Jason Rohrer */ class OutboundChannel : public Thread { public: /** * Constructs an OutboundChannel and starts the sending thread. * * @param inOutputStream the stream wrapped by this channel. * Must be destroyed by caller. * @param inHost the host on the other end of this channel. * Will be destroyed when this class is destroyed. * @param inLimiter the limiter for outbound messages. * Must be destroyed by caller after this class is destroyed. * @param inQueueSize the size of the send queue. Defaults to 50. */ OutboundChannel( OutputStream *inOutputStream, HostAddress *inHost, MessagePerSecondLimiter *inLimiter, unsigned long inQueueSize = 50 ); /** * Stops the sending thread and destroys this channel. */ ~OutboundChannel(); /** * Sends a message to this channel's receiver. * * Thread safe. * * This call queues the message to be sent, so it returns before * the send is complete. * * @param inMessage the message to send. * Must be destroyed by caller if non-const. * @param inPriority the priority of this message. * Values less than or equal to 0 indicate default priority, * while positive values suggest higher priority. * Granularity of prioritization is implementation dependent. * Defaults to 0. * * @return true if the channel is still functioning properly, * or false if the channel has been broken. */ char sendMessage( char *inMessage, int inPriority = 0 ); /** * Gets the host receiving from this channel. * * @return this channel's receiving host. * Must be destroyed by caller. */ HostAddress * getHost( ); /** * Sets the address of the remotely connected host. * * @param inHost the host on the other end of this channel. * Will be destroyed when this class is destroyed. */ void setHost( HostAddress *inHost ); /** * Gets the number of outbound messages that have been sent on * this channel. * * Thread safe. * * @return the number of sent messages. */ int getSentMessageCount(); /** * Gets the number of outbound messages that are currently * queued for this channel. * * Thread safe. * * @return the number of queued messages. */ int getQueuedMessageCount(); /** * Gets the number of outbound messages that have been dropped * by this channel. * * Thread safe. * * @return the number of dropped messages. */ int getDroppedMessageCount(); // implements the Thread interface void run(); protected: MutexLock *mLock; Semaphore *mMessageReadySemaphore; OutputStream *mStream; HostAddress *mHost; MessagePerSecondLimiter *mLimiter; char mConnectionBroken; char mThreadStopped; SimpleVector *mMessageQueue; SimpleVector *mHighPriorityMessageQueue; int mMaxQueueSize; int mDroppedMessageCount; int mSentMessageCount; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/MultipleConnectionPreventer.h0000640000175000017500000000305007674671762030472 0ustar pabspabs/* * Modification History * * 2003-June-20 Jason Rohrer * Created. */ #ifndef MULTIPLE_CONNECTION_PREVENTER_INCLUDED_H #define MULTIPLE_CONNECTION_PREVENTER_INCLUDED_H #include "minorGems/system/MutexLock.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/network/HostAddress.h" /** * Class that tracks current connections of a p2p node and prevents * multiple connections to the same address. * * @author Jason Rohrer */ class MultipleConnectionPreventer { public: MultipleConnectionPreventer(); ~MultipleConnectionPreventer(); /** * Adds a connection to a host, and checks if the connection * is permitted. * * The connection will not be permitted if another connection * to the address already exists. * * Thread safe. * * @param inAddress the address of the connection. * Must be destroyed by caller. * * @return true if the connection is permitted, or false otherwise. */ char addConnection( HostAddress *inAddress ); /** * Reports that a connection has been broken. * * Thread safe. * * @param inAddress the address of the broken connection. * Must be destroyed by caller. */ void connectionBroken( HostAddress *inAddress ); protected: MutexLock *mLock; SimpleVector *mConnections; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/OutboundChannel.cpp0000640000175000017500000001363410157121106026364 0ustar pabspabs/* * Modification History * * 2003-June-22 Jason Rohrer * Copied from the konspire2b project and modified. * * 2003-July-27 Jason Rohrer * Added a setHost function. * * 2003-September-23 Jason Rohrer * Replaced sleep-waiting with semaphores. This results in a major speedup. * * 2003-October-9 Jason Rohrer * Added support for message limiters. * * 2004-January-11 Jason Rohrer * Made include paths explicit to help certain compilers. * * 2004-December-12 Jason Rohrer * Added a queue size parameter. */ #include "minorGems/network/p2pParts/OutboundChannel.h" OutboundChannel::OutboundChannel( OutputStream *inOutputStream, HostAddress *inHost, MessagePerSecondLimiter *inLimiter, unsigned long inQueueSize ) : mLock( new MutexLock() ), mMessageReadySemaphore( new Semaphore() ), mStream( inOutputStream ), mHost( inHost ), mLimiter( inLimiter ), mConnectionBroken( false ), mThreadStopped( false ), mMessageQueue( new SimpleVector() ), mHighPriorityMessageQueue( new SimpleVector() ), mMaxQueueSize( inQueueSize ), mDroppedMessageCount( 0 ), mSentMessageCount( 0 ) { // start our thread start(); } OutboundChannel::~OutboundChannel() { mLock->lock(); mThreadStopped = true; mLock->unlock(); // wake the thread up if it is waiting mMessageReadySemaphore->signal(); // wait for our thread to get the stop signal and finish join(); mLock->lock(); delete mMessageReadySemaphore; // clear the queues int numMessages = mMessageQueue->size(); int i; for( i=0; igetElement( i ) ); delete [] message; } delete mMessageQueue; numMessages = mHighPriorityMessageQueue->size(); for( i=0; igetElement( i ) ); delete [] message; } delete mHighPriorityMessageQueue; delete mHost; mLock->unlock(); delete mLock; } char OutboundChannel::sendMessage( char * inMessage, int inPriority ) { mLock->lock(); char sent; if( !mConnectionBroken ) { // add it to the queue SimpleVector *queueToUse; if( inPriority <=0 ) { queueToUse = mMessageQueue; } else { queueToUse = mHighPriorityMessageQueue; } queueToUse->push_back( stringDuplicate( inMessage ) ); sent = true; if( queueToUse->size() > mMaxQueueSize ) { // the queue is over-full // drop the oldest message char *message = *( queueToUse->getElement( 0 ) ); queueToUse->deleteElement( 0 ); delete [] message; mDroppedMessageCount++; } } else { // channel no longer working sent = false; } mLock->unlock(); if( sent ) { mMessageReadySemaphore->signal(); } return sent; } HostAddress * OutboundChannel::getHost() { return mHost->copy(); } void OutboundChannel::setHost( HostAddress *inHost ) { delete mHost; mHost = inHost->copy(); } int OutboundChannel::getSentMessageCount() { mLock->lock(); int count = mSentMessageCount; mLock->unlock(); return count; } int OutboundChannel::getQueuedMessageCount() { mLock->lock(); int count = mMessageQueue->size() + mHighPriorityMessageQueue->size(); mLock->unlock(); return count; } int OutboundChannel::getDroppedMessageCount() { mLock->lock(); int count = mDroppedMessageCount; mLock->unlock(); return count; } void OutboundChannel::run() { mLock->lock(); char stopped = mThreadStopped; mLock->unlock(); while( !stopped ) { // get a message from the queue, checking high priority queue first char *message = NULL; mLock->lock(); if( mHighPriorityMessageQueue->size() >= 1 ) { message = *( mHighPriorityMessageQueue->getElement( 0 ) ); mHighPriorityMessageQueue->deleteElement( 0 ); } else if( mMessageQueue->size() >= 1 ) { message = *( mMessageQueue->getElement( 0 ) ); mMessageQueue->deleteElement( 0 ); } mLock->unlock(); // note that we're unlocked during the send, so messages // can be freely added to the queue without blocking while we send // this message if( message != NULL ) { // obey the limit // we will block here if message rate is too high mLimiter->messageTransmitted(); int bytesSent = mStream->writeString( message ); int messageLength = strlen( message ); delete [] message; char sent; if( bytesSent == messageLength ) { sent = true; mLock->lock(); mSentMessageCount++; mLock->unlock(); } else { sent = false; } if( !sent ) { // connection is broken // stop this thread mLock->lock(); mConnectionBroken = true; mThreadStopped = true; mLock->unlock(); } } else { // no messages in the queue. // wait for more messages to be ready mMessageReadySemaphore->wait(); } // check if we've been stopped mLock->lock(); stopped = mThreadStopped; mLock->unlock(); } } Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/HostCatcher.cpp0000640000175000017500000001516010161606173025506 0ustar pabspabs/* * Modification History * * 2003-June-22 Jason Rohrer * Copied from the konspire2b project and modified. * * 2003-July-27 Jason Rohrer * Fixed a bug when catcher is empty. * * 2003-December-21 Jason Rohrer * Fixed a memory leak when catcher is full. * * 2004-January-11 Jason Rohrer * Made include paths explicit to help certain compilers. * * 2004-December-20 Jason Rohrer * Changed to convert to numerical form before comparing against host list. * Changed getHost to return hosts in random order. * Added a getOrderedHost function that returns hosts in linear order. */ #include "minorGems/network/p2pParts/HostCatcher.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/random/StdRandomSource.h" HostCatcher::HostCatcher( int inMaxListSize ) : mMaxListSize( inMaxListSize ), mHostVector( new SimpleVector() ), mLock( new MutexLock() ), mRandSource( new StdRandomSource() ) { } HostCatcher::~HostCatcher() { mLock->lock(); int numHosts = mHostVector->size(); for( int i=0; igetElement( i ) ); } delete mHostVector; mLock->unlock(); delete mLock; delete mRandSource; } void HostCatcher::addHost( HostAddress * inHost ) { // convert to numerical form once and for all here // (to avoid converting over and over in equals checks below) HostAddress *numericalAddress = inHost->getNumericalAddress(); if( numericalAddress != NULL ) { mLock->lock(); // make sure this host doesn't already exist in our list char exists = false; int numHosts = mHostVector->size(); for( int i=0; igetElement( i ) ); if( otherHost->equals( numericalAddress ) ) { exists = true; // jump out of loop i = numHosts; } } if( !exists ) { mHostVector->push_back( numericalAddress->copy() ); } while( mHostVector->size() > mMaxListSize ) { // remove first host from queue HostAddress *host = *( mHostVector->getElement( 0 ) ); mHostVector->deleteElement( 0 ); delete host; } mLock->unlock(); delete numericalAddress; } } HostAddress * HostCatcher::getHostOrdered( ) { mLock->lock(); int numHosts = mHostVector->size(); if( numHosts == 0 ) { mLock->unlock(); return NULL; } // remove first host from queue HostAddress *host = *( mHostVector->getElement( 0 ) ); mHostVector->deleteElement( 0 ); // add host to end of queue mHostVector->push_back( host ); HostAddress *hostCopy = host->copy(); mLock->unlock(); return hostCopy; } HostAddress * HostCatcher::getHost( ) { mLock->lock(); int numHosts = mHostVector->size(); if( numHosts == 0 ) { mLock->unlock(); return NULL; } // remove random host from queue int index = mRandSource->getRandomBoundedInt( 0, numHosts - 1 ); HostAddress *host = *( mHostVector->getElement( index ) ); mHostVector->deleteElement( index ); // add host to end of queue mHostVector->push_back( host ); HostAddress *hostCopy = host->copy(); mLock->unlock(); return hostCopy; } SimpleVector *HostCatcher::getHostList( int inMaxHostCount, HostAddress *inSkipHost ) { HostAddress *hostToSkip; if( inSkipHost != NULL ) { hostToSkip = inSkipHost->copy(); } else { // don't skip any host // create a dummy host that won't match any other valid hosts // make sure dummy is in numerical form to avoid DNS lookups hostToSkip = new HostAddress( stringDuplicate( "1.1.1.1" ), 1 ); } SimpleVector *collectedHosts = new SimpleVector(); char repeat = false; int numCollected = 0; // This function assumes that getHostOrdered() draws // hosts in order with no repetition except when we have // exhausted the host supply. // Note that this will not be true when other threads // have getHostOrdered() (or getHost) calls interleaved with ours, but this // should be a rare case. It will simply result // in a smaller host list being returned. HostAddress *firstHost = getHostOrdered(); if( firstHost == NULL ) { // the catcher is empty delete hostToSkip; // an empty host list return collectedHosts; } if( ! hostToSkip->equals( firstHost ) ) { collectedHosts->push_back( firstHost ); numCollected++; } while( numCollected < inMaxHostCount && !repeat ) { HostAddress *nextHost = getHostOrdered(); if( nextHost->equals( firstHost ) ) { delete nextHost; repeat = true; } else { if( ! hostToSkip->equals( nextHost ) ) { collectedHosts->push_back( nextHost ); numCollected++; } else { delete nextHost; } } } if( hostToSkip->equals( firstHost ) ) { // we didn't include firstHost in our collectedHosts, so // we must delete it. delete firstHost; } delete hostToSkip; return collectedHosts; } void HostCatcher::addHostList( SimpleVector * inHostList ) { int numToAdd = inHostList->size(); for( int i=0; igetElement( i ) ) ); } } void HostCatcher::noteHostBad( HostAddress * inHost ) { mLock->lock(); // make sure this host already exists in our list char exists = false; HostAddress *foundHost = NULL; int numHosts = mHostVector->size(); for( int i=0; igetElement( i ) ); if( otherHost->equals( inHost ) ) { exists = true; // delete the host that we've found mHostVector->deleteElement( i ); foundHost = otherHost; // jump out of loop i = numHosts; } } if( exists ) { delete foundHost; //mHostVector->push_back( foundHost ); } mLock->unlock(); } Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/HostCatcher.h0000640000175000017500000000730210161606173025152 0ustar pabspabs/* * Modification History * * 2003-June-22 Jason Rohrer * Copied from the konspire2b project and modified. * * 2004-December-20 Jason Rohrer * Changed getHost to return hosts in random order. * Added a getOrderedHost function that returns hosts in linear order. */ #ifndef HOST_CATCHER_INCLUDED #define HOST_CATCHER_INCLUDED #include "minorGems/network/HostAddress.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/util/random/RandomSource.h" #include "minorGems/system/MutexLock.h" /** * Manages a collection of hosts. * * @author Jason Rohrer */ class HostCatcher { public: /** * Constructs a host catcher. * * @param inMaxListSize the maximum number of hosts to hold. */ HostCatcher( int inMaxListSize ); ~HostCatcher(); /** * Adds a host to this catcher. * * Thread safe. * * @param inHost the host to add. * Must be destroyed by caller. */ void addHost( HostAddress * inHost ); /** * Gets a "fresh" host from this catcher. * * The returned host is "fresh" in that it has not * been returned by this call in a while. * * Thread safe. * * @return a host, or NULL if there are no hosts. * Must be destroyed by caller if non-NULL. */ HostAddress * getHost( ); /** * Gets a "fresh" host list from this catcher. * The result of this function call is similar to that obtained * by calling getHost inMaxHostCount times and constructing * a HostList from the result, except that this function * will produce a list with no repeated hosts. * * Thread safe. * * @para inMaxHostCount the maximum number of hosts to retrieve. * @param inSkipHost a host to skip when building the list, * or NULL to not skip any host. (Useful * fo constructing a host list at the request of another host.) * Defaults to NULL. Must be destroyed by caller. * * @return a host list containing <= inMaxHostCount hosts. * Vector and addresses must be destroyed by caller. */ SimpleVector *getHostList( int inMaxHostCount, HostAddress *inSkipHost = NULL ); /** * Adds an entire list of hosts to this catcher. * * Thread safe. * * @param inHostList the list to add. * Vector and addresses must be destroyed by caller. */ void addHostList( SimpleVector *inHostList ); /** * Tells this catcher that a host is "bad" * (in other words, dead, dropping connections, etc.). * * Thread safe. * * @param inHost the bad host. * Must be destroyed by caller. */ void noteHostBad( HostAddress * inHost ); protected: int mMaxListSize; SimpleVector *mHostVector; MutexLock *mLock; RandomSource *mRandSource; /** * Gets a "fresh" host from this catcher, walking through the host * list in order. * * The returned host is "fresh" in that it has not * been returned by this call in a while. * * Thread safe. * * @return a host, or NULL if there are no hosts. * Must be destroyed by caller if non-NULL. */ HostAddress *getHostOrdered(); }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/MultiSourceDownloader.cpp0000640000175000017500000000764710154110372027575 0ustar pabspabs/* * Modification History * * 2004-November-21 Jason Rohrer * Created. * * 2004-November-23 Jason Rohrer * Fixed compile errors caused by multiple definitions. * * 2004-December-4 Jason Rohrer * Fixed bug in source indexing. */ #include "MultiSourceDownloader.h" #include int MULTISOURCE_DOWNLOAD_IN_PROGRESS = 0; int MULTISOURCE_DOWNLOAD_FAILED = 1; int MULTISOURCE_DOWNLOAD_CANCELED = 2; void multiSourceGetFile( void *inFileDescriptor, unsigned long inFileSize, unsigned long inChunkSize, int inNumSources, void **inFileSources, unsigned char * (*inChunkGetter)( void *, void *, unsigned long, unsigned long ), char (*inDownloadProgressHandler)( int, unsigned long, void * ), void *inProgressHandlerExtraArgument, char *inDestinationPath ) { FILE *outputFile = fopen( inDestinationPath, "wb" ); if( outputFile == NULL ) { inDownloadProgressHandler( MULTISOURCE_DOWNLOAD_FAILED, 0, inProgressHandlerExtraArgument ); return; } // for now, just fetch chunks in linear order, using each file source // until it fails char failed = false; char canceled = false; unsigned long chunksSoFar = 0; unsigned long numChunks = inFileSize / inChunkSize; if( inFileSize % inChunkSize != 0 ) { // extra partial chunk numChunks++; } unsigned long bytesSoFar = 0; int sourceIndex = 0; while( sourceIndex < inNumSources && !failed && !canceled && chunksSoFar < numChunks ) { void *source = inFileSources[ sourceIndex ]; unsigned long chunkSize = inChunkSize; if( chunksSoFar * inChunkSize + chunkSize > inFileSize ) { // partial chunk chunkSize = inFileSize - chunksSoFar * inChunkSize; } unsigned char *chunkData = inChunkGetter( source, inFileDescriptor, chunksSoFar, chunkSize ); if( chunkData != NULL ) { chunksSoFar += 1; bytesSoFar += chunkSize; unsigned long numWritten = fwrite( chunkData, 1, chunkSize, outputFile ); if( numWritten == chunkSize ) { char shouldContinue = inDownloadProgressHandler( MULTISOURCE_DOWNLOAD_IN_PROGRESS, bytesSoFar, inProgressHandlerExtraArgument ); if( !shouldContinue ) { canceled = true; // call handler last time inDownloadProgressHandler( MULTISOURCE_DOWNLOAD_CANCELED, bytesSoFar, inProgressHandlerExtraArgument ); } } else { // failed to write to file, so download cannot continue inDownloadProgressHandler( MULTISOURCE_DOWNLOAD_FAILED, bytesSoFar, inProgressHandlerExtraArgument ); failed = true; } delete [] chunkData; } else { // else go on to the next host sourceIndex ++; } } // check if we got here because we ran out of hosts (another type of // failure if( !failed && !canceled && chunksSoFar < numChunks ) { inDownloadProgressHandler( MULTISOURCE_DOWNLOAD_FAILED, bytesSoFar, inProgressHandlerExtraArgument ); } fclose( outputFile ); } Cultivation_9+dfsg1_UnixSource/minorGems/network/p2pParts/protocolUtils.cpp0000640000175000017500000000644507721440564026177 0ustar pabspabs/* * Modification History * * 2003-August-12 Jason Rohrer * Created. * * 2003-August-22 Jason Rohrer * Added function for getting a token after reading. */ #include "minorGems/network/p2pParts/protocolUtils.h" #include "minorGems/util/log/AppLog.h" #include "minorGems/util/stringUtils.h" char *readStreamUpToTag( InputStream *inInputStream, char *inTag, int inMaxCharsToRead ) { char *readCharBuffer = new char[ inMaxCharsToRead + 1 ]; int numCharsRead = 0; char tagSeen = false; char readError = false; int tagLength = strlen( inTag ); // start with empty string readCharBuffer[ numCharsRead ] = '\0'; while( numCharsRead < inMaxCharsToRead && !tagSeen && !readError ) { long numRead = inInputStream->readByte( (unsigned char *) ( &( readCharBuffer[ numCharsRead ] ) ) ); if( numRead != 1 ) { readError = true; readCharBuffer[ numCharsRead ] = '\0'; } else { numCharsRead++; // terminate and check if buffer ends with our tag readCharBuffer[ numCharsRead ] = '\0'; if( numCharsRead > tagLength ) { char *possibleBodyStart = &( readCharBuffer[ numCharsRead - tagLength ] ); if( strcmp( possibleBodyStart, inTag ) == 0 ) { tagSeen = true; } } } } if( !readError && tagSeen ) { char *returnString = stringDuplicate( readCharBuffer ); delete [] readCharBuffer; return returnString; } else { char *message = autoSprintf( "Failed to find end tag \"%s\", read %d characters:\n%s\n", inTag, numCharsRead, readCharBuffer ); AppLog::info( "readStreamUpToTag", message ); delete [] message; delete [] readCharBuffer; return NULL; } } char *readStreamUpToTagAndGetToken( InputStream *inInputStream, char *inTag, int inMaxCharsToRead, int inTokenNumber ) { // read the string char *readString = readStreamUpToTag( inInputStream, inTag, inMaxCharsToRead ); if( readString == NULL ) { return NULL; } SimpleVector *readTokens = tokenizeString( readString ); delete [] readString; // second token should be their key char *selectedToken = NULL; int numTokens = readTokens->size(); if( numTokens > inTokenNumber ) { selectedToken = stringDuplicate( *( readTokens->getElement( inTokenNumber ) ) ); } else { char *message = autoSprintf( "Looking for token %d, but only %d tokens available\n", inTokenNumber, numTokens ); AppLog::error( "readStreamUpToTagAndGetToken", message ); } for( int i=0; igetElement( i ) ); } delete readTokens; // will be NULL if not enough tokens read return selectedToken; } Cultivation_9+dfsg1_UnixSource/minorGems/network/SocketServer.h0000640000175000017500000000347107554101513023652 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2002-March-27 Jason Rohrer * Added a timeout to accept. */ #include "minorGems/common.h" #ifndef SOCKET_SERVER_CLASS_INCLUDED #define SOCKET_SERVER_CLASS_INCLUDED #include #include "Socket.h" /** * Network socket server that listens for connections on a certain port. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class SocketServer { public: /** * Constructs a SocketServer and begins listening for connections. * * @param inPort the port to listen on. * @param inMaxQueuedConnections the number of connection requests * that will be queued before further requests are refused. */ SocketServer( int inPort, int inMaxQueuedConnections ); ~SocketServer(); /** * Accepts a an incoming connection on our port. * * @param inTimeoutInMilliseconds the timeout in milliseconds, * or -1 for no timeout. Defaults to -1. * @param outTimedOut pre-allocated char where timeout * flag will be returned. If non-NULL, true will * be inserted upon timeout, and false will be inserted * upon other error or no error/timeout. * Must be destroyed by caller if non-NULL. * Defaults to NULL. * * @return a socket for the accepted connection, * or NULL if a socket error/timeout occurred. */ Socket *acceptConnection( long inTimeoutInMilliseconds = -1, char *outTimedOut = NULL ); private: /** * Used by platform-specific implementations. */ void *mNativeObjectPointer; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/socketTestCompile0000750000175000017500000000065710157336507024461 0ustar pabspabsg++ -g -o socketTest -I../.. socketTest.cpp linux/SocketLinux.cpp linux/SocketClientLinux.cpp linux/SocketServerLinux.cpp linux/HostAddressLinux.cpp ../system/linux/MutexLockLinux.cpp NetworkFunctionLocks.cpp g++ -g -o socketClientTest -I../.. socketClientTest.cpp linux/SocketLinux.cpp linux/SocketClientLinux.cpp linux/SocketServerLinux.cpp linux/HostAddressLinux.cpp ../system/linux/MutexLockLinux.cpp NetworkFunctionLocks.cpp Cultivation_9+dfsg1_UnixSource/minorGems/network/linux/0000750000175000017500000000000011401021060022173 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/network/linux/gnut_net.c0000640000175000017500000002560107552330061024207 0ustar pabspabs/* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ #include #include #include #ifndef WIN32 #include #include #include #include #include #include #include #include #include #endif #include #include #include #include #include #include #include #include "gnut_lib.h" #include "gnut_threads.h" #include "gnut_if.h" #include "protocol.h" uchar local_ip[4]; // uint32 net_local_ip() // // returns the local ip address // uchar *net_local_ip() { return local_ip; } int gnut_net_host() { char host[256]; struct hostent *he; int ret; g_debug(2,"entering\n"); ret=gethostname(host,sizeof(host)); if (ret<0) { perror("gnut_net_host, gethostname"); return ret; } he=gethostbyname(host); if (he==NULL) { perror("gnut_net_host, gethostbyname"); return ret; } memcpy(local_ip,he->h_addr_list[0],4); return 0; } // int net_init(char *iface) // // Does misc. net init stuff. Mostly just finds our local IP address // int net_init(char *iface) { struct sockaddr_in *sinptr=NULL; g_debug(2,"entering\n"); if (iface!=NULL) { if (inet_aton(iface,(struct in_addr *) local_ip)) { g_debug(1,"local was set by command to: %i.%i.%i.%i\n",local_ip[0],local_ip[1], local_ip[2],local_ip[3]); return 0; } } if ((sinptr = get_if_addr(iface))==NULL) { g_debug(1,"Can't get local IP address through interface, trying host name...\n"); gnut_net_host(); } else { memcpy(local_ip,&(sinptr->sin_addr),sizeof(local_ip)); } g_debug(1,"local ip is: %i.%i.%i.%i\n",local_ip[0],local_ip[1], local_ip[2],local_ip[3]); return 0; } // exactly like the real read, except that it returns -2 // if no data was read before the timeout occurred... int timed_read(int sock, char *buf, int len, int secs) { fd_set fsr; struct timeval tv; int ret; ret=fcntl(sock,F_SETFL,O_NONBLOCK); FD_ZERO(&fsr); FD_SET(sock,&fsr); tv.tv_sec=secs; tv.tv_usec=0; ret=select(sock+1,&fsr,NULL,NULL,&tv); if (ret==0) { return -2; } if (ret<0) return ret; ret=read(sock,buf,len); fcntl(sock,F_SETFL,0); return ret; } // int timed_connect(int sock, struct sockaddr *sa,int addrlen,int secs) // // just like connect except that it times out after time secs // returns -2 on timeout, otherwise same as connect // int timed_connect(int sockfd, struct sockaddr *sa, int addrlen,int secs) { int ret; fd_set fsr; struct timeval tv; int val,len; g_debug(1,"entering sock=%i secs=%i\n",sockfd,secs); ret=fcntl(sockfd,F_SETFL,O_NONBLOCK); g_debug(5,"fcntl returned %i\n",ret); if (ret<0) return ret; ret=connect(sockfd,sa,addrlen); g_debug(5,"connect returned %i\n",ret); if (ret==0) { g_debug(0,"immediate connection!\n"); // wooah! immediate connection return -2; } // if (errno != EINPROGRESS) { // perror("timed_connect, connect"); // return ret; // } FD_ZERO(&fsr); FD_SET(sockfd,&fsr); tv.tv_sec=secs; tv.tv_usec=0; ret=select(sockfd+1,NULL,&fsr,NULL,&tv); g_debug(5,"select returned %i\n",ret); if (ret==0) { // timeout g_debug(1,"timeout\n"); fcntl(sockfd,F_SETFL,0); return -2; } len=4; ret=getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&val,&len); g_debug(5,"getsockopt returned %i val=%i\n",ret,val); if (ret<0) { g_debug(1,"getsockopt returned: %i\n",ret); return ret; } if (val!=0) { g_debug(3,"returning failure!\n"); return -1; } ret=fcntl(sockfd,F_SETFL,0); g_debug(1,"fcntl: %i\n",ret); g_debug(3,"returning success val=%i\n",val); return 0; } // int create_listen_socket(int * port) // // attempts to create a socket to listen on port // returns the actual port bound to in port or <0 if not // successfull // int create_listen_socket(int *port) { struct sockaddr_in sin; int sock; int ret,i; int val; g_debug(3,"entering\n"); sock=socket(AF_INET,SOCK_STREAM,6); if (sock<0) { perror("create_listen_socket, socket"); return sock; } val=1; ret=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val)); i=0; ret=-1; while (ret<0) { sin.sin_addr.s_addr=INADDR_ANY; sin.sin_port=htons(*port); sin.sin_family=AF_INET; ret=bind(sock,(struct sockaddr *) &sin,sizeof(struct sockaddr_in)); if (++i>50) { (*port)++; i=0; } } if (ret<0) { perror("create_listen_socket, bind"); return ret; } ret=listen(sock,5); if (ret<0) { perror("create_listen_socket, listen"); return ret; } g_debug(1,"bound to port %i\n",*port); printf("Bound to port: %i\n",*port); ret=fcntl(sock,F_SETFL,O_NONBLOCK); if (ret<0) { perror("create_listen_socket, fcntl"); return ret; } return sock; } // int read_line(int sock, char *buf, int len) // // reads a line from sock into buf, not exceeding len chars // returns 0 on EOF, -1 on error, or num of characters read // and -2 if buffer length exceeded // int read_line(int sock, char *buf, int len) { int i,ret; char c; char *ptr; ptr=buf; g_debug(3,"entering\n"); fcntl(sock,F_SETFL,0); buf[0]=0; for (i=1;i=1) perror("read_line, read"); return -1; } } buf[i]=0; g_debug(3,"returning success\n"); return i; } #ifndef PTHREADS_DRAFT4 pthread_mutex_t make_connection_mutex=PTHREAD_MUTEX_INITIALIZER; #else pthread_mutex_t make_connection_mutex; #endif struct hostent *gnut_hostent_copy(struct hostent *he) { struct hostent *n; int i,len; if (!he) return NULL; n=(struct hostent *) calloc(sizeof(struct hostent),1); memcpy(n,he,sizeof(struct hostent)); if (he->h_name) n->h_name=strdup(he->h_name); for (len=0;he->h_addr_list[len];len++); n->h_addr_list=(char **) calloc(sizeof(char *) * (len+1),1); for (i=0;ih_addr_list[i]=xmalloc(4); memcpy(n->h_addr_list[i],he->h_addr_list[i],4); } return n; } void gnut_hostent_delete(struct hostent *he) { int i,len; g_debug(1,"entering\n"); for (len=0;he->h_addr_list[len];len++); g_debug(1,"len=%i\n",len); for (i=0;ih_addr_list[i]) free(he->h_addr_list[i]); } g_debug(1,"freeing h_name\n"); if (he->h_name) free(he->h_name); g_debug(1,"freeing h_addr_list\n"); free(he->h_addr_list); g_debug(1,"freeing he\n"); free(he); g_debug(1,"returning\n"); } // int make_connection(char *host, uint port, uchar ip[4]) // // creates a socket, and attempts to make a connection to the host // named. it tries multiple resolved ip addresses if necessary, // returning the socket on success and storing the good ip number in ip // otherwise it returns <0 // // Notes: due to the fact that gethostbyname is not thread safe // we need to protect this function with a mutex // int make_connection(uchar *host, uint port, uchar ip[4]) { struct hostent *he; struct sockaddr_in sin; int i,sock=-1,ret; g_debug(1,"entering\n"); pthread_mutex_lock(&make_connection_mutex); he=gnut_hostent_copy(gethostbyname(host)); if (he==NULL) { pthread_mutex_unlock(&make_connection_mutex); return -1; } pthread_mutex_unlock(&make_connection_mutex); for (i=0,ret=-1;he->h_addr_list[i]!=NULL && ret<0;i++) { g_debug(1,"trying host %i.%i.%i.%i:%i\n", (uchar) he->h_addr_list[i][0],(uchar) he->h_addr_list[i][1], (uchar) he->h_addr_list[i][2],(uchar) he->h_addr_list[i][3],port); sock=socket(AF_INET,SOCK_STREAM,6); if (sock<0) { perror("make_connection, socket"); gnut_hostent_delete(he); return sock; } sin.sin_family=AF_INET; sin.sin_port=htons(port); memcpy(&sin.sin_addr.s_addr,he->h_addr_list[i],4); ret=timed_connect(sock,(struct sockaddr *) &sin, sizeof(struct sockaddr_in),10); g_debug(5,"timed_connect returned: %i\n",ret); if (ret<0) { g_debug(1,"host bad, closing\n"); close(sock); } else { break; } } if (ret<0 || he->h_addr_list[i]==NULL) { g_debug(1,"returning failure\n"); gnut_hostent_delete(he); return -2; } g_debug(5,"about to copy ip from slot %i\n",i); memcpy(ip,he->h_addr_list[i],4); g_debug(4,"trying to unlock mutex\n"); gnut_hostent_delete(he); g_debug(1,"returning success\n"); return sock; } // int send_packet (int sock, gnutella_packet *gpa) // // sends the packet described by gpa over socket sock // return 0 on success or <0 for error // int send_packet(int sock, gnutella_packet *gpa) { int ret; int t; g_debug(3,"entering\n"); ret=write(sock,(char *) &gpa->gh,sizeof(gnutella_hdr)); if (ret<0) { if (gnut_lib_debug>3) perror("send_packet, write header"); return ret; } memcpy(&t,gpa->gh.dlen,4); t=GUINT32_FROM_LE(t); if (t>0) { ret=write(sock,gpa->data,t); if (ret<0) { if (gnut_lib_debug>3) perror("send_packet, write data"); return ret; } } g_debug(3,"returning success\n"); return 23+t; } // int read_packet(int sock, gnutella_packet *gpa) // // reads a packet from the socket sock into the packet // structure of gpa. // returns 0 on success or <0 on error // int read_packet(int sock, gnutella_packet *gpa) { int ret; char *ptr; int left; uint dlen; int i; g_debug(3,"entering\n"); ptr=(char *) &gpa->gh; for (left=sizeof(gnutella_hdr);left>0;) { ret=timed_read(sock,ptr,left,10); g_debug(6,"timed_read returned: %i\n",ret); if (ret==-2) return 0; if (ret==0) { return -2; } else if (ret<0) { if (errno!=EINTR) { if (gnut_lib_debug>3) perror("read_packet, header"); return ret; } else ret=0; } ptr+=ret; left-=ret; } assert(left==0); memcpy(&dlen,gpa->gh.dlen,4); dlen=GUINT32_FROM_LE(dlen); while (dlen>32767 || (gpa->gh.func==129 && dlen>65536)) { // We're out of sync! I'm going to do large reads until // one returns 23, which is probably a ping packet.... g_debug(2,"out of sync! func=%i dlen=%i\n",gpa->gh.func,dlen); ptr=(char *) xmalloc(100); ret=1; i=0; while (ret>0 && ret!=23 && ret!=49) { ret=timed_read(sock,ptr,60,2); g_debug(6,"timed_read returned: %i\n",ret); if (ret==-2 || (++i>5)) { free(ptr); return 0; } } if (ret<=0) { free(ptr); return ret; } free(ptr); memcpy(&gpa->gh,ptr,23); memcpy(&dlen,gpa->gh.dlen,4); dlen=GUINT32_FROM_LE(dlen); } if (dlen>0) { gpa->data=(char *) calloc(dlen,1); ptr=gpa->data; for (left=dlen;left>0;) { ret=timed_read(sock,ptr,left,10); g_debug(6,"timed_read returned: %i\n",ret); if (ret==-2) return 0; if (ret==0) return -2; if (ret<0) { if (gnut_lib_debug>3) perror("read_packet, data"); return ret; } ptr+=ret; left-=ret; } } if (dlen>3000) g_debug(2,"OVERSIZE packet! size: %i\n",dlen); g_debug(3,"returning success\n"); return 23+dlen; } Cultivation_9+dfsg1_UnixSource/minorGems/network/linux/socketServerTimeoutTest.compile0000750000175000017500000000016007455745246030474 0ustar pabspabsg++ -g -o socketServerTimeoutTest socketServerTimeoutTest.cpp -I../../.. SocketServerLinux.cpp SocketLinux.cpp Cultivation_9+dfsg1_UnixSource/minorGems/network/linux/SocketClientLinux.cpp0000640000175000017500000002034211327623675026343 0ustar pabspabs/* * Modification History * * 2001-January-10 Jason Rohrer * Created. * * 2001-January-15 Jason Rohrer * Commented out a redundant print statement (it's better if these kinds * of messages are handled at a higher level). * * 2001-January-28 Jason Rohrer * Changed to comply with new initSocketFramework Socket interface. * * 2001-January-29 Jason Rohrer * Fixed an endian bug with the port number. * * 2001-November-13 Jason Rohrer * Added a missing include. * * 2001-December-12 Jason Rohrer * Changed the include order to make BSD compatible. * * 2002-August-5 Jason Rohrer * Removed use of obsolete call. * * 2002-October-13 Jason Rohrer * Added support for timeout on connect. * * 2002-October-17 Jason Rohrer * Added missing include. * Added support for BSD's version of getsockopt. * * 2002-Novenber-27 Jason Rohrer * Changed to print error number. There's a bug that needs to be fixed. * * 2002-December-2 Jason Rohrer * Fixed resource leak when connection fails. * * 2003-March-18 Jason Rohrer * Fixed a thread safety issue with gethostbyname. * * 2004-January-2 Jason Rohrer * Fixed a memory leak when host name lookup fails. * * 2004-January-4 Jason Rohrer * Added use of network function locks. * * 2006-April-25 Jason Rohrer * Added missing check for a NULL outTimedOut parameter. * * 2008-September-30 Jason Rohrer * Added support for non-blocking connect. * * 2009-April-3 Jason Rohrer * OpenBSD support. * * 2009-June-15 Jason Rohrer * Fixed bug when connect timeout not used. * * 2010-January-26 Jason Rohrer * Fixed socklen_t on later versions of MacOSX. */ #include "minorGems/network/SocketClient.h" #include "minorGems/network/NetworkFunctionLocks.h" #include "minorGems/system/MutexLock.h" #include #include #include #include #include #include #include #include #include #include #include // BSD does not define socklen_t #ifdef BSD #ifndef IPHONE #ifndef __OpenBSD__ #ifndef _SOCKLEN_T // later versions of MacOS define it and mark it typedef int socklen_t; #endif #endif #endif #endif // prototypes struct in_addr *nameToAddress( char *inAddress ); int timed_connect( int inSocketID, struct sockaddr *inSocketAddress, int inAddressLength, int inTimeoutInMilliseconds ); Socket *SocketClient::connectToServer( HostAddress *inAddress, long inTimeoutInMilliseconds, char *outTimedOut ) { if( !Socket::isFrameworkInitialized() ) { // try to init the framework int error = Socket::initSocketFramework(); if( error == -1 ) { printf( "initializing network socket framework failed\n" ); return NULL; } } int socketID = socket( AF_INET, SOCK_STREAM, 0 ); if( socketID == -1 ) { printf( "Creating socket failed, error %d.\n", errno ); return NULL; } union sock { struct sockaddr s; struct sockaddr_in i; } sock; struct in_addr *internet_address = nameToAddress( inAddress->mAddressString ); if( internet_address == NULL ) { printf( "Host name lookup failed: " ); inAddress->print(); printf( "\n" ); close( socketID ); return NULL; } sock.i.sin_family = AF_INET; sock.i.sin_port = htons( inAddress->mPort ); sock.i.sin_addr = *internet_address; int error; if( inTimeoutInMilliseconds != -1 ) { // use timeout error = timed_connect( socketID, &sock.s, sizeof( struct sockaddr ), inTimeoutInMilliseconds ); if( outTimedOut != NULL ) { if( error == -2 ) { *outTimedOut = true; if( inTimeoutInMilliseconds == 0 ) { // caller didn't want to wait at all error = 0; } else { // treat timeout as error error = -1; } } else { *outTimedOut = false; } } } else { // don't use timeout error = connect( socketID, &sock.s, sizeof( struct sockaddr ) ); } delete internet_address; if( error == -1 ) { //printf( "Connecting to host failed: " ); //inAddress->print(); //printf( "\n" ); close( socketID ); return NULL; } // package into a Socket and return it Socket *returnSocket = new Socket(); int *idSpace = new int[1]; idSpace[0] = socketID; returnSocket->mNativeObjectPointer = (void *)idSpace; if( outTimedOut != NULL && *outTimedOut ) { // not connected yet returnSocket->setConnected( false ); } return returnSocket; } /* Converts ascii text to in_addr struct. NULL is returned if the address can not be found. Result must be destroyed by caller. Adapted from the Unix Socket FAQ */ struct in_addr *nameToAddress( char *inAddress ) { struct hostent *host; static struct in_addr saddr; struct in_addr *copiedSaddr = new struct in_addr; /* First try it as aaa.bbb.ccc.ddd. */ // this is obsolete on linux // saddr.s_addr = inet_addr( inAddress ); int error = inet_aton( inAddress, &saddr ); if( error != 0 ) { // copy to avoid returning pointer to stack memcpy( copiedSaddr, &saddr, sizeof( struct in_addr ) ); return copiedSaddr; } // must keep this locked until we are done copying the in_addr out // of the returned hostent NetworkFunctionLocks::mGetHostByNameLock.lock(); char hostFound = false; host = gethostbyname( inAddress ); if( host != NULL ) { memcpy( copiedSaddr, *host->h_addr_list, sizeof( struct in_addr ) ); hostFound = true; } NetworkFunctionLocks::mGetHostByNameLock.unlock(); if( hostFound ) { return copiedSaddr; } else { delete copiedSaddr; } return NULL; } /* timed_connect adapted from gnut, by Josh Pieper */ /* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ // just like connect except that it times out after time secs // returns -2 on timeout, otherwise same as connect int timed_connect( int inSocketID, struct sockaddr *inSocketAddress, int inAddressLength, int inTimeoutInMilliseconds ) { int ret; fd_set fsr; struct timeval tv; int val; socklen_t len; //g_debug(1,"entering sock=%i secs=%i\n",inSocketID,inTimeoutInSeconds); ret = fcntl( inSocketID, F_SETFL, O_NONBLOCK ); //g_debug(5,"fcntl returned %i\n",ret); if( ret < 0 ) { return ret; } ret = connect( inSocketID, inSocketAddress, inAddressLength ); //g_debug(5,"connect returned %i\n",ret); if( ret == 0 ) { //g_debug(0,"immediate connection!\n"); // wooah! immediate connection // return -2; // changed from what Josh originally returned (-2) // immediate connection *can* happen sometimes, right? // for example, when connecting to localhost... return 0; } // if (errno != EINPROGRESS) { // perror("timed_connect, connect"); // return ret; // } FD_ZERO( &fsr ); FD_SET( inSocketID, &fsr ); tv.tv_sec = inTimeoutInMilliseconds / 1000; int remainder = inTimeoutInMilliseconds % 1000; tv.tv_usec = remainder * 1000; ret = select( inSocketID+1, NULL, &fsr, NULL, &tv ); //g_debug(5,"select returned %i\n",ret); if( ret==0 ) { // timeout //g_debug(1,"timeout\n"); fcntl( inSocketID, F_SETFL, 0 ); return -2; } len = 4; ret = getsockopt( inSocketID, SOL_SOCKET, SO_ERROR, &val, &len ); //g_debug(5,"getsockopt returned %i val=%i\n",ret,val); if( ret < 0 ) { //g_debug(1,"getsockopt returned: %i\n",ret); return ret; } if (val!=0) { //g_debug(3,"returning failure!\n"); return -1; } ret = fcntl( inSocketID, F_SETFL, 0 ); //g_debug(1,"fcntl: %i\n",ret); //g_debug(3,"returning success val=%i\n",val); return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/network/linux/socketServerTimeoutTest.cpp0000640000175000017500000000226007455745246027627 0ustar pabspabs/* * Modification History * * 2002-April-12 Jason Rohrer * Created. */ #include "minorGems/network/Socket.h" #include "minorGems/network/SocketServer.h" #include void usage( char *inAppName ); int main( int inNumArgs, char **inArgs ) { if( inNumArgs != 2 ) { usage( inArgs[0] ); } int port; int numRead = sscanf( inArgs[1], "%d", &port ); if( numRead != 1 ) { usage( inArgs[0] ); } SocketServer *server = new SocketServer( port, 100 ); char timedOut; Socket *sock; printf( "waiting for connection on port %d\n", port ); sock = server->acceptConnection( 5000, &timedOut ); if( timedOut ) { printf( "timed out after 5 seconds, waiting again\n" ); sock = server->acceptConnection( 5000, &timedOut ); } if( !timedOut && sock != NULL ) { printf( "connection received\n" ); delete sock; } else { printf( "timed out again\n" ); } delete server; return 1; } void usage( char *inAppName ) { printf( "Usage:\n" ); printf( " %s port_number\n", inAppName ); exit( 1 ); } Cultivation_9+dfsg1_UnixSource/minorGems/network/linux/gnut_lib.c0000640000175000017500000000401707552330060024164 0ustar pabspabs/* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ #include #include #include #include #include #ifndef WIN32 #include #endif #ifdef MCHECK #include #endif #include "gnut_lib.h" #include "gnut_threads.h" int gnut_mprobe(void *d) { #ifdef MCHECK return mprobe(d); #else return 0; #endif } #ifndef HAVE_XMALLOC #ifndef xmalloc char *xmalloc(int size) { char *ptr; ptr=malloc(size); if (!ptr) { g_debug(0,"malloc(%d) failed!\n", size); exit(1); } return ptr; } #endif #endif char *expand_path(char *a) { char *b; if (strncmp(a,"~/", 2)==0) { #ifndef WIN32 char *c; c=getenv("HOME"); #else char c[MAX_PATH]; GetWindowsDirectory(c, MAX_PATH); #endif b=(char *)xmalloc(strlen(a)+strlen(c)+1); strcpy(b, c); strcat(b, &a[1]); return b; } b=(char *) xmalloc(strlen(a)+1); strcpy(b,a); return b; } int gnut_lib_debug=0; #ifndef PTHREADS_DRAFT4 pthread_mutex_t _g_debug_mutex=PTHREAD_MUTEX_INITIALIZER; #else pthread_mutex_t _g_debug_mutex; #endif #ifndef WIN32 void _g_debug(char *file, int line, int num, char *format, ...) { va_list argp; if (gnut_lib_debug>=num) { pthread_mutex_lock(&_g_debug_mutex); fprintf(stderr,"%i %s:%i > ", getpid(),file,line); fflush(stdout); va_start(argp,format); vfprintf(stderr,format,argp); va_end(argp); pthread_mutex_unlock(&_g_debug_mutex); } } #else void g_debug(int a, char *format, ...) { va_list argp; if (gnut_lib_debug>=a) { pthread_mutex_lock(&_g_debug_mutex); fprintf(stderr,"%li > ", (long) pthread_self()); va_start(argp,format); vfprintf(stderr,format,argp); va_end(argp); pthread_mutex_unlock(&_g_debug_mutex); } } #endif char *gnut_strdelimit(char *string, char *delim, char new_delim) { char *c; for (c=string;*c;c++) { if (strchr(delim,*c)) *c=new_delim; } return string; } Cultivation_9+dfsg1_UnixSource/minorGems/network/linux/SocketServerLinux.cpp0000640000175000017500000001356311327623676026403 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-15 Jason Rohrer * Commented out redundant status messages that should be handled at a * higher level. * * 2001-January-28 Jason Rohrer * Changed to comply with new initSocketFramework Socket interface. * * 2001-January-29 Jason Rohrer * Fixed an endian bug with the port number. * * 2001-May-12 Jason Rohrer * Added a port number to the "bad socket bind" error message. * * 2001-November-13 Jason Rohrer * Added a missing include. * * 2001-December-12 Jason Rohrer * Changed some type usage and the include order to make BSD compatible. * Added a BSD definition test to make socklen_t type compatible * on both BSD and Linux. * * 2002-March-27 Jason Rohrer * Added a timeout to accept. * * 2002-March-29 Jason Rohrer * Fixed a bug in return value when accept fails. * * 2002-April-11 Jason Rohrer * Changed to use select instead of poll to make it more portable. * Fixed a bug in the timeout implementation. * * 2002-April-12 Jason Rohrer * Changed to use poll on Linux and select on BSD, * since select does not seem to work on LinuxX86. * Fixed X86 accept/select bug by getting rid of union * and some unused parameters (it seems that stack space was a problem). * * 2004-February-23 Jason Rohrer * Added socket option to clear up lingering bound ports after shutdown. * Changed to close server socket instead of using shutdown. * * 2010-January-26 Jason Rohrer * Fixed socklen_t on later versions of MacOSX. */ #include "minorGems/network/SocketServer.h" #include #include #include #include #include #include #include // BSD does not define socklen_t #ifdef BSD #ifndef IPHONE #ifndef __OpenBSD__ #ifndef _SOCKLEN_T // later versions of MacOS define it and mark it typedef int socklen_t; #endif #endif #endif #endif #ifndef BSD // poll not available on BSD #include #endif /** * BSD Compilation note: * * Use g++ option -DBSD to define the BSD preprocessor variable. */ SocketServer::SocketServer( int inPort, int inMaxQueuedConnections ) { int error = 0; if( !Socket::isFrameworkInitialized() ) { // try to init the framework int error = Socket::initSocketFramework(); if( error == -1 ) { printf( "initializing network socket framework failed\n" ); exit( 1 ); } } // create the socket int sockID = socket( AF_INET, SOCK_STREAM, 0 ); if( sockID == -1 ) { printf( "Failed to construct a socket\n" ); exit( 1 ); } // store socket id in native object pointer int *idStorage = new int[1]; idStorage[0] = sockID; mNativeObjectPointer = (void *)idStorage; // this setsockopt code partially copied from gnut // set socket option to enable reusing addresses so that the socket is // unbound immediately when the server is shut down // (otherwise, rebinding to the same port will fail for a while // after the server is shut down) int reuseAddressValue = 1; error = setsockopt( sockID, SOL_SOCKET, // socket-level option SO_REUSEADDR, // reuse address option &reuseAddressValue, // value to set for this option sizeof( reuseAddressValue) ); // size of the value if( error == -1 ) { printf( "Failed to set socket options\n" ); exit( 1 ); } // bind socket to the port struct sockaddr_in address; address.sin_family = AF_INET; address.sin_port = htons( inPort ); address.sin_addr.s_addr = INADDR_ANY; error = bind( sockID, (struct sockaddr *) &address, sizeof( address ) ); if( error == -1 ) { printf( "Bad socket bind, port %d\n", inPort ); exit( 1 ); } // start listening for connections error = listen( sockID, inMaxQueuedConnections ); if( error == -1 ) { printf( "Bad socket listen\n" ); exit(1); } } SocketServer::~SocketServer() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; close( socketID ); delete [] socketIDptr; } Socket *SocketServer::acceptConnection( long inTimeoutInMilliseconds, char *outTimedOut ) { // printf( "Waiting for a connection.\n" ); // extract socket id from native object pointer int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( outTimedOut != NULL ) { *outTimedOut = false; } if( inTimeoutInMilliseconds != -1 ) { // if we have a timeout specified, select before accepting // this found in the Linux man page for select, // but idea (which originally used poll) was found // in the Unix Socket FAQ fd_set rfds; struct timeval tv; int retval; // insert our socket descriptor into this set FD_ZERO( &rfds ); FD_SET( socketID, &rfds ); // convert our timeout into the structure's format tv.tv_sec = inTimeoutInMilliseconds / 1000; tv.tv_usec = ( inTimeoutInMilliseconds % 1000 ) * 1000 ; retval = select( socketID + 1, &rfds, NULL, NULL, &tv ); if( retval == 0 ) { // timeout if( outTimedOut != NULL ) { *outTimedOut = true; } return NULL; } } int acceptedID = accept( socketID, NULL, NULL ); if( acceptedID == -1 ) { printf( "Failed to accept a network connection.\n" ); return NULL; } else { Socket *acceptedSocket = new Socket(); int *idStorage = new int[1]; idStorage[0] = acceptedID; acceptedSocket->mNativeObjectPointer = (void *)idStorage; //printf( "Connection received.\n" ); return acceptedSocket; } } Cultivation_9+dfsg1_UnixSource/minorGems/network/linux/HostAddressLinux.cpp0000640000175000017500000001115011364473137026171 0ustar pabspabs/* * Modification History * * 2001-December-14 Jason Rohrer * Created. * Fixed a typo. * Added a missing include. * * 2002-March-25 Jason Rohrer * Added function for conversion to numerical addresses. * * 2002-March-26 Jason Rohrer * Changed to use strdup. * Fixed problem on some platforms where local address does * not include domain name. * Fixed so that numerical address is actually retrieved by * getNumericalAddress(). * * 2002-April-6 Jason Rohrer * Replaced use of strdup. * * 2002-April-8 Jason Rohrer * Changed to return numerical address from getLocalAddress, * since it is simpler, and we probably want the numerical * address anyway. * This new implementation also fixes a BSD domain-name lookup bug. * Changed to check if address is numerical before * performing a lookup on it. * * 2002-July-10 Jason Rohrer * Fixed a bug that occurs when fetching numerical address fails. * * 2004-January-4 Jason Rohrer * Added use of network function locks. * * 2009-December-22 Jason Rohrer * New method of getting local address. * * 2010-April-23 Jason Rohrer * ifa->if_addr can be NULL. */ #include "minorGems/network/HostAddress.h" #include "minorGems/network/NetworkFunctionLocks.h" #include "minorGems/util/stringUtils.h" #include #include #include #include #include #include #include HostAddress *HostAddress::getLocalAddress() { int bufferLength = 200; char *buffer = new char[ bufferLength ]; // old method, tended to return a 127.0.0.1 address //gethostname( buffer, bufferLength ); // new method, iterate through interfaces struct ifaddrs *ifaddr, *ifa; if( getifaddrs( &ifaddr ) == -1 ) { delete [] buffer; return NULL; } // Walk through linked list, maintaining head pointer so we // can free list later // stop when we find an addr other than 127.0.0.1 char found = false; for( ifa = ifaddr; ifa != NULL && !found; ifa = ifa->ifa_next) { // only consider IPv4 // NULL check, too, because on at least one VPN, NULL // addresses are present in list? if( ifa->ifa_addr != NULL && ifa->ifa_addr->sa_family == AF_INET ) { int s = getnameinfo( ifa->ifa_addr, sizeof( struct sockaddr_in ), buffer, bufferLength, NULL, 0, NI_NUMERICHOST ); if( s != 0 ) { printf("getnameinfo() failed: %s\n", gai_strerror( s ) ); delete [] buffer; return NULL; } if( strcmp( buffer, "127.0.0.1" ) != 0 ) { found = true; } } } freeifaddrs(ifaddr); if( found ) { char *numerical = stringDuplicate( buffer ); delete [] buffer; // this class will destroy name for us HostAddress *numericalAddress = new HostAddress( numerical, 0 ); return numericalAddress; } else { delete [] buffer; return NULL; } } HostAddress *HostAddress::getNumericalAddress() { // make sure we're not already numerical if( this->isNumerical() ) { return this->copy(); } // need to use two function locks here // first, lock for gethostbyname NetworkFunctionLocks::mGetHostByNameLock.lock(); struct hostent *host = gethostbyname( mAddressString ); if (host != NULL) { // this line adapted from the // Unix Socket FAQ struct in_addr *inetAddress = (struct in_addr *) *host->h_addr_list; // now lock for inet_ntoa NetworkFunctionLocks::mInet_ntoaLock.lock(); // the returned string is statically allocated, so copy it char *inetAddressString = stringDuplicate( inet_ntoa( *inetAddress ) ); NetworkFunctionLocks::mInet_ntoaLock.unlock(); // done with returned hostent now, so unlock first lock NetworkFunctionLocks::mGetHostByNameLock.unlock(); return new HostAddress( inetAddressString, mPort ); } else { // unlock first lock before returning NetworkFunctionLocks::mGetHostByNameLock.unlock(); return NULL; } } char HostAddress::isNumerical() { struct in_addr addressStruct; int isValid = inet_aton( mAddressString, &addressStruct ); if( isValid == 0 ) { return false; } else { return true; } } Cultivation_9+dfsg1_UnixSource/minorGems/network/linux/socketSelectAcceptBug.cpp0000640000175000017500000000600707455757734027157 0ustar pabspabs/* * Demonstration of "accept after select" socket bug. * * This program works fine on: * LinuxPPC, FreeBSD, and MacOSX * * This program does not work on: * LinuxX86 Kernel 2.4 * (accept fails after select returns) * * This bug has something to do with the size of the * variables on the stack... * Getting rid of the union (which is unnecessary) seems * to fix the problem. * * * Jason Rohrer * 2002-April-12 * rohrer@cse.ucsc.edu */ #include #include #include #include #include #include #include #include union sock { struct sockaddr s; struct sockaddr_in i; } sock; void usage( char *inAppName ); int acceptConnection( int inSocketID, long inTimeoutMS ); int port; int main( int inNumArgs, char **inArgs ) { if( inNumArgs != 2 ) { usage( inArgs[0] ); } int numRead = sscanf( inArgs[1], "%d", &port ); // create a socket and start listening for connections printf( "Creating socket\n" ); int socketID = socket( AF_INET, SOCK_STREAM, 0 ); if( socketID == -1 ) { printf( "Socket creation failed\n" ); exit( 1 ); } // bind socket to the port union sock sockAddress; sockAddress.i.sin_family = AF_INET; sockAddress.i.sin_port = htons( port ); sockAddress.i.sin_addr.s_addr = INADDR_ANY; printf( "Binding to socket at port %d\n", port ); int error = bind( socketID, &(sockAddress.s), sizeof( struct sockaddr ) ); if( error == -1 ) { printf( "Bad socket bind, port %d\n", port ); exit( 1 ); } // start listening for connections, max queued = 100 connections printf( "Starting to listen to socket\n" ); error = listen( socketID, 100 ); if( error == -1 ) { printf( "Bad socket listen\n" ); exit(1); } // no we're listening to this socket int acceptedID = acceptConnection( socketID, 50000 ); if( acceptedID != -1 ) { // close the connection shutdown( acceptedID, SHUT_RDWR ); close( acceptedID ); } shutdown( socketID, SHUT_RD ); return 1; } int acceptConnection( int inSocketID, long inTimeoutMS ) { // use select to timeout if no connection received in 10 seconds struct timeval tv; socklen_t addressLength; union sock acceptedAddress; int acceptedID = accept( inSocketID, &( acceptedAddress.s ), &addressLength ); if( acceptedID == -1 ) { printf( "Failed to accept the connection\n" ); printf( "Error = %d\n", errno ); return -1; } else { printf( "Successfully accepted the connection\n" ); return acceptedID; } } void usage( char *inAppName ) { printf( "Usage:\n" ); printf( " %s port_number\n", inAppName ); exit( 1 ); } Cultivation_9+dfsg1_UnixSource/minorGems/network/linux/socketSelectAcceptBug.compile0000750000175000017500000000007107455750432030006 0ustar pabspabsg++ -g -o socketSelectAcceptBug socketSelectAcceptBug.cppCultivation_9+dfsg1_UnixSource/minorGems/network/linux/SocketLinux.cpp0000640000175000017500000002312311327661021025167 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-10 Jason Rohrer * Fixed a bug in the receive code (in use of timed_read). * * 2001-January-15 Jason Rohrer * Added a safeguard against receives that exceed the * network buffer size. * * 2001-January-28 Jason Rohrer * Changed to comply with new initSocketFramework Socket interface. * Added a close() call to the destructor. * * 2001-January-29 Jason Rohrer * Fixed compile bug by including unistd.h for close call. * * 2001-November-13 Jason Rohrer * Changed to use ::send function instead of __send function. * Changed timeout parameter to signed, since -1 is a possible argument. * * 2001-December-12 Jason Rohrer * Changed the include order to make BSD compatible. * * 2002-August-2 Jason Rohrer * Changed to ignore SIGPIPE. * Added functon for getting remote host address. * Added fix for linux-BSD differences. * * 2002-November-15 Jason Rohrer * Fixed a security hole when getting the remote host address. * * 2003-February-3 Jason Rohrer * Added a function for getting the local host address from a socket. * * 2003-February-21 Jason Rohrer * Fixed a BSD compatibility bug. * * 2004-January-4 Jason Rohrer * Added use of network function locks. * * 2004-March-23 Jason Rohrer * Removed timeout error message. * * 2004-December13 Jason Rohrer * Added a breakConnection function. * * 2005-July-5 Jason Rohrer * Added port number when getting address of remote host. * * 2006-May-28 Jason Rohrer * Changed timeout behavior slightly to support emulation of non-blocking mode. * Added support for non-blocking sends. * * 2006-June-5 Jason Rohrer * Fixed error checking for non-blocking sends. * * 2006-June-23 Jason Rohrer * Changed implementation of non-blocking sends to work on Mac. * * 2008-September-30 Jason Rohrer * Added support for non-blocking connect. * Fixed close-detection in non-blocking read. * * 2009-April-3 Jason Rohrer * OpenBSD support. * * 2010-January-26 Jason Rohrer * Fixed socklen_t on later versions of MacOSX. * * 2010-January-26 Jason Rohrer * Added support for disabling Nagle algorithm. */ #include "minorGems/network/Socket.h" #include "minorGems/network/NetworkFunctionLocks.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // BSD does not define socklen_t #ifdef BSD #ifndef IPHONE #ifndef __OpenBSD__ #ifndef _SOCKLEN_T // later versions of MacOS define it and mark it typedef int socklen_t; #endif #endif #endif #endif // prototypes int timed_read( int inSock, unsigned char *inBuf, int inLen, long inMilliseconds ); /** * Linux-specific implementation of the Socket class member functions. * * May also be compatible with other POSIX-like systems. * * To compile: * no special options needed */ char Socket::sInitialized = false; int Socket::initSocketFramework() { // ignore SIGPIPE, which occurs on send whenever the receiver // has closed the socket signal(SIGPIPE, SIG_IGN); sInitialized = true; return 0; } Socket::~Socket() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( !mIsConnectionBroken ) { shutdown( socketID, SHUT_RDWR ); mIsConnectionBroken = true; } close( socketID ); delete [] socketIDptr; } int Socket::isConnected() { if( mConnected ) { return 1; } int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; int ret; fd_set fsr; struct timeval tv; int val; socklen_t len; FD_ZERO( &fsr ); FD_SET( socketID, &fsr ); // check if connection event waiting right now // timeout of 0 tv.tv_sec = 0; tv.tv_usec = 0; ret = select( socketID + 1, NULL, &fsr, NULL, &tv ); if( ret==0 ) { // timeout return 0; } // no timeout // error? len = 4; ret = getsockopt( socketID, SOL_SOCKET, SO_ERROR, &val, &len ); if( ret < 0 ) { // error return -1; } if( val != 0 ) { // error return -1; } // success mConnected = true; return 1; } void Socket::setNoDelay( int inValue ) { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; int flag = inValue; setsockopt( socketID, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int) ); } int Socket::send( unsigned char *inBuffer, int inNumBytes, char inAllowedToBlock, char inAllowDelay ) { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( inAllowedToBlock ) { if( ! inAllowDelay ) { // turn nodelay on setNoDelay( 1 ); } int returnVal = ::send( socketID, inBuffer, inNumBytes, 0 ); if( ! inAllowDelay ) { // turn nodelay back off setNoDelay( 0 ); } return returnVal; } else { // set to non-blocking mode int result = fcntl( socketID, F_SETFL, O_NONBLOCK ); if( result < 0 ) { return result; } if( ! inAllowDelay ) { // turn nodelay on setNoDelay( 1 ); } int returnValue = ::send( socketID, inBuffer, inNumBytes, // no flags 0 ); if( ! inAllowDelay ) { // turn nodelay back off setNoDelay( 0 ); } // back into blocking mode result = fcntl( socketID, F_SETFL, 0 ); if( result < 0 ) { return result; } if( returnValue == -1 && errno == EAGAIN ) { return -2; } else { return returnValue; } } } int Socket::receive( unsigned char *inBuffer, int inNumBytes, long inTimeout ) { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( inTimeout == -1 ) { // use MSG_WAITALL flag here to block until inNumBytes has arrived return recv( socketID, inBuffer, inNumBytes, MSG_WAITALL ); } else { return timed_read( socketID, inBuffer, inNumBytes, inTimeout ); } } void Socket::breakConnection() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( !mIsConnectionBroken ) { shutdown( socketID, SHUT_RDWR ); mIsConnectionBroken = true; } close( socketID ); } HostAddress *Socket::getRemoteHostAddress() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; // adapted from Unix Socket FAQ socklen_t len; struct sockaddr_in sin; len = sizeof sin; int error = getpeername( socketID, (struct sockaddr *) &sin, &len ); if( error ) { return NULL; } // this is potentially insecure, since a fake DNS name might be returned // we should use the IP address only // // struct hostent *host = gethostbyaddr( (char *) &sin.sin_addr, // sizeof sin.sin_addr, // AF_INET ); NetworkFunctionLocks::mInet_ntoaLock.lock(); // returned string is statically allocated, copy it char *ipAddress = stringDuplicate( inet_ntoa( sin.sin_addr ) ); NetworkFunctionLocks::mInet_ntoaLock.unlock(); int port = ntohs( sin.sin_port ); return new HostAddress( ipAddress, port ); } HostAddress *Socket::getLocalHostAddress() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; // adapted from GTK-gnutalla code, and elsewhere struct sockaddr_in addr; socklen_t len = sizeof( struct sockaddr_in ); int result = getsockname( socketID, (struct sockaddr*)( &addr ), &len ); if( result == -1 ) { return NULL; } else { char *stringAddress = inet_ntoa( addr.sin_addr ); return new HostAddress( stringDuplicate( stringAddress ), 0 ); } } /* timed_read adapted from gnut, by Josh Pieper */ /* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ // exactly like the real read, except that it returns -2 // if no data was read before the timeout occurred... int timed_read( int inSock, unsigned char *inBuf, int inLen, long inMilliseconds ) { fd_set fsr; struct timeval tv; int ret; //ret = fcntl( inSock, F_SETFL, O_NONBLOCK ); FD_ZERO( &fsr ); FD_SET( inSock, &fsr ); tv.tv_sec = inMilliseconds / 1000; int remainder = inMilliseconds % 1000; tv.tv_usec = remainder * 1000; ret = select( inSock + 1, &fsr, NULL, NULL, &tv ); if( ret==0 ) { // printf( "Timed out waiting for data on socket receive.\n" ); return -2; } if( ret<0 ) { printf( "Selecting socket during receive failed.\n" ); return ret; } // do not use MSG_WAITALL flag here, since we just want to return // data that is available ret = recv( inSock, inBuf, inLen, 0 ); if( ret == 0 ) { // select came back as 1, but no data there // connection closed on remote end return -1; } //fcntl( inSock, F_SETFL, 0 ); return ret; } Cultivation_9+dfsg1_UnixSource/minorGems/network/linux/gnut_lib.h0000640000175000017500000000435207552330061024174 0ustar pabspabs/* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ #ifndef GNUT_LIB_H #define GNUT_LIB_H 0 #include #ifndef WIN32 #include #include #endif #ifdef DMALLOC #include #endif #ifndef MIN #define MIN(a,b) (((a < b)) ? (a) : (b)) #define MAX(a,b) (((a > b)) ? (a) : (b)) #endif #ifndef __bswap_16 #define __bswap_16(x) \ ((((x) >> 8) & 0xff) | (((x) &0xff) <<8)) #endif #ifndef __bswap_32 #define __bswap_32(x) \ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) #endif #ifndef GUINT16_TO_LE #ifdef WORDS_BIGENDIAN #define GUINT16_TO_LE(x) __bswap_16(x) #define GUINT32_TO_LE(x) __bswap_32(x) #define GUINT16_FROM_LE(x) __bswap_16(x) #define GUINT32_FROM_LE(x) __bswap_32(x) #else #define GUINT16_TO_LE(x) (x) #define GUINT32_TO_LE(x) (x) #define GUINT16_FROM_LE(x) (x) #define GUINT32_FROM_LE(x) (x) #endif #endif typedef unsigned int uint32; typedef unsigned short uint16; typedef unsigned char uchar; #ifndef WIN32 typedef unsigned long long uint64; //#define g_debug(num, format, args... ) #define g_debug(num, format, args... ) _g_debug(__FUNCTION__,__LINE__, num, format, ##args) void _g_debug(char *,int,int num, char *,...); #endif extern int gnut_lib_debug; int gnut_mprobe(void *); char *gnut_strdelimit(char *string, char *delimeters, char new_delim); char *expand_path(char *); #ifndef xmalloc char *xmalloc(int size); #endif #ifdef WIN32 #include #define VERSION "0.3.22/win32" void g_debug(int, char *, ...); typedef unsigned __int64 uint64; typedef unsigned int uint; #define sleep Sleep char *strtok_r(char *, const char *, char **); int getopt(int argc, char *argv[], char *); extern char *optarg; extern int optind; #define strncasecmp strnicmp #define inet_aton(string, ip) (ip)->s_addr = inet_addr(string) #define write(sock, buf, len) send(sock, buf, len, 0) #define read(sock, buf, len) recv(sock, buf, len, 0) #define close(sock) closesocket(sock) #define F_SETFL 1 #define O_NONBLOCK 1 int fcntl(int sock, int, uint); #define flock(a,b) 0 #endif #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/Socket.h0000640000175000017500000002304511327661010022456 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-28 Jason Rohrer * Added a static framework init function. * * 2001-November-13 Jason Rohrer * Changed timeout parameter to signed, since -1 is a possible argument. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-August-2 Jason Rohrer * Added functon for getting remote host address. * * 2003-February-3 Jason Rohrer * Added a function for getting the local host address from a socket. * * 2003-August-12 Jason Rohrer * Added more verbose comment about receive timeout parameter. * Added a function for flushing socket sends. * * 2003-November-20 Jason Rohrer * Made flush function robust against bogus receive return values. * * 2004-December13 Jason Rohrer * Added a breakConnection function. * * 2005-July-5 Jason Rohrer * Added port number when getting address of remote host. * * 2006-May-28 Jason Rohrer * Changed timeout behavior slightly to support emulation of non-blocking mode. * Added support for non-blocking sends. * * 2008-September-30 Jason Rohrer * Added support for non-blocking connect. * * 2010-January-26 Jason Rohrer * Added support for disabling Nagle algorithm. */ #include "minorGems/common.h" #ifndef SOCKET_CLASS_INCLUDED #define SOCKET_CLASS_INCLUDED #include "minorGems/network/HostAddress.h" #include "minorGems/system/Time.h" #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Network socket. Does not contain an interface for listening to connections * (see SocketServer for these interfaces) or for establishing connections * (see SocketClient for these interfaces). * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class Socket { public: /** * Constructor for a socket. * * Should not be called directly. Use SocketClient or SocketServer * to obtain an outbound or inbound socket connection. */ Socket(); // destroying a Socket closes the connection ~Socket(); /** * Initializes the socket framework. Must be called * once by program before sockets are used. Note * that SocketClient and SocketServer both should call * init automatically when they are first used. * * @return 0 on success, or -1 on failure. */ static int initSocketFramework(); /** * Gets whether the socket framework has been initialized. * * @return true if the framework has been initialized. */ static char isFrameworkInitialized(); /** * For outbound sockets that were opened in non-blocking mode: * * Get whether socket is connected yet. * * @return 1 on success, 0 on still waiting, -1 on error. */ int isConnected(); /** * Sends bytes through this socket. * * @param inBuffer the buffer of bytes to send. * @param inNumBytes the number of bytes to send. * @param inAllowedToBlock set to false to prevent blocking. * Defaults to true. * @param inAllowDelay set to false to disable Nagle algorithm, * where a buffer should be sent NOW without waiting for * more data accumulation. * Defaults to true. * * @return the number of bytes sent successfully, * or -1 for a socket error. * Returns -2 if not allowed to block and the operation * would block. */ int send( unsigned char *inBuffer, int inNumBytes, char inAllowedToBlock = true, char inAllowDelay = true ); /** * Receives bytes from this socket. * * @param inBuffer the buffer where received bytes will be put. * Must be pre-allocated memory space. * @param inNumBytes the number of bytes to read from the socket. * @param inTimeout the timeout for this receive operation in * milliseconds. Set to -1 for an infinite timeout. * -2 is returned from this call in the event of a timeout. * If timeout is set, only available data is returned (up * to, but not more than inNumBytes). The socket does not * wait for all inNumBytes to become available before returning. * Thus, a timeout return value (-2) means no data was available * before the timeout. If some data is available, we never * time out. If no timeout is set (-1 for infinite), the socket * waits for all inNumBytes to arrive before returning. * Thus, setting a timeout of 0 effectively puts the socket * into non-blocking mode. * * @return the number of bytes read successfully, * or -1, -2 for a socket error or timeout, respectively. */ int receive( unsigned char *inBuffer, int inNumBytes, long inTimeout ); /** * Flushes sent data through the socket. * * Intended to be called before deleting the socket to ensure * that data goes through. The mechanism used by this function * does not guarantee that data goes through, but it works * better than simply closing the socket on many platforms. * * The method used by this call works best if the remote * host closes the connection. For example, if we send the * remote host a "connection closing" indicator, then * call writeFlushBeforeClose, and the remote host closes * the connection upon receiving the indicator, all of our * sent data will be received by the remote host after * writeFlushBeforeClose returns (assuming that the maximum * time is long enough for the remote host to actually close * the connection). * * Good maximum times should be in the several-second range, * though the Apache system uses 30 seconds. For slow connections, * this might be necessary. * * This call will cause any received data to be discarded. * * This call DOES NOT close the socket. The socket must be deleted * as usual after this call returns. * * @param inMaxTimeInMilliseconds the maximum time to wait * in milliseconds. This call may block slightly longer than * this if the remote host is still sending data. */ void sendFlushBeforeClose( int inMaxTimeInMilliseconds ); /** * Forces this socket's connection to break, causing any blocked * send or receive operations to fail. * * Note that this operation is inherently not thread-safe, so * some external mechanism must be used to ensure that the socket * is not destroyed by another thread before breakConnection is called. * The SocketManager class is one such external mechanism. */ void breakConnection(); /** * Gets the host connected to the other end of this socket. * * @return the address of the remote host, or NULL if obtaining * the address fails. The port of the returned address * will always be set to the port that the host is connecting from. * Must be destroyed by caller if non-NULL. */ HostAddress *getRemoteHostAddress(); /** * Gets the local address attached to this socket. * * Getting the local address from a socket is more * accurate than non-connected methods (for example, the methods * used in HostAddress.h implementations. * * @return the address of the local host, or NULL if obtaining * the address fails. The port of the returned address * will always be set to 0. * Must be destroyed by caller if non-NULL. */ HostAddress *getLocalHostAddress(); /** * Used by platform-specific implementations. */ void *mNativeObjectPointer; // called by socket client to set connected status void setConnected( char inConnected ) { mConnected = inConnected; } private: static char sInitialized; char mConnected; char mIsConnectionBroken; // toggle Nagle algorithm (inValue=1 turns it off) void setNoDelay( int inValue ); }; inline Socket::Socket() : mConnected( true ), mIsConnectionBroken( false ) { } inline char Socket::isFrameworkInitialized() { return sInitialized; } inline void Socket::sendFlushBeforeClose( int inMaxTimeInMilliseconds ) { unsigned char *tempBuffer = new unsigned char[1]; int numRead = -2; int timeout = 1000; if( timeout > inMaxTimeInMilliseconds ) { timeout = inMaxTimeInMilliseconds; } long totalTimeout = 0; unsigned long startSec; unsigned long startMsec; Time::getCurrentTime( &startSec, &startMsec ); // keep reading data from socket until we get an error // or wait too long (pass our max timeout) while( numRead != -1 && totalTimeout < inMaxTimeInMilliseconds ) { numRead = this->receive( tempBuffer, 1, timeout ); // track total time whether the receive timed out or not totalTimeout = (long)( Time::getMillisecondsSince( startSec, startMsec ) ); } delete [] tempBuffer; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/socketClientTest.cpp0000640000175000017500000000560307620021426025052 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-28 Jason Rohrer * Added more output. * Changed so that it doesn't sleep in the middle of a block * since the requirements of the socket interface have changed * and implementations no longer have to support this behavior. * * 2001-December-12 Jason Rohrer * Changed to use new HostAddress constructor. * * 2002-July-30 Jason Rohrer * Fixed a bug in localhost address. Added a test of streams. * * 2002-July-31 Jason Rohrer * Changed to test multiple blocks. * * 2002-December-2 Jason Rohrer * Changed to test socket creation failure. * Added counter to output. * * 2002-February-4 Jason Rohrer * Added test of getting local address from socket. */ #include "Socket.h" #include "SocketStream.h" #include "SocketServer.h" #include "SocketClient.h" #include "HostAddress.h" #include int main() { char *name = new char[99]; sprintf( name, "63.249.65.249" ); //char *name = new char[99]; //sprintf( name, "192.168.1.2" ); //char *name = "192.168.1.1"; //int addressLength = 11; //char *name = "monolith.2y.net"; //int addressLength = 15; int port = 5158; HostAddress *address = new HostAddress( name, port ); printf( "Trying to connect to server: " ); address->print(); printf( "\n" ); Socket *sock; int numConnections = 0; while( true ) { sock = SocketClient::connectToServer( address ); if( sock == NULL ) { printf( "%d Connecting to server failed\n", numConnections ); //return 1; } else { printf( "%d Connection established\n", numConnections ); HostAddress *localAddress = sock->getLocalHostAddress(); if( localAddress != NULL ) { printf( "Our local address (fetched from socket) is " ); localAddress->print(); printf( "\n" ); delete localAddress; } delete sock; } //usleep( 1000 ); numConnections++; } int numBytes = 4000; unsigned char *buffer = new unsigned char[numBytes]; for( int i=0; iwrite( buffer, numBytes ); printf( "Sent %d successfully,\tcount = %d\n", numSent, count ); count++; } int checksum = 0; for( int i=0; i /** * A SocketStream that logs inbound and outbound data. * * @author Jason Rohrer */ class LoggingSocketStream : public SocketStream { public: /** * Constructs a SocketStream. * * @param inSocket the newtork socket wrapped by this stream. * inSocket is NOT destroyed when the stream is destroyed. * @param inEnableInboundLog set to true to enable inbound logging. * @param inEnableOutboundLog set to true to enable outbound logging. * @param inLogSizeLimit the size limit of each log (both inbound * and outbound), in bytes. * Logging will stop after inLogSizeLimit bytes have been * written to each log. * @param inLogNamePrefix a string that will be used to name * the log files (appending .in and .out for the two logs). * Should be unique. * Must be destroyed by caller. */ LoggingSocketStream( Socket *inSocket, char inEnableInboundLog, char inEnableOutboundLog, unsigned long inLogSizeLimit, char *inLogNamePrefix ); virtual ~LoggingSocketStream(); // overrides the SocketStream implementations long read( unsigned char *inBuffer, long inNumBytes ); long write( unsigned char *inBuffer, long inNumBytes ); protected: char mEnableInboundLog; char mEnableOutboundLog; unsigned long mLogSizeLimit; FILE *mInboundLogFile; FILE *mOutboundLogFile; unsigned long mInboundSizeSoFar; unsigned long mOutboundSizeSoFar; }; inline LoggingSocketStream::LoggingSocketStream( Socket *inSocket, char inEnableInboundLog, char inEnableOutboundLog, unsigned long inLogSizeLimit, char *inLogNamePrefix ) : SocketStream( inSocket ), mEnableInboundLog( inEnableInboundLog ), mEnableOutboundLog( inEnableOutboundLog ), mLogSizeLimit( inLogSizeLimit ), mInboundLogFile( NULL ), mOutboundLogFile( NULL ), mInboundSizeSoFar( 0 ), mOutboundSizeSoFar( 0 ) { if( mEnableInboundLog ) { char *inboundFileName = autoSprintf( "%s.in", inLogNamePrefix ); mInboundLogFile = fopen( inboundFileName, "wb" ); delete [] inboundFileName; } if( mEnableOutboundLog ) { char *outboundFileName = autoSprintf( "%s.out", inLogNamePrefix ); mOutboundLogFile = fopen( outboundFileName, "wb" ); delete [] outboundFileName; } } inline LoggingSocketStream::~LoggingSocketStream() { if( mInboundLogFile != NULL ) { fclose( mInboundLogFile ); mInboundLogFile = NULL; } if( mOutboundLogFile != NULL ) { fclose( mOutboundLogFile ); mOutboundLogFile = NULL; } } inline long LoggingSocketStream::read( unsigned char *inBuffer, long inNumBytes ) { long returnVal = SocketStream::read( inBuffer, inNumBytes ); if( mEnableInboundLog && mInboundSizeSoFar < mLogSizeLimit && returnVal == inNumBytes ) { int numToLog = inNumBytes; if( mInboundSizeSoFar + numToLog > mLogSizeLimit ) { numToLog = mLogSizeLimit - mInboundSizeSoFar; } if( mInboundLogFile != NULL ) { fwrite( inBuffer, 1, numToLog, mInboundLogFile ); fflush( mInboundLogFile ); } mInboundSizeSoFar += numToLog; } return returnVal; } inline long LoggingSocketStream::write( unsigned char *inBuffer, long inNumBytes ) { long returnVal = SocketStream::write( inBuffer, inNumBytes ); if( mEnableOutboundLog && mOutboundSizeSoFar < mLogSizeLimit && returnVal == inNumBytes ) { int numToLog = inNumBytes; if( mOutboundSizeSoFar + numToLog > mLogSizeLimit ) { numToLog = mLogSizeLimit - mOutboundSizeSoFar; } if( mOutboundLogFile != NULL ) { fwrite( inBuffer, 1, numToLog, mOutboundLogFile ); fflush( mOutboundLogFile ); } mOutboundSizeSoFar += numToLog; } return returnVal; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/LookupThread.h0000640000175000017500000000272111145450322023625 0ustar pabspabs/* * Modification History * * 2008-October-21 Jason Rohrer * Created. * * 2009-February-3 Jason Rohrer * Changed to subclass FinishedSignalThread. * * 2009-February-14 Jason Rohrer * Changed to copy inAddress internally. */ #ifndef LOOKUP_THREAD_CLASS_INCLUDED #define LOOKUP_THREAD_CLASS_INCLUDED #include "minorGems/system/FinishedSignalThread.h" #include "minorGems/system/MutexLock.h" #include "minorGems/network/HostAddress.h" /** * Thread that performs DNS lookup on a host name. * * @author Jason Rohrer */ class LookupThread : public FinishedSignalThread { public: /** * Constructs and starts a lookup thread. * * @param inAddress the address to lookup. Destroyed by caller, * copied internally. */ LookupThread( HostAddress *inAddress ); // joins and destroys this thread ~LookupThread(); /** * Returns true if lookup done. */ char isLookupDone(); /** * Returns numerical address result, or NULL if lookup failed. * * Must be destroyed by caller if non-NULL. */ HostAddress *getResult(); // override the run method from Thread void run(); private: MutexLock mLock; HostAddress *mAddress; HostAddress *mNumericalAddress; char mLookupDone; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/win32/0000750000175000017500000000000011401021061021777 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/network/win32/SocketWin32.cpp0000640000175000017500000002572711332434305024610 0ustar pabspabs/* * Modification History * * 2001-January-28 Jason Rohrer * Created. * * 2001-February-4 Jason Rohrer * Fixed receive so that it waits for all requested bytes to arrive. * * 2001-March-4 Jason Rohrer * Replaced include of and with * to fix compile bugs encountered with newer windows compilers. * * 2001-May-12 Jason Rohrer * Fixed a bug in socket receive error checking. * * 2001-November-13 Jason Rohrer * Changed timeout parameter to signed, since -1 is a possible argument. * * 2002-April-15 Jason Rohrer * Removed call to WSAGetLastError, since it seems to pick up errors from * non-socket calls. For example, if sys/stat.h stat() is called on a file * that does not exist, WSAGetLastError returns 2, which is not even a * winsock error code. We should probably report this bug, huh? * * 2002-August-2 Jason Rohrer * Added functon for getting remote host address, but no implementation. * * 2002-August-5 Jason Rohrer * Added implementation of getRemoteHostAddress(). * * 2002-September-8 Jason Rohrer * Fixed a major looping bug with broken sockets. * * 2002-November-15 Jason Rohrer * Fixed a security hole when getting the remote host address. * * 2003-February-4 Jason Rohrer * Added a function for getting the local host address from a socket. * Still need to test the win32 version of this. * * 2003-February-5 Jason Rohrer * Fixed a bug in call to gethostname. Removed unused variable. * * 2004-January-4 Jason Rohrer * Added use of network function locks. * * 2004-January-11 Jason Rohrer * Fixed a bug in handling of timeout return value. * * 2004-March-23 Jason Rohrer * Removed timeout error message. * * 2004-December-13 Jason Rohrer * Added a breakConnection function. * * 2004-December-24 Jason Rohrer * Fixed bug in close call. * * 2005-July-5 Jason Rohrer * Added port number when getting address of remote host. * * 2006-May-28 Jason Rohrer * Changed timeout behavior slightly to support emulation of non-blocking mode. * * 2006-June-5 Jason Rohrer * Added support for non-blocking sends. * * 2008-September-30 Jason Rohrer * Added support for non-blocking connect. * Fixed close-detection in non-blocking read. * * 2010-January-26 Jason Rohrer * Added support for disabling Nagle algorithm. */ #include "minorGems/network/Socket.h" #include "minorGems/network/NetworkFunctionLocks.h" #include #include #include #include #include // prototypes int timed_read( int inSock, unsigned char *inBuf, int inLen, long inMilliseconds ); /** * Windows-specific implementation of the Socket class member functions. * */ // Win32 does not define socklen_t typedef int socklen_t; char Socket::sInitialized = false; int Socket::initSocketFramework() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 0 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { // no usable DLL found printf( "WinSock DLL version 1.0 or higher not found.\n" ); return -1; } sInitialized = true; return 0; } Socket::~Socket() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( !mIsConnectionBroken ) { // 2 specifies shutting down both sends and receives shutdown( socketID, 2 ); mIsConnectionBroken = true; } closesocket( socketID ); delete [] socketIDptr; } int Socket::isConnected() { if( mConnected ) { return 1; } int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; int ret; fd_set fsr; struct timeval tv; int val; socklen_t len; FD_ZERO( &fsr ); FD_SET( socketID, &fsr ); // check if connection event waiting right now // timeout of 0 tv.tv_sec = 0; tv.tv_usec = 0; ret = select( socketID + 1, NULL, &fsr, NULL, &tv ); if( ret==0 ) { // timeout return 0; } // no timeout // error? len = 4; ret = getsockopt( socketID, SOL_SOCKET, SO_ERROR, (char*)( &val ), &len ); if( ret < 0 ) { // error return -1; } if( val != 0 ) { // error return -1; } // success mConnected = true; return 1; } void Socket::setNoDelay( int inValue ) { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; int flag = inValue; int result = setsockopt( socketID, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int) ); } int Socket::send( unsigned char *inBuffer, int inNumBytes, char inAllowedToBlock, char inAllowDelay ) { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( inAllowedToBlock ) { if( ! inAllowDelay ) { // turn nodelay on setNoDelay( 1 ); } int returnVal = ::send( socketID, (char*)inBuffer, inNumBytes, 0 ); if( ! inAllowDelay ) { // turn nodelay back off setNoDelay( 0 ); } return returnVal; } else { // 1 for non-blocking, 0 for blocking u_long socketMode = 1; ioctlsocket( socketID, FIONBIO, &socketMode ); if( ! inAllowDelay ) { // turn nodelay on setNoDelay( 1 ); } int result = ::send( socketID, (char*)inBuffer, inNumBytes, 0 ); if( ! inAllowDelay ) { // turn nodelay back off setNoDelay( 0 ); } // set back to blocking socketMode = 0; ioctlsocket( socketID, FIONBIO, &socketMode ); if( result == -1 && WSAGetLastError() == WSAEWOULDBLOCK ) { return -2; } else { return result; } } } int Socket::receive( unsigned char *inBuffer, int inNumBytes, long inTimeout ) { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; int numReceived = 0; char error = false; char errorReturnValue = -1; char stopLooping = false; // for win32, we can't specify MSG_WAITALL // so we have too loop until the entire message is received, // as long as there is no error. // note that if a timeout is set, we use the stopLooping flag // to return only the available data (we do not emulate MSG_WAITALL) while( numReceived < inNumBytes && !error && !stopLooping ) { // the number of bytes left to receive int numRemaining = inNumBytes - numReceived; // pointer to the spot in the buffer where the // remaining bytes should be stored unsigned char *remainingBuffer = &( inBuffer[ numReceived ] ); int numReceivedIn; if( inTimeout == -1 ) { numReceivedIn = recv( socketID, (char*)remainingBuffer, numRemaining, 0 ); } else { numReceivedIn = timed_read( socketID, remainingBuffer, numRemaining, inTimeout ); // stop looping after one timed read stopLooping = true; } if( numReceivedIn > 0 ) { numReceived += numReceivedIn; } else { error = true; if( numReceivedIn == 0 ) { // the socket was gracefully closed errorReturnValue = -1; } else if( numReceivedIn == SOCKET_ERROR ) { // socket error errorReturnValue = -1; } else if( numReceivedIn == -2 ) { // timeout errorReturnValue = -2; } else { printf( "Unexpected return value from socket receive: %d.\n", numReceivedIn ); errorReturnValue = -1; } } } if( error ) { return errorReturnValue; } else { return numReceived; } } void Socket::breakConnection() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( !mIsConnectionBroken ) { shutdown( socketID, 2 ); mIsConnectionBroken = true; } closesocket( socketID ); } HostAddress *Socket::getRemoteHostAddress() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; // adapted from Unix Socket FAQ socklen_t len; struct sockaddr_in sin; len = sizeof sin; int error = getpeername( socketID, (struct sockaddr *) &sin, &len ); if( error ) { return NULL; } // this is potentially insecure, since a fake DNS name might be returned // we should use the IP address only // // struct hostent *host = gethostbyaddr( (char *) &sin.sin_addr, // sizeof sin.sin_addr, // AF_INET ); NetworkFunctionLocks::mInet_ntoaLock.lock(); // returned string is statically allocated, copy it char *ipAddress = stringDuplicate( inet_ntoa( sin.sin_addr ) ); NetworkFunctionLocks::mInet_ntoaLock.unlock(); int port = ntohs( sin.sin_port ); return new HostAddress( ipAddress, port ); } HostAddress *Socket::getLocalHostAddress() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; // adapted from GTK-gnutalla code, and elsewhere struct sockaddr_in addr; int len = sizeof( struct sockaddr_in ); int result = getsockname( socketID, (struct sockaddr*)( &addr ), &len ); if( result == -1 ) { return NULL; } else { char *stringAddress = inet_ntoa( addr.sin_addr ); return new HostAddress( stringDuplicate( stringAddress ), 0 ); } } /* timed_read adapted from gnut, by Josh Pieper */ /* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ // exactly like the real read, except that it returns -2 // if no data was read before the timeout occurred... int timed_read( int inSock, unsigned char *inBuf, int inLen, long inMilliseconds ) { fd_set fsr; struct timeval tv; int ret; FD_ZERO( &fsr ); FD_SET( inSock, &fsr ); tv.tv_sec = inMilliseconds / 1000; int remainder = inMilliseconds % 1000; tv.tv_usec = remainder * 1000; ret = select( inSock + 1, &fsr, NULL, NULL, &tv ); if( ret==0 ) { // printf( "Timed out waiting for data on socket receive.\n" ); return -2; } if( ret<0 ) { printf( "Selecting socket during receive failed.\n" ); return ret; } ret = recv( inSock, (char*)inBuf, inLen, 0 ); if( ret == 0 ) { // select came back as 1, but no data there // connection closed on remote end return -1; } return ret; } Cultivation_9+dfsg1_UnixSource/minorGems/network/win32/HostAddressWin32.cpp0000640000175000017500000000715111103257444025575 0ustar pabspabs/* * Modification History * * 2001-December-14 Jason Rohrer * Created. * Has not been compiled or tested under windows. * * 2002-March-25 Jason Rohrer * Added function for conversion to numerical addresses. * * 2002-March-26 Jason Rohrer * Changed to use strdup. * Fixed problem on some platforms where local address does * not include domain name. * Fixed so that numerical address is actually retrieved by * getNumericalAddress(). * * 2002-April-6 Jason Rohrer * Replaced use of strdup. * * 2002-April-7 Jason Rohrer * Changed to return numerical address from getLocalAddress, * since getdomainname() call not available on Win32. * * 2002-April-8 Jason Rohrer * Changed to init socket framework before getting local address. * Changed to check if address is numerical before * performing a lookup on it. * * 2004-January-4 Jason Rohrer * Added use of network function locks. * * 2008-November-2 Jason Rohrer * Added framework init in getNumericalAddress. */ #include "minorGems/network/HostAddress.h" #include "minorGems/network/NetworkFunctionLocks.h" #include "minorGems/util/stringUtils.h" #include #include #include #include "minorGems/network/Socket.h" HostAddress *HostAddress::getLocalAddress() { // first make sure sockets are initialized if( !Socket::isFrameworkInitialized() ) { // try to init the framework int error = Socket::initSocketFramework(); if( error == -1 ) { printf( "initializing network socket framework failed\n" ); return NULL; } } int bufferLength = 200; char *buffer = new char[ bufferLength ]; gethostname( buffer, bufferLength ); char *name = stringDuplicate( buffer ); delete [] buffer; // this class will destroy name for us HostAddress *nameAddress = new HostAddress( name, 0 ); HostAddress *fullAddress = nameAddress->getNumericalAddress(); delete nameAddress; return fullAddress; } HostAddress *HostAddress::getNumericalAddress() { // make sure we're not already numerical if( this->isNumerical() ) { return this->copy(); } if( !Socket::isFrameworkInitialized() ) { // try to init the framework int error = Socket::initSocketFramework(); if( error == -1 ) { printf( "initializing network socket framework failed\n" ); return NULL; } } // need to use two function locks here // first, lock for gethostbyname NetworkFunctionLocks::mGetHostByNameLock.lock(); struct hostent *host = gethostbyname( mAddressString ); if (host != NULL) { // this line adapted from the // Unix Socket FAQ struct in_addr *inetAddress = (struct in_addr *) *host->h_addr_list; // now lock for inet_ntoa NetworkFunctionLocks::mInet_ntoaLock.lock(); // the returned string is statically allocated, so copy it char *inetAddressString = stringDuplicate( inet_ntoa( *inetAddress ) ); NetworkFunctionLocks::mInet_ntoaLock.unlock(); // done with returned hostent now, so unlock first lock NetworkFunctionLocks::mGetHostByNameLock.unlock(); return new HostAddress( inetAddressString, mPort ); } else { // unlock first lock before returning NetworkFunctionLocks::mGetHostByNameLock.unlock(); return NULL; } } char HostAddress::isNumerical() { unsigned long returnedValue = inet_addr( mAddressString ); if( returnedValue == INADDR_NONE ) { return false; } else { return true; } } Cultivation_9+dfsg1_UnixSource/minorGems/network/win32/SocketClientWin32.cpp0000640000175000017500000001724311262433554025750 0ustar pabspabs/* * Modification History * * 2001-January-28 Jason Rohrer * Created. * * 2001-January-29 Jason Rohrer * Fixed an endian bug that was messing up the port numbers. * * 2002-October-13 Jason Rohrer * Added support for timeout on connect. * Fixed several windows compile bugs. * * 2002-December-2 Jason Rohrer * Fixed resource leak when connection fails. * Changed close calls to win32 closesocket calls. * * 2004-January-2 Jason Rohrer * Fixed a thread safety issue with gethostbyname. * * 2004-January-4 Jason Rohrer * Added use of network function locks. * * 2006-April-25 Jason Rohrer * Added missing check for a NULL outTimedOut parameter. * * 2008-September-30 Jason Rohrer * Added support for non-blocking connect. * * 2009-October-5 Jason Rohrer * Fixed bug when connect timeout not used. */ #include "minorGems/network/SocketClient.h" #include "minorGems/network/NetworkFunctionLocks.h" #include "minorGems/system/MutexLock.h" #include #include #include #include #include // prototypes struct in_addr *nameToAddress( char *inAddress ); int timed_connect( int inSocketID, struct sockaddr *inSocketAddress, int inAddressLength, int inTimeoutInMilliseconds ); Socket *SocketClient::connectToServer( HostAddress *inAddress, long inTimeoutInMilliseconds, char *outTimedOut ) { if( !Socket::isFrameworkInitialized() ) { // try to init the framework int error = Socket::initSocketFramework(); if( error == -1 ) { printf( "initializing network socket framework failed\n" ); return NULL; } } unsigned long socketID = socket( AF_INET, SOCK_STREAM, 0 ); if( socketID == INVALID_SOCKET ) { printf( "Creating socket failed.\n" ); return NULL; } union sock { struct sockaddr s; struct sockaddr_in i; } sock; //struct in_addr internet_address; //struct hostent *hp; struct in_addr *internet_address = nameToAddress( inAddress->mAddressString ); if( internet_address == NULL ) { printf( "Host name lookup failed: " ); inAddress->print(); printf( "\n" ); closesocket( socketID ); return NULL; } //hp = gethostbyname( inAddress->mAddressString ); //memcpy( &internet_address, *( hp->h_addr_list ), sizeof( struct in_addr ) ); sock.i.sin_family = AF_INET; sock.i.sin_port = htons( inAddress->mPort ); sock.i.sin_addr = *internet_address; int error; if( inTimeoutInMilliseconds != -1 ) { // use timeout error = timed_connect( socketID, &sock.s, sizeof( struct sockaddr ), inTimeoutInMilliseconds ); if( outTimedOut != NULL ) { if( error == -2 ) { *outTimedOut = true; if( inTimeoutInMilliseconds == 0 ) { // caller didn't want to wait at all error = 0; } else { // treat timeout as error error = -1; } } else { *outTimedOut = false; } } } else { // don't use timeout error = connect( socketID, &sock.s, sizeof( struct sockaddr ) ); } delete internet_address; if( error == -1 ) { //printf( "Connecting to host failed: " ); //inAddress->print(); //printf( "\n" ); closesocket( socketID ); return NULL; } // package into a Socket and return it Socket *returnSocket = new Socket(); unsigned long *idSpace = new unsigned long[1]; idSpace[0] = socketID; returnSocket->mNativeObjectPointer = (void *)idSpace; if( outTimedOut != NULL && *outTimedOut ) { // not connected yet returnSocket->setConnected( false ); } return returnSocket; } /* Converts ascii text to in_addr struct. NULL is returned if the address can not be found. Result must be destroyed by caller. Adapted from the Unix Socket FAQ */ struct in_addr *nameToAddress( char *inAddress ) { struct hostent *host; static struct in_addr saddr; struct in_addr *copiedSaddr = new struct in_addr; /* First try it as aaa.bbb.ccc.ddd. */ saddr.s_addr = inet_addr( inAddress ); if( saddr.s_addr != INADDR_NONE ) { // copy to avoid returning pointer to stack memcpy( copiedSaddr, &saddr, sizeof( struct in_addr ) ); return copiedSaddr; } // must keep this locked until we are done copying the in_addr out // of the returned hostent NetworkFunctionLocks::mGetHostByNameLock.lock(); char hostFound = false; host = gethostbyname( inAddress ); if( host != NULL ) { memcpy( copiedSaddr, *host->h_addr_list, sizeof( struct in_addr ) ); hostFound = true; } NetworkFunctionLocks::mGetHostByNameLock.unlock(); if( hostFound ) { return copiedSaddr; } else { delete copiedSaddr; } return NULL; } /* timed_read adapted from gnut, by Josh Pieper */ /* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ // just like connect except that it times out after time secs // returns -2 on timeout, otherwise same as connect int timed_connect( int inSocketID, struct sockaddr *inSocketAddress, int inAddressLength, int inTimeoutInMilliseconds ) { int ret; fd_set fsr; struct timeval tv; int val; int len; //g_debug(1,"entering sock=%i secs=%i\n",inSocketID,inTimeoutInSeconds); //ret = fcntl( inSocketID, F_SETFL, O_NONBLOCK ); // use ioctlsocket on win32 instead of fcntl // set to non-blocking mode unsigned long onFlag = 1; ret = ioctlsocket( inSocketID, FIONBIO, &onFlag ); //g_debug(5,"fcntl returned %i\n",ret); if( ret < 0 ) { return ret; } ret = connect( inSocketID, inSocketAddress, inAddressLength ); //g_debug(5,"connect returned %i\n",ret); if( ret == 0 ) { //g_debug(0,"immediate connection!\n"); // wooah! immediate connection // return -2; // changed from what Josh originally returned (-2) // immediate connection *can* happen sometimes, right? // for example, when connecting to localhost... return 0; } // if (errno != EINPROGRESS) { // perror("timed_connect, connect"); // return ret; // } FD_ZERO( &fsr ); FD_SET( inSocketID, &fsr ); tv.tv_sec = inTimeoutInMilliseconds / 1000; int remainder = inTimeoutInMilliseconds % 1000; tv.tv_usec = remainder * 1000; ret = select( inSocketID+1, NULL, &fsr, NULL, &tv ); //g_debug(5,"select returned %i\n",ret); if( ret==0 ) { // timeout //g_debug(1,"timeout\n"); //fcntl( inSocketID, F_SETFL, 0 ); unsigned long offFlag = 0; ret = ioctlsocket( inSocketID, FIONBIO, &offFlag ); return -2; } len = 4; ret = getsockopt( inSocketID, SOL_SOCKET, SO_ERROR, (char*)( &val ), &len ); //g_debug(5,"getsockopt returned %i val=%i\n",ret,val); if( ret < 0 ) { //g_debug(1,"getsockopt returned: %i\n",ret); return ret; } if (val!=0) { //g_debug(3,"returning failure!\n"); return -1; } //ret = fcntl( inSocketID, F_SETFL, 0 ); // use ioctlsocket on win32 instead of fcntl // set back to blocking mode unsigned long offFlag = 0; ret = ioctlsocket( inSocketID, FIONBIO, &offFlag ); //g_debug(1,"fcntl: %i\n",ret); //g_debug(3,"returning success val=%i\n",val); return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/network/win32/WSABugDemo.cpp0000640000175000017500000000140207457040432024421 0ustar pabspabs#include #include #include #include int main() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 0 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { // no usable DLL found printf( "WinSock DLL version 1.0 or higher not found.\n" ); return -1; } int error = WSAGetLastError(); printf( "WSA Error before stat call = %d\n", error ); struct stat fileInfo; int statError = stat( "DoesNotExist", &fileInfo ); printf( "stat error = %d\n", statError ); error = WSAGetLastError(); printf( "WSA Error after stat call = %d\n", error ); return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/network/win32/SocketServerWin32.cpp0000640000175000017500000000762710016424475026003 0ustar pabspabs/* * Modification History * * 2001-January-28 Jason Rohrer * Created. * * 2001-January-29 Jason Rohrer * Fixed an endian bug that was messing up the port numbers. * * 2002-April-6 Jason Rohrer * Changed to implement the new timeout-on-accept interface, * though did no actually implement timeouts. * * 2002-September-8 Jason Rohrer * Added a check for a potentially NULL argument. * * 2004-February-23 Jason Rohrer * Added timeout code for accepting connections. * Removed use of shutdown on listening socket. */ #include "minorGems/network/SocketServer.h" #include #include union sock { struct sockaddr s; struct sockaddr_in i; } sock; SocketServer::SocketServer( int inPort, int inMaxQueuedConnections ) { int error = 0; if( !Socket::isFrameworkInitialized() ) { // try to init the framework int error = Socket::initSocketFramework(); if( error == -1 ) { printf( "initializing network socket framework failed\n" ); exit( 1 ); } } // create the socket int sockID = socket( AF_INET, SOCK_STREAM, 0 ); if( sockID == INVALID_SOCKET ) { printf( "Creating socket failed.\n" ); exit( 1 ); } // store socket id in native object pointer int *idStorage = new int[1]; idStorage[0] = sockID; mNativeObjectPointer = (void *)idStorage; // bind socket to the port union sock sockAddress; sockAddress.i.sin_family = AF_INET; sockAddress.i.sin_port = htons( inPort ); sockAddress.i.sin_addr.s_addr = INADDR_ANY; error = bind( sockID, &(sockAddress.s), sizeof( struct sockaddr ) ); if( error == -1 ) { printf( "Network socket bind to port %d failed\n", inPort ); exit( 1 ); } // start listening for connections error = listen( sockID, inMaxQueuedConnections ); if( error == -1 ) { printf( "Listening for network socket connections failed.\n" ); exit(1); } } SocketServer::~SocketServer() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; closesocket( socketID ); delete [] socketIDptr; } Socket *SocketServer::acceptConnection( long inTimeoutInMilliseconds, char *outTimedOut ) { if( outTimedOut != NULL ) { *outTimedOut = false; } // printf( "Waiting for a connection.\n" ); // extract socket id from native object pointer int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; // same timeout code as in linux version if( inTimeoutInMilliseconds != -1 ) { // if we have a timeout specified, select before accepting // this found in the Linux man page for select, // but idea (which originally used poll) was found // in the Unix Socket FAQ fd_set rfds; struct timeval tv; int retval; // insert our socket descriptor into this set FD_ZERO( &rfds ); FD_SET( socketID, &rfds ); // convert our timeout into the structure's format tv.tv_sec = inTimeoutInMilliseconds / 1000; tv.tv_usec = ( inTimeoutInMilliseconds % 1000 ) * 1000 ; retval = select( socketID + 1, &rfds, NULL, NULL, &tv ); if( retval == 0 ) { // timeout if( outTimedOut != NULL ) { *outTimedOut = true; } return NULL; } } //int addressLength; //union sock acceptedAddress; struct sockaddr acceptedAddress; //int addressLength = sizeof( struct sockaddr ); int acceptedID = accept( socketID, &( acceptedAddress ), NULL ); if( acceptedID == -1 ) { printf( "Failed to accept a network connection.\n" ); return NULL; } Socket *acceptedSocket = new Socket(); int *idStorage = new int[1]; idStorage[0] = acceptedID; acceptedSocket->mNativeObjectPointer = (void *)idStorage; //printf( "Connection received.\n" ); return acceptedSocket; } Cultivation_9+dfsg1_UnixSource/minorGems/network/SocketStream.h0000640000175000017500000000712510005505371023632 0ustar pabspabs/* * Modification History * * 2001-January-10 Jason Rohrer * Created. * * 2001-January-15 Jason Rohrer * Changed so that underlying socket is not destroyed when the stream * is destroyed. Updated to match new Stream interface, which uses longs * instead of ints. * * 2001-February-21 Jason Rohrer * Changed to set stream error messages on socket errors. * * 2002-July-31 Jason Rohrer * Added fix for partial sends. * * 2003-August-12 Jason Rohrer * Added support for read timeouts. * * 2004-January-27 Jason Rohrer * Made functions virtual to support subclassing. */ #include "minorGems/common.h" #ifndef SOCKET_STREAM_CLASS_INCLUDED #define SOCKET_STREAM_CLASS_INCLUDED #include "Socket.h" #include "minorGems/io/InputStream.h" #include "minorGems/io/OutputStream.h" /** * A input and output stream interface for a network socket. * * @author Jason Rohrer */ class SocketStream : public InputStream, public OutputStream { public: /** * Constructs a SocketStream. * * @param inSocket the newtork socket wrapped by this stream. * inSocket is NOT destroyed when the stream is destroyed. */ SocketStream( Socket *inSocket ); // a virtual destructor to ensure that subclass destructors are called virtual ~SocketStream(); /** * Sets the timeout for reads on this socket. * * The timeout defaults to -1 (no timeout). * * @param inMilliseconds the timeout in milliseconds, * or -1 to specify no timeout. */ void setReadTimeout( long inMilliseconds ); /** * Gets the timeout for reads on this socket. * * @return the timeout in milliseconds, * or -1 to indicate no timeout. */ long getReadTimeout(); // implements the InputStream interface. // virtual to allow subclasses to override // in addition, -2 is returned if the read times out. virtual long read( unsigned char *inBuffer, long inNumBytes ); // implements the OutputStream interface virtual long write( unsigned char *inBuffer, long inNumBytes ); protected: Socket *mSocket; long mReadTimeout; }; inline SocketStream::SocketStream( Socket *inSocket ) : mSocket( inSocket ), mReadTimeout( -1 ) { } inline SocketStream::~SocketStream() { // does nothing // exists only to allow for subclass destructors } inline void SocketStream::setReadTimeout( long inMilliseconds ) { mReadTimeout = inMilliseconds; } inline long SocketStream::getReadTimeout() { return mReadTimeout; } inline long SocketStream::read( unsigned char *inBuffer, long inNumBytes ) { int numReceived = mSocket->receive( inBuffer, inNumBytes, mReadTimeout ); if( numReceived == -1 ) { // socket error InputStream::setNewLastErrorConst( "Network socket error on receive." ); } return numReceived; } inline long SocketStream::write( unsigned char *inBuffer, long inNumBytes ) { long numTotalSent = 0; while( numTotalSent < inNumBytes ) { unsigned char *partialBuffer = &( inBuffer[numTotalSent] ); int numRemaining = inNumBytes - numTotalSent; int numSent = mSocket->send( partialBuffer, numRemaining ); if( numSent == -1 || numSent == 0 ) { // socket error OutputStream::setNewLastErrorConst( "Network socket error on send." ); return -1; } numTotalSent += numSent; } return numTotalSent; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/network/Message.h0000640000175000017500000000162507554101513022616 0ustar pabspabs/* * Modification History * * 2001-January-15 Jason Rohrer * Created. * * 2001-February-3 Jason Rohrer * Updated serialization code to use new interfaces. */ #include "minorGems/common.h" #ifndef MESSAGE_CLASS_INCLUDED #define MESSAGE_CLASS_INCLUDED #include "minorGems/io/Serializable.h" /** * A simple message that carries a 4 byte opcode. Useful for sending a * simple service request. * * @author Jason Rohrer */ class Message : public Serializable { public: long mOpcode; // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline int Message::serialize( OutputStream *inOutputStream ) { return inOutputStream->writeLong( mOpcode ); } inline int Message::deserialize( InputStream *inInputStream ) { return inInputStream->readLong( &mOpcode ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/0000750000175000017500000000000011401021060020274 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/math/BigInt.h0000640000175000017500000001045707474074412021661 0ustar pabspabs/* * Modification History * * 2002-May-25 Jason Rohrer * Created. * Made getZero static. */ #ifndef BIG_INT_INCLUDED #define BIG_INT_INCLUDED /** * A multi-byte integer representation. * * Some of the ideas used in this class were gleaned * from studying Sun's Java 1.3 BigInteger implementation. * * @author Jason Rohrer. */ class BigInt { public: /** * Constructs an integer. * * @param inSign the sign of this integer: * -1 if negative, +1 if positive, and 0 if zero. * @param inNumBytes the number of bytes in this integer. * @param inBytes the bytes for this integer. * Copied internally, so must be destroyed by caller. */ BigInt( int inSign, int inNumBytes, unsigned char *inBytes ); /** * Constructs an integer from a 32-bit int. * * @param inInt the int to use. */ BigInt( int inInt ); ~BigInt(); /** * Adds an integer to this integer. * * @praram inOtherInt the int to add. * Must be destroyed by caller. * * @return a newly allocated integer containing the sum. * Must be destroyed by caller. */ BigInt *add( BigInt *inOtherInt ); /** * Subtracts an integer from this integer. * * @praram inOtherInt the int to subtract. * Must be destroyed by caller. * * @return a newly allocated integer containing the difference. * Must be destroyed by caller. */ BigInt *subtract( BigInt *inOtherInt ); /** * Gets whether this integer is less than another integer. * * @praram inOtherInt the integer test. * Must be destroyed by caller. * * @return true if this integer is less than the other. */ char isLessThan( BigInt *inOtherInt ); /** * Gets whether this integer is equal to another integer. * * @praram inOtherInt the integer test. * Must be destroyed by caller. * * @return true if this integer is equal to the other. */ char isEqualTo( BigInt *inOtherInt ); /** * Gets a copy of this integer. * * @return a newly allocated integer containing the copy. * Must be destroyed by caller. */ BigInt *copy(); /** * Gets an integer equal to zero. * * @return a newly allocated integer containing zero. * Must be destroyed by caller. */ static BigInt *getZero(); /** * Converts this integer to a decimal string. * * @return a \0-terminated ascii decimal string. * Must be destroyed by caller. */ //char *convertToDecimalString(); /** * Converts this integer to a hex string. * * @return a \0-terminated ascii hexx string. * Must be destroyed by caller. */ char *convertToHexString(); /** * Converts this integer to a 32-bit int. * * If this integer contains more than 32-bits, the high-order * bits will be discarded, though the sign will be preserved. */ int convertToInt(); /** * -1 if negative, +1 if positive, and 0 if zero. */ int mSign; int mNumBytes; /** * Integer is stored in big endian byte order. */ unsigned char *mBytes; protected: /** * Flips the byte order of this integer. * * @return a newly allocated integer containing the flipped version. * Must be destroyed by caller. */ BigInt *flipByteOrder(); /** * Computes the hex representation of a four-bit int. * * @param inInt the four-bit int to convert. * * @return the int as a hex ascii character, * in {0, 1, ..., A, B, ..., F}. */ char fourBitIntToHex( int inInt ); }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/stats/0000750000175000017500000000000011401021060021432 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/math/stats/PairwiseOrderingErrorEvaluator.h0000640000175000017500000000237007215720625030005 0ustar pabspabs/* * Modification History * * 2000-September-28 Jason Rohrer * Created. */ #ifndef PAIRWISE_ORDERING_ERROR_EVALUATOR_INCLUDED #define PAIRWISE_ORDERING_ERROR_EVALUATOR_INCLUDED #include "ErrorEvaluator.h" /** * Implementation of ErrorEvaluator that generates an error * value based on the number of pairs of elements in the * the test vector that are not given the same order by their values * as are the corresponding pair of values in the correct vector. * * I.e., if A[i] > A[j], then B[i] < B[j] adds 1 to the error term, * while B[i] > B[j] adds 0 to the error term. * * @author Jason Rohrer */ class PairwiseOrderingErrorEvaluator : public ErrorEvaluator { // implements ErrorEvaluator interface double evaluate( double *inVectorA, double *inVectorB, int inLength ); }; inline double PairwiseOrderingErrorEvaluator::evaluate( double *inVectorA, double *inVectorB, int inLength ) { double sum = 0; // examine all pairs of components in the vectors for( int i=0; i inVectorA[j] && inVectorB[i] <= inVectorB[j] ) || ( inVectorA[i] < inVectorA[j] && inVectorB[i] >= inVectorB[j] ) ) { sum++; } } } return sum; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/stats/ErrorEvaluator.h0000640000175000017500000000131107215720625024601 0ustar pabspabs/* * Modification History * * 2000-September-28 Jason Rohrer * Created. */ #ifndef ERROR_EVALUATOR_INCLUDED #define ERROR_EVALUATOR_INCLUDED /** * Interface for a class that evaluates a vector against another * to produce a scalar error value. * * @author Jason Rohrer */ class ErrorEvaluator { public: /** * Evaluates two input vectors to produce an output error value. * * @param inVectorA the first vector (the actual, or "correct" value) * @param inVectorB the second vector ( the predicted, or "test" value) * * @return the error value between the two vectors */ virtual double evaluate( double *inVectorA, double *inVectorB, int inLength ) = 0; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/stats/L1ErrorEvaluator.h0000640000175000017500000000134407215720625025004 0ustar pabspabs/* * Modification History * * 2000-September-28 Jason Rohrer * Created. */ #ifndef L1_ERROR_EVALUATOR_INCLUDED #define L1_ERROR_EVALUATOR_INCLUDED #include "ErrorEvaluator.h" /** * L1 distance implementation of ErrorEvaluator. * * @author Jason Rohrer */ class L1ErrorEvaluator : public ErrorEvaluator { public: // implements ErrorEvaluator interface double evaluate( double *inVectorA, double *inVectorB, int inLength ); }; inline double L1ErrorEvaluator::evaluate( double *inVectorA, double *inVectorB, int inLength ) { double sum = 0; for( int i=0; ievaluate() ); } long InvertExpression::getID() { return mID; } inline long InvertExpression::staticGetID() { return mID; } inline void InvertExpression::print() { printf( "( 1/" ); mArgument->print(); printf( " )" ); } inline Expression *InvertExpression::copy() { InvertExpression *copy = new InvertExpression( mArgument->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/BinaryLogicExpression.h0000640000175000017500000001064107655132767027172 0ustar pabspabs/* * Modification History * * 2003-May-2 Jason Rohrer * Created. * * 2003-May-3 Jason Rohrer * Fixed a few compile-time errors. */ #ifndef BINARY_LOGIC_EXPRESSION_INCLUDED #define BINARY_LOGIC_EXPRESSION_INCLUDED #include "Expression.h" #include "BinaryOperationExpression.h" /** * Expression implementation of a binary logic operation. * * Evaluating this expression returns 1 if the logic operation is true * and 0 if it is false. * * During evaluation, positive values are treated as logical true, * while all other values (zero and negative) are treated as false. * * @author Jason Rohrer */ class BinaryLogicExpression : public BinaryOperationExpression { public: static const int LOGIC_AND = 0; static const int LOGIC_OR = 1; static const int LOGIC_XOR = 2; /** * Constructs a binary logic operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inLogicOperation the logical operation, one of * LOGIC_AND, LOGIC_OR, LOGIC_XOR. * @param inArgumentA the first argument. * @param inArgumentB the second argument. */ BinaryLogicExpression( int inLogicOperation, Expression *inArgumentA, Expression *inArgumentB ); /** * Gets the logical operation. * * @return one of LOGIC_AND, LOGIC_OR, LOGIC_XOR. */ int getLogicOperation(); /** * Sets the logical operation * * @param inLogicOperation LOGIC_AND, LOGIC_OR, LOGIC_XOR. */ void setLogicOperation( int inLogicOperation ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; int mLogicOperation; }; // static init long BinaryLogicExpression::mID = 16; inline BinaryLogicExpression::BinaryLogicExpression( int inLogicOperation, Expression *inArgumentA, Expression *inArgumentB ) : BinaryOperationExpression( inArgumentA, inArgumentB ), mLogicOperation( inLogicOperation ) { } inline int BinaryLogicExpression::getLogicOperation() { return mLogicOperation; } inline void BinaryLogicExpression::setLogicOperation( int inLogicOperation ) { mLogicOperation = inLogicOperation; } inline double BinaryLogicExpression::evaluate() { if( mLogicOperation == LOGIC_AND ) { if( mArgumentA->evaluate() > 0 && mArgumentB->evaluate() > 0 ) { return 1; } else { return 0; } } else if( mLogicOperation == LOGIC_OR ) { if( mArgumentA->evaluate() > 0 || mArgumentB->evaluate() > 0 ) { return 1; } else { return 0; } } else if( mLogicOperation == LOGIC_XOR ) { double valA = mArgumentA->evaluate(); double valB = mArgumentB->evaluate(); if( ( valA > 0 && valB <= 0 ) || ( valA <= 0 && valB > 0 ) ) { return 1; } else { return 0; } } else { // unknown operation type // default to false return 0; } } inline long BinaryLogicExpression::getID() { return mID; } inline long BinaryLogicExpression::staticGetID() { return mID; } inline void BinaryLogicExpression::print() { printf( "( " ); mArgumentA->print(); if( mLogicOperation == LOGIC_AND ) { printf( " and " ); } else if( mLogicOperation == LOGIC_OR ) { printf( " or " ); } else if( mLogicOperation == LOGIC_XOR ) { printf( " xor " ); } else { printf( " UNKNOWN_LOGIC_OPERATION " ); } mArgumentB->print(); printf( " )" ); } inline Expression *BinaryLogicExpression::copy() { BinaryLogicExpression *copy = new BinaryLogicExpression( mLogicOperation, mArgumentA->copy(), mArgumentB->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/ConstantExpression.h0000640000175000017500000000473607347050154026554 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. * * 2001-September-9 Jason Rohrer * Added an extractArgument() implemenation. */ #ifndef CONSTANT_EXPRESSION_INCLUDED #define CONSTANT_EXPRESSION_INCLUDED #include "Expression.h" /** * Expression implementation of a constant. * * @author Jason Rohrer */ class ConstantExpression : public Expression { public: /** * Constructs a constant expression. * * @param inValue the constant value. */ ConstantExpression( double inValue ); /** * Sets the constant value. * * @param inValue the constant value. */ void setValue( double inValue ); /** * Gets the constant value. * * @return the constant value. */ double getValue(); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getNumArguments(); virtual long getID(); virtual Expression *copy(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); virtual Expression *extractArgument( long inArgumentNumber ); virtual void print(); protected: double mValue; static long mID; }; // static init long ConstantExpression::mID = 1; inline ConstantExpression::ConstantExpression( double inValue ) : mValue( inValue ) { } inline void ConstantExpression::setValue( double inValue ) { mValue = inValue; } inline double ConstantExpression::getValue() { return mValue; } inline double ConstantExpression::evaluate() { return mValue; } long ConstantExpression::getNumArguments() { return 0; } inline long ConstantExpression::getID() { return mID; } inline Expression *ConstantExpression::getArgument( long inArgumentNumber ) { return NULL; } inline void ConstantExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { // do nothing return; } inline Expression *ConstantExpression::extractArgument( long inArgumentNumber ) { return NULL; } inline Expression *ConstantExpression::copy() { ConstantExpression *copy = new ConstantExpression( mValue ); return copy; } inline long ConstantExpression::staticGetID() { return mID; } inline void ConstantExpression::print() { printf( "( %f )", mValue ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/SumExpression.h0000640000175000017500000000337207252524064025523 0ustar pabspabs/* * Modification History * * 2001-February-10 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. */ #ifndef SUM_EXPRESSION_INCLUDED #define SUM_EXPRESSION_INCLUDED #include "Expression.h" #include "BinaryOperationExpression.h" /** * Expression implementation of a binary sum operation. * * @author Jason Rohrer */ class SumExpression : public BinaryOperationExpression { public: /** * Constructs a binary sum operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inArgumentA the first argument. * @param inArgumentB the second argument. */ SumExpression( Expression *inArgumentA, Expression *inArgumentB ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long SumExpression::mID = 7; inline SumExpression::SumExpression( Expression *inArgumentA, Expression *inArgumentB ) : BinaryOperationExpression( inArgumentA, inArgumentB ) { } inline double SumExpression::evaluate() { return mArgumentA->evaluate() + mArgumentB->evaluate(); } inline long SumExpression::getID() { return mID; } inline long SumExpression::staticGetID() { return mID; } inline void SumExpression::print() { printf( "( " ); mArgumentA->print(); printf( " + " ); mArgumentB->print(); printf( " )" ); } inline Expression *SumExpression::copy() { SumExpression *copy = new SumExpression( mArgumentA->copy(), mArgumentB->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/Variable.h0000640000175000017500000000332607655253371024432 0ustar pabspabs/* * Modification History * * 2003-April-28 Jason Rohrer * Created. * * 2003-April-29 Jason Rohrer * Added missing destructor. */ #ifndef VARIABLE_INCLUDED #define VARIABLE_INCLUDED #include "minorGems/util/stringUtils.h" /** * Wrapper for a variable value. * * @author Jason Rohrer */ class Variable { public: /** * Constructs a variable, setting its value. * * @param inName the name of the variable. * Must be destroyed by caller if non-const. * @param inValue the initial value of the variable. */ Variable( char *inName, double inValue ); virtual ~Variable(); /** * Gets the value of this variable. * * @return the variable's value. */ virtual double getValue(); /** * Sets the value for this variable. * * @param inValue the value. */ virtual void setValue( double inValue ); /** * Gets the name of this variable. * * @return the name. * Must be destroyed by caller. */ virtual char *getName(); protected: char *mName; double mValue; }; inline Variable::Variable( char *inName, double inValue ) : mName( stringDuplicate( inName ) ), mValue( inValue ) { } inline Variable::~Variable() { delete [] mName; } inline double Variable::getValue() { return mValue; } inline void Variable::setValue( double inValue ) { mValue = inValue; } inline char *Variable::getName() { return stringDuplicate( mName ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/ComparisonExpression.h0000640000175000017500000001246407654613702027077 0ustar pabspabs/* * Modification History * * 2003-May-2 Jason Rohrer * Created. */ #ifndef COMPARISON_EXPRESSION_INCLUDED #define COMPARISON_EXPRESSION_INCLUDED #include "Expression.h" #include "BinaryOperationExpression.h" /** * Expression implementation of a binary comparison operation. * * Evaluating this expression returns 1 if the comparisson is true * and 0 if it is false. * * @author Jason Rohrer */ class ComparisonExpression : public BinaryOperationExpression { public: static const int GREATER_THAN = 0; static const int LESS_THAN = 1; static const int GREATER_THAN_OR_EQUAL_TO = 2; static const int LESS_THAN_OR_EQUAL_TO = 3; static const int EQUAL_TO = 4; static const int NOT_EQUAL_TO = 5; /** * Constructs a binary comparison operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inComparison the comparison to make, one of * GREATER_THAN, LESS_THAN, GREATER_THAN_OR_EQUAL_TO, * LESS_THAN_OR_EQUAL_TO, EQUAL_TO, NOT_EQUAL_TO. * @param inArgumentA the first argument. * @param inArgumentB the second argument. */ ComparisonExpression( int inComparison, Expression *inArgumentA, Expression *inArgumentB ); /** * Gets the comparison being made. * * @return one of GREATER_THAN, LESS_THAN, GREATER_THAN_OR_EQUAL_TO, * LESS_THAN_OR_EQUAL_TO, EQUAL_TO, NOT_EQUAL_TO. */ int getComparison(); /** * Sets the comparison to make. * * @param inComparison the comparison to make, one of * GREATER_THAN, LESS_THAN, GREATER_THAN_OR_EQUAL_TO, * LESS_THAN_OR_EQUAL_TO, EQUAL_TO, NOT_EQUAL_TO. */ void setComparison( int inComparison ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; int mComparison; }; // static init long ComparisonExpression::mID = 15; inline ComparisonExpression::ComparisonExpression( int inComparison, Expression *inArgumentA, Expression *inArgumentB ) : BinaryOperationExpression( inArgumentA, inArgumentB ), mComparison( inComparison ) { } inline int ComparisonExpression::getComparison() { return mComparison; } inline void ComparisonExpression::setComparison( int inComparison ) { mComparison = inComparison; } inline double ComparisonExpression::evaluate() { if( mComparison == GREATER_THAN ) { if( mArgumentA->evaluate() > mArgumentB->evaluate() ) { return 1; } else { return 0; } } else if( mComparison == LESS_THAN ) { if( mArgumentA->evaluate() < mArgumentB->evaluate() ) { return 1; } else { return 0; } } else if( mComparison == GREATER_THAN_OR_EQUAL_TO ) { if( mArgumentA->evaluate() >= mArgumentB->evaluate() ) { return 1; } else { return 0; } } else if( mComparison == LESS_THAN_OR_EQUAL_TO ) { if( mArgumentA->evaluate() <= mArgumentB->evaluate() ) { return 1; } else { return 0; } } else if( mComparison == EQUAL_TO ) { if( mArgumentA->evaluate() == mArgumentB->evaluate() ) { return 1; } else { return 0; } } else if( mComparison == NOT_EQUAL_TO ) { if( mArgumentA->evaluate() != mArgumentB->evaluate() ) { return 1; } else { return 0; } } else { // unknown comparison type // default to false return 0; } } inline long ComparisonExpression::getID() { return mID; } inline long ComparisonExpression::staticGetID() { return mID; } inline void ComparisonExpression::print() { printf( "( " ); mArgumentA->print(); if( mComparison == GREATER_THAN ) { printf( " > " ); } else if( mComparison == LESS_THAN ) { printf( " < " ); } else if( mComparison == GREATER_THAN_OR_EQUAL_TO ) { printf( " >= " ); } else if( mComparison == LESS_THAN_OR_EQUAL_TO ) { printf( " <= " ); } else if( mComparison == EQUAL_TO ) { printf( " == " ); } else if( mComparison == NOT_EQUAL_TO ) { printf( " != " ); } else { printf( " UNKNOWN_COMPARISON " ); } mArgumentB->print(); printf( " )" ); } inline Expression *ComparisonExpression::copy() { ComparisonExpression *copy = new ComparisonExpression( mComparison, mArgumentA->copy(), mArgumentB->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/UnaryOperationExpression.h0000640000175000017500000000551207347047600027735 0ustar pabspabs/* * Modification History * * 2001-February-10 Jason Rohrer * Created. * * 2001-September-4 Jason Rohrer * Changed the destructor to virtual to fix a memory leak. * * 2001-September-9 Jason Rohrer * Changed setArgument to be more intelligent about duplicated calls. * Added an extractArgument() implemenation. */ #ifndef UNARY_OPERATION_EXPRESSION_INCLUDED #define UNARY_OPERATION_EXPRESSION_INCLUDED #include "Expression.h" /** * Abstract base for an unary operation expression object. * * @author Jason Rohrer */ class UnaryOperationExpression : public Expression { public: /** * Constructs a unary operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. Defaults to NULL. */ UnaryOperationExpression( Expression *inArgument = NULL ); virtual ~UnaryOperationExpression(); /** * Sets the argument for the operation. * * @param inArgument the argument. Is destroyed * when the class is destroyed, or by another * call to setArgument. */ void setArgument( Expression *inArgument ); /** * Gets the argument for the operation. * * @return the argument. Is destroyed * when the class is destroyed, or by another * call to setArgumentB. Returns NULL if the * argument was never set. */ Expression *getArgument(); // implements the Expression interface virtual long getNumArguments(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); virtual Expression *extractArgument( long inArgumentNumber ); protected: Expression *mArgument; }; inline UnaryOperationExpression::UnaryOperationExpression( Expression *inArgument ) : mArgument( inArgument ) { } inline UnaryOperationExpression::~UnaryOperationExpression() { if( mArgument != NULL ) { delete mArgument; } } inline void UnaryOperationExpression::setArgument( Expression *inArgument ) { if( mArgument != NULL && inArgument != mArgument ) { delete mArgument; } mArgument = inArgument; } inline Expression *UnaryOperationExpression::getArgument() { return mArgument; } long UnaryOperationExpression::getNumArguments() { return 1; } inline Expression *UnaryOperationExpression::getArgument( long inArgumentNumber ) { if( inArgumentNumber == 0 ) { return getArgument(); } else { return NULL; } } inline void UnaryOperationExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { if( inArgumentNumber == 0 ) { setArgument( inArgument ); return; } else { return; } } inline Expression *UnaryOperationExpression::extractArgument( long inArgumentNumber ) { Expression *returnArg = mArgument; mArgument = NULL; return returnArg; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/ProductExpression.h0000640000175000017500000000346307252524064026400 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. */ #ifndef PRODUCT_EXPRESSION_INCLUDED #define PRODUCT_EXPRESSION_INCLUDED #include "Expression.h" #include "BinaryOperationExpression.h" /** * Expression implementation of a binary product operation. * * @author Jason Rohrer */ class ProductExpression : public BinaryOperationExpression { public: /** * Constructs a binary product operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inArgumentA the first argument. * @param inArgumentB the second argument. */ ProductExpression( Expression *inArgumentA, Expression *inArgumentB ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long ProductExpression::mID = 5; inline ProductExpression::ProductExpression( Expression *inArgumentA, Expression *inArgumentB ) : BinaryOperationExpression( inArgumentA, inArgumentB ) { } inline double ProductExpression::evaluate() { return mArgumentA->evaluate() * mArgumentB->evaluate(); } inline long ProductExpression::getID() { return mID; } inline long ProductExpression::staticGetID() { return mID; } inline void ProductExpression::print() { printf( "( " ); mArgumentA->print(); printf( " * " ); mArgumentB->print(); printf( " )" ); } inline Expression *ProductExpression::copy() { ProductExpression *copy = new ProductExpression( mArgumentA->copy(), mArgumentB->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/SqrtExpression.h0000640000175000017500000000334307653343375025717 0ustar pabspabs/* * Modification History * * 2003-April-28 Jason Rohrer * Created. */ #ifndef SQRT_EXPRESSION_INCLUDED #define SQRT_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include /** * Expression implementation of a unary sqrt operation. * * @author Jason Rohrer */ class SqrtExpression : public UnaryOperationExpression { public: /** * Constructs a unary sqrt operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ SqrtExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long SqrtExpression::mID = 13; inline SqrtExpression::SqrtExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double SqrtExpression::evaluate() { return sqrt( mArgument->evaluate() ); } inline long SqrtExpression::getID() { return mID; } inline long SqrtExpression::staticGetID() { return mID; } inline void SqrtExpression::print() { printf( "( sqrt" ); mArgument->print(); printf( " )" ); } inline Expression *SqrtExpression::copy() { SqrtExpression *copy = new SqrtExpression( mArgument->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/expressionTester.cpp0000640000175000017500000000552607653343375026634 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-February-12 Jason Rohrer * Added code to test serialization. * * 2001-February-24 Jason Rohrer * Fixed incorrect delete usage. * * 2001-April-12 Jason Rohrer * Changed to comply with new FileInput/OutputStream interface * (underlying File must be destroyed explicitly). */ #include "Expression.h" #include "ConstantExpression.h" #include "PowerExpression.h" #include "ProductExpression.h" #include "NegateExpression.h" #include "InvertExpression.h" #include "SinExpression.h" #include "LnExpression.h" #include "SumExpression.h" #include "ExpressionSerializer.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include "minorGems/io/file/FileOutputStream.h" // test function for expressions int main() { ProductExpression *expression = new ProductExpression( new PowerExpression( new ConstantExpression( 5 ), new ConstantExpression( 6 ) ), new NegateExpression( new SinExpression( new ConstantExpression( 19 ) ) ) ); InvertExpression *invExpression = new InvertExpression( expression ); SumExpression *sumExpression = new SumExpression( invExpression, new ConstantExpression( 2 ) ); sumExpression->print(); printf( "\n" ); printf( "%f\n", sumExpression->evaluate() ); printf( "Writing to file.\n" ); char **pathSteps = new char*[2]; pathSteps[0] = new char[10]; pathSteps[1] = new char[10]; sprintf( pathSteps[0], "test" ); sprintf( pathSteps[1], "file" ); int *stepLength = new int[2]; stepLength[0] = 4; stepLength[1] = 4; Path *path = new Path( pathSteps, 2, stepLength, false ); File *file = new File( path, "test.out", 8 ); FileOutputStream *outStream = new FileOutputStream( file, false ); char *error = outStream->getLastError(); if( error != NULL ) { printf( "Error: %s\n", error ); delete error; } ExpressionSerializer::serializeExpression( sumExpression, outStream ); delete outStream; delete file; printf( "Reading back in from file.\n" ); pathSteps = new char*[2]; pathSteps[0] = new char[10]; pathSteps[1] = new char[10]; sprintf( pathSteps[0], "test" ); sprintf( pathSteps[1], "file" ); stepLength = new int[2]; stepLength[0] = 4; stepLength[1] = 4; path = new Path( pathSteps, 2, stepLength, false ); file = new File( path, "test.out", 8 ); FileInputStream *inStream = new FileInputStream( file ); error = inStream->getLastError(); if( error != NULL ) { printf( "Error: %s\n", error ); delete error; } Expression *readExpression; ExpressionSerializer::deserializeExpression( &readExpression, inStream ); delete inStream; delete file; readExpression->print(); printf( "\n" ); printf( "%f\n", readExpression->evaluate() ); delete sumExpression; delete readExpression; return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/ExpressionMutator.h0000640000175000017500000001604107350157715026413 0ustar pabspabs/* * Modification History * * 2001-September-9 Jason Rohrer * Created. * * 2001-September-13 Jason Rohrer * Made an error message more verbose. */ #ifndef EXPRESSION_MUTATOR_INCLUDED #define EXPRESSION_MUTATOR_INCLUDED #include "minorGems/util/random/RandomSource.h" #include "Expression.h" #include "ConstantExpression.h" #include "InvertExpression.h" #include "ProductExpression.h" #include "PowerExpression.h" #include "SumExpression.h" #include "NegateExpression.h" #include "SinExpression.h" #include "FixedConstantExpression.h" #include "MultiConstantArgumentExpression.h" /** * Utility class for mutating expressions. * * Note that the number and position of constant expressions * within the expression will not change during mutation. Thus, * the structure of the expression remains constant while the operations * being performed at each node are mutated. * * @author Jason Rohrer */ class ExpressionMutator { public: /** * Mutates an expression. * * Note that this function's implementation is recursive. * * @param inExpression the expression to mutate. Will be destroyed * by this function (or if not destroyed, then passed back through * the return value). Only the return value, not inExpression, * can be accessed safely after this function returns. * @param inMutationProb the probability of mutations at each * node in inExpression. * @param inMaxMutation the fraction of a fixed constant's value * by which it can be mutated. For example, if a fixed constant * has the value 100, and inMaxMutation is 0.5, then the mutated * constant will be a random value in the range [50, 150]. * @param inRandSource the source of random numbers to use. * Must be destroyed by caller. * * @return a new expression that is a mutated version of inExpression. * (Note that the returned expression may be inExpression). */ static Expression *mutateExpression( Expression *inExpression, double inMutationProb, double inMaxMutation, RandomSource *inRandSource ); }; inline Expression *ExpressionMutator::mutateExpression( Expression *inExpression, double inMutationProb, double inMaxMutation, RandomSource *inRandSource ) { long expressionID = inExpression->getID(); // first, deal with constant case if( expressionID == ConstantExpression::staticGetID() ) { // mutate the constant's value ConstantExpression *c = (ConstantExpression *)inExpression; double value = c->getValue(); double maxMutationAmount = inMaxMutation * value; // a value in [-1, 1] double mutator = ( inRandSource->getRandomDouble() * 2.0 ) - 1.0; value = value + mutator * maxMutationAmount; c->setValue( value ); return c; } else if( expressionID == FixedConstantExpression::staticGetID() ) { // mutate the fixed constant's value FixedConstantExpression *c = (FixedConstantExpression *)inExpression; double value = c->getValue(); double maxMutationAmount = inMaxMutation * value; // a value in [-1, 1] double mutator = ( inRandSource->getRandomDouble() * 2.0 ) - 1.0; value = value + mutator * maxMutationAmount; delete c; return new FixedConstantExpression( value ); } // next deal with the multi-argument case else if( expressionID == MultiConstantArgumentExpression::staticGetID() ) { MultiConstantArgumentExpression *m = (MultiConstantArgumentExpression*)inExpression; Expression *wrappedExpression = m->extractWrappedExpression(); wrappedExpression = mutateExpression( wrappedExpression, inMutationProb, inMaxMutation, inRandSource ); m->setWrappedExpression( wrappedExpression ); return m; } else { // the general expression case Expression *newExpression = inExpression; if( inRandSource->getRandomDouble() <= inMutationProb ) { // we need to mutate this node long newID; if( expressionID == InvertExpression::staticGetID() || expressionID == NegateExpression::staticGetID() || expressionID == SinExpression::staticGetID() ) { // a unary expression // pick another expression type at random newID = expressionID; while( newID == expressionID ) { int randValue = inRandSource->getRandomBoundedInt( 0, 2 ); switch( randValue ) { case 0: newID = InvertExpression::staticGetID(); break; case 1: newID = NegateExpression::staticGetID(); break; case 2: newID = SinExpression::staticGetID(); break; default: // we should never hit this // note that this newID will cause a printed // error below newID = -1; break; } } } else { // a binary expression // pick another expression type at random newID = expressionID; while( newID == expressionID ) { int randValue = inRandSource->getRandomBoundedInt( 0, 2 ); switch( randValue ) { case 0: newID = PowerExpression::staticGetID(); break; case 1: newID = ProductExpression::staticGetID(); break; case 2: newID = SumExpression::staticGetID(); break; default: // we should never hit this // note that this newID will cause a printed // error below newID = -1; break; } } } // now build an expression based on newID if( newID == InvertExpression::staticGetID() ) { newExpression = new InvertExpression( NULL ); } else if( newID == NegateExpression::staticGetID() ) { newExpression = new NegateExpression( NULL ); } else if( newID == PowerExpression::staticGetID() ) { newExpression = new PowerExpression( NULL, NULL ); } else if( newID == ProductExpression::staticGetID() ) { newExpression = new ProductExpression( NULL, NULL ); } else if( newID == SinExpression::staticGetID() ) { newExpression = new SinExpression( NULL ); } else if( newID == SumExpression::staticGetID() ) { newExpression = new SumExpression( NULL, NULL ); } else { printf( "Error in ExpressionMutator: ID does not " ); printf( "match a known expression type: %d\n", newID ); } // at this point, we have constructed a new expression node // with the same number of arguments as the old node // we need to fill in the arguments from inExpression for( int i=0; igetNumArguments(); i++ ) { // use extractArgument so that the argument // won't be destroyed when inExpression is destroyed Expression *argument = inExpression->extractArgument( i ); newExpression->setArgument( i, argument ); } // destroy the old node delete inExpression; } // mutate the sub nodes for( int i=0; igetNumArguments(); i++ ) { Expression *argument = newExpression->extractArgument( i ); // mutate the argument argument = mutateExpression( argument, inMutationProb, inMaxMutation, inRandSource ); newExpression->setArgument( i, argument ); } return newExpression; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/PowerExpression.h0000640000175000017500000000356707252524064026061 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. */ #ifndef POWER_EXPRESSION_INCLUDED #define POWER_EXPRESSION_INCLUDED #include "Expression.h" #include "BinaryOperationExpression.h" #include /** * Expression implementation of a binary power operation. * Raises the first argument to the power of the second, * as in argA^argB. * * @author Jason Rohrer */ class PowerExpression : public BinaryOperationExpression { public: /** * Constructs a binary power operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inArgumentA the first argument. * @param inArgumentB the second argument. */ PowerExpression( Expression *inArgumentA, Expression *inArgumentB ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long PowerExpression::mID = 4; inline PowerExpression::PowerExpression( Expression *inArgumentA, Expression *inArgumentB ) : BinaryOperationExpression( inArgumentA, inArgumentB ) { } inline double PowerExpression::evaluate() { return pow( mArgumentA->evaluate(), mArgumentB->evaluate() ); } inline long PowerExpression::getID() { return mID; } inline long PowerExpression::staticGetID() { return mID; } inline void PowerExpression::print() { printf( "( " ); mArgumentA->print(); printf( " ^ " ); mArgumentB->print(); printf( " )" ); } inline Expression *PowerExpression::copy() { PowerExpression *copy = new PowerExpression( mArgumentA->copy(), mArgumentB->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/TanExpression.h0000640000175000017500000000332107653343375025504 0ustar pabspabs/* * Modification History * * 2003-April-28 Jason Rohrer * Created. */ #ifndef TAN_EXPRESSION_INCLUDED #define TAN_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include /** * Expression implementation of a unary tan operation. * * @author Jason Rohrer */ class TanExpression : public UnaryOperationExpression { public: /** * Constructs a unary tan operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ TanExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long TanExpression::mID = 12; inline TanExpression::TanExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double TanExpression::evaluate() { return tan( mArgument->evaluate() ); } inline long TanExpression::getID() { return mID; } inline long TanExpression::staticGetID() { return mID; } inline void TanExpression::print() { printf( "( tan" ); mArgument->print(); printf( " )" ); } inline Expression *TanExpression::copy() { TanExpression *copy = new TanExpression( mArgument->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/BinaryOperationExpression.h0000640000175000017500000001045107347047600030061 0ustar pabspabs/* * Modification History * * 2001-February-10 Jason Rohrer * Created. * * 2001-September-4 Jason Rohrer * Changed the destructor to virtual to fix a memory leak. * * 2001-September-9 Jason Rohrer * Changed setArgument to be more intelligent about duplicated calls. * Added an extractArgument() implemenation. */ #ifndef BINARY_OPERATION_EXPRESSION_INCLUDED #define BINARY_OPERATION_EXPRESSION_INCLUDED #include "Expression.h" /** * Abstract base for an binary operation expression object. * * @author Jason Rohrer */ class BinaryOperationExpression : public Expression { public: /** * Constructs a binary operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inArgumentA the first argument. Defaults to NULL. * @param inArgumentA the first argument. Defaults to NULL. */ BinaryOperationExpression( Expression *inArgumentA = NULL, Expression *inArgumentB = NULL ); virtual ~BinaryOperationExpression(); /** * Sets the first argument for the operation. * * @param inArgument the first argument. Is destroyed * when the class is destroyed, or by another * call to setArgumentA. */ void setArgumentA( Expression *inArgument ); /** * Sets the second argument for the operation. * * @param inArgument the second argument. Is destroyed * when the class is destroyed, or by another * call to setArgumentB. */ void setArgumentB( Expression *inArgument ); /** * Gets the first argument for the operation. * * @return the first argument. Is destroyed * when the class is destroyed, or by another * call to setArgumentB. Returns NULL if the * argument was never set. */ Expression *getArgumentA(); /** * Gets the second argument for the operation. * * @return the second argument. Is destroyed * when the class is destroyed, or by another * call to setArgumentB. Returns NULL if the * argument was never set. */ Expression *getArgumentB(); // implements the Expression interface virtual long getNumArguments(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); virtual Expression *extractArgument( long inArgumentNumber ); protected: Expression *mArgumentA; Expression *mArgumentB; }; inline BinaryOperationExpression::BinaryOperationExpression( Expression *inArgumentA, Expression *inArgumentB ) : mArgumentA( inArgumentA ), mArgumentB( inArgumentB ) { } inline BinaryOperationExpression::~BinaryOperationExpression() { if( mArgumentA != NULL ) { delete mArgumentA; } if( mArgumentB != NULL ) { delete mArgumentB; } } inline void BinaryOperationExpression::setArgumentA( Expression *inArgument ) { if( mArgumentA != NULL && inArgument != mArgumentA ) { delete mArgumentA; } mArgumentA = inArgument; } inline void BinaryOperationExpression::setArgumentB( Expression *inArgument ) { if( mArgumentB != NULL && inArgument != mArgumentB ) { delete mArgumentB; } mArgumentB = inArgument; } inline Expression *BinaryOperationExpression::getArgumentA() { return mArgumentA; } inline Expression *BinaryOperationExpression::getArgumentB() { return mArgumentB; } inline long BinaryOperationExpression::getNumArguments() { return 2; } inline Expression *BinaryOperationExpression::getArgument( long inArgumentNumber ) { switch( inArgumentNumber ) { case 0: return getArgumentA(); break; case 1: return getArgumentB(); break; default: return NULL; break; } } inline void BinaryOperationExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { switch( inArgumentNumber ) { case 0: setArgumentA( inArgument ); return; break; case 1: setArgumentB( inArgument ); return; break; default: return; break; } } inline Expression *BinaryOperationExpression::extractArgument( long inArgumentNumber ) { Expression *returnArg; switch( inArgumentNumber ) { case 0: returnArg = mArgumentA; mArgumentA = NULL; return returnArg; break; case 1: returnArg = mArgumentB; mArgumentB = NULL; return returnArg; break; default: return NULL; break; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/MultiConstantArgumentExpression.h0000640000175000017500000002001207347050250031250 0ustar pabspabs/* * Modification History * * 2001-August-30 Jason Rohrer * Created. * * 2001-August-31 Jason Rohrer * Added public access to the wrapped expression. * Fixed a comment and some bugs. * Fixed a memory leak. * * 2001-September-9 Jason Rohrer * Added public function for setting the wrapped expression after * construction. * Added an extractArgument() implemenation. * Added an extractWrappedExpression() function to work * around some destruction issues. */ #ifndef MULTI_CONSTANT_ARGUMENT_EXPRESSION_INCLUDED #define MULTI_CONSTANT_ARGUMENT_EXPRESSION_INCLUDED #include "Expression.h" #include "ConstantExpression.h" #include "minorGems/util/SimpleVector.h" /** * An expression implementation that treats the constants * in another expression as assignable arguments. * * Most common usage pattern: * Set up an Expression containing one constant for each * assignable argument and pass this expression into * the constructor. * * Notes: * * getArugment() returns the actual constant expression * contained in the wrapped expression. It therefore * should not be destroyed by the caller. * * setArgument() evaluates the passed-in expression * and then sets the value of the constant expression to the * value produced by the evaluation. * The passed-in expression must be destroyed by the caller, * and it can be destroyed as soon as setArgument() returns. * * @author Jason Rohrer */ class MultiConstantArgumentExpression : public Expression { public: /** * Constructs a new MultiArgumentExpression. * * @param inExpression the expression to use as the * body of this new expression, where each constant in * inExpression will be used as an argument to the * new expression. Will be destroyed when this class * is destroyed. */ MultiConstantArgumentExpression( Expression *inExpression ); ~MultiConstantArgumentExpression(); /** * Sets the expression wrapped by this mult-argument expression. * * Note that calling this function destroys the currently wrapped * expression. * * @param inExpression the expression to use as the * body of this new expression, where each constant in * inExpression will be used as an argument to the * new expression. Will be destroyed when this class * is destroyed. */ void setWrappedExpression( Expression *inExpression ); /** * Gets the expression wrapped by this mult-argument expression. * * @return the expression wrapped by this expression. * Will be destroyed when this class is destroyed. */ Expression *getWrappedExpression(); /** * Gets the expression wrapped by this mult-argument expression, * and sets the internal expression to NULL (thus, the returned * argument will not be destroyed when this class is destroyed. * * @return the expression wrapped by this expression. Must * be destroyed by caller. */ Expression *extractWrappedExpression(); // these implement the Expression interface virtual double evaluate(); virtual long getID(); virtual long getNumArguments(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); // note that extractArgument() always returns NULL for this class virtual Expression *extractArgument( long inArgumentNumber ); virtual void print(); virtual Expression *copy(); /** * A static version of getID(). */ static long staticGetID(); protected: Expression *mExpression; static long mID; long mNumConstants; ConstantExpression **mConstants; /** * Finds the constants in an expression in a consistent way * (using depth-first, left-first search). * * @param inExpression the expression to search for constants in. * @param outExpressions a pointer where the array of found * expressions will be returned. * * @return the number of constants found. */ long findConstants( Expression *inExpression, ConstantExpression ***outExpressions ); /** * Recursive proceedure used by findConstants() * * @param inExpression the (sub)expression to find constants in. * @param inConstantVector the vector to add found constants to. */ void findConstantsRecursive( Expression *inExpression, SimpleVector *inConstantVector ); }; // static init long MultiConstantArgumentExpression::mID = 8; inline MultiConstantArgumentExpression:: MultiConstantArgumentExpression( Expression *inExpression ) : mExpression( NULL ), mConstants( NULL ) { setWrappedExpression( inExpression ); } inline MultiConstantArgumentExpression:: ~MultiConstantArgumentExpression() { delete [] mConstants; delete mExpression; } inline void MultiConstantArgumentExpression:: setWrappedExpression( Expression *inExpression ) { if( mExpression != NULL && mExpression != inExpression ) { delete mExpression; } mExpression = inExpression; if( mConstants != NULL ) { delete [] mConstants; } mNumConstants = findConstants( inExpression, &mConstants ); } inline Expression *MultiConstantArgumentExpression:: getWrappedExpression() { return mExpression; } inline Expression *MultiConstantArgumentExpression:: extractWrappedExpression() { Expression *wrappedExpression = mExpression; mExpression = NULL; return wrappedExpression; } inline double MultiConstantArgumentExpression::evaluate() { return mExpression->evaluate(); } inline long MultiConstantArgumentExpression::getID() { return mID; } inline long MultiConstantArgumentExpression::staticGetID() { return mID; } inline long MultiConstantArgumentExpression::getNumArguments() { return mNumConstants; } inline Expression *MultiConstantArgumentExpression::getArgument( long inArgumentNumber ) { if( inArgumentNumber >= 0 && inArgumentNumber < mNumConstants ) { return mConstants[inArgumentNumber]; } else { return NULL; } } inline void MultiConstantArgumentExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { Expression *expressionToSet = getArgument( inArgumentNumber ); if( expressionToSet != NULL ) { ConstantExpression *constantExpression = (ConstantExpression*)expressionToSet; // set the constant to the evaluation of inArgument constantExpression->setValue( inArgument->evaluate() ); } else { printf( "MultiConstantArgumentExpression: setting an out of range " ); printf( "argument number, %ld\n", inArgumentNumber ); } } inline Expression *MultiConstantArgumentExpression::extractArgument( long inArgumentNumber ) { return NULL; } inline void MultiConstantArgumentExpression::print() { mExpression->print(); } inline Expression *MultiConstantArgumentExpression::copy() { return new MultiConstantArgumentExpression( mExpression->copy() ); } inline long MultiConstantArgumentExpression::findConstants( Expression *inExpression, ConstantExpression ***outExpressions ) { SimpleVector *constantVector = new SimpleVector(); findConstantsRecursive( inExpression, constantVector ); long numConstants = constantVector->size(); ConstantExpression **returnArray = new ConstantExpression*[ numConstants ]; for( int i=0; igetElement(i) ); } delete constantVector; // set passed-in pointer to our array *outExpressions = returnArray; return numConstants; } inline void MultiConstantArgumentExpression::findConstantsRecursive( Expression *inExpression, SimpleVector *inConstantVector ) { if( inExpression->getID() == ConstantExpression::staticGetID() ) { // the passed-in expression is a constant inConstantVector->push_back( (ConstantExpression *)inExpression ); return; } else { // call recursively on each argument for( int i=0; igetNumArguments(); i++ ) { Expression *argument = inExpression->getArgument( i ); findConstantsRecursive( argument, inConstantVector ); } return; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/LnExpression.h0000640000175000017500000000331607653343375025337 0ustar pabspabs/* * Modification History * * 2003-April-28 Jason Rohrer * Created. */ #ifndef LN_EXPRESSION_INCLUDED #define LN_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include /** * Expression implementation of a unary ln (natural log) operation. * * @author Jason Rohrer */ class LnExpression : public UnaryOperationExpression { public: /** * Constructs a unary ln operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ LnExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long LnExpression::mID = 10; inline LnExpression::LnExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double LnExpression::evaluate() { return log( mArgument->evaluate() ); } inline long LnExpression::getID() { return mID; } inline long LnExpression::staticGetID() { return mID; } inline void LnExpression::print() { printf( "( ln" ); mArgument->print(); printf( " )" ); } inline Expression *LnExpression::copy() { LnExpression *copy = new LnExpression( mArgument->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/SinExpression.h0000640000175000017500000000305307252524064025504 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. */ #ifndef SIN_EXPRESSION_INCLUDED #define SIN_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include /** * Expression implementation of a unary sin operation. * * @author Jason Rohrer */ class SinExpression : public UnaryOperationExpression { public: /** * Constructs a unary sin operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ SinExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long SinExpression::mID = 6; inline SinExpression::SinExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double SinExpression::evaluate() { return sin( mArgument->evaluate() ); } inline long SinExpression::getID() { return mID; } inline long SinExpression::staticGetID() { return mID; } inline void SinExpression::print() { printf( "( sin" ); mArgument->print(); printf( " )" ); } inline Expression *SinExpression::copy() { SinExpression *copy = new SinExpression( mArgument->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/Expression.h0000640000175000017500000000717407657013075025046 0ustar pabspabs/* * Modification History * * 2001-February-10 Jason Rohrer * Created. * * 2001-March-7 Jason Rohrer * Added a copy() function interface. * * 2001-September-4 Jason Rohrer * Added a virtual destructor to fix a major memory leak. * * 2001-September-9 Jason Rohrer * Added an extractArgument() function to work around some * object destruction issues. * * 2003-May-9 Jason Rohrer * Added support for replacing a variable. */ #ifndef EXPRESSION_INCLUDED #define EXPRESSION_INCLUDED #include #include "Variable.h" /** * Interface for an expression object. * * @author Jason Rohrer */ class Expression { public: virtual ~Expression(); /** * Evaluates this expression. * * @return the value of this expression. */ virtual double evaluate() = 0; /** * Gets the unique ID of this expression subtype. * * @return the ID of this subtype. */ virtual long getID() = 0; /** * Gets the number of arguments taken by this expression subtype. * * @return the number of arguments for this subtype. */ virtual long getNumArguments() = 0; /** * Gets a specified argument for this expression. * * @param inArgumentNumber the index of this argument. * * @return the specified argument. Will be destroyed * when this class is destroyed. Returns NULL if * the argument has not been set. */ virtual Expression *getArgument( long inArgumentNumber ) = 0; /** * Sets a specified argument for this expression. * * @param inArgumentNumber the index of this argument. * @param inArgument the specified argument. Will be destroyed * when this class is destroyed, or by another call to * setArgument. */ virtual void setArgument( long inArgumentNumber, Expression *inArgument ) = 0; /** * Extracts an argument from this expression. * * This is similar to getArgument(), except that internally * this expression's argument is set to NULL. In other words, * this allows you to setArgument( NULL ) without destroying * the argument. * * @param inArgumentNumber the argument to get. Must be destroyed * by caller. Returns NULL if the argument has not been set. */ virtual Expression *extractArgument( long inArgumentNumber ) = 0; /** * Prints this expression to standard out. */ virtual void print() = 0; /** * Makes a copy of this expression recursively. * * @return a copy of this expression. */ virtual Expression *copy() = 0; /** * Recursively replace a variable in this expression. * * Default implementation calls replacement recursively on each * argument. Expressions that actually deal with variables * should override this implementation. * * @param inTarget the target to replace. * Must be destroyed by caller. * @param inReplacement the variable to replace the target with. * Must be destroyed by caller after this expression is destroyed. */ virtual void replaceVariable( Variable *inTarget, Variable *inReplacement ); }; inline Expression::~Expression() { // do nothing } inline void Expression::replaceVariable( Variable *inTarget, Variable *inReplacement ) { // call recursively on each of our arguments int numArgs = getNumArguments(); for( int i=0; ireplaceVariable( inTarget, inReplacement ); } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/RandomVariable.h0000640000175000017500000000265307655253371025575 0ustar pabspabs/* * Modification History * * 2003-May-4 Jason Rohrer * Created. */ #ifndef RANDOM_VARIABLE_INCLUDED #define RANDOM_VARIABLE_INCLUDED #include "minorGems/util/stringUtils.h" #include "minorGems/util/random/RandomSource.h" /** * Wrapper for a random-valued variable. * * @author Jason Rohrer */ class RandomVariable : public Variable{ public: /** * Constructs a variable * * @param inName the name of the variable. * Must be destroyed by caller if non-const. * @param inRandSource the source for random numbers. * Must be destroyed by caller after this class is destroyed. */ RandomVariable( char *inName, RandomSource *inRandSource ); virtual ~RandomVariable(); // overrides Variable functions virtual double getValue(); virtual void setValue( double inValue ); protected: RandomSource *mRandSource; }; inline RandomVariable::RandomVariable( char *inName, RandomSource *inRandSource ) : Variable( inName, 0 ), mRandSource( inRandSource ) { } inline RandomVariable::~RandomVariable() { } inline double RandomVariable::getValue() { return mRandSource->getRandomDouble(); } inline void RandomVariable::setValue( double inValue ) { // do nothing } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/RandomExpressionFactory.h0000640000175000017500000001005707345177105027530 0ustar pabspabs/* * Modification History * * 2001-August-30 Jason Rohrer * Created. * * 2001-August-31 Jason Rohrer * Finished implementation. * Fixed a compile bug. * * 2001-September-4 Jason Rohrer * Added support for FixedConstantExpressions. */ #ifndef RANDOM_EXPRESSION_FACTORY_INCLUDED #define RANDOM_EXPRESSION_FACTORY_INCLUDED #include "Expression.h" #include "ConstantExpression.h" #include "FixedConstantExpression.h" #include "InvertExpression.h" #include "ProductExpression.h" #include "PowerExpression.h" #include "SumExpression.h" #include "NegateExpression.h" #include "SinExpression.h" #include "minorGems/util/random/RandomSource.h" /** * Utility class for constructing random expressions. * * @author Jason Rohrer */ class RandomExpressionFactory { public: /** * Constructs a random expression factory. * * @param inRandSource the source for random numbers * to use while constructing expressions. * Must be destroyed by caller after this class is destroyed. */ RandomExpressionFactory( RandomSource *inRandSource ); /** * Recursively constructs a random expression with all parameters * filled. * * @param inProbOfStopping the probability of stopping at each * branch of the expression. Upon stopping on a certain branch * of the recursion, it simply returns a constant expression. * @param inProbOfFixedConstant the probability of a fixed * constant expression being inserted upon stopping (as opposed * to a (mutable) constant expression). * @param inMaxDepth if this is reached, the recursion * stops regardless of inProbOfStopping. * @param inConstantMax the maximum value for a constant expression. * * @return the constructed expression. */ Expression *constructRandomExpression( double inProbOfStopping, double inProbOfFixedConstant, int inMaxDepth, double inConstantMax ); protected: RandomSource *mRandSource; }; inline RandomExpressionFactory::RandomExpressionFactory( RandomSource *inRandSource ) : mRandSource( inRandSource ) { } inline Expression *RandomExpressionFactory::constructRandomExpression( double inProbOfStopping, double inProbOfFixedConstant, int inMaxDepth, double inConstantMax ) { // fill in constant expressions only at the leaves if( inMaxDepth == 0 || mRandSource->getRandomDouble() <= inProbOfStopping ) { // stop if( mRandSource->getRandomDouble() <= inProbOfFixedConstant ) { return new FixedConstantExpression( inConstantMax * ( mRandSource->getRandomDouble() ) ); } else { return new ConstantExpression( inConstantMax * ( mRandSource->getRandomDouble() ) ); } } else { // keep filling in non constant expressions randomly // FILL IN HERE // we have 6 expression types int randVal = mRandSource->getRandomBoundedInt( 0, 3 ); Expression *outExpression; // pick an expression type switch( randVal ) { case 0: outExpression = new NegateExpression( NULL ); break; case 1: outExpression = new ProductExpression( NULL, NULL ); break; case 2: outExpression = new SinExpression( NULL ); break; case 3: outExpression = new SumExpression( NULL, NULL ); break; default: // should never happen, but... printf( "RandomExpressionFactory: " ); printf( "Error while generating random expression\n" ); // default to a constant expression of 0 outExpression = new ConstantExpression( 0 ); break; } // now recursively fill in the arguments in succession for( int i=0; igetNumArguments(); i++ ) { // create a random expression as the argument // note that we decrement inMaxDepth here Expression *argument = constructRandomExpression( inProbOfStopping, inProbOfFixedConstant, inMaxDepth - 1, inConstantMax ); // set the argument into our expression outExpression->setArgument( i, argument ); } // now expression is complete. return outExpression; } // end of non-constant else case } // end of constructRandomExpression() #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/NegateExpression.h0000640000175000017500000000306607252524064026162 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. */ #ifndef NEGATE_EXPRESSION_INCLUDED #define NEGATE_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" /** * Expression implementation of a unary negate operation. * * @author Jason Rohrer */ class NegateExpression : public UnaryOperationExpression { public: /** * Constructs a unary negate operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ NegateExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long NegateExpression::mID = 3; inline NegateExpression::NegateExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double NegateExpression::evaluate() { return -( mArgument->evaluate() ); } inline long NegateExpression::getID() { return mID; } inline long NegateExpression::staticGetID() { return mID; } inline void NegateExpression::print() { printf( "( -" ); mArgument->print(); printf( " )" ); } inline Expression *NegateExpression::copy() { NegateExpression *copy = new NegateExpression( mArgument->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/FixedConstantExpression.h0000640000175000017500000000444607347050154027532 0ustar pabspabs/* * Modification History * * 2001-September-4 Jason Rohrer * Created. * * 2001-September-9 Jason Rohrer * Added an extractArgument() implemenation. */ #ifndef FIXED_CONSTANT_EXPRESSION_INCLUDED #define FIXED_CONSTANT_EXPRESSION_INCLUDED #include "Expression.h" /** * Expression implementation of a fixed constant. * * @author Jason Rohrer */ class FixedConstantExpression : public Expression { public: /** * Constructs a FixedConstant expression. * * @param inValue the constant value. */ FixedConstantExpression( double inValue ); /** * Gets the constant value. * * @return the constant value. */ double getValue(); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getNumArguments(); virtual long getID(); virtual Expression *copy(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); virtual Expression *extractArgument( long inArgumentNumber ); virtual void print(); protected: double mValue; static long mID; }; // static init long FixedConstantExpression::mID = 9; inline FixedConstantExpression::FixedConstantExpression( double inValue ) : mValue( inValue ) { } inline double FixedConstantExpression::getValue() { return mValue; } inline double FixedConstantExpression::evaluate() { return mValue; } long FixedConstantExpression::getNumArguments() { return 0; } inline long FixedConstantExpression::getID() { return mID; } inline Expression *FixedConstantExpression::getArgument( long inArgumentNumber ) { return NULL; } inline void FixedConstantExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { // do nothing return; } inline Expression *FixedConstantExpression::extractArgument( long inArgumentNumber ) { return NULL; } inline Expression *FixedConstantExpression::copy() { FixedConstantExpression *copy = new FixedConstantExpression( mValue ); return copy; } inline long FixedConstantExpression::staticGetID() { return mID; } inline void FixedConstantExpression::print() { printf( "( %f )", mValue ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/CosExpression.h0000640000175000017500000000332107653343375025506 0ustar pabspabs/* * Modification History * * 2003-April-28 Jason Rohrer * Created. */ #ifndef COS_EXPRESSION_INCLUDED #define COS_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include /** * Expression implementation of a unary cos operation. * * @author Jason Rohrer */ class CosExpression : public UnaryOperationExpression { public: /** * Constructs a unary cos operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ CosExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long CosExpression::mID = 11; inline CosExpression::CosExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double CosExpression::evaluate() { return cos( mArgument->evaluate() ); } inline long CosExpression::getID() { return mID; } inline long CosExpression::staticGetID() { return mID; } inline void CosExpression::print() { printf( "( cos" ); mArgument->print(); printf( " )" ); } inline Expression *CosExpression::copy() { CosExpression *copy = new CosExpression( mArgument->copy() ); return copy; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/VariableExpression.h0000640000175000017500000000633307657013075026510 0ustar pabspabs/* * Modification History * * 2003-April-29 Jason Rohrer * Created. * * 2003-May-9 Jason Rohrer * Added support for replacing a variable. */ #ifndef VARIABLE_EXPRESSION_INCLUDED #define VARIABLE_EXPRESSION_INCLUDED #include "Expression.h" #include "Variable.h" /** * Expression that contains a single variable. * * @author Jason Rohrer */ class VariableExpression : public Expression { public: /** * Constructs a variable expression. * * @param inVariable the variable. * Must be destroyed by caller after this class is destroyed. */ VariableExpression( Variable *inVariable ); /** * Gets this expression's variable. * * @return the variable. * Must not be destroyed by caller. */ Variable *getVariable(); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); virtual long getNumArguments(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); virtual Expression *extractArgument( long inArgumentNumber ); // overrides the default implementation virtual void replaceVariable( Variable *inTarget, Variable *inReplacement ); protected: static long mID; Variable *mVariable; }; // static init long VariableExpression::mID = 14; inline VariableExpression::VariableExpression( Variable *inVariable ) : mVariable( inVariable ) { } inline Variable *VariableExpression::getVariable() { return mVariable; } inline double VariableExpression::evaluate() { return mVariable->getValue(); } inline long VariableExpression::getID() { return mID; } inline long VariableExpression::staticGetID() { return mID; } inline void VariableExpression::print() { char *varName = mVariable->getName(); printf( " %s ", varName ); delete [] varName; } inline Expression *VariableExpression::copy() { // don't copy our variable VariableExpression *copy = new VariableExpression( mVariable ); return copy; } long VariableExpression::getNumArguments() { return 0; } inline Expression *VariableExpression::getArgument( long inArgumentNumber ) { return NULL; } inline void VariableExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { // do nothing return; } inline Expression *VariableExpression::extractArgument( long inArgumentNumber ) { return NULL; } inline void VariableExpression::replaceVariable( Variable *inTarget, Variable *inReplacement ) { if( mVariable == inTarget ) { mVariable = inReplacement; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/expression/ExpressionSerializer.h0000640000175000017500000001374707360711032027070 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-7 Jason Rohrer * Removed and extra if statement from the deserialization function. * * 2001-August-31 Jason Rohrer * Added support for MultiConstantArgumentExpressions. * * 2001-September-4 Jason Rohrer * Added support for FixedConstantExpressions. * * 2001-October-9 Jason Rohrer * Fixed a bug in deserializing MultiConstantArgumentExpressions. */ #ifndef EXPRESSION_SERIALIZER_INCLUDED #define EXPRESSION_SERIALIZER_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include "BinaryOperationExpression.h" #include "ConstantExpression.h" #include "InvertExpression.h" #include "ProductExpression.h" #include "PowerExpression.h" #include "SumExpression.h" #include "NegateExpression.h" #include "SinExpression.h" #include "FixedConstantExpression.h" #include "MultiConstantArgumentExpression.h" #include "minorGems/io/OutputStream.h" #include "minorGems/io/InputStream.h" #include "minorGems/io/TypeIO.h" /** * Utility class for serializing expressions * * @author Jason Rohrer */ class ExpressionSerializer { public: /** * Serializes an expression onto a stream. * * @param inExpression the expression to serialize. * @param inOutputStream the stream to write to. * * @return the number of bytes written. */ static long serializeExpression( Expression *inExpression, OutputStream *inOutputStream ); /** * Deserializes an expression from a stream. * * @param outExpression a pointer to where the expression * pointer will be returned. A new expression is created, * and it must be destroyed by the caller. * @param inInputStream the stream to read from. * * @return the number of bytes read. */ static long deserializeExpression( Expression **outExpression, InputStream *inInputStream ); }; // these can both be implemented recursively... exciting! inline long ExpressionSerializer::serializeExpression( Expression *inExpression, OutputStream *inOutputStream ) { int numBytes = 0; long expressionID = inExpression->getID(); // write the expression ID numBytes += inOutputStream->writeLong( expressionID ); // first, deal with constant case if( expressionID == ConstantExpression::staticGetID() ) { // write the constant ConstantExpression *c = (ConstantExpression *)inExpression; numBytes += inOutputStream->writeDouble( c->getValue() ); } else if( expressionID == FixedConstantExpression::staticGetID() ) { // write the constant FixedConstantExpression *c = (FixedConstantExpression *)inExpression; numBytes += inOutputStream->writeDouble( c->getValue() ); } // next deal with the multi-argument case else if( expressionID == MultiConstantArgumentExpression::staticGetID() ) { MultiConstantArgumentExpression *m = (MultiConstantArgumentExpression*)inExpression; Expression *wrappedExpression = m->getWrappedExpression(); numBytes += serializeExpression( wrappedExpression, inOutputStream ); } // finally, deal with the general case else { // write each of the expression's arguments in succession for( int i=0; igetNumArguments(); i++ ) { // call serialize recursively numBytes += serializeExpression( inExpression->getArgument( i ), inOutputStream ); } } return numBytes; } inline long ExpressionSerializer::deserializeExpression( Expression **outExpression, InputStream *inInputStream ) { int numBytes = 0; long expressionID; // read the expression ID numBytes += inInputStream->readLong( &expressionID ); // first, deal with constant case if( expressionID == ConstantExpression::staticGetID() ) { double constantValue; numBytes += inInputStream->readDouble( &constantValue ); *outExpression = new ConstantExpression( constantValue ); } else if( expressionID == FixedConstantExpression::staticGetID() ) { double constantValue; numBytes += inInputStream->readDouble( &constantValue ); *outExpression = new FixedConstantExpression( constantValue ); } // next deal with the multi-argument case else if( expressionID == MultiConstantArgumentExpression::staticGetID() ) { // call deserialize recursively Expression *wrappedExpression; numBytes += deserializeExpression( &wrappedExpression, inInputStream ); MultiConstantArgumentExpression *m = new MultiConstantArgumentExpression( wrappedExpression ); *outExpression = (Expression *)m; } // finally, deal with the general case else { // switch based on expression type // note that we can't use switch/case here because // staticGetID doesn't return a constant if( expressionID == InvertExpression::staticGetID() ) { *outExpression = new InvertExpression( NULL ); } else if( expressionID == NegateExpression::staticGetID() ) { *outExpression = new NegateExpression( NULL ); } else if( expressionID == PowerExpression::staticGetID() ) { *outExpression = new PowerExpression( NULL, NULL ); } else if( expressionID == ProductExpression::staticGetID() ) { *outExpression = new ProductExpression( NULL, NULL ); } else if( expressionID == SinExpression::staticGetID() ) { *outExpression = new SinExpression( NULL ); } else if( expressionID == SumExpression::staticGetID() ) { *outExpression = new SumExpression( NULL, NULL ); } else { printf( "Unknown expression type %d read from stream\n", expressionID ); // default to a constant expression of 0 *outExpression = new ConstantExpression( 0 ); } // now deserialize the arguments // read each of the expression's arguments in succession for( int i=0; i<(*outExpression)->getNumArguments(); i++ ) { // call deserialize recursively Expression *argument; numBytes += deserializeExpression( &argument, inInputStream ); // set the argument into our expression (*outExpression)->setArgument( i, argument ); } } // end non-constant case return numBytes; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/geometry/0000750000175000017500000000000011401021060022127 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/math/geometry/Angle3D.h0000640000175000017500000001106710476647514023560 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2000-December-14 Jason Rohrer * Added data members and constructors. * * 2001-January-10 Jason Rohrer * Made class serializable. * * 2001-January-15 Jason Rohrer * Added a print() function. * * 2001-February-10 Jason Rohrer * Added a linear sum function. * * 2001-March-11 Jason Rohrer * Added a scale function. * * 2004-February-13 Jason Rohrer * Added setComponents functions. * * 2006-September-3 Jason Rohrer * Added zero-angle constructor. */ #ifndef ANGLE_3D_INCLUDED #define ANGLE_3D_INCLUDED #include #include "minorGems/io/Serializable.h" /** * Angle in 3-space. * * @author Jason Rohrer */ class Angle3D : public Serializable { public: // rotations in radians around x, y, and z axis double mX, mY, mZ; /** * Constructs an Angle3D. */ Angle3D( double inX, double inY, double inZ ); /** * Constructs a zero angle. */ Angle3D(); /** * Constructs an Angle3D by copying the parameters from * another Angle3D. * * @param inOther angle to copy parameters from. */ Angle3D( Angle3D *inOther ); /** * Sets the components of this angle. */ void setComponents( double inX, double inY, double inZ ); /** * Sets components by copying the parameters from * another Angle3D. * * @param inOther vector to copy parameters from. * Must be destroyed by caller. */ void setComponents( Angle3D *inOther ); /** * Sums another angle with this angle. * * @param inOther angle to add to this angle. */ void add( Angle3D *inOther ); /** * Subtracts a angle from this angle. * * @param inOther angle to subtract from this angle. */ void subtract( Angle3D *inOther ); /** * Multiplies this angle by a scalar. * * @param inScalar scalar to multiply this angle by. */ void scale( double inScalar ); /** * Computes the linear weighted sum of two angles. * * @param inFirst the first angle. * @param inSecond the second angle. * @param inFirstWeight the weight given to the first angle in the * sum. The second angle is weighted (1-inFirstWeight). * * @return the sum angle. Must be destroyed by caller. */ static Angle3D *linearSum( Angle3D *inFirst, Angle3D *inSecond, double inFirstWeight ); /** * Prints this angle to standard out. */ void print(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline Angle3D::Angle3D( double inX, double inY, double inZ ) : mX( inX ), mY( inY ), mZ( inZ ) { } inline Angle3D::Angle3D() : mX( 0 ), mY( 0 ), mZ( 0 ) { } inline Angle3D::Angle3D( Angle3D *inOther ) : mX( inOther->mX ), mY( inOther->mY ), mZ( inOther->mZ ) { } inline void Angle3D::setComponents( double inX, double inY, double inZ ) { mX = inX; mY = inY; mZ = inZ; } inline void Angle3D::setComponents( Angle3D *inOther ) { setComponents( inOther->mX, inOther->mY, inOther->mZ ); } inline void Angle3D::add( Angle3D *inOther ) { mX += inOther->mX; mY += inOther->mY; mZ += inOther->mZ; } inline void Angle3D::subtract( Angle3D *inOther ) { mX -= inOther->mX; mY -= inOther->mY; mZ -= inOther->mZ; } inline void Angle3D::scale( double inScalar ) { mX *= inScalar; mY *= inScalar; mZ *= inScalar; } inline Angle3D *Angle3D::linearSum( Angle3D *inFirst, Angle3D *inSecond, double inFirstWeight ) { double secondWeight = 1 - inFirstWeight; double x = inFirstWeight * inFirst->mX + secondWeight * inSecond->mX; double y = inFirstWeight * inFirst->mY + secondWeight * inSecond->mY; double z = inFirstWeight * inFirst->mZ + secondWeight * inSecond->mZ; return new Angle3D( x, y, z ); } inline int Angle3D::serialize( OutputStream *inOutputStream ) { int numBytes = 0; numBytes += inOutputStream->writeDouble( mX ); numBytes += inOutputStream->writeDouble( mY ); numBytes += inOutputStream->writeDouble( mZ ); return numBytes; } inline int Angle3D::deserialize( InputStream *inInputStream ) { int numBytes = 0; numBytes += inInputStream->readDouble( &( mX ) ); numBytes += inInputStream->readDouble( &( mY ) ); numBytes += inInputStream->readDouble( &( mZ ) ); return numBytes; } inline void Angle3D::print() { printf( "(%f, %f, %f)", mX, mY, mZ ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/geometry/Transform3D.h0000640000175000017500000001757307237024761024507 0ustar pabspabs/* * Modification History * * 2001-January-16 Jason Rohrer * Created. * Added a missing getMatrix() function. * * 2001-February-3 Jason Rohrer * Updated serialization code to use new interfaces. */ #ifndef TRANSFORM_3D_INCLUDED #define TRANSFORM_3D_INCLUDED #include #include #include "minorGems/io/Serializable.h" #include "Vector3D.h" #include "Angle3D.h" /** * An affine transformation in 3D. * * @author Jason Rohrer */ class Transform3D : public Serializable { public: /** * Creates a new identity transform. */ Transform3D(); /** * Creates a new transform by copying another transform. * * @param inOther the transform to copy. */ Transform3D( Transform3D *inOther ); /** * Adds a rotation to the end of this transform. Note that the * rotations specified by inAngle are applied in the following order: * rotX, rotY, rotZ * * @param inAngle angle rotation to add. Must be destructed by caller. */ void rotate( Angle3D *inAngle ); /** * Adds a scaling operation to the end of this transform. * * @param inScale the uniform scale factor. */ void scale( double inScale ); /** * Adds a scaling operation to the end of this transform. * * @param inScaleX the x direction scale factor. * @param inScaleY the y direction scale factor. * @param inScaleZ the z direction scale factor. */ void scale( double inScaleX, double inScaleY, double inScaleZ ); /** * Adds a translation to the end of this transform. * * @param inTranslation angle rotation to add. * Must be destructed by caller. */ void translate( Vector3D *inTranslation ); /** * Adds a transformation to the end of this transformation. * * @param inTransform the transform to add. inTransform is no * modified by this call, and must be destroyed by the caller. */ void transform( Transform3D *inTransform ); /** * Transforms a vector using the built-up transformation. * * @param inTarget the vector to transform. The object passed * in is modified directly, and must be destroyed by the caller. */ void apply( Vector3D *inTarget ); /** * Transforms a vector using the built-up transformation, but skips * the translation part of the transform. This is useful when * applying the transform to normals, when translations will screw * them up. * * @param inTarget the vector to transform. The object passed * in is modified directly, and must be destroyed by the caller. */ void applyNoTranslation( Vector3D *inTarget ); /** * Gets the transformation matrix underlying this transform. * * @return the transformation matrix. */ double *getMatrix(); /** * Prints the transformation matrix to standard out. */ void print(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); private: double mMatrix[4][4]; /** * Multiplies mMatrix by inMatrix, i.e., mMatrix = inMatrix * mMatrix. * * Note that this reversed multiplication order works for concatonating * transformation matrices when we're using column vectors for our 3D * points. Thus, if after the above operation, we compute: * point = mMatrix * point, * we will be transformping point first by the original * mMatrix and then by inMatrix. * * @param inMatrix the matrix to multiply mMatrix by. */ void multiply( double inMatrix[][4] ); /** * Multiplies inMatrixA by inMatrixB, i.e., * inMatrixA = inMatrixA * inMatrixB. * * @param inMatrixA the first matrix in the multiplication, and * the destination of the result. * @param inMatrixB the second matrix in the multiplication. */ //void multiply( double[4][4] inMatrixA, double[4][4] inMatrixB ); }; inline Transform3D::Transform3D() { double tempM[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; memcpy( mMatrix, tempM, 16 * sizeof( double ) ); } inline Transform3D::Transform3D( Transform3D *inOther ) { memcpy( mMatrix, inOther->getMatrix(), 16 * sizeof( double ) ); } inline void Transform3D::rotate( Angle3D *inAngle ) { double aX = inAngle->mX; double rotX[4][4] = { { 1, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 1 } }; rotX[1][1] = cos( aX ); rotX[1][2] = -sin( aX ); rotX[2][1] = sin( aX ); rotX[2][2] = cos( aX ); double aY = inAngle->mY; double rotY[4][4] = { { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 1 } }; rotY[0][0] = cos( aY ); rotY[0][2] = sin( aY ); rotY[2][0] = -sin( aY ); rotY[2][2] = cos( aY ); double aZ = inAngle->mZ; double rotZ[4][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; rotZ[0][0] = cos( aZ ); rotZ[0][1] = -sin( aZ ); rotZ[1][0] = sin( aZ ); rotZ[1][1] = cos( aZ ); multiply( rotX ); multiply( rotY ); multiply( rotZ ); } inline void Transform3D::scale( double inScale ) { scale( inScale, inScale, inScale ); } inline void Transform3D::scale( double inScaleX, double inScaleY, double inScaleZ ) { double scaleM[4][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 1 } }; scaleM[0][0] = inScaleX; scaleM[1][1] = inScaleY; scaleM[2][2] = inScaleZ; multiply( scaleM ); } inline void Transform3D::translate( Vector3D *inTranslation ) { double translateM[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; translateM[0][3] = inTranslation->mX; translateM[1][3] = inTranslation->mY; translateM[2][3] = inTranslation->mZ; multiply( translateM ); } inline void Transform3D::transform( Transform3D *inTransform ) { double tempM[4][4]; memcpy( tempM, inTransform->getMatrix(), 16 * sizeof( double ) ); multiply( tempM ); } inline void Transform3D::apply( Vector3D *inTarget ) { double x = inTarget->mX; double y = inTarget->mY; double z = inTarget->mZ; inTarget->mX = mMatrix[0][0] * x + mMatrix[0][1] * y + mMatrix[0][2] * z + mMatrix[0][3]; inTarget->mY = mMatrix[1][0] * x + mMatrix[1][1] * y + mMatrix[1][2] * z + mMatrix[1][3]; inTarget->mZ = mMatrix[2][0] * x + mMatrix[2][1] * y + mMatrix[2][2] * z + mMatrix[2][3]; } inline void Transform3D::applyNoTranslation( Vector3D *inTarget ) { double x = inTarget->mX; double y = inTarget->mY; double z = inTarget->mZ; inTarget->mX = mMatrix[0][0] * x + mMatrix[0][1] * y + mMatrix[0][2] * z; inTarget->mY = mMatrix[1][0] * x + mMatrix[1][1] * y + mMatrix[1][2] * z; inTarget->mZ = mMatrix[2][0] * x + mMatrix[2][1] * y + mMatrix[2][2] * z; } inline void Transform3D::multiply( double inMatrix[][4] ) { double destM[4][4]; for( int dY=0; dY<4; dY++ ) { for( int dX=0; dX<4; dX++ ) { // take a row of inMatrix corresponding to dY // take a column of mMatrix corresponding to dX destM[dY][dX] = 0; for( int i=0; i<4; i++ ) { destM[dY][dX] += inMatrix[dY][i] * mMatrix[i][dX]; } } } memcpy( mMatrix, destM, 16 * sizeof( double ) ); } inline double *Transform3D::getMatrix() { return &( mMatrix[0][0] ); } inline void Transform3D::print() { for( int y=0; y<4; y++ ) { for( int x=0; x<4; x++ ) { printf( "%f ", mMatrix[y][x] ); } printf( "\n" ); } } inline int Transform3D::serialize( OutputStream *inOutputStream ) { int numBytes = 0; for( int y=0; y<4; y++ ) { for( int x=0; x<4; x++ ) { numBytes += inOutputStream->writeDouble( mMatrix[y][x] ); } } return numBytes; } inline int Transform3D::deserialize( InputStream *inInputStream ) { int numBytes = 0; for( int y=0; y<4; y++ ) { for( int x=0; x<4; x++ ) { numBytes += inInputStream->readDouble( &( mMatrix[y][x] ) ); } } return numBytes; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/geometry/GeometricObject3D.h0000640000175000017500000000230607216162537025566 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2000-December-13 Jason Rohrer * Added scale() and copy() functions. */ #ifndef GEOMETRIC_OBJECT_3D_INCLUDED #define GEOMETRIC_OBJECT_3D_INCLUDED #include "Vector3D.h" /** * Interface for any geometric objects in 3-space. * * @author Jason Rohrer */ class GeometricObject3D { public: /** * Moves the object. * * @param inVector a non-normalized vector describing the motion * of the object. */ virtual void move( Vector3D *inVector ) = 0; /** * Rotates the object about the origin. * * @param inAngle the angle to rotate the object by. */ virtual void rotate( Angle3D *inAngle ) = 0; /** * Rotates the object about the origin in reverse direction. * * @param inAngle the angle to rotate the object by * in reverse direction. */ virtual void reverseRotate( Angle3D *inAngle ) = 0; /** * Scales the object about the origin. * * @param inScalar value by which to scale. */ virtual void scale( double inScalar ) = 0; /** * Makes an identical copy of this geometric object. */ virtual GeometricObject3D *copy() = 0; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/geometry/Triangle3D.h0000640000175000017500000001710510204365360024257 0ustar pabspabs/* * Modification History * * 2001-April-14 Jason Rohrer * Created. * * 2005-February-15 Jason Rohrer * Made destructor virtual to quell warnings. */ #ifndef TRIANGLE_3D_INCLUDED #define TRIANGLE_3D_INCLUDED #include "GeometricObject3D.h" #include "LineSegment3D.h" #include "minorGems/io/Serializable.h" #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /** * Triangle (in 3-space). * * @author Jason Rohrer */ class Triangle3D : public GeometricObject3D, public Serializable { public: Vector3D *mPoints[3]; /** * Constructs a triangle. * * The vectors are copied, so the caller * is responsible for deallocating the passed in vectors. * * @param inPointA the first point. * @param inPointB the second point. * @param inPointC the third point. * * The "front" face of this trangle is defined as the face * viewed when the above vertices are in counter-clockwise order. */ Triangle3D( Vector3D *inPointA, Vector3D *inPointB, Vector3D *inPointC); /** * Constructs a triangle by copying parameters * from another triangle. * * @param inOtherTriangle the triangle to copy. */ Triangle3D( Triangle3D *inOtherTriangle ); virtual ~Triangle3D(); /** * Gets a line segment from this triangle. * * @param inIndex the line segment to get, in [0,2]. * * @return a new line segment, or NULL if the index is out of range. * Must be destroyed by caller. */ LineSegment3D *getLineSegment( int inIndex ); /** * Gets the normal for the plane containing this triangle. * * @return the normal vector. Must be destroyed by caller. */ Vector3D *getNormal(); /** * Computes the normal projection of a point onto the plane * defined by this triangle. A point is returned even * if it is not inside the bounds of the triangle. * * @param inPoint the point to project. Must be destroyed by caller. * * @return a new vector representing the projection of the point. * Must be destroyed by caller. */ Vector3D *projectOntoPlane( Vector3D *inPoint ); /** * Checks if a point is inside the bounds of this triangle. * * @param inPoint the point to check. Must be destroyed by caller. * * @return true iff the point is in bounds. */ char isInBounds( Vector3D *inPoint ); // these implement the GeometricObject3D interface void move( Vector3D *inVector ); void rotate( Angle3D *inAngle ); void reverseRotate( Angle3D *inAngle ); void scale( double inScalar ); GeometricObject3D *copy(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline Triangle3D::Triangle3D( Vector3D *inEndpointA, Vector3D *inEndpointB, Vector3D *inEndpointC ) { mPoints[0] = new Vector3D( inEndpointA ); mPoints[1] = new Vector3D( inEndpointB ); mPoints[2] = new Vector3D( inEndpointC ); } inline Triangle3D::Triangle3D( Triangle3D *inOtherTriangle ) { mPoints[0] = new Vector3D( inOtherTriangle->mPoints[0] ); mPoints[1] = new Vector3D( inOtherTriangle->mPoints[1] ); mPoints[2] = new Vector3D( inOtherTriangle->mPoints[2] ); } inline Triangle3D::~Triangle3D() { delete mPoints[0]; delete mPoints[1]; delete mPoints[2]; } inline LineSegment3D *Triangle3D::getLineSegment( int inIndex ) { if( inIndex < 0 || inIndex > 2 ) { return NULL; } else { // can pass in mPoints directly, since constructor copies them return new LineSegment3D( mPoints[inIndex], mPoints[ ( inIndex + 1 ) % 2 ] ); } } inline Vector3D *Triangle3D::getNormal() { // cross C-B with A-B to get a normal pointing towards // the viewer when seeing the points in counter-clockwise order Vector3D *firstLeg = new Vector3D( mPoints[2] ); firstLeg->subtract( mPoints[1] ); Vector3D *secondLeg = new Vector3D( mPoints[0] ); secondLeg->subtract( mPoints[1] ); Vector3D *normal = firstLeg->cross( secondLeg ); normal->normalize(); delete firstLeg; delete secondLeg; return normal; } inline Vector3D *Triangle3D::projectOntoPlane( Vector3D *inPoint ) { // formula found at: // http://astronomy.swin.edu.au/pbourke/geometry/pointplane/ //minimum distance = //(A (xa - xb) + B (ya - yb) + C (za - zb)) / sqrt(A^2 + B^2 + C^2) Vector3D *normal = getNormal(); double minDistance = normal->mX * ( inPoint->mX - mPoints[0]->mX ) + normal->mY * ( inPoint->mY - mPoints[0]->mY ) + normal->mZ * ( inPoint->mZ - mPoints[0]->mZ ); minDistance = minDistance / sqrt( normal->mX * normal->mX + normal->mY * normal->mY + normal->mZ * normal->mZ ); double dot = inPoint->dot( normal ); Vector3D *returnPoint = new Vector3D( inPoint ); if( dot > 0 ) { // inPoint on front side of plane normal->scale( -minDistance ); } else { // inPoint on back side of plane normal->scale( minDistance ); } returnPoint->add( normal ); delete normal; return returnPoint; } inline char Triangle3D::isInBounds( Vector3D *inPoint ) { // this is a nice formula, found at // http://astronomy.swin.edu.au/pbourke/geometry/insidepoly/ // compute the angle between inPoint and every pair of points in the // triangle (each edge). If the sum is 2*pi, then the point // is inside the triangle. // note that we have a hard-coded epsilon value here double epsilon = 0.000001; double angleSum = 0; Vector3D *firstLeg = new Vector3D( mPoints[0] ); firstLeg->subtract( inPoint ); Vector3D *secondLeg = new Vector3D( mPoints[1] ); secondLeg->subtract( inPoint ); Vector3D *thirdLeg = new Vector3D( mPoints[2] ); thirdLeg->subtract( inPoint ); angleSum += acos( firstLeg->dot( secondLeg ) / ( firstLeg->getLength() * secondLeg->getLength() ) ); angleSum += acos( secondLeg->dot( thirdLeg ) / ( secondLeg->getLength() * thirdLeg->getLength() ) ); angleSum += acos( thirdLeg->dot( firstLeg ) / ( thirdLeg->getLength() * firstLeg->getLength() ) ); delete firstLeg; delete secondLeg; delete thirdLeg; if( angleSum < ( 2 * M_PI - epsilon ) ) { // angle too small for point to be inside plane return false; } else { return true; } } inline void Triangle3D::move( Vector3D *inVector ) { mPoints[0]->add( inVector ); mPoints[1]->add( inVector ); mPoints[2]->add( inVector ); } inline void Triangle3D::rotate( Angle3D *inAngle ) { mPoints[0]->rotate( inAngle ); mPoints[1]->rotate( inAngle ); mPoints[2]->rotate( inAngle ); } inline void Triangle3D::reverseRotate( Angle3D *inAngle ) { mPoints[0]->reverseRotate( inAngle ); mPoints[1]->reverseRotate( inAngle ); mPoints[2]->reverseRotate( inAngle ); } inline void Triangle3D::scale( double inScalar ) { mPoints[0]->scale( inScalar ); mPoints[1]->scale( inScalar ); mPoints[2]->scale( inScalar ); } inline GeometricObject3D *Triangle3D::copy() { Triangle3D *copiedTriangle = new Triangle3D( this ); return (GeometricObject3D*)copiedTriangle; } inline int Triangle3D::serialize( OutputStream *inOutputStream ) { int numBytesWritten = 0; numBytesWritten += mPoints[0]->serialize( inOutputStream ); numBytesWritten += mPoints[1]->serialize( inOutputStream ); numBytesWritten += mPoints[2]->serialize( inOutputStream ); return numBytesWritten; } inline int Triangle3D::deserialize( InputStream *inInputStream ) { int numBytesRead = 0; numBytesRead += mPoints[0]->deserialize( inInputStream ); numBytesRead += mPoints[1]->deserialize( inInputStream ); numBytesRead += mPoints[2]->deserialize( inInputStream ); return numBytesRead; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/geometry/Vector3D.h0000640000175000017500000004320110465423012023746 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2000-December-13 Jason Rohrer * Added a function for getting the distance between two vectors. * Added implementations for current functions. * * 2000-December-17 Jason Rohrer * Added a normalize function. * * 2000-December-18 Jason Rohrer * Added a print function. * Added a length function. * * 2000-December-20 Jason Rohrer * Added a cross product function. * * 2001-January-10 Jason Rohrer * Made class serializable. * * 2001-February-3 Jason Rohrer * Updated serialization code to use new interfaces. * * 2001-February-10 Jason Rohrer * Added a linear sum function. * * 2003-June-20 Jason Rohrer * Added function for getting Z angle between vectors. * * 2005-February-4 Jason Rohrer * Added setCoordinates functions. * * 2005-February-6 Jason Rohrer * Added equals function. * * 2005-February-15 Jason Rohrer * Added function for getting Y angle between vectors. * * 2005-February-22 Jason Rohrer * Added function for getting X angle between vectors. * Added function for rotating about an arbitrary axis. * * 2005-February-25 Jason Rohrer * Fixed bugs in get_AngleTo functions when vectors are close to equal. * * 2005-February-28 Jason Rohrer * Fixed bug in getXAngleTo. * Fixed bugs in get_AngleTo functions when vectors are close to opposite. * * 2005-March-3 Jason Rohrer * Fixed bug in getAngleTo when vectors are close to equal or opposite. * * 2005-March-18 Jason Rohrer * Added a getXZDistance function. * * 2006-August-6 Jason Rohrer * Added a no-arg constructor. */ #ifndef VECTOR_3D_INCLUDED #define VECTOR_3D_INCLUDED #include #include #include "Angle3D.h" #include "minorGems/io/Serializable.h" /** * Geometric vector in 3-space. * * @author Jason Rohrer */ class Vector3D : public Serializable { public: double mX, mY, mZ; /** * Constructs a Vector3D. */ Vector3D( double inX, double inY, double inZ ); /** * Constructs a zero-filled Vector3D. */ Vector3D(); /** * Constructs a Vector3D by copying the parameters from * another Vector3D. * * @param inOther vector to copy parameters from. */ Vector3D( Vector3D *inOther ); /** * Sets the values in this vector */ void setCoordinates( double inX, double inY, double inZ ); /** * Sets coordinates by copying the parameters from * another Vector3D. * * @param inOther vector to copy parameters from. * Must be destroyed by caller. */ void setCoordinates( Vector3D *inOther ); /** * Normalizes this vector so that it has a length of 1. */ void normalize(); /** * Tests if another vector is equal to this vector. * * @param inOther vector to test for equality with this vector. * * @return true if equal, false if not. */ char equals( Vector3D *inOther ); /** * Sums another vector with this vector. * * @param inOther vector to add to this vector. */ void add( Vector3D *inOther ); /** * Subtracts a vector from this vector. * * @param inOther vector to subtract from this vector. */ void subtract( Vector3D *inOther ); /** * Computes a dot product of this vector with another. * * @param inOther vector to perform the dot product with. * * @return the dot product of the two vectors. */ double dot( Vector3D *inOther ); /** * Computes a cross product of this vector with another * ( this x other ). The cross computed is right handed. * * @param inOther vector to perform the cross product with. * * @return the cross product of the two vectors. Must be * destroyed by caller. */ Vector3D *cross( Vector3D *inOther ); /** * Computes the angle between this vector and another vector. * * @param inOther vector to find the angle to. * * @return the angle between the two vectors in radians. * This angle is around the axis given by the cross of the two * vectors. * Must be destroyed by caller. */ double getAngleTo( Vector3D *inOther ); /** * Rotates this vector around an arbitrary axis. * * @param inAxis the axis of rotation. Must be normalized. * Must be destroyed by caller. * @param the angle in radians. */ void rotate( Vector3D *inAxis, double inAngle ); /** * Computes the angle between this vector and another vector in * the x-y plane (in other words, the z-axis rotation angle). * * @param inOther vector to find the angle to. * * @return the angle between the two vectors in the x-y plane. * Must be destroyed by caller. */ Angle3D *getZAngleTo( Vector3D *inOther ); /** * Computes the angle between this vector and another vector in * the x-z plane (in other words, the y-axis rotation angle). * * @param inOther vector to find the angle to. * * @return the angle between the two vectors in the x-z plane. * Must be destroyed by caller. */ Angle3D *getYAngleTo( Vector3D *inOther ); /** * Computes the angle between this vector and another vector in * the y-z plane (in other words, the x-axis rotation angle). * * @param inOther vector to find the angle to. * * @return the angle between the two vectors in the y-z plane. * Must be destroyed by caller. */ Angle3D *getXAngleTo( Vector3D *inOther ); /** * Computes the linear weighted sum of two vectors. * * @param inFirst the first vector. * @param inSecond the second vector. * @param inFirstWeight the weight given to the first vector in the * sum. The second vector is weighted (1-inFirstWeight). * * @return the sum vector. Must be destroyed by caller. */ static Vector3D *linearSum( Vector3D *inFirst, Vector3D *inSecond, double inFirstWeight ); /** * Gets the length of this vector. * * @return this vector's length. */ double getLength(); /** * Multiplies this vector by a scalar. * * @param inScalar scalar to multiply this vector by. */ void scale( double inScalar ); /** * Gets the scalar distance between two vectors. * * @param inOther vector to compute the distance with. * * @return the distance between the two vectors. */ double getDistance( Vector3D *inOther ); /** * Gets the scalar distance between two vectors ignoring the y * components. * * @param inOther vector to compute the distance with. * * @return the xz distance between the two vectors. */ double getXZDistance( Vector3D *inOther ); /** * Rotates the vector about the origin. * * @param inAngle the angle to rotate the vector by. */ void rotate( Angle3D *inAngle ); /** * Rotates the vector about the origin in reverse direction. * * @param inAngle the angle to rotate the object by * in reverse direction. */ void reverseRotate( Angle3D *inAngle ); /** * Prints this vector to standard out. */ void print(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline Vector3D::Vector3D( double inX, double inY, double inZ ) : mX( inX ), mY( inY ), mZ( inZ ) { } inline Vector3D::Vector3D() : mX( 0 ), mY( 0 ), mZ( 0 ) { } inline Vector3D::Vector3D( Vector3D *inOther ) : mX( inOther->mX ), mY( inOther->mY ), mZ( inOther->mZ ) { } inline void Vector3D::setCoordinates( double inX, double inY, double inZ ) { mX = inX; mY = inY; mZ = inZ; } inline void Vector3D::setCoordinates( Vector3D *inOther ) { setCoordinates( inOther->mX, inOther->mY, inOther->mZ ); } inline void Vector3D::normalize() { scale( 1/sqrt( dot( this ) ) ); } inline char Vector3D::equals( Vector3D *inOther ) { return mX == inOther->mX && mY == inOther->mY && mZ == inOther->mZ; } inline void Vector3D::add( Vector3D *inOther ) { mX += inOther->mX; mY += inOther->mY; mZ += inOther->mZ; } inline void Vector3D::subtract( Vector3D *inOther ) { mX -= inOther->mX; mY -= inOther->mY; mZ -= inOther->mZ; } inline double Vector3D::dot( Vector3D *inOther ) { return mX * inOther->mX + mY * inOther->mY + mZ * inOther->mZ; } inline Vector3D *Vector3D::cross( Vector3D *inOther ) { double i = this->mY * inOther->mZ - this->mZ * inOther->mY; double j = this->mZ * inOther->mX - this->mX * inOther->mZ; double k = this->mX * inOther->mY - this->mY * inOther->mX; return new Vector3D( i, j, k ); } inline double Vector3D::getAngleTo( Vector3D *inOther ) { // normalize and remove z component Vector3D *normalThis = new Vector3D( this ); normalThis->normalize(); Vector3D *normalOther = new Vector3D( inOther ); normalOther->normalize(); double cosineOfAngle = normalThis->dot( normalOther ); // cosine is ambiguous (same for negative and positive angles) // compute cross product of vectors // the magnitude of the cross is the sine of the angle between the two // vectors Vector3D *crossVector = normalThis->cross( normalOther ); double sineOfAngle = crossVector->getLength(); delete crossVector; delete normalThis; delete normalOther; double angle = acos( cosineOfAngle ); if( sineOfAngle > 0 ) { angle = -angle; } // if vectors are very close, our dot product above might give // a value greater than 1 due to round-off errors // this causes acos to return NAN if( cosineOfAngle >= 1 ) { angle = 0; } // also need to worry if vectors are complete opposites else if( cosineOfAngle <= -1 ) { angle = M_PI; } return angle; } inline void Vector3D::rotate( Vector3D *inAxis, double inAngle ) { // this formula found here: // http://www.gamedev.net/reference/articles/article1199.asp double c = cos( inAngle ); double s = sin( inAngle ); double t = 1 - c; // we assume inAxis is a unit vector (normalized) double x = inAxis->mX; double y = inAxis->mY; double z = inAxis->mZ; double sx = s * x; double sy = s * y; double sz = s * z; double tx = t * x; double ty = t * y; double txx = tx * x; double txy = tx * y; double txz = tx * z; double tyy = ty * y; double tyz = ty * z; double tzz = t * z * z; /* The rotation matrix is: (txx + c) (txy + sz) (txz - sy) (txy - sz) (tyy + c) (tyz + sx) (txz + sy) (tyz - sx) (tzz + c) */ double newX = (txx + c) * mX + (txy + sz) * mY + (txz - sy) * mZ; double newY = (txy - sz) * mX + (tyy + c) * mY + (tyz + sx) * mZ; double newZ = (txz + sy) * mX + (tyz - sx) * mY + (tzz + c) * mZ; mX = newX; mY = newY; mZ = newZ; } inline Angle3D *Vector3D::getZAngleTo( Vector3D *inOther ) { // normalize and remove z component Vector3D *normalThis = new Vector3D( this ); normalThis->mZ = 0; normalThis->normalize(); Vector3D *normalOther = new Vector3D( inOther ); normalOther->mZ = 0; normalOther->normalize(); double cosineOfZAngle = normalThis->dot( normalOther ); // cosine is ambiguous (same for negative and positive angles) // compute dot product with perpendicular vector to get sine // sign of sine will tell us whether angle is positive or negative Angle3D *rightAngleZ = new Angle3D( 0, 0, M_PI / 2 ); normalThis->rotate( rightAngleZ ); delete rightAngleZ; double sineOfZAngle = normalThis->dot( normalOther ); delete normalThis; delete normalOther; double zAngle = acos( cosineOfZAngle ); if( sineOfZAngle < 0 ) { zAngle = -zAngle; } // if vectors are very close, our dot product above might give // a value greater than 1 due to round-off errors // this causes acos to return NAN if( cosineOfZAngle >= 1 ) { zAngle = 0; } // also need to worry if vectors are complete opposites else if( cosineOfZAngle <= -1 ) { zAngle = M_PI; } return new Angle3D( 0, 0, zAngle ); } inline Angle3D *Vector3D::getYAngleTo( Vector3D *inOther ) { // normalize and remove y component Vector3D *normalThis = new Vector3D( this ); normalThis->mY = 0; normalThis->normalize(); Vector3D *normalOther = new Vector3D( inOther ); normalOther->mY = 0; normalOther->normalize(); double cosineOfYAngle = normalThis->dot( normalOther ); // cosine is ambiguous (same for negative and positive angles) // compute dot product with perpendicular vector to get sine // sign of sine will tell us whether angle is positive or negative Angle3D *rightAngleY = new Angle3D( 0, M_PI / 2, 0 ); normalThis->rotate( rightAngleY ); delete rightAngleY; double sineOfYAngle = normalThis->dot( normalOther ); delete normalThis; delete normalOther; double yAngle = acos( cosineOfYAngle ); if( sineOfYAngle < 0 ) { yAngle = -yAngle; } // if vectors are very close, our dot product above might give // a value greater than 1 due to round-off errors // this causes acos to return NAN if( cosineOfYAngle >= 1 ) { yAngle = 0; } // also need to worry if vectors are complete opposites else if( cosineOfYAngle <= -1 ) { yAngle = M_PI; } return new Angle3D( 0, yAngle, 0 ); } inline Angle3D *Vector3D::getXAngleTo( Vector3D *inOther ) { // normalize and remove y component Vector3D *normalThis = new Vector3D( this ); normalThis->mX = 0; normalThis->normalize(); Vector3D *normalOther = new Vector3D( inOther ); normalOther->mX = 0; normalOther->normalize(); double cosineOfXAngle = normalThis->dot( normalOther ); // cosine is ambiguous (same for negative and positive angles) // compute dot product with perpendicular vector to get sine // sign of sine will tell us whether angle is positive or negative Angle3D *rightAngleX = new Angle3D( M_PI / 2, 0, 0 ); normalThis->rotate( rightAngleX ); delete rightAngleX; double sineOfXAngle = normalThis->dot( normalOther ); delete normalThis; delete normalOther; double xAngle = acos( cosineOfXAngle ); if( sineOfXAngle < 0 ) { xAngle = -xAngle; } // if vectors are very close, our dot product above might give // a value greater than 1 due to round-off errors // this causes acos to return NAN if( cosineOfXAngle >= 1 ) { xAngle = 0; } // also need to worry if vectors are complete opposites else if( cosineOfXAngle <= -1 ) { xAngle = M_PI; } return new Angle3D( xAngle, 0, 0 ); } inline Vector3D *Vector3D::linearSum( Vector3D *inFirst, Vector3D *inSecond, double inFirstWeight ) { double secondWeight = 1 - inFirstWeight; double x = inFirstWeight * inFirst->mX + secondWeight * inSecond->mX; double y = inFirstWeight * inFirst->mY + secondWeight * inSecond->mY; double z = inFirstWeight * inFirst->mZ + secondWeight * inSecond->mZ; return new Vector3D( x, y, z ); } inline double Vector3D::getLength() { return sqrt( dot( this ) ); } inline void Vector3D::scale( double inScalar ) { mX *= inScalar; mY *= inScalar; mZ *= inScalar; } inline double Vector3D::getDistance( Vector3D *inOther ) { double delX = mX - inOther->mX; double delY = mY - inOther->mY; double delZ = mZ - inOther->mZ; return sqrt( delX * delX + delY * delY + delZ * delZ ); } inline double Vector3D::getXZDistance( Vector3D *inOther ) { double delX = mX - inOther->mX; double delZ = mZ - inOther->mZ; return sqrt( delX * delX + delZ * delZ ); } inline void Vector3D::rotate( Angle3D *inAngle ) { if( inAngle->mX != 0 ) { double cosTheta = cos( inAngle->mX ); double sinTheta = sin( inAngle->mX ); double oldY = mY; mY = mY * cosTheta - mZ * sinTheta; mZ = oldY * sinTheta + mZ * cosTheta; } if( inAngle->mY != 0 ) { double cosTheta = cos( inAngle->mY ); double sinTheta = sin( inAngle->mY ); double oldX = mX; mX = cosTheta * mX + sinTheta * mZ; mZ = -sinTheta * oldX + cosTheta * mZ; } if( inAngle->mZ != 0 ) { double cosTheta = cos( inAngle->mZ ); double sinTheta = sin( inAngle->mZ ); double oldX = mX; mX = cosTheta * mX - sinTheta * mY; mY = sinTheta * oldX + cosTheta * mY; } } inline void Vector3D::reverseRotate( Angle3D *inAngle ) { Angle3D *actualAngle = new Angle3D( -inAngle->mX, -inAngle->mY, -inAngle->mZ ); rotate( actualAngle ); delete actualAngle; } inline void Vector3D::print() { printf( "(%f, %f, %f)", mX, mY, mZ ); } inline int Vector3D::serialize( OutputStream *inOutputStream ) { int numBytes = 0; numBytes += inOutputStream->writeDouble( mX ); numBytes += inOutputStream->writeDouble( mY ); numBytes += inOutputStream->writeDouble( mZ ); return numBytes; } inline int Vector3D::deserialize( InputStream *inInputStream ) { int numBytes = 0; numBytes += inInputStream->readDouble( &( mX ) ); numBytes += inInputStream->readDouble( &( mY ) ); numBytes += inInputStream->readDouble( &( mZ ) ); return numBytes; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/geometry/LineSegment3D.h0000640000175000017500000001134710204365360024726 0ustar pabspabs/* * Modification History * * 2001-April-14 Jason Rohrer * Created. * * 2005-February-15 Jason Rohrer * Made destructor virtual to quell warnings. */ #ifndef LINE_SEGMENT_3D_INCLUDED #define LINE_SEGMENT_3D_INCLUDED #include "GeometricObject3D.h" #include "minorGems/io/Serializable.h" #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /** * Line segment (in 3-space). * * @author Jason Rohrer */ class LineSegment3D : public GeometricObject3D, public Serializable { public: Vector3D *mEndpoints[2]; /** * Constructs a line segment. * * The vectors are copied, so the caller * is responsible for deallocating the passed in vectors. * * @param inEndpointA the first endpoint. * @param inEndpointB the second endpoint. */ LineSegment3D( Vector3D *inEndpointA, Vector3D *inEndpointB ); /** * Constructs a line segment by copying parameters * from another line segment. * * @param inOtherSegment the line segment to copy. */ LineSegment3D( LineSegment3D *inOtherSegment ); virtual ~LineSegment3D(); /** * Gets the projection of a point onto this line segment * * @param inPoint the point to project. Must be destroyed * by caller. * * @return a new vector representing the projected point, or * NULL if the projection is not on this line segment. */ Vector3D *projectPoint( Vector3D *inPoint ); // these implement the GeometricObject3D interface void move( Vector3D *inVector ); void rotate( Angle3D *inAngle ); void reverseRotate( Angle3D *inAngle ); void scale( double inScalar ); GeometricObject3D *copy(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline LineSegment3D::LineSegment3D( Vector3D *inEndpointA, Vector3D *inEndpointB ) { mEndpoints[0] = new Vector3D( inEndpointA ); mEndpoints[1] = new Vector3D( inEndpointB ); } inline LineSegment3D::LineSegment3D( LineSegment3D *inOtherSegment ) { mEndpoints[0] = new Vector3D( inOtherSegment->mEndpoints[0] ); mEndpoints[1] = new Vector3D( inOtherSegment->mEndpoints[1] ); } inline LineSegment3D::~LineSegment3D() { delete mEndpoints[0]; delete mEndpoints[1]; } inline Vector3D *LineSegment3D::projectPoint( Vector3D *inPoint ) { // compute the vector for the underlying line of this segment Vector3D *supportingVector = new Vector3D( mEndpoints[0] ); supportingVector->subtract( mEndpoints[1] ); supportingVector->normalize(); // now find the lengths of the projection of each endpoint // onto this supporting vector double lengthA = supportingVector->dot( mEndpoints[0] ); double lengthB = supportingVector->dot( mEndpoints[1] ); // find the length of the projection of inPoint double lengthInPoint = supportingVector->dot( inPoint ); // projection is off the segment if lengthInPoint is not // between lengthA and lengthB if( lengthInPoint > lengthA && lengthInPoint > lengthB ) { delete supportingVector; return NULL; } else if( lengthInPoint < lengthA && lengthInPoint < lengthB ) { delete supportingVector; return NULL; } else { // length is in between the two // compute difference from A's projection double difference = lengthInPoint - lengthA; Vector3D *returnVector = new Vector3D( mEndpoints[0] ); supportingVector->scale( difference ); returnVector->add( supportingVector ); delete supportingVector; return returnVector; } } inline void LineSegment3D::move( Vector3D *inVector ) { mEndpoints[0]->add( inVector ); mEndpoints[1]->add( inVector ); } inline void LineSegment3D::rotate( Angle3D *inAngle ) { mEndpoints[0]->rotate( inAngle ); mEndpoints[1]->rotate( inAngle ); } inline void LineSegment3D::reverseRotate( Angle3D *inAngle ) { mEndpoints[0]->reverseRotate( inAngle ); mEndpoints[1]->reverseRotate( inAngle ); } inline void LineSegment3D::scale( double inScalar ) { mEndpoints[0]->scale( inScalar ); mEndpoints[1]->scale( inScalar ); } inline GeometricObject3D *LineSegment3D::copy() { LineSegment3D *copiedSegment = new LineSegment3D( this ); return (GeometricObject3D*)copiedSegment; } inline int LineSegment3D::serialize( OutputStream *inOutputStream ) { int numBytesWritten = 0; numBytesWritten += mEndpoints[0]->serialize( inOutputStream ); numBytesWritten += mEndpoints[1]->serialize( inOutputStream ); return numBytesWritten; } inline int LineSegment3D::deserialize( InputStream *inInputStream ) { int numBytesRead = 0; numBytesRead += mEndpoints[0]->deserialize( inInputStream ); numBytesRead += mEndpoints[1]->deserialize( inInputStream ); return numBytesRead; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/geometry/Sphere.h0000640000175000017500000001702507266142706023565 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2000-December-14 Jason Rohrer * Added implementation for all member functions. * * 2001-January-10 Jason Rohrer * Made class serializable. * * 2001-January-24 Jason Rohrer * Fixed a bug in the deserialization function. * * 2001-February-2 Jason Rohrer * Added M_PI definition. * * 2001-April-14 Jason Rohrer * Added line segment and triangle intersection tests. */ #ifndef SPHERE_INCLUDED #define SPHERE_INCLUDED #include "GeometricObject3D.h" #include "LineSegment3D.h" #include "Triangle3D.h" #include "minorGems/io/Serializable.h" #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /** * Geometric sphere (in 3-space). * * @author Jason Rohrer */ class Sphere : public GeometricObject3D, public Serializable { public: Vector3D *mCenter; double mRadius; /** * Constructs a Sphere. * * @param inCenter a vector containing the coordinates of the * sphere's center. The vector is copied, so the caller * is responsible for deallocating the passed in vector. * * @param inRadius the radius of the sphere. */ Sphere( Vector3D *inCenter, double inRadius ); /** * Constructs a Sphere by copying parameters from another Sphere. * * @param inOtherSphere the sphere to copy. */ Sphere( Sphere *inOtherSphere ); ~Sphere(); /** * Gets whether this sphere intersects with another. * * @param inOtherSphere the sphere to test for intersection * with this sphere. * * @return true iff this sphere intersects with the other. */ char intersects( Sphere *inOtherSphere ); /** * Gets the normalized direction of intersection of this * sphere with another. * * @param inOtherSphere the sphere to test for intersection * with this sphere. * * @return a normalized vector direction of the intersection, * or NULL if there is no intersection. */ Vector3D *getIntersection( Sphere *inOtherSphere ); /** * Gets whether a line segment intersects or is inside this sphere. * * @param inSegment the segment to test. * * @return true if the segment intersects with or is contained by * this sphere. */ char intersects( LineSegment3D *inSegment ); /** * Gets whether a triangle intesects or is inside this sphere. * * * @param inTriangle the triangle to test. * * @return true if the triangle intersects with or is contained by * this sphere. */ char intersects( Triangle3D *inTriangle ); /** * Gets the volume of this sphere. * * @return the volume of this sphere. */ double getVolume(); // these implement the GeometricObject3D interface void move( Vector3D *inVector ); void rotate( Angle3D *inAngle ); void reverseRotate( Angle3D *inAngle ); void scale( double inScalar ); GeometricObject3D *copy(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline Sphere::Sphere( Vector3D *inCenter, double inRadius ) : mCenter( new Vector3D( inCenter ) ), mRadius( inRadius ) { } inline Sphere::Sphere( Sphere *inOtherSphere ) : mCenter( new Vector3D( inOtherSphere->mCenter ) ), mRadius( inOtherSphere->mRadius ) { } inline Sphere::~Sphere() { delete mCenter; } inline char Sphere::intersects( Sphere *inOtherSphere ) { double distance = mCenter->getDistance( inOtherSphere->mCenter ); if( distance <= mRadius + inOtherSphere->mRadius ) { return true; } else { return false; } } inline Vector3D *Sphere::getIntersection( Sphere *inOtherSphere ) { if( intersects( inOtherSphere ) ) { Vector3D * returnVector = new Vector3D( inOtherSphere->mCenter ); returnVector->subtract( mCenter ); returnVector->normalize(); return returnVector; } else { return NULL; } } /* * These intersection algorithms were found in the following paper: * * ERIT -- A Collection of Efficient and Reliable Intersection Tests * by Martin Held * Journal of Graphics Tools */ inline char Sphere::intersects( LineSegment3D *inSegment ) { // first check if either endpoint is inside this sphere if( inSegment->mEndpoints[0]->getDistance( mCenter ) <= mRadius ) { return true; } else if( inSegment->mEndpoints[1]->getDistance( mCenter ) <= mRadius ) { return true; } else { // both endpoints outside // project center onto the line Vector3D *projectedCenter = inSegment->projectPoint( mCenter ); if( projectedCenter == NULL ) { // projection is outside of the segment return false; } else { // projection is inside of the segment // check distance from center double distance = projectedCenter->getDistance( mCenter ); delete projectedCenter; if( distance <= mRadius ) { return true; } else { return false; } } } } inline char Sphere::intersects( Triangle3D *inTriangle ) { char intersectionFound; // first, check each triangle edge segment for intersection LineSegment3D *segmentA = inTriangle->getLineSegment( 0 ); intersectionFound = intersects( segmentA ); delete segmentA; if( intersectionFound ) { return true; } LineSegment3D *segmentB = inTriangle->getLineSegment( 1 ); intersectionFound = intersects( segmentB ); delete segmentB; if( intersectionFound ) { return true; } LineSegment3D *segmentC = inTriangle->getLineSegment( 2 ); intersectionFound = intersects( segmentC ); delete segmentC; if( intersectionFound ) { return true; } // if we're at this point, none of the edges intersected. // we still need to check if the interior region of the triangle // is inside the sphere Vector3D *projectedCenter = inTriangle->projectOntoPlane( mCenter ); if( inTriangle->isInBounds( projectedCenter ) ) { if( projectedCenter->getDistance( mCenter ) <= mRadius ) { // the projected center is inside the triangle and // inside the sphere intersectionFound = true; } else { // the projected center is inside the triangle, but outside // the sphere intersectionFound = false; } } else { // center projection not even in triangle bounds, so no intersection intersectionFound = false; } delete projectedCenter; return intersectionFound; } inline double Sphere::getVolume() { return (4/3) * M_PI * mRadius * mRadius * mRadius; } inline void Sphere::move( Vector3D *inVector ) { mCenter->add( inVector ); } inline void Sphere::rotate( Angle3D *inAngle ) { mCenter->rotate( inAngle ); } inline void Sphere::reverseRotate( Angle3D *inAngle ) { mCenter->reverseRotate( inAngle ); } inline void Sphere::scale( double inScalar ) { mCenter->scale( inScalar ); mRadius *= inScalar; } inline GeometricObject3D *Sphere::copy() { Sphere *copiedSphere = new Sphere( this ); return (GeometricObject3D*)copiedSphere; } inline int Sphere::serialize( OutputStream *inOutputStream ) { int numBytesWritten = 0; numBytesWritten += mCenter->serialize( inOutputStream ); unsigned char* doublePointer = (unsigned char *)( &mRadius ); numBytesWritten += inOutputStream->write( doublePointer, sizeof( double ) ); return numBytesWritten; } inline int Sphere::deserialize( InputStream *inInputStream ) { int numBytesRead = 0; numBytesRead += mCenter->deserialize( inInputStream ); unsigned char* doublePointer = (unsigned char *)( &mRadius ); numBytesRead += inInputStream->read( doublePointer, sizeof( double ) ); return numBytesRead; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/probability/0000750000175000017500000000000011401021060022614 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/math/probability/ProbabilityMassFunction.h0000640000175000017500000002034707376065743027646 0ustar pabspabs/* * Modification History * * 2001-November-17 Jason Rohrer * Created. * Added a constructor for explicit initialization of elements. * Made serializable. * * 2001-November-18 Jason Rohrer * Added function for finding the minimum probability value. * Added a print function. */ #ifndef PROBABILITY_MASS_FUNCTION_INCLUDED #define PROBABILITY_MASS_FUNCTION_INCLUDED #include "minorGems/util/SimpleVector.h" #include "minorGems/util/random/RandomSource.h" #include "minorGems/io/Serializable.h" /** * A discrete probability distribution. * All probabilities are constrained to sum to 1. * * @author Jason Rohrer */ class ProbabilityMassFunction : public Serializable { public: /** * Constructs a probability mass function. * * @param inRandSource the random source to use when * sampling from this function. * Must be destroyed by caller after this class is destroyed. */ ProbabilityMassFunction( RandomSource *inRandSource ); /** * Constructs a probability mass function with elements * already inserted. * * @param inRandSource the random source to use when * sampling from this function. * Must be destroyed by caller after this class is destroyed. * @param inNumElements the number of elements * this PMF should be initialized with. * @param inProbabilities the probabilities to start with. * Note that these values will be normalized before * being stored. * Will be destroyed by this constructor. */ ProbabilityMassFunction( RandomSource *inRandSource, int inNumElements, double *inProbabilities ); virtual ~ProbabilityMassFunction(); /** * Adds an element and a probability to * this mass function. * * Note that since the distribution * always sums to 1, inProbability will * be normalized and thus the value set will likely * not exactly equal the value specified by the caller. * * @param inProbability the probability of the new element. * * @return the index of the new element in the distribution/ * Note that the actual index of this element * may change in the future as elements are added to * and removed from this mass function. */ virtual unsigned long addElement( double inProbability ); /** * Removes an element from this mass function. * * @param inIndex the index of the element. */ virtual void removeElement( unsigned long inIndex ); /** * Gets the probability of an element. * * @param inIndex the index of the element. * * @return the probability of the element at inIndex, * or 0 if inIndex is not a valid index. */ virtual double getProbability( unsigned long inIndex ); /** * Sets the probability of an element. * * Note that since the distribution * always sums to 1, inProbability will * be normalized and thus the value set will likely * not exactly equal the value specified by the caller. * * @param inIndex the index of the element. * @param inProbability the probability of the element at inIndex. */ virtual void setProbability( unsigned long inIndex, double inProbability ); /** * Samples an element at random according to this distribution. * * @return the index of the sampled element. */ virtual unsigned long sampleElement(); /** * Gets the minimum probability value of this function. * * @return the minimum probability value. */ virtual double getMinimumProbability(); /** * Prints the probabilities in this mass function to standard out. */ virtual void print(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); protected: RandomSource *mRandSource; SimpleVector< double > *mProbabilityVector; /** * Normalizes this distribution so that it sums to 1. */ virtual void normalize(); /** * Gets the net sum of this probability distribution. * * @return the sum of this distribution. */ virtual double getProbabilitySum(); }; inline ProbabilityMassFunction::ProbabilityMassFunction( RandomSource *inRandSource ) : mRandSource( inRandSource ), mProbabilityVector( new SimpleVector< double >() ) { } inline ProbabilityMassFunction::ProbabilityMassFunction( RandomSource *inRandSource, int inNumElements, double *inProbabilities ) : mRandSource( inRandSource ), mProbabilityVector( new SimpleVector< double >( inNumElements ) ){ for( int i=0; ipush_back( inProbabilities[i] ); } normalize(); } inline ProbabilityMassFunction::~ProbabilityMassFunction() { // we can simply delete this vector, since its elements are primitives delete mProbabilityVector; } inline unsigned long ProbabilityMassFunction::addElement( double inProbability ) { mProbabilityVector->push_back( inProbability ); normalize(); return mProbabilityVector->size(); } inline void ProbabilityMassFunction::removeElement( unsigned long inIndex ) { mProbabilityVector->deleteElement( (int)inIndex ); normalize(); } inline double ProbabilityMassFunction::getProbability( unsigned long inIndex ) { if( inIndex >= 0 && inIndex < mProbabilityVector->size() ) { return *( mProbabilityVector->getElement( inIndex ) ); } else { return 0; } } inline void ProbabilityMassFunction::setProbability( unsigned long inIndex, double inProbability ) { if( inIndex >= 0 && inIndex < mProbabilityVector->size() ) { *( mProbabilityVector->getElement( inIndex ) ) = inProbability; normalize(); } } inline unsigned long ProbabilityMassFunction::sampleElement() { double randVal = mRandSource->getRandomDouble(); // compute a running CDF value until we surpass randVal double currentCDFVal = 0; int currentIndex = -1; while( currentCDFVal < randVal ) { currentIndex++; double currentProb = *( mProbabilityVector->getElement( currentIndex ) ); currentCDFVal += currentProb; } // when we exit the while loop, we are at the index of the // element whose probability pushed the CDF past randVal if( currentIndex == -1 ) { // randVal was 0 return 0; } else { return currentIndex; } } inline double ProbabilityMassFunction::getMinimumProbability() { // works, since all values <= 1 double minValue = 2; int numElements = mProbabilityVector->size(); for( int i=0; igetElement( i ) ); if( prob < minValue ) { minValue = prob; } } return minValue; } inline void ProbabilityMassFunction::normalize() { double currentSum = getProbabilitySum(); if( currentSum != 1 ) { double invCurrentSum = 1.0 / currentSum; int numElements = mProbabilityVector->size(); for( int i=0; igetElement( i ); *prob = (*prob) * invCurrentSum; } double newSum = getProbabilitySum(); } } inline double ProbabilityMassFunction::getProbabilitySum() { double sum = 0; int numElements = mProbabilityVector->size(); for( int i=0; igetElement( i ) ); } return sum; } inline void ProbabilityMassFunction::print() { long numElements = mProbabilityVector->size(); for( int i=0; igetElement( i ) ); printf( "%lf ", prob ); } } inline int ProbabilityMassFunction::serialize( OutputStream *inOutputStream ) { int numBytes = 0; long numElements = mProbabilityVector->size(); numBytes += inOutputStream->writeLong( numElements ); for( int i=0; iwriteDouble( *( mProbabilityVector->getElement( i ) ) ); } return numBytes; } inline int ProbabilityMassFunction::deserialize( InputStream *inInputStream ) { int numBytes = 0; delete mProbabilityVector; long numElements; numBytes += inInputStream->readLong( &numElements ); mProbabilityVector = new SimpleVector< double >( numElements ); for( int i=0; ireadDouble( &prob ); mProbabilityVector->push_back( prob ); } normalize(); return numBytes; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/math/BigInt.cpp0000640000175000017500000003336107474074412022213 0ustar pabspabs/* * Modification History * * 2002-May-25 Jason Rohrer * Created. * Changed to remove extra zeros when subtracting. */ #include "BigInt.h" #include "minorGems/util/stringUtils.h" #include #include BigInt::BigInt( int inSign, int inNumBytes, unsigned char *inBytes ) : mSign( inSign ), mNumBytes( inNumBytes ), mBytes( new unsigned char[ inNumBytes ] ) { memcpy( mBytes, inBytes, mNumBytes ); } BigInt::BigInt( int inInt ) { if( inInt > 0 ) { mSign = 1; } else if( inInt == 0 ) { mSign = 0; } else { mSign = -1; // flip sign so conversion works inInt *= -1; } mNumBytes = 4; int bytesToSkip = 0; int baseFactor = 1; mBytes = new unsigned char[ 4 ]; int i; for( i=mNumBytes-1; i>=mNumBytes-4; i-- ) { mBytes[i] = 0xFF & ( inInt / baseFactor ); baseFactor *= 256; } if( mBytes[0] == 0 ) { bytesToSkip++; if( mBytes[1] == 0 ) { bytesToSkip++; if( mBytes[2] == 0 ) { bytesToSkip++; if( mBytes[3] == 0 ) { bytesToSkip++; } } } } mNumBytes -= bytesToSkip; unsigned char *temp = new unsigned char[ mNumBytes ]; memcpy( temp, &( mBytes[ bytesToSkip ] ), mNumBytes ); delete [] mBytes; mBytes = temp; } BigInt::~BigInt() { delete [] mBytes; } BigInt *BigInt::add( BigInt *inOtherInt ) { //char *stringA = convertToHexString(); //char *stringB = inOtherInt->convertToHexString(); //printf( "Adding %s to %s\n", stringA, stringB ); //delete [] stringA; //delete [] stringB; if( mSign == 0 && inOtherInt->mSign == 0 ) { return getZero(); } else if( mSign == 0 && inOtherInt->mSign != 0 ) { return inOtherInt->copy(); } else if( mSign != 0 && inOtherInt->mSign == 0 ) { return copy(); } else if( mSign == -1 && inOtherInt->mSign == -1 ) { // adding two negatives // add them as positives, then flip sign of result BigInt *copyA = copy(); BigInt *copyB = inOtherInt->copy(); copyA->mSign = 1; copyB->mSign = 1; BigInt *result = copyA->add( copyB ); delete copyA; delete copyB; result->mSign = -1; return result; } else if( mSign == 1 && inOtherInt->mSign == -1 ) { // flip other sign and subtract BigInt *copy = inOtherInt->copy(); copy->mSign = 1; BigInt *result = subtract( copy ); delete copy; return result; } else if( mSign == -1 && inOtherInt->mSign == 1 ) { // flip our sign and subtract BigInt *copy = this->copy(); copy->mSign = 1; BigInt *result = inOtherInt->subtract( copy ); delete copy; return result; } else { // both this int and arg int are positive int maxLength = mNumBytes; if( maxLength < inOtherInt->mNumBytes ) { maxLength = inOtherInt->mNumBytes; } // leave room for carry unsigned char *result = new unsigned char[ maxLength + 1 ]; // flip to little-endian order to make adding easier BigInt *intA = flipByteOrder(); BigInt *intB = inOtherInt->flipByteOrder(); int sum = 0; for( int i=0; imNumBytes ) { byteA = intA->mBytes[i]; } if( i < intB->mNumBytes ) { byteB = intB->mBytes[i]; } // add in high-order bits from last sum sum = byteA + byteB + ( sum >> 8 ); // truncate to low-order bits result[i] = (unsigned char)sum; } // stick remaining carry into last byte result[maxLength] = (unsigned char)( sum >> 8 ); BigInt *resultInt; if( result[maxLength] == 0 ) { // result array will be copied skipping carry byte resultInt = new BigInt( 1, maxLength, result ); } else { // keep carry byte resultInt = new BigInt( 1, maxLength + 1, result ); } BigInt *flippedResult = resultInt->flipByteOrder(); delete intA; delete intB; delete resultInt; delete [] result; return flippedResult; } } BigInt *BigInt::subtract( BigInt *inOtherInt ) { //char *stringA = convertToHexString(); //char *stringB = inOtherInt->convertToHexString(); //printf( "Subtracting %s from %s\n", stringB, stringA ); //delete [] stringA; //delete [] stringB; if( mSign == 0 && inOtherInt->mSign == 0 ) { return getZero(); } else if( mSign != 0 && inOtherInt->mSign == 0 ) { return copy(); } else if( mSign == 0 && inOtherInt->mSign != 0 ) { BigInt *copy = inOtherInt->copy(); // flip sign copy->mSign = -( copy->mSign ); return copy; } else if( isEqualTo( inOtherInt ) ) { return getZero(); } else if( inOtherInt->mSign == -1 ) { // flip sign and add BigInt *copy = inOtherInt->copy(); copy->mSign = 1; BigInt *result = add( copy ); delete copy; return result; } else if( mSign == -1 ) { // flip our sign and add BigInt *copy = this->copy(); copy->mSign = 1; BigInt *result = inOtherInt->add( copy ); delete copy; // flip sign of result result->mSign = -1; return result; } else if( isLessThan( inOtherInt ) ) { // trying to subtract a larger number // flip subtraction order, then flip sign of result BigInt *result = inOtherInt->subtract( this ); result->mSign = -( result->mSign ); return result; } else { // we're subtracting a smaller positive number from // a larger positive number int maxLength = mNumBytes; if( maxLength < inOtherInt->mNumBytes ) { maxLength = inOtherInt->mNumBytes; } // result cannot be longer than the larger number unsigned char *result = new unsigned char[ maxLength ]; // flip to little-endian order to make subtracting easier BigInt *intA = this->flipByteOrder(); BigInt *intB = inOtherInt->flipByteOrder(); int borrow = 0; for( int i=0; imNumBytes ) { byteA = intA->mBytes[i]; } if( i < intB->mNumBytes ) { byteB = intB->mBytes[i]; } int diff = byteA - byteB - borrow; // compute next borrow borrow = 0; while( diff < 0 ) { borrow++; diff += 256; } result[i] = (unsigned char)diff; } BigInt *resultInt; if( borrow == 0 ) { // skip any trailing 0 bytes int bytesToSkip = 0; int index = maxLength - 1; while( index >= 0 && result[index] == 0 ) { bytesToSkip ++; index--; } // result array will be copied skipping borrow byte // and skipping any trailing 0 bytes resultInt = new BigInt( true, maxLength - bytesToSkip, result ); } else { printf( "Error: final borrow not zero: %d\n", borrow ); resultInt = getZero(); } BigInt *flippedResult = resultInt->flipByteOrder(); delete intA; delete intB; delete resultInt; delete [] result; return flippedResult; } } char BigInt::isLessThan( BigInt *inOtherInt ) { if( mSign > inOtherInt->mSign ) { return false; } else if( mSign < inOtherInt->mSign ) { return true; } else { // signs are equal if( mSign == 0 ) { return false; } else if( mSign == -1 && mNumBytes > inOtherInt->mNumBytes ) { return true; } else if( mSign == 1 && mNumBytes > inOtherInt->mNumBytes ) { return false; } else if( mSign == 1 && mNumBytes < inOtherInt->mNumBytes ) { return true; } else if( mSign == -1 && mNumBytes < inOtherInt->mNumBytes ) { return false; } else if( isEqualTo( inOtherInt ) ) { return false; } else { // numbers are the same length but are not equal // start with high-order bytes for( int i=0; i inOtherInt->mBytes[i] ) { if( mSign == 1 ) { return false; } else { return true; } } else if ( mBytes[i] < inOtherInt->mBytes[i] ) { if( mSign == 1 ) { return true; } else { return false; } } } // if we made it here, the numbers are equal... // but we tested for equality above: must be an error printf( "Error: equality test in isLessThan failed\n" ); return false; } } } char BigInt::isEqualTo( BigInt *inOtherInt ) { if( mSign != inOtherInt->mSign ) { return false; } else if( mNumBytes != inOtherInt->mNumBytes ) { return false; } else { // same length, same sign for( int i=0; imBytes[i] ) { // mismatch found return false; } } // no mismatch found return true; } } BigInt *BigInt::copy() { return new BigInt( mSign, mNumBytes, mBytes ); } BigInt *BigInt::getZero() { unsigned char bytes[0]; return new BigInt( 0, 0, bytes ); } /* THIS DOES NOT WORK! char *BigInt::convertToDecimalString() { char *tempString = new char[4]; char *resultString = new char[ mNumBytes * 3 + 1 ]; int stringIndex = 0; for( int i=0; i> 4 ); int lowBits = 0xF & ( currentByte ); resultHexString[ hexStringIndex ] = fourBitIntToHex( highBits ); hexStringIndex++; resultHexString[ hexStringIndex ] = fourBitIntToHex( lowBits ); hexStringIndex++; } resultHexString[ hexStringIndex ] = '\0'; return resultHexString; } } int BigInt::convertToInt() { int result = 0; int baseFactor = 1; for( int i=mNumBytes-1; i>=mNumBytes-4; i-- ) { if( i >=0 ) { result += mBytes[i] * baseFactor; } baseFactor *= 256; } return mSign * result; } BigInt *BigInt::flipByteOrder() { BigInt *returnInt = copy(); unsigned char *newBytes = returnInt->mBytes; int newIndex = mNumBytes - 1; for( int i=0; i int main() { unsigned char intA[] = { 100, 200, 99, 23, 89, 100, 233 }; unsigned char intB[] = { 10, 78, 243, 9, 0 }; BigInt *bigIntA = new BigInt( 1, 7, intA ); BigInt *bigIntB = new BigInt( -1, 5, intB ); char *hexA = bigIntA->convertToHexString(); char *hexB = bigIntB->convertToHexString(); printf( "A = %s\n", hexA ); printf( "B = %s\n", hexB ); BigInt *sum = bigIntA->subtract( bigIntB ); char *hexSum = sum->convertToHexString(); printf( "Sum = %s\n", hexSum ); delete [] hexA; delete [] hexB; delete [] hexSum; delete bigIntA; delete bigIntB; delete sum; int c = 0x58B11F; BigInt *intC = new BigInt( c ); char *hexC = intC->convertToHexString(); printf( "C = %s\n", hexC ); int extractedC = intC->convertToInt(); if( extractedC == c ) { printf( "equal\n" ); } else { printf( "not equal\n" ); } delete [] hexC; delete intC; int limit = 300; printf( "Testing pair operations for all pairs in -%d..%d ...\n", limit, limit ); char failed = false; for( int i=-limit; iadd( intJ ); BigInt *intDiff = intI->subtract( intJ ); int sum = i + j; int diff = i - j; if( sum != intSum->convertToInt() ) { printf( "sum test failed for %d, %d\n", i, j ); printf( " real sum = %d, computed sum = %d\n", sum, intSum->convertToInt() ); failed = true; } if( diff != intDiff->convertToInt() ) { printf( "diff test failed for %d, %d\n", i, j ); printf( " real diff = %d, computed diff = %d\n", diff, intDiff->convertToInt() ); failed = true; } if( intI->isLessThan( intJ ) && ( i >= j ) ) { printf( "first less than test failed for %d, %d\n", i, j ); failed = true; } if( intJ->isLessThan( intI ) && ( j >= i ) ) { printf( "second less than test failed for %d, %d\n", i, j ); failed = true; } if( intI->isEqualTo( intJ ) && ( i != j ) ) { printf( "equality test failed for %d, %d\n", i, j ); failed = true; } delete intSum; delete intDiff; delete intJ; } delete intI; } if( !failed ) { printf( "test passed\n" ); } return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/common.h0000640000175000017500000000033707554101512021027 0ustar pabspabs/* * Modification History * * 2002-October-18 Jason Rohrer * Created. */ #ifndef MINOR_GEMS_COMMON_INCLUDED #define MINOR_GEMS_COMMON_INCLUDED #include "minorGems/util/development/memory/debugMemory.h" #endif Cultivation_9+dfsg1_UnixSource/minorGems/ui/0000750000175000017500000000000011401021107017762 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/ui/event/0000750000175000017500000000000011401021107021103 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/ui/event/ActionListener.h0000640000175000017500000000132310452237532024217 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * * 2006-July-3 Jason Rohrer * Fixed warnings. */ #ifndef ACTION_LISTENER_INCLUDED #define ACTION_LISTENER_INCLUDED #include "minorGems/ui/GUIComponent.h" /** * An interface for a class that can handle a GUI action. * * @author Jason Rohrer */ class ActionListener { public: virtual ~ActionListener(); /** * Tells this class that an action has been performed. * * @param inTarget the GUI component on which the action * is being performed. */ virtual void actionPerformed( GUIComponent *inTarget ) = 0; }; inline ActionListener::~ActionListener() { } #endif Cultivation_9+dfsg1_UnixSource/minorGems/ui/event/ActionListenerList.h0000640000175000017500000000475711375054541025073 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * * 2001-September-17 Jason Rohrer * Fixed a missing include. * * 2006-July-3 Jason Rohrer * Fixed warnings. * * 2009-December-22 Jason Rohrer * New SimpleVector delete call. * * 2010-May-19 Jason Rohrer * Added call to check if a listener is on the list. */ #ifndef ACTION_LISTENER_LIST_INCLUDED #define ACTION_LISTENER_LIST_INCLUDED #include "ActionListener.h" #include "minorGems/ui/GUIComponent.h" #include "minorGems/util/SimpleVector.h" /** * A utility class to be subclassed by classes needing * to handle a list of action listeners. * * @author Jason Rohrer */ class ActionListenerList : protected SimpleVector { public: virtual ~ActionListenerList(); /** * Adds an action listener. * * @param inListener the listener to add. Must * be destroyed by caller after this class has been destroyed. */ virtual void addActionListener( ActionListener *inListener ); /** * Removes an action listener. * * @param inListener the listener to remove. Must * be destroyed by caller. */ virtual void removeActionListener( ActionListener *inListener ); /** * Checks if action listener added * * @param inListener the listener to look for. Must * be destroyed by caller. * * @return true iff listener is on the list */ virtual char isListening( ActionListener *inListener ); /** * Tells all registered listeners that an action has been * performed. * * @param inTarget the GUI component on which the action * is being performed. */ virtual void fireActionPerformed( GUIComponent *inTarget ); }; inline ActionListenerList::~ActionListenerList() { } inline void ActionListenerList::addActionListener( ActionListener *inListener ) { push_back( inListener ); } inline void ActionListenerList::removeActionListener( ActionListener *inListener ) { deleteElementEqualTo( inListener ); } inline char ActionListenerList::isListening( ActionListener *inListener ) { int index = getElementIndex( inListener ); if( index == -1 ) { return false; } else { return true; } } inline void ActionListenerList::fireActionPerformed( GUIComponent *inTarget ) { for( int i=0; iactionPerformed( inTarget ); } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/ui/GUIComponent.h0000640000175000017500000000060007350771543022467 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. */ #ifndef GUI_COMPONENT_INCLUDED #define GUI_COMPONENT_INCLUDED #include "minorGems/ui/GUIComponent.h" /** * An generic superclass for all gui components. * Allows for easy event source determination during event passing. * * @author Jason Rohrer */ class GUIComponent { }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/ui/Keyboard.h0000640000175000017500000000316110447750664021727 0ustar pabspabs/* * Modification History * * 2000-December-7 Jason Rohrer * Created. * * 2000-December-8 Jason Rohrer * Changed so that key state functions take a string instead of * an integer vkey code. * * 2006-June-26 Jason Rohrer * Added function to get events that are waiting in the queue. */ #ifndef KEYBOARD_INCLUDED #define KEYBOARD_INCLUDED /** * Interface for accessing keyboard input. * * Note: * Certain implementations may require a ScreenGraphics object to * be constructed before accessing the keyboard (i.e., to give the * keyboard input a context). */ class Keyboard { public: /* * Key descriptions: * * For the standard ascii characters, pass in the string containing * the lower case character, for example, "a" for the character A. * * For other keys, the descriptors have not been defined yet. */ /** * Gets whether a key is down. * * @param inKeyDescription string describing the queried key. * * @return true if key represented by given key code is down. */ //char getKeyDown( int vKeyCode ); char getKeyDown( const char *inKeyDescription ); /** * Gets whether a key is up. * * @param inKeyDescription string describing the queried key. * * @return true if key represented by given key code is up. */ //char getKeyUp( int vKeyCode ); char getKeyUp( const char *inKeyDescription ); /** * Gets the next keyboard event. * * @return the ASCII encoding of the pressed key, or -1 if no * keyboard events are waiting. */ int getKeyPressedEvent(); }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/ui/ProgressBar.h0000640000175000017500000000257007205611501022403 0ustar pabspabs// Jason Rohrer // ProgressBar.h /** * * ProgressBar Gui element * * * Created 11-6-99 * Mods: * Jason Rohrer 11-8-99 Changed to use GraphicBuffer object as screen buffer * */ #ifndef PROGRESS_BAR_INCLUDED #define PROGRESS_BAR_INCLUDED #include "Color.h" #include "GraphicBuffer.h" class ProgressBar { public: // construct a progress bar in a specific location with // specific border and maximum colors ProgressBar( int x, int y, int w, int h, Color bordC, Color hC, Color tipC ); ~ProgressBar(); void setProgress( float fractionFull ); // draw progress bar into a graphic buffer void draw( GraphicBuffer &buff ); // erase progress bar from buffer void erase( GraphicBuffer &buff, Color &bgColor ); private: unsigned long *imageMap; int startX; int startY; int high; int wide; int barHigh; // width and heigth of area inside borders int barWide; int *mapYOffset; Color borderC; // color of border Color highC; // color of progress bar max Color barTipC; static const int borderWide = 2; float lastProgress; // fraction of last progress }; inline void ProgressBar::draw( GraphicBuffer &buff ) { buff.drawImage(imageMap, mapYOffset, startX, startY, wide, high); } inline void ProgressBar::erase( GraphicBuffer &buff, Color &bgColor ) { buff.eraseImage(startX, startY, wide, high, bgColor); } #endifCultivation_9+dfsg1_UnixSource/minorGems/ui/linux/0000750000175000017500000000000011401021107021121 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/ui/linux/MouseLinux.cpp0000640000175000017500000000270607274131574023772 0ustar pabspabs/* * Modification History * * 2000-November-28 Jason Rohrer * Created. * * 2001-May-2 Jason Rohrer * Changed to use more standard SDL include location. */ #include "minorGems/ui/Mouse.h" #include /** * Note: Linux implementation: * Requires that a ScreenGraphics be constructed before accessing the mouse. */ void Mouse::getLocation( int *outX, int *outY ) { SDL_PumpEvents(); SDL_GetMouseState( outX, outY ); /* SDL_PumpEvents( void ); int numEventsToGet = 99; SDL_Event *events = new SDL_Event[numEventsToGet]; // get events from the queue int numEventsRetrieved = SDL_PeepEvents( events, numEventsToGet, SDL_GETEVENT, SDL_MOUSEMOTIONMASK ); // for mouse motion, we only care about the last event SDL_Event lastEvent = events[ numEventsRetrieved - 1 ]; delete [] events; */ } char Mouse::isButtonDown( int inButtonNumber ) { SDL_PumpEvents(); int x, y; Uint8 buttonState = SDL_GetMouseState( &x, &y ); if( inButtonNumber == 0 ) { return( buttonState & SDL_BUTTON_LMASK ); } if( mNumButtons >=3 ) { // if we care about 3 buttons, then count the middle button // as button 1 if( inButtonNumber == 1 ) { return( buttonState & SDL_BUTTON_MMASK ); } if( inButtonNumber == 2 ) { return( buttonState & SDL_BUTTON_RMASK ); } } else { // we care about 2 or fewer buttons if( inButtonNumber == 1 ) { return( buttonState & SDL_BUTTON_RMASK ); } } return false; } Cultivation_9+dfsg1_UnixSource/minorGems/ui/linux/KeyboardLinux.cpp0000640000175000017500000000644610447750665024453 0ustar pabspabs/* * Modification History * * 2000-December-7 Jason Rohrer * Created. * * 2000-December-8 Jason Rohrer * Changed so that key state functions take a string instead of * an integer vkey code. * * 2001-May-2 Jason Rohrer * Changed to use more standard SDL include location. * * 2006-June-26 Jason Rohrer * Added function to get events that are waiting in the queue. */ #include "minorGems/ui/Keyboard.h" #include #include /** * Note: Linux implementation: * Requires that a ScreenGraphics be constructed before accessing the keyboard. */ // prototypes: /** * Maps an ascii string description of a key, such as "a", to an SDL keycode. * * @param inKeyDescription an ascii description of a key. * * @return the SDL keycode. */ int getKeyCode( const char *inKeyDescription ); /** * Maps a keycode to an ascii character. * * @param inSDLKeycode the keycode. * * @return the ascii character, or -1 if the keycode is not mappable to ascii. */ int getKeyASCII( int inSDLKeycode ); #define M_KEY SDLK_m #define N_KEY SDLK_n #define S_KEY SDLK_s #define Q_KEY SDLK_q #define L_KEY SDLK_l #define R_KEY SDLK_r #define T_KEY SDLK_t //char Keyboard::getKeyDown( int vKeyCode ) { char Keyboard::getKeyDown( const char *inKeyDescription ) { SDL_PumpEvents(); Uint8 *keys; keys = SDL_GetKeyState( NULL ); return keys[ getKeyCode( inKeyDescription ) ] == SDL_PRESSED; } //char Keyboard::getKeyUp( int vKeyCode ) { char Keyboard::getKeyUp( const char *inKeyDescription ) { SDL_PumpEvents(); Uint8 *keys; keys = SDL_GetKeyState( NULL ); return keys[ getKeyCode( inKeyDescription ) ] == SDL_RELEASED; } int Keyboard::getKeyPressedEvent() { SDL_Event event; if( SDL_PollEvent( &event ) ) { switch( event.type ) { case SDL_KEYDOWN: return getKeyASCII( event.key.keysym.sym ); break; } } else { return -1; } } int getKeyCode( const char *inKeyDescription ) { // note that strcmp functions return 0 if strings match if( !strcasecmp( inKeyDescription, "m" ) ) { return SDLK_m; } else if( !strcasecmp( inKeyDescription, "n" ) ) { return SDLK_n; } else if( !strcasecmp( inKeyDescription, "s" ) ) { return SDLK_s; } else if( !strcasecmp( inKeyDescription, "q" ) ) { return SDLK_q; } else if( !strcasecmp( inKeyDescription, "l" ) ) { return SDLK_l; } else if( !strcasecmp( inKeyDescription, "r" ) ) { return SDLK_r; } else if( !strcasecmp( inKeyDescription, "t" ) ) { return SDLK_t; } } int getKeyASCII( int inSDLKeycode ) { switch( inSDLKeycode ) { case SDLK_m: return 'm'; break; case SDLK_n: return 'n'; break; case SDLK_s: return 's'; break; case SDLK_q: return 'a'; break; case SDLK_l: return 'l'; break; case SDLK_r: return 'r'; break; case SDLK_t: return 't'; break; default: return -1; break; } } /* #define M_KEY SDLK_m #define N_KEY SDLK_n #define S_KEY SDLK_s #define Q_KEY SDLK_q #define L_KEY SDLK_l #define R_KEY SDLK_r #define T_KEY SDLK_t */ Cultivation_9+dfsg1_UnixSource/minorGems/ui/SetMouseMac.cpp0000640000175000017500000000140107205611500022661 0ustar pabspabs// Jason Rohrer // SetMouseMac.cpp /** * * implementation of SetMouse on Mac * This uses a hack that may not be supported in later OSs (e.g., OSX) * * Created 1-14-2000 * Mods: */ #include #include "SetMouse.h" void SetMouse (int x, int y) { Point base; // This routine donated to MacMAME by John Stiles // Picked up for RadiosGL from the Mac GLQuake site Point *RawMouse = (Point*) 0x82C; Point *MTemp = (Point*) 0x828; Ptr CrsrNew = (Ptr) 0x8CE; Ptr CrsrCouple = (Ptr) 0x8CF; base.v = y; base.h = x; LocalToGlobal(&base); *RawMouse = base; *MTemp = base; *CrsrNew = *CrsrCouple; } // do nothing, these are needed in windows only void CaptureMouse() { } void ReleaseMouse() { }Cultivation_9+dfsg1_UnixSource/minorGems/ui/ProgressBar.cpp0000640000175000017500000000500207205611501022727 0ustar pabspabs// Jason Rohrer // ProgressBar.cpp /** * * ProgressBar Gui element implementation * * * Created 11-6-99 * Mods: * Jason Rohrer 11-8-99 Changed to use GraphicBuffer object as screen buffer * */ #include "ProgressBar.h" ProgressBar::ProgressBar(int x, int y, int w, int h, Color bordC, Color hC, Color tipC) { startX = x; startY = y; wide = w; high = h; barWide = wide - 2*borderWide; barHigh = high - 2*borderWide; borderC = bordC; highC = hC; barTipC = tipC; imageMap = new unsigned long[high * wide]; mapYOffset = new int[high]; // precalc y offsets into 2d image map for( int y=0; yhigh-borderWide-1 || x>wide-borderWide-1 ) { imageMap[ yContrib + x ] = borderC.composite; // border } else { imageMap[ yContrib + x ] = 0xFF000000; // black inside } } } lastProgress = 0; } ProgressBar::~ProgressBar() { delete [] imageMap; delete [] mapYOffset; } void ProgressBar::setProgress(float fractionFull) { if( fractionFull < 0) fractionFull = 0; if( fractionFull > 1) fractionFull = 1; if( fractionFull < lastProgress ) { // decreasing proress, erase part of bar int deleteXStart = (int)((fractionFull) * barWide + borderWide); int deleteXEnd = (int)((lastProgress) * barWide + borderWide); for( int y=borderWide; y lastProgress) { //progress has increased int addXStart = (int)((lastProgress) * barWide + borderWide); int addXEnd = (int)((fractionFull) * barWide + borderWide); float weight = lastProgress; float deltaWeight = (fractionFull - lastProgress) / (addXEnd - addXStart); for( int x=addXStart; x #include "SetMouse.h" char captured = false; void SetMouse( int x, int y ) { POINT p; p.x = x; p.y = y; HWND window = GetActiveWindow(); ClientToScreen( window, &p ); SetCursorPos( p.x, p.y ); //SetCursorPos( x, y ); } // send all mouse movements to our window, even those outside the border void CaptureMouse() { HWND window = GetActiveWindow(); SetCapture( window ); } void ReleaseMouse() { ReleaseCapture(); }Cultivation_9+dfsg1_UnixSource/minorGems/ui/Mouse.h0000640000175000017500000000327407210735616021256 0ustar pabspabs/* * Modification History * * 2000-November-28 Jason Rohrer * Created. */ #ifndef MOUSE_INCLUDED #define MOUSE_INCLUDED /** * Interface for accessing mouse input. * * Note: * Certain implementations may require a ScreenGraphics object to * be constructed before accessing the mouse (i.e., to give the * mouse coordinates a context). */ class Mouse { public: /** * Constructs a Mouse. * * @param inNumButtons the number of buttons on the mouse that should * be monitored. Default = 1. */ Mouse( int inNumButtons ); ~Mouse(); /** * Gets the location of the mouse. * * @param outX pointer to location where x component will be returned. * @param outY pointer to location where y component will be returned. */ void getLocation( int *outX, int *outY ); /** * Gets whether the main mouse button is down. * * @return true if the main mouse button is down. */ char isButtonDown(); /** * Gets whether a specified mouse button is down. * * @param inButtonNumber the number of the button to check for * (0 is the main mouse button). * * @return true if the specified mouse button is down. */ char isButtonDown( int inButtonNumber ); private: int mXLocation; int mYLocation; int mNumButtons; // array of booleans that represent the current // (or last known) state of each button char *mButtonDown; }; inline Mouse::Mouse( int inNumButtons = 1 ) : mNumButtons( inNumButtons ), mButtonDown( new char[inNumButtons] ) { } inline Mouse::~Mouse() { delete [] mButtonDown; } inline char Mouse::isButtonDown() { return isButtonDown( 0 ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/ui/Plot.h0000640000175000017500000000262507205611501021071 0ustar pabspabs// Jason Rohrer // Plot.h /** * * Scrolling Plot Gui element * * * Created 11-7-99 * Mods: * Jason Rohrer 11-8-99 Changed to use GraphicBuffer object as screen buffer * */ #ifndef PLOT_INCLUDED #define PLOT_INCLUDED #include #include "Color.h" #include "GraphicBuffer.h" class Plot { public: // construct a plot in a specific location with // specific border, background, and line colors Plot( int x, int y, int w, int h, Color bordC, Color bC, Color lnC ); ~Plot(); // add a point to the right of plot, cause plot to scroll left void addPoint( float p ); // draw plot into a graphic buffer void draw( GraphicBuffer &buff ); // erase plot from buffer void erase( GraphicBuffer &buff, Color &bgColor ); private: unsigned long *imageMap; int startX; int startY; int high; int wide; int innerHigh; // width and heigth of area inside borders int innerWide; int *mapYOffset; Color borderC; // color of border Color bgC; // color of progress bar max Color lineC; // color of plot line static const int borderWide = 2; float *plotVals; // values of plot line }; inline void Plot::draw( GraphicBuffer &buff ) { buff.drawImage(imageMap, mapYOffset, startX, startY, wide, high); } inline void Plot::erase( GraphicBuffer &buff, Color &bgColor ) { buff.eraseImage(startX, startY, wide, high, bgColor); } #endifCultivation_9+dfsg1_UnixSource/minorGems/ui/SetMouse.h0000640000175000017500000000042407205611500021711 0ustar pabspabs// Jason Rohrer // SetMouse.h /** * * Interface for force-setting mouse cursor position * * Created 1-14-2000 * Mods: * Jason Rohrer 1-18-2000 Added capture and release mouse functions to help Win32 */ void SetMouse( int x, int y); void CaptureMouse(); void ReleaseMouse();Cultivation_9+dfsg1_UnixSource/minorGems/ui/Plot.cpp0000640000175000017500000000456107205611501021425 0ustar pabspabs// Jason Rohrer // Plot.cpp /** * * Scrolling Plot Gui element implementation * * * Created 11-7-99 * Mods: * Jason Rohrer 11-8-99 Changed to use GraphicBuffer object as screen buffer * */ #include "Plot.h" Plot::Plot( int x, int y, int w, int h, Color bordC, Color bC, Color lnC ) { startX = x; startY = y; wide = w; high = h; innerWide = wide - 2*borderWide; innerHigh = high - 2*borderWide; borderC = bordC; bgC = bC; lineC = lnC; imageMap = new unsigned long[high * wide]; mapYOffset = new int[high]; // precalc y offsets into 2d image map for( int y=0; yhigh-borderWide-1 || x>wide-borderWide-1 ) { imageMap[ yContrib + x ] = borderC.composite; // border } else { imageMap[ yContrib + x ] = bgC.composite; // background } } } plotVals = new float[innerWide]; for( int i=0; i 0) { invLargest = 1/largest; } // fill plot with bg color for( int y=borderWide; y innerHigh + borderWide ) y = innerHigh + borderWide -1; if( y < borderWide ) y = borderWide; imageMap[ mapYOffset[y] + x ] = lineC.composite; // line } /* for( int x=0; x> ${MINOR_GEMS_DEPENDENCY_FILE}.temp cat ${MINOR_GEMS_DEPENDENCY_FILE}.temp | ${MINOR_GEMS_SED_FIX_COMMAND_A} | ${MINOR_GEMS_SED_FIX_COMMAND_B} >> ${MINOR_GEMS_DEPENDENCY_FILE} rm -f ${MINOR_GEMS_DEPENDENCY_FILE}.temp include ${MINOR_GEMS_DEPENDENCY_FILE} Cultivation_9+dfsg1_UnixSource/minorGems/sound/0000750000175000017500000000000011462022731020510 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/sound/SoundPlayer.h0000640000175000017500000000542010426423173023134 0ustar pabspabs/* * Modification History * * 2006-April-30 Jason Rohrer * Created. Adapted from Transcend/game/SoundPlayer.h * * 2006-May-4 Jason Rohrer * Added current play position timestamp to SampleSource interface. */ #ifndef SOUND_PLAYER_INCLUDED #define SOUND_PLAYER_INCLUDED #include "minorGems/sound/portaudio/pa_common/portaudio.h" #include "minorGems/sound/portaudio/pablio/pablio.h" /** * Abstract interface for a source of samples. * * @author Jason Rohrer. */ class SampleSource { public: // virtual destructor so subclasses can override it virtual ~SampleSource() { } /** * Gets samples from this sample source. * * @param inNumSamples the number of samples to get. * @param outSampleBuffer the buffer where the samples should be * returned. * Allocated and destroyed by caller. * @param inPlayTime the timestamp of the current play position * in seconds. */ virtual void getSamples( int inNumSamples, float *outSampleBuffer, double inPlayTime ) = 0; }; /** * Class that plays sound through portaudio. * * @author Jason Rohrer */ class SoundPlayer { public: /** * Constructs a sound player. * * @param inSampleRate the number of samples per second. * Should be a "standard" rate, like 44100, 22050, etc. * @param inStereo true if this player should play stereo. * @param inSampleSource the source that this player will * use whenever portaudio needs more samples. * Must be destroyed by caller after this class is destroyed. */ SoundPlayer( int inSampleRate, char inStereo, SampleSource *inSampleSource ); ~SoundPlayer(); /** * Gets whether this player is stereo. * * @return true if stereo. */ char isStereo() { return mStereo; } /** * Gets the sample rate of this player. * * @return the sample rate. */ int getSampleRate() { return mSampleRate; } /** * Gets the sample source used by this player. * * @return the sample source. Not destroyed by caller. */ SampleSource *getSampleSource() { return mSampleSource; } protected: unsigned long mSampleRate; char mStereo; SampleSource *mSampleSource; char mAudioInitialized; PortAudioStream *mAudioStream; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/sound/midi/0000750000175000017500000000000011401021061021416 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/sound/midi/pic/0000750000175000017500000000000011401021061022171 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/sound/midi/pic/testMidi.c0000640000175000017500000000357607307545617024167 0ustar pabspabs/* * Modification History * * 2001-June-4 Jason Rohrer * Created. * * 2001-June-5 Jason Rohrer * Changed to trigger notes on an input signal. * Changed to dynamically set note velocity based on * trigger time. * * 2001-June-6 Jason Rohrer * Made note velocity pluck times twice as long. * Added a randomized pitch element. */ #include "midi.h" // avoid local variables char tempI; char noteHoldTime; char tempVelocity; char tempPitch; char betweenNoteTime; /** * Tests midi implementation by sending a stream of notes. * * @author Jason Rohrer */ void main( void ) { midiSetup(); // set B0 to output set_bit( STATUS, RP0 ); // set B0 to output clear_bit( TRISB, 0 ); // set B1 to input set_bit( TRISB, 1 ); clear_bit( STATUS, RP0 ); noteHoldTime = 0; tempVelocity = 0; betweenNoteTime = 0; while( 1 ) { noteHoldTime = 0; // if pin is on if( PORTB & 00000010B ) { turnNoteOff( 0, tempPitch, tempVelocity ); // time how long the pin is on in 2's of ms while( PORTB & 00000010B ) { delay_ms( 2 ); if( noteHoldTime < 127 ) { noteHoldTime++; } } // pin is off // sound a note with a velocity // corresponding to how long the pin was on tempVelocity = 127 - noteHoldTime; // betweenNoteTime contains a rather random // number in 0-255 tempPitch = betweenNoteTime >> 1; turnNoteOn( 0, tempPitch, tempVelocity ); // start counting again betweenNoteTime = 0; } else { // pin is off // this will cycle when it hits 255 // we will be essentially generating // a random number by timing how // long the note is off betweenNoteTime++; // do nothing } /* for( tempI=0; tempI<=127; tempI++ ) { set_bit( PORTB, 0 ); turnNoteOn( 0, tempI, 64 ); delay_s( 1 ); clear_bit( PORTB, 0 ); delay_s( 1 ); turnNoteOff( 0, tempI, 64 ); } */ } } Cultivation_9+dfsg1_UnixSource/minorGems/sound/midi/pic/midi.h0000640000175000017500000000313407307317446023317 0ustar pabspabs/* * Modification History * * 2001-June-4 Jason Rohrer * Created. * * 2001-June-5 Jason Rohrer * Changed back to non-inverted serial output. * This output type has been tested and works with MIDI. */ /** * A collection of midi utility functions. * * Uses C2C serial to send MIDI. Note that * MIDI operates at TTL [0-5] levels, not RS232 [0-10] levels, so * no external MAX-232 chip is needed. * * @author Jason Rohrer */ // settings for software serial // code copied from: // http://www.iptel-now.de/HOWTO/PIC/pic.html //RS232 settings #pragma RS232_TXPORT PORTA #pragma RS232_RXPORT PORTA #pragma RS232_TXPIN 1 #pragma RS232_RXPIN 4 // // BAUD for MIDI is 31.25 Kbps #pragma RS232_BAUD 31250 // inverted bit values for midi #pragma TRUE_RS232 1 //Timing settings #pragma CLOCK_FREQ 10000000 // end copied code /** * Sets up the chip for midi transmission. Must be called * before calling any of the other midi routines. */ void midiSetup(); /** * Turns a note on. * * @param inChannel the channel number, in [0,15] * (which correspond to midi channels 1-16). * @param inKey the key number, in [0,127]. * Middle C is 60. * @param inVelocity the note velocity, in [0,127]. */ void turnNoteOn( char inChannel, char inKey, char inVelocity ); /** * Turns a note off. * * @param inChannel the channel number, in [0,15] * (which correspond to midi channels 1-16). * @param inKey the key number, in [0,127]. * Middle C is 60. * @param inVelocity the note velocity, in [0,127]. */ void turnNoteOff( char inChannel, char inKey, char inVelocity ); Cultivation_9+dfsg1_UnixSource/minorGems/sound/midi/pic/midi.c0000640000175000017500000000323707307057505023313 0ustar pabspabs/* * Modification History * * 2001-June-4 Jason Rohrer * Created. */ #include "midi.h" // avoid local function variables char midiTempCharA; void midiSetup() { // Hardware Initialization for software serial // code copied from: // http://www.iptel-now.de/HOWTO/PIC/pic.html disable_interrupt( GIE ); set_bit( STATUS, RP0 ); // turn on pin 3 for reading, and leave others as output pins set_tris_a( 00010000b ); set_tris_b( 0 ); clear_bit( STATUS, RP0 ); output_port_a( 0 ); output_port_b( 0 ); // end copied code } void turnNoteOn( char inChannel, char inKey, char inVelocity ) { // the on code midiTempCharA = 10010000B; // make sure the channel is in range if( inChannel > 15 ) { return; } // stick the channel into the last 4 bits midiTempCharA = midiTempCharA | inChannel; // make sure the key is in range if( inKey > 127 ) { return; } // make sure the velocity is in range if( inVelocity > 127 ) { return; } // send the channel putchar( midiTempCharA ); // send the key putchar( inKey ); // send the velocity putchar( inVelocity ); } void turnNoteOff( char inChannel, char inKey, char inVelocity ) { // the off code midiTempCharA = 10000000B; // make sure the channel is in range if( inChannel > 15 ) { return; } // stick the channel into the last 4 bits midiTempCharA = midiTempCharA | inChannel; // make sure the key is in range if( inKey > 127 ) { return; } // make sure the velocity is in range if( inVelocity > 127 ) { return; } // send the channel putchar( midiTempCharA ); // send the key putchar( inKey ); // send the velocity putchar( inVelocity ); } Cultivation_9+dfsg1_UnixSource/minorGems/sound/formats/0000750000175000017500000000000011401021061022147 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/sound/formats/aiff.h0000640000175000017500000000144610047466057023262 0ustar pabspabs/* * Modification History * * 2004-May-9 Jason Rohrer * Created. */ #ifndef AIFF_INCLUDED #define AIFF_INCLUDED /** * Constructs an AIFF header. * * @param inNumChannels the number of channels. * @param inSampleSizeInBits the size of each sound sample in bits. * @param inSampleRateInHertz the number of sample frames per second. * @param inNumSampleFrames the total number of sample frames in the file. * @param outHeaderLength pointer to where the length of the header, in bytes, * should be returned. * * @return the header. * Must be destroyed by caller. */ unsigned char *getAIFFHeader( int inNumChannels, int inSampleSizeInBits, int inSampleRateInHertz, int inNumSampleFrames, int *outHeaderLength ); #endif Cultivation_9+dfsg1_UnixSource/minorGems/sound/formats/testAIFF.cpp0000640000175000017500000000451210047466057024312 0ustar pabspabs/* * Modification History * * 2004-May-9 Jason Rohrer * Created. */ #include "aiff.h" #include #include #include "minorGems/util/random/StdRandomSource.h" int main() { int numChannels = 2; int numNotes = 5; int noteLengthInSeconds = 1; int sampleSize = 16; int sampleRate = 44100; int samplesPerNote = noteLengthInSeconds * sampleRate; // generate the header int headerSize; unsigned char *aiffHeader = getAIFFHeader( 2, sampleSize, sampleRate, numNotes * samplesPerNote, &headerSize ); FILE *aiffFile = fopen( "test.aiff", "wb" ); printf( "Header size = %d\n", headerSize ); fwrite( aiffHeader, 1, headerSize, aiffFile ); delete [] aiffHeader; StdRandomSource randSource; for( int i=0; i> 8 & 0xFF; unsigned char lsb = val && 0xFF; // output twice, once for each channel fwrite( &msb, 1, 1, aiffFile ); fwrite( &lsb, 1, 1, aiffFile ); fwrite( &msb, 1, 1, aiffFile ); fwrite( &lsb, 1, 1, aiffFile ); } } fclose( aiffFile ); return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/sound/formats/aiff.cpp0000640000175000017500000000564610047466057023623 0ustar pabspabs/* * Modification History * * 2004-May-9 Jason Rohrer * Created. */ #include "aiff.h" #include "minorGems/util/StringBufferOutputStream.h" unsigned char *getAIFFHeader( int inNumChannels, int inSampleSizeInBits, int inSampleRateInHertz, int inNumSampleFrames, int *outHeaderLength ) { /* Header Information 32 bits 'FORM' 32 bits ckSize 32 bits 'AIFF' 32 bits 'COMM' 32 bits ckSize '18' 16 bits numChannels 32 bits num SampleFrames 16 bits sampleSize '16' 80 bits sampleRate { 16 bits = '16398' 16 bits = '44100' remaining 48 bits = '0' } 32 bits 'SSND' 32 bits ckSize 32 bits offset '0' 32 bits block size '0' FINALLY: sound data #bytes in 'FORM' chunk = bytes in sound data + 46 #bytes in 'SSND' chunk = bytes in sound data + 8 */ int soundSizeInBytes = ( inNumChannels * inNumSampleFrames * inSampleSizeInBits ) / 8; StringBufferOutputStream *headerStream = new StringBufferOutputStream(); headerStream->writeString( "FORM" ); // form chunk ID headerStream->writeLong( 46 + soundSizeInBytes ); // size of form chunk headerStream->writeString( "AIFF" ); // form type headerStream->writeString( "COMM" ); // common chunk ID headerStream->writeLong( 18 ); // common chunk size headerStream->writeShort( inNumChannels ); headerStream->writeLong( inNumSampleFrames ); // number of frames in sound // data headerStream->writeShort( inSampleSizeInBits ); // size of each sample headerStream->writeLong( inSampleRateInHertz | 16398<<16 ); // sample rate in Hz plus // mysterious most significant // bits headerStream->writeLong( 0 ); // 48 bits of 0 padding headerStream->writeShort( 0 ); headerStream->writeString( "SSND" ); // Sound chunk ID headerStream->writeLong( 8 + soundSizeInBytes ); // size of sound chunk headerStream->writeLong( 0 ); // offset headerStream->writeLong( 0 ); // block size unsigned char *returnBuffer = headerStream->getBytes( outHeaderLength ); delete headerStream; return returnBuffer; } Cultivation_9+dfsg1_UnixSource/minorGems/sound/SoundPlayer.cpp0000640000175000017500000000671610426423173023500 0ustar pabspabs/* * Modification History * * 2006-April-30 Jason Rohrer * Created. Adapted from Transcend/game/SoundPlayer.cpp * * 2006-May-4 Jason Rohrer * Added current play position timestamp to SampleSource interface. */ #include "SoundPlayer.h" #include // callback passed into portaudio static int portaudioCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData ) { SoundPlayer *player = (SoundPlayer *)userData; int numSamples = framesPerBuffer; if( player->isStereo() ) { numSamples = numSamples * 2; } SampleSource *source = player->getSampleSource(); double playTime = outTime / player->getSampleRate(); source->getSamples( numSamples, (float *)outputBuffer, playTime ); return 0; } SoundPlayer::SoundPlayer( int inSampleRate, char inStereo, SampleSource *inSampleSource ) : mSampleRate( inSampleRate ), mStereo( inStereo ), mSampleSource( inSampleSource ) { int numChannels = 1; if( mStereo ) { numChannels = 2; } PaError error = Pa_Initialize(); if( error == paNoError ) { error = Pa_OpenStream( &mAudioStream, paNoDevice,// default input device 0, // no input paFloat32, // 32 bit floating point input NULL, Pa_GetDefaultOutputDeviceID(), numChannels, // mono or stereo output paFloat32, // 32 bit floating point output NULL, mSampleRate, 1024, // frames per buffer 0, // number of buffers, if zero then use default minimum paClipOff, // we won't output out of range samples so // don't bother clipping them portaudioCallback, (void *)this ); // pass self-pointer to callback function if( error == paNoError ) { error = Pa_StartStream( mAudioStream ); if( error == paNoError ) { mAudioInitialized = true; } else { fprintf( stderr, "Error starting audio stream\n" ); Pa_CloseStream( mAudioStream ); } } else { fprintf( stderr, "Error opening audio stream\n" ); Pa_Terminate(); } } else { fprintf( stderr, "Error initializing audio framework\n" ); } if( error != paNoError ) { fprintf( stderr, "Error number: %d\n", error ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( error ) ); mAudioInitialized = false; } } SoundPlayer::~SoundPlayer() { if( mAudioInitialized ) { PaError error = Pa_StopStream( mAudioStream ); if( error == paNoError ) { error = Pa_CloseStream( mAudioStream ); if( error != paNoError ) { fprintf( stderr, "Error closingaudio stream\n" ); } } else { fprintf( stderr, "Error stopping audio stream\n" ); } Pa_Terminate(); if( error != paNoError ) { fprintf( stderr, "Error number: %d\n", error ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( error) ); } } } Cultivation_9+dfsg1_UnixSource/minorGems/io/0000750000175000017500000000000011401021060017752 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/io/Serializable.h0000640000175000017500000000345610340437222022556 0ustar pabspabs/* * Modification History * * 2001-January-10 Jason Rohrer * Created. * Added untility functions for converting integers to and from byte arrays. * * 2001-January-15 Jason Rohrer * Made utility functions static. * Fixed a major bug in the longToBytes function. * * 2001-February-3 Jason Rohrer * Removed the long io functions, which are now contained in the * input- and outputStream classes. * * 2005-November-21 Jason Rohrer * Fixed a warning by adding a virtual destructor. */ #include "minorGems/common.h" #ifndef SERIALIZABLE_CLASS_INCLUDED #define SERIALIZABLE_CLASS_INCLUDED #include "InputStream.h" #include "OutputStream.h" /** * Interface for an object that can be serialized to and deserialized * from a stream. * * Note that the deserialize function is meant to be called from an already * constructed object (to set object parameters using data read from the * stream), and is not a method of obtaining an object. * * All multi-byte data members should be encoded in using a big endian format. * * @author Jason Rohrer */ class Serializable { public: /** * Writes this object out to a stream. * * @param inOutputStream the stream to write to. * * @return the number of bytes written successfully, * or -1 for a stream error. */ virtual int serialize( OutputStream *inOutputStream ) = 0; /** * Reads this object in from a stream. * * @param inInputStream the stream to read from. * * @return the number of bytes read successfully, * or -1 for a stream error. */ virtual int deserialize( InputStream *inInputStream ) = 0; virtual ~Serializable(); }; inline Serializable::~Serializable() { // does nothing // exists to ensure that subclass destructors are called } #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/file/0000750000175000017500000000000011401021060020671 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/io/file/unix/0000750000175000017500000000000011401021060021654 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/io/file/unix/DirectoryUnix.cpp0000640000175000017500000000141207753761036025224 0ustar pabspabs/* * Modification History * * 2003-January-23 Jason Rohrer * Created. * * 2003-November-10 Jason Rohrer * Added makeDirectory function. */ #include "minorGems/io/file/Directory.h" #include char Directory::removeDirectory( File *inFile ) { char *fileName = inFile->getFullFileName(); int result = rmdir( fileName ); delete [] fileName; if( result == 0 ) { return true; } else { return false; } } char Directory::makeDirectory( File *inFile ) { char *stringName = inFile->getFullFileName(); int result = mkdir( stringName, 0xFFFF ); delete [] stringName; if( 0 == result ) { return true; } else { return false; } } Cultivation_9+dfsg1_UnixSource/minorGems/io/file/FileInputStream.h0000640000175000017500000000740211364154511024141 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-April-12 Jason Rohrer * Changed so that File is not destroyed when this stream is destroyed. * * 2001-April-29 Jason Rohrer * Fixed a bug in the use of fread * (num elements and element size swapped). * Fixed a memory leak in the error message handling. * * 2006-November-23 Jason Rohrer * Fixed a memory leak in error message handling. * * 2010-April-22 Jason Rohrer * Added support for text mode. */ #include "minorGems/common.h" #ifndef FILE_INPUT_STREAM_CLASS_INCLUDED #define FILE_INPUT_STREAM_CLASS_INCLUDED #include "minorGems/io/InputStream.h" #include "File.h" #include /** * File implementation of an InputStream. * * @author Jason Rohrer */ class FileInputStream : public InputStream { public: /** * Constructs an input stream. * * @param inFile the file to open for reading. * If the file does not exist, all calls to read will fail. * inFile is NOT destroyed when this class is destroyed. * @param inTextMode true to open the file as text, false as binary. * Defaults to false. */ FileInputStream( File *inFile, char inTextMode = false ); /** * Destroys this stream and closes the file. */ ~FileInputStream(); /** * Gets the file attached to this stream. * * @return the file used by this stream. * Should not be modified or destroyed by caller until after * this class is destroyed. */ File *getFile(); // implementst InputStream interface virtual long read( unsigned char *inBuffer, long inNumBytes ); private: File *mFile; FILE *mUnderlyingFile; }; inline FileInputStream::FileInputStream( File *inFile, char inTextMode ) : mFile( inFile ) { int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); if( inTextMode ) { mUnderlyingFile = fopen( fileName, "r" ); } else { mUnderlyingFile = fopen( fileName, "rb" ); } if( mUnderlyingFile == NULL ) { // file open failed. char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "Opening file %s failed.", fileName ); setNewLastError( stringBuffer ); } delete [] fileName; } inline FileInputStream::~FileInputStream() { if( mUnderlyingFile != NULL ) { fclose( mUnderlyingFile ); } } inline File *FileInputStream::getFile() { return mFile; } inline long FileInputStream::read( unsigned char *inBuffer, long inNumBytes ) { if( mUnderlyingFile != NULL ) { long numRead = fread( inBuffer, 1, inNumBytes, mUnderlyingFile ); if( numRead < inNumBytes ) { int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); if( feof( mUnderlyingFile ) ) { // we reached the end of the file. char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "Reached end of file %s on read.", fileName ); setNewLastError( stringBuffer ); delete [] fileName; } else { // some other kind of error occured char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "Reading from file %s failed.", fileName ); setNewLastError( stringBuffer ); delete [] fileName; if( numRead == 0 ) { // a complete read failure return -1; } } } return numRead; } else { // file was not opened properly int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "File %s was not opened properly before reading.", fileName ); delete [] fileName; setNewLastError( stringBuffer ); return -1; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/file/FileOutputStream.h0000640000175000017500000000667211356652402024355 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-April-12 Jason Rohrer * Changed so that File is not destroyed when this stream is destroyed. * * 2001-April-29 Jason Rohrer * Fixed a bug in the use of fwrite * (num elements and element size swapped). * Fixed a memory leak in the error message handling. * * 2006-August-22 Jason Rohrer * Fixed include order bug. * * 2010-April-6 Jason Rohrer * Fixed memory leak. */ #include "minorGems/common.h" #include "File.h" #ifndef FILE_OUTPUT_STREAM_CLASS_INCLUDED #define FILE_OUTPUT_STREAM_CLASS_INCLUDED #include "minorGems/io/OutputStream.h" #include /** * File implementation of an OutputStream. * * @author Jason Rohrer */ class FileOutputStream : public OutputStream { public: /** * Constructs an output stream. * * @param inFile the file to open for writing. * If the file does not exist, it will be created. * inFile is NOT destroyed when this class is destroyed. * @param inAppend set to true to append to file. If * file does not exist, file is still created. Defaults * to false. */ FileOutputStream( File *inFile, char inAppend = false ); /** * Destroys this stream and closes the file. */ ~FileOutputStream(); /** * Gets the file attached to this stream. * * @return the file used by this stream. * Should not be modified or destroyed by caller until after * this class is destroyed. */ File *getFile(); // implementst OutputStream interface virtual long write( unsigned char *inBuffer, long inNumBytes ); private: File *mFile; FILE *mUnderlyingFile; }; inline FileOutputStream::FileOutputStream( File *inFile, char inAppend ) : mFile( inFile ) { int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); if( inAppend ) { mUnderlyingFile = fopen( fileName, "ab" ); } else { mUnderlyingFile = fopen( fileName, "wb" ); } if( mUnderlyingFile == NULL ) { // file open failed. char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "Opening file %s failed.", fileName ); setNewLastError( stringBuffer ); } delete [] fileName; } inline FileOutputStream::~FileOutputStream() { if( mUnderlyingFile != NULL ) { fclose( mUnderlyingFile ); } } inline File *FileOutputStream::getFile() { return mFile; } inline long FileOutputStream::write( unsigned char *inBuffer, long inNumBytes ) { if( mUnderlyingFile != NULL ) { long numWritten = fwrite( inBuffer, 1, inNumBytes, mUnderlyingFile ); if( numWritten < inNumBytes ) { int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); // some other kind of error occured char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "Writing to file %s failed.", fileName ); setNewLastError( stringBuffer ); delete [] fileName; if( numWritten == 0 ) { // a complete write failure return -1; } } return numWritten; } else { // file was not opened properly int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "File %s was not opened properly before writing.", fileName ); delete [] fileName; setNewLastError( stringBuffer ); return -1; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/file/testPath.cpp0000640000175000017500000000112207522316361023212 0ustar pabspabs/* * Modification History * * 2002-August-1 Jason Rohrer * Created. */ #include "Path.h" #include int main() { char *pathString = "/test/this/thing"; printf( "using path string = %s\n", pathString ); printf( "Constructing path.\n" ); Path *path = new Path( pathString ); printf( "Extracting path string.\n" ); char *extractedPathString = path->getPathStringTerminated(); printf( "extracted path string = %s\n", extractedPathString ); delete [] extractedPathString; delete path; return 1; } Cultivation_9+dfsg1_UnixSource/minorGems/io/file/File.h0000640000175000017500000005422311374544330021753 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-February-25 Jason Rohrer * Fixed file name bugs in length and existence functions. * * 2001-May-11 Jason Rohrer * Added a missing include. * * 2001-November-3 Jason Rohrer * Added a function for checking if a file is a directory. * Added a function for getting the child files of a directory. * Added a function for getting a pathless file name. * * 2001-November-13 Jason Rohrer * Made name length parameter optional in constructor. * Made return length parameter optional in name getting functions. * * 2001-November-17 Jason Rohrer * Added a functions for removing a file and for copying a file. * * 2002-March-11 Jason Rohrer * Added destruction comment to getFullFileName(). * * 2002-March-13 Jason Rohrer * Changed mName to be \0-terminated to fix interaction bugs with Path. * Fixed a missing delete. * Added a function for creating a directory. * * 2002-March-31 Jason Rohrer * Fixed some bad syntax. * * 2002-April-6 Jason Rohrer * Replaced use of strdup. * * 2002-April-8 Jason Rohrer * Fixed fopen bug. * * 2002-April-11 Jason Rohrer * Fixed a memory leak. * Fixed a casting error. * * 2002-June-28 Jason Rohrer * Added a function for copying a file class. * * 2002-August-3 Jason Rohrer * Added a function for getting the parent file. * * 2002-August-5 Jason Rohrer * Used an unused error variable. * * 2002-September-11 Jason Rohrer * Added return value to remove. * * 2003-January-27 Jason Rohrer * Added a function for reading file contents. * * 2003-February-3 Jason Rohrer * Added a function for writing a string to a file. * * 2003-March-13 Jason Rohrer * Added a function for getting a child file from a directory. * * 2003-June-2 Jason Rohrer * Fixed parent directory behavior when current file is root directory. * Fixed a bug in getting child files of root directory. * * 2003-November-6 Jason Rohrer * Added function for getting last modification time. * * 2003-November-10 Jason Rohrer * Changed to use platform-dependent makeDirectory function. * * 2004-January-4 Jason Rohrer * Added recursive child file functions. * * 2005-August-29 Jason Rohrer * Fixed an uninitialized variable warning. * * 2010-March-6 Jason Rohrer * Added versions of writeToFile readFileContents for binary data. * * 2010-April-23 Jason Rohrer * Fixed a string length bug when line ends are Windows. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/common.h" #ifndef FILE_CLASS_INCLUDED #define FILE_CLASS_INCLUDED #include #include #include #include #include "Path.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/util/stringUtils.h" /** * File interface. Provides access to information about a * file. * * @author Jason Rohrer */ class File { public: /** * Constructs a file. * * @param inPath the path for this file. * Is destroyed when this class is destroyed. * Pass in NULL to specify * no path (the current working directory). * @param inName the name of the file to open. * Must be destroyed by caller if not const. * Copied internally. * @param inNameLength length of the name in chars, * or -1 to use the c-string length of inName * (assuming that inName is \0-terminated). * Defaults to -1. */ File( Path *inPath, const char *inName, int inNameLength = -1 ); ~File(); /** * Gets whether this file is a directory. * * @return true iff this file is a directory. */ char isDirectory(); /** * Makes a directory in the location of this file. * * Can only succeed if exists() is false. * * @return true iff directory creation succeeded. */ char makeDirectory(); /** * Gets the files contained in this file if it is a directory. * * @param outNumFiles pointer to where the number of * files will be returned. * * @return an array of files, or NULL if this * file is not a directory, is an empty directory, or doesn't exist. * Must be destroyed by caller if non-NULL. */ File **getChildFiles( int *outNumFiles ); /** * Gets the files contained in this file if it is a directory and * recursively in subdirectories of this file. * * @param inDepthLimit the maximum subdirectory depth to recurse into. * If inDepthLimit is 0, then only child files in this directory * will be returned. * @param outNumFiles pointer to where the number of * files will be returned. * * @return an array of files, or NULL if this * file is not a directory, is an empty directory (or a directory * containing empty subdirectories), or doesn't exist. * Must be destroyed by caller if non-NULL. */ File **getChildFilesRecursive( int inDepthLimit, int *outNumFiles ); /** * Gets a child of this directory. * * @param inChildFileName the name of the child file. * Must be destroyed by caller if non-const. * * @return the child file (even if it does not exist), or NULL if * this file is not a directory. * Must be destroyed by caller if non-NULL. */ File *getChildFile( const char *inChildFileName ); /** * Gets the parent directory of this file. * * @return the parent directory of this file. * Must be destroyed by caller. */ File *getParentDirectory(); /** * Gets the length of this file. * * @return the length of this file in bytes. Returns * 0 if the file does not exist. */ long getLength(); /** * Gets whether a file exists. * * @return true if the file exists. */ char exists(); /** * Gets the last modification time of this file. * * @return the modification time in seconds based on the * system clock. Returns 0 if the file does not exist. */ unsigned long getModificationTime(); /** * Removes this file from the disk, if it exists. * * @return true iff the remove succeeded, false if the removal * fails or the file does not exist. */ char remove(); /** * Copies this file object (does not copy the file described by * this object). * * @return a deep copy of this file object. */ File *copy(); /** * Copies the contents of this file into another file. * * @param inDestination the file to copy this file into. * If it exists, it will be overwritten. * If it does not exist, it will be created. * Must be destroyed by caller. * @param inBlockSize the block size to use when copying. * Defaults to blocks of 5000 bytes. */ void copy( File *inDestination, long inBlockSize = 5000 ); /** * Gets the full-path file name sufficient * to access this file from the current working * directory. * * @param outLength pointer to where the name length, in * characters, will be returned. Set to NULL to ignore * the output length. Defaults to NULL. * * @return the full path file name for this file, * in platform-specific form. Must be destroyed by caller. * The returned string is '\0' terminated, but this * extra character is not included in the length. * Must be destroyed by caller. */ char *getFullFileName( int *outLength = NULL ); /** * Gets the pathless name of this file. * * @param outLength pointer to where the name length, in * characters, will be returned. Set to NULL to ignore * the output length. Defaults to NULL. * * @return the name of this file. Must be destroyed by caller. */ char *getFileName( int *outLength = NULL ); /** * Reads the contents of this file. * * @return a \0-terminated string containing the file contents, * or NULL if reading the file into memory failed. * Must be destroyed by caller. */ char *readFileContents(); /** * Reads the contents of this file. * * @param outLength pointer to where the return array length should * be returned. * @param inTextMode true to open the file as text, false as binary. * Defaults to false. * * @return an array containing the binary file contents, * or NULL if reading the file into memory failed. * Must be destroyed by caller. */ unsigned char *readFileContents( int *outLength, char inTextMode = false ); /** * Writes a string to this file. * * @param inString the \0-terminated string to write. * Must be destroyed by caller if non-const. * * @return true if the file was written to successfully, or * false otherwise. */ char writeToFile( const char *inString ); /** * Writes a binary data to this file. * * @param inData the data to write. * Must be destroyed by caller if non-const. * @param inLength length of inData. * * @return true if the file was written to successfully, or * false otherwise. */ char writeToFile( unsigned char *inData, int inLength ); private: Path *mPath; char *mName; int mNameLength; /** * Gets the files contained in this file if it is a directory and * recursively in subdirectories of this file. * * @param inDepthLimit the maximum subdirectory depth to recurse into. * If inDepthLimit is 0, then only child files in this directory * will be returned. * @param inResultVector vector to add the discovered files to. * Must be destroyed by caller. */ void getChildFilesRecursive( int inDepthLimit, SimpleVector *inResultVector ); }; inline File::File( Path *inPath, const char *inName, int inNameLength ) : mPath( inPath ), mNameLength( inNameLength ) { if( inNameLength == -1 ) { inNameLength = strlen( inName ); mNameLength = inNameLength; } // copy name internally mName = stringDuplicate( inName ); } inline File::~File() { delete [] mName; if( mPath != NULL ) { delete mPath; } } inline long File::getLength() { struct stat fileInfo; // get full file name int length; char *stringName = getFullFileName( &length ); int statError = stat( stringName, &fileInfo ); delete [] stringName; if( statError == 0 ) { return fileInfo.st_size; } else { // file does not exist return 0; } } inline char File::isDirectory() { struct stat fileInfo; // get full file name int length; char *stringName = getFullFileName( &length ); int statError = stat( stringName, &fileInfo ); delete [] stringName; if( statError == -1 ) { return false; } else { return S_ISDIR( fileInfo.st_mode ); } } inline File **File::getChildFiles( int *outNumFiles ) { int length; char *stringName = getFullFileName( &length ); DIR *directory = opendir( stringName ); if( directory != NULL ) { SimpleVector< File* > *fileVector = new SimpleVector< File* >(); struct dirent *entry = readdir( directory ); if( entry == NULL ) { delete fileVector; closedir( directory ); delete [] stringName; *outNumFiles = 0; return NULL; } while( entry != NULL ) { // skip parentdir and thisdir files, if they occur if( strcmp( entry->d_name, "." ) && strcmp( entry->d_name, ".." ) ) { Path *newPath; if( mPath != NULL ) { newPath = mPath->append( mName ); } else { if( Path::isRoot( mName ) ) { // use name as a string path newPath = new Path( mName ); } else { char **folderPathArray = new char*[1]; folderPathArray[0] = mName; // a non-absolute path to this directory's contents int numSteps = 1; char absolute = false; newPath = new Path( folderPathArray, numSteps, absolute ); delete [] folderPathArray; } } // safe to pass d_name in directly because it is copied // internally by the constructor fileVector->push_back( new File( newPath, entry->d_name, strlen( entry->d_name ) ) ); } entry = readdir( directory ); } // now we have a vector full of this directory's files int vectorSize = fileVector->size(); *outNumFiles = vectorSize; if( vectorSize == 0 ) { delete fileVector; closedir( directory ); delete [] stringName; return NULL; } else { File **returnFiles = new File *[vectorSize]; for( int i=0; igetElement( i ) ); } delete fileVector; closedir( directory ); delete [] stringName; return returnFiles; } } else { delete [] stringName; *outNumFiles = 0; return NULL; } } inline File **File::getChildFilesRecursive( int inDepthLimit, int *outNumFiles ) { // create a vector for results SimpleVector *resultVector = new SimpleVector(); // call the recursive function getChildFilesRecursive( inDepthLimit, resultVector ); // extract results from vector File **resultArray = NULL; int numResults = resultVector->size(); if( numResults > 0 ) { resultArray = resultVector->getElementArray(); } delete resultVector; *outNumFiles = numResults; return resultArray; } inline void File::getChildFilesRecursive( int inDepthLimit, SimpleVector *inResultVector ) { // get our child files int numChildren; File **childFiles = getChildFiles( &numChildren ); if( childFiles != NULL ) { // for each child, add it to vector and // recurse into it if it is a directory for( int i=0; ipush_back( child ); if( child->isDirectory() ) { // skip recursion if we have hit our depth limit if( inDepthLimit > 0 ) { // recurse into this subdirectory child->getChildFilesRecursive( inDepthLimit - 1, inResultVector ); } } } delete [] childFiles; } } inline File *File::getChildFile( const char *inChildFileName ) { // make sure we are a directory if( !isDirectory() ) { return NULL; } // get a path to this directory Path *newPath; if( mPath != NULL ) { newPath = mPath->append( mName ); } else { char **folderPathArray = new char*[1]; folderPathArray[0] = mName; // a non-absolute path to this directory's contents int numSteps = 1; char absolute = false; newPath = new Path( folderPathArray, numSteps, absolute ); delete [] folderPathArray; } return new File( newPath, inChildFileName ); } inline File *File::getParentDirectory() { if( mPath != NULL ) { char *parentName; Path *parentPath; if( strcmp( mName, ".." ) == 0 ) { // already a parent dir reference // append one more parent dir reference with parentName below parentPath = mPath->append( ".." ); parentName = stringDuplicate( ".." ); } else { // not a parent dir reference, so we can truncate parentPath = mPath->truncate(); parentName = mPath->getLastStep(); } File *parentFile = new File( parentPath, parentName ); delete [] parentName; return parentFile; } else { if( Path::isRoot( mName ) ) { // we are already at the root return new File( NULL, mName ); } else { // append parent dir symbol to path char **parentPathSteps = new char*[1]; parentPathSteps[0] = mName; Path *parentPath = new Path( parentPathSteps, 1, false ); const char *parentName = ".."; File *parentFile = new File( parentPath, parentName ); delete [] parentPathSteps; return parentFile; } } } inline char File::exists() { struct stat fileInfo; // get full file name int length; char *stringName = getFullFileName( &length ); int statError = stat( stringName, &fileInfo ); delete [] stringName; if( statError == 0 ) { return true; } else { // file does not exist return false; } } inline unsigned long File::getModificationTime() { struct stat fileInfo; // get full file name int length; char *stringName = getFullFileName( &length ); int statError = stat( stringName, &fileInfo ); delete [] stringName; if( statError == 0 ) { return fileInfo.st_mtime; } else { // file does not exist return 0; } } inline char File::remove() { char returnVal = false; if( exists() ) { char *stringName = getFullFileName(); int error = ::remove( stringName ); if( error == 0 ) { returnVal = true; } delete [] stringName; } return returnVal; } inline File *File::copy() { Path *pathCopy = NULL; if( mPath != NULL ) { pathCopy = mPath->copy(); } return new File( pathCopy, mName ); } inline void File::copy( File *inDestination, long inBlockSize ) { char *thisFileName = getFullFileName(); char *destinationFileName = inDestination->getFullFileName(); FILE *thisFile = fopen( thisFileName, "rb" ); FILE *destinationFile = fopen( destinationFileName, "wb" ); long length = getLength(); long bytesCopied = 0; char *buffer = new char[ inBlockSize ]; while( bytesCopied < length ) { long bytesToCopy = inBlockSize; // end of file case if( length - bytesCopied < bytesToCopy ) { bytesToCopy = length - bytesCopied; } fread( buffer, 1, bytesToCopy, thisFile ); fwrite( buffer, 1, bytesToCopy, destinationFile ); bytesCopied += bytesToCopy; } fclose( thisFile ); fclose( destinationFile ); delete [] buffer; delete [] thisFileName; delete [] destinationFileName; } inline char *File::getFileName( int *outLength ) { char *returnName = stringDuplicate( mName ); if( outLength != NULL ) { *outLength = mNameLength; } return returnName; } inline char *File::getFullFileName( int *outLength ) { int length = mNameLength; int pathLength = 0; char *path = NULL; if( mPath != NULL ) { path = mPath->getPathString( &pathLength ); length += pathLength; } // extra character for '\0' termination char *returnString = new char[ length + 1 ]; if( path != NULL ) { memcpy( returnString, path, pathLength ); memcpy( &( returnString[pathLength] ), mName, mNameLength ); delete [] path; } else { // no path, so copy the name directly in memcpy( returnString, mName, mNameLength ); } // terminate the string returnString[ length ] = '\0'; if( outLength != NULL ) { *outLength = length; } return returnString; } #include "minorGems/io/file/FileInputStream.h" #include "minorGems/io/file/FileOutputStream.h" inline char *File::readFileContents() { int length; // text mode! unsigned char *data = readFileContents( &length, true ); if( data == NULL ) { return NULL; } char *dataString = new char[ length + 1 ]; memcpy( dataString, data, length ); dataString[ length ] = '\0'; delete [] data; return dataString; } inline unsigned char *File::readFileContents( int *outLength, char inTextMode ) { if( exists() ) { int length = getLength(); unsigned char *returnData = new unsigned char[ length ]; if( returnData != NULL ) { FileInputStream *input = new FileInputStream( this, inTextMode ); int numRead = input->read( returnData, length ); delete input; // in text mode, read length might not equal binary file length, // due to line end conversion if( numRead == length || ( inTextMode && numRead >= 0 ) ) { *outLength = numRead; return returnData; } else { delete [] returnData; return NULL; } } else { // failed to allocate this much memory return NULL; } } else { return NULL; } } inline char File::writeToFile( const char *inString ) { return writeToFile( (unsigned char *)inString, strlen( inString ) ); } inline char File::writeToFile( unsigned char *inData, int inLength ) { FileOutputStream *output = new FileOutputStream( this ); long numWritten = output->write( inData, inLength ); delete output; if( inLength == numWritten ) { return true; } else { return false; } } #include "Directory.h" inline char File::makeDirectory() { if( exists() ) { return false; } else { return Directory::makeDirectory( this ); } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/file/UniversalFileIO.h0000640000175000017500000000572407205611501024067 0ustar pabspabs// Jason Rohrer // UniversalFileIO.h /** * * Object that handles universal file reading and writing * Writes files as big endian, even on little endian machines * * Assumes that longs and floats are 32-bits * * Created 1-12-99 * Mods: * Jason Rohrer 1-30-2000 Fixed fwrite functions to work on little endian machines */ #ifndef UNIVERSAL_FILE_IO_INCLUDED #define UNIVERSAL_FILE_IO_INCLUDED #include class UniversalFileIO { public: UniversalFileIO(); long freadLong( FILE *f ); float freadFloat( FILE *f ); void fwriteLong( FILE *f, long x ); void fwriteFloat( FILE *f, float x ); private: char machineBigEndian; char bytesInLong; char bytesInFloat; }; inline UniversalFileIO::UniversalFileIO() : machineBigEndian( true ), bytesInLong( 4 ), bytesInFloat( 4 ) { // test whether machine is big endian long test = 1; char testChopped = (*(char*)&test); if( testChopped == 1 ) machineBigEndian = false; } inline long UniversalFileIO::freadLong( FILE *f ) { if( machineBigEndian ) { long returnVal; fread((void *)&returnVal, sizeof(long), 1, f); return returnVal; } else { unsigned char *buffer = new unsigned char[bytesInLong]; fread((void *)buffer, sizeof(char), bytesInLong, f); // now put the bytes into a long long returnVal = (long)( buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3] ); delete [] buffer; return returnVal; } } inline float UniversalFileIO::freadFloat( FILE *f ) { if( machineBigEndian ) { float returnVal; fread( (void *)&returnVal, sizeof(float), 1, f ); return returnVal; } else { unsigned char *buffer = new unsigned char[bytesInFloat]; fread( (void *)buffer, sizeof(char), bytesInFloat, f ); // now put the bytes into a long long temp = (long)(buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]); delete [] buffer; return *((float *) &temp); // convert long into a float } } inline void UniversalFileIO::fwriteLong( FILE *f, long x ) { if( machineBigEndian ) { fwrite( (void *)&x, sizeof(long), 1, f ); } else { unsigned char *buffer = new unsigned char[bytesInLong]; buffer[0] = (unsigned char)(x >> 24); // put bytes from long into char buffer buffer[1] = (unsigned char)(x >> 16); buffer[2] = (unsigned char)(x >> 8); buffer[3] = (unsigned char)(x); fwrite( (void *)buffer, sizeof(char), bytesInLong, f ); } } inline void UniversalFileIO::fwriteFloat( FILE *f, float x ) { if( machineBigEndian ) { fwrite( (void *)&x, sizeof(float), 1, f ); } else { unsigned char *buffer = new unsigned char[bytesInFloat]; long temp = *((long*)&x); // convert float into long so that bit-wise ops can be performed buffer[0] = (unsigned char)(temp >> 24); // put bytes from float into char buffer buffer[1] = (unsigned char)(temp >> 16); buffer[2] = (unsigned char)(temp >> 8); buffer[3] = (unsigned char)(temp); fwrite( (void *)buffer, sizeof(char), bytesInFloat, f ); } } #endifCultivation_9+dfsg1_UnixSource/minorGems/io/file/Path.h0000640000175000017500000003556411373324323021774 0ustar pabspabs/* * Modification History * * 2001-February-12 Jason Rohrer * Created. * * 2001-May-11 Jason Rohrer * Added a version of getPathString that * returns a '\0' terminated string. * * 2001-September-21 Jason Rohrer * Added a missing include. * * 2001-September-23 Jason Rohrer * Added a copy function. * Made some comments more explicit. * Changed the constructor to allow for const path step strings. * * 2001-November-3 Jason Rohrer * Added a function for appending a string to a path. * Changed the interface to the main constructor. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-April-11 Jason Rohrer * Fixed a variable scoping bug. * * 2002-July-2 Jason Rohrer * Fixed a major memory leak in copy(). * * 2002-August-1 Jason Rohrer * Added support for path truncation. * Added support for parsing platform-dependent path strings. * * 2003-May-29 Jason Rohrer * Fixed a bug when an extra delimeters are at the end of the path. * Fixed a bug when string path consists only of root. * * 2003-June-2 Jason Rohrer * Fixed a bug in absolute path detection. * Added platform-specific functions for root and absolute path detection. * Fixed a memory bug when string path contains root only. * Fixed a path step bug when path is root. * Fixed bugs in truncate and append when non-default root string is used. * * 2005-August-29 Jason Rohrer * Fixed an uninitialized variable warning. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/common.h" #ifndef PATH_CLASS_INCLUDED #define PATH_CLASS_INCLUDED #include #include "minorGems/util/stringUtils.h" #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Platform-independent file path interface. Contains * all of path except for file name. Thus, appending * a file name to the path will produce a complete file path. * * E.g., on Linux, file path: * temp/files/ * file name: * test.txt * full path: * temp/files/test.txt * * @author Jason Rohrer */ class Path { public: /** * Constructs a path. * * @param inPathSteps an array of c-strings representing * each step in the path, with no delimeters. * For example, { "temp", "files" } to represent * the linux path temp/files. * Must be destroyed by caller since copied internally. * @param inNumSteps the number of strings in the path. * @param inAbsolute set to true to make this an absolute * path. For example, in Linux, an absolute path * is one that starts with '/', as in /usr/include/. * The effects of inAbsolute vary by platform. * @param inRootString the root string for this path if it * is absolute, or NULL to specify a default root. * Defaults to NULL. * Must be destroyed by caller if non-NULL. */ Path( char **inPathSteps, int inNumSteps, char inAbsolute, char *inRootString = NULL ); /** * Constructs a path by parsing a platform-dependent path string. * * @param inPathSteps a \0-terminated string representing the path. * Must be destroyed by caller. */ Path( const char *inPathString ); ~Path(); /** * Returns a complete, platform-dependent string path. * * @param outLength pointer to where the path length, in * characters, will be returned. * * @return a new char array containing the path. Note * that the string is not terminated by '\0'. Must * be destroyed by the caller. */ char *getPathString( int *outLength ); /** * Returns a complete, platform-dependent string path, terminated * bye '\0'. * * @return a new char array containing the path. Note * that the string IS terminated by '\0'. Must * be destroyed by the caller. */ char *getPathStringTerminated(); /** * Gets the platform-specific path delimeter. * * Note that this function is implemented separately for * each supported platform. * * @return the path delimeter. */ static char getDelimeter(); /** * Gets start characters for an absolute path. * * Note that this function is implemented separately for * each supported platform. * * @param outLength pointer to where the string length, in * characters, will be returned. * * @return the absolute path start string characters. For * example, on Linux, this would be the string "/". * Must be destroyed by the caller. */ static char *getAbsoluteRoot( int *outLength ); /** * Gets whether a path string is absolute. * * Note that this function is implemented separately for * each supported platform. * * @param inPathString the string to check. * Must be destroyed by caller if non-const. * * @return true if the string is absolute, or false otherwise. */ static char isAbsolute( const char *inPathString ); /** * Extracts the root string from a path string. * * * @param inPathString the string to check. * Must be destroyed by caller if non-const. * * @return the root string, or NULL if inPathString is not * absolute. Must be destroyed by caller if non-NULL. */ static char *extractRoot( const char *inPathString ); /** * Gets whether a path string is a root path. * * Note that this function is implemented separately for * each supported platform. For example, on Unix, only "/" * is the root path, while on Windows, both "c:\" and "d:\" might * be root paths. * * @param inPathString the string to check. * Must be destroyed by caller if non-const. * * @return true if the string is a root string, or false otherwise. */ static char isRoot( const char *inPathString ); /** * Gets start string for an absolute path. * * @return the absolute path start string in \0-terminated form. * Must be destroyed by the caller. */ static char *getAbsoluteRootString(); /** * Copies this path. * * @return a new path that is a deep copy of this path. */ Path *copy(); /** * Constructs a new path by appending an additional * step onto this path. * * @param inStepString the step to add to this path. * Must be destroyed by caller if non-const. * * @return a new path with the extra step. * Must be destroyed by caller. */ Path *append( const char *inStepString ); /** * Constructs a new path by removing the last step from this path. * * @return a new path, or NULL if there is only one step in this path. * Must be destroyed by caller. */ Path *truncate(); /** * Gets the last step in this path. * * @return the last step. Must be destroyed by caller. */ char *getLastStep(); private: char **mPathSteps; int mNumSteps; int *mStepLength; char mAbsolute; // the root string of this path, if it is absolute char *mRootString; }; inline Path::Path( char **inPathSteps, int inNumSteps, char inAbsolute, char *inRootString ) : mNumSteps( inNumSteps ), mAbsolute( inAbsolute ), mRootString( NULL ) { if( inRootString != NULL ) { mRootString = stringDuplicate( inRootString ); } // copy the path steps mPathSteps = new char*[ mNumSteps ]; mStepLength = new int[ mNumSteps ]; for( int i=0; i 1 ) { // don't count tail end delimeters delimCount++; } currentDelimPointer = strstr( &( currentDelimPointer[1] ), delimString ); } // no delimeter at end of path mNumSteps = delimCount + 1; mPathSteps = new char*[ mNumSteps ]; mStepLength = new int[ mNumSteps ]; // now extract the chars between delimeters as path steps currentDelimPointer = strstr( pathRootSkipped, delimString ); int stepIndex = 0; currentDelimPointer[0] = '\0'; mPathSteps[ stepIndex ] = stringDuplicate( pathRootSkipped ); mStepLength[ stepIndex ] = strlen( mPathSteps[ stepIndex ] ); stepIndex++; while( currentDelimPointer != NULL ) { char *nextDelimPointer = strstr( &( currentDelimPointer[1] ), delimString ); if( nextDelimPointer != NULL ) { nextDelimPointer[0] = '\0'; } mPathSteps[ stepIndex ] = stringDuplicate( &( currentDelimPointer[1] ) ); mStepLength[ stepIndex ] = strlen( mPathSteps[ stepIndex ] ); stepIndex++; currentDelimPointer = nextDelimPointer; } } else { // no delimeters if( strlen( pathRootSkipped ) > 0 ) { mNumSteps = 1; mPathSteps = new char*[1]; mPathSteps[0] = stringDuplicate( pathRootSkipped ); mStepLength = new int[1]; mStepLength[0] = strlen( mPathSteps[0] ); } else { // path with root only mNumSteps = 0; mPathSteps = new char*[0]; mStepLength = new int[0]; } } delete [] delimString; delete [] pathStringCopy; } inline Path::~Path() { // delete each step for( int i=0; i= 1 ) { return stringDuplicate( mPathSteps[ mNumSteps - 1 ] ); } else { if( mAbsolute ) { if( mRootString != NULL ) { return stringDuplicate( mRootString ); } else { return getAbsoluteRootString(); } } else { // no path steps and not absolute... return stringDuplicate( "" ); } } } inline char *Path::getAbsoluteRootString() { int rootLength; char *root = getAbsoluteRoot( &rootLength ); char *rootString = new char[ rootLength + 1 ]; strncpy( rootString, root, rootLength ); // strncopy won't add termination if length limit reached rootString[ rootLength ] = '\0'; delete [] root; return rootString; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/file/linux/0000750000175000017500000000000011401021060022030 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/io/file/linux/PathLinux.cpp0000640000175000017500000000235111374544330024475 0ustar pabspabs/* * Modification History * * 2001-February-12 Jason Rohrer * Created. * * 2001-August-1 Jason Rohrer * Added missing length return value. * * 2003-June-2 Jason Rohrer * Added support for new path checking functions. * * 2010-May-18 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/io/file/Path.h" #include "minorGems/util/stringUtils.h" /* * Linux-specific path implementation. May be compatible * with other posix-complient systems. */ char Path::getDelimeter() { return '/'; } char *Path::getAbsoluteRoot( int *outLength ) { char *returnString = new char[1]; returnString[0] = '/'; *outLength = 1; return returnString; } char Path::isAbsolute( const char *inPathString ) { if( inPathString[0] == '/' ) { return true; } else { return false; } } char *Path::extractRoot( const char *inPathString ) { if( isAbsolute( inPathString ) ){ return stringDuplicate( "/" ); } else { return NULL; } } char Path::isRoot( const char *inPathString ) { if( strcmp( inPathString, "/" ) == 0 ) { return true; } else { return false; } } Cultivation_9+dfsg1_UnixSource/minorGems/io/file/Directory.h0000640000175000017500000000262207753761021023040 0ustar pabspabs/* * Modification History * * 2003-January-23 Jason Rohrer * Created. * * * 2003-November-10 Jason Rohrer * Added makeDirectory function. */ #include "minorGems/common.h" #include "minorGems/io/file/File.h" #ifndef DIRECTORY_INCLUDED #define DIRECTORY_INCLUDED /** * Class of static directory functions. * * This class exists because most directory operations are * platform-dependent, and a large body of existing code * depends on a platform-independent File.h. * * @author Jason Rohrer. */ class Directory { public: /** * Removes a directory. * * The directory must be empty for this call to succeed. * * @param inFile the file representing the directory. * Must be destroyed by caller. * * @return true if the directory is removed successfully, or * false otherwise (for example, if the directory is not empy). */ static char removeDirectory( File *inFile ); /** * Makes a directory. * * @param inFile the file representing the directory. * Must be destroyed by caller. * * @return true if the directory is removed successfully, or * false otherwise (for example, if the directory is not empy). */ static char makeDirectory( File *inFile ); }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/file/win32/0000750000175000017500000000000011401021060021633 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/io/file/win32/DirectoryWin32.cpp0000640000175000017500000000140507753761036025164 0ustar pabspabs/* * Modification History * * 2003-January-23 Jason Rohrer * Created. * * 2003-November-10 Jason Rohrer * Added makeDirectory function. */ #include "minorGems/io/file/Directory.h" #include char Directory::removeDirectory( File *inFile ) { char *fileName = inFile->getFullFileName(); int result = _rmdir( fileName ); delete [] fileName; if( result == 0 ) { return true; } else { return false; } } char Directory::makeDirectory( File *inFile ) { char *stringName = inFile->getFullFileName(); int result = mkdir( stringName ); delete [] stringName; if( 0 == result ) { return true; } else { return false; } } Cultivation_9+dfsg1_UnixSource/minorGems/io/file/win32/dirent.cpp0000640000175000017500000000654307552400302023651 0ustar pabspabs/* * Modification History * * 2002-April-7 Jason Rohrer * Added a mkdir wrapper for CodeWarrior. * * 2002-April-11 Jason Rohrer * Changed type of mode parameter to work with Visual C++. * Added missing include. * * 2002-July-22 Jason Rohrer * Commented out mkdir replacement function to work with new MSL. * * 2002-October-13 Jason Rohrer * Re-added mkdir wrapper function, since both CW4 and VC++ need it. */ /* Implementation of POSIX directory browsing functions and types for Win32. Kevlin Henney (mailto:kevlin@acm.org), March 1997. Copyright Kevlin Henney, 1997. All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation for any purpose is hereby granted without fee, provided that this copyright and permissions notice appear in all copies and derivatives, and that no charge may be made for the software and its documentation except to cover cost of distribution. This software is supplied "as is" without express or implied warranty. But that said, if there are any problems please get in touch. */ #include #include #include #include #include #include struct DIR { long handle; /* -1 for failed rewind */ struct _finddata_t info; struct dirent result; /* d_name null iff first time */ char *name; /* NTBS */ }; DIR *opendir(const char *name) { DIR *dir = 0; if(name && name[0]) { size_t base_length = strlen(name); const char *all = /* the root directory is a special case... */ strchr("/\\", name[base_length - 1]) ? "*" : "/*"; if((dir = (DIR *) malloc(sizeof *dir)) != 0 && (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0) { strcat(strcpy(dir->name, name), all); if((dir->handle = _findfirst(dir->name, &dir->info)) != -1) { dir->result.d_name = 0; } else /* rollback */ { free(dir->name); free(dir); dir = 0; } } else /* rollback */ { free(dir); dir = 0; errno = ENOMEM; } } else { errno = EINVAL; } return dir; } int closedir(DIR *dir) { int result = -1; if(dir) { if(dir->handle != -1) { result = _findclose(dir->handle); } free(dir->name); free(dir); } if(result == -1) /* map all errors to EBADF */ { errno = EBADF; } return result; } struct dirent *readdir(DIR *dir) { struct dirent *result = 0; if(dir && dir->handle != -1) { if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) { result = &dir->result; result->d_name = dir->info.name; } } else { errno = EBADF; } return result; } void rewinddir(DIR *dir) { if(dir && dir->handle != -1) { _findclose(dir->handle); dir->handle = _findfirst(dir->name, &dir->info); dir->result.d_name = 0; } else { errno = EBADF; } } int mkdir( const char *pathname, unsigned int mode ) { return mkdir( pathname ); } Cultivation_9+dfsg1_UnixSource/minorGems/io/file/win32/PathWin32.cpp0000640000175000017500000000336011374544330024104 0ustar pabspabs/* * Modification History * * 2001-February-12 Jason Rohrer * Created. * * 2001-March-4 Jason Rohrer * Fixed delimeter constants. * * 2001-August-1 Jason Rohrer * Added missing length return value. * * 2003-June-2 Jason Rohrer * Added support for new path checking functions. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/io/file/Path.h" #include "minorGems/util/stringUtils.h" /* * Windows-specific path implementation. */ char Path::getDelimeter() { return '\\'; } char *Path::getAbsoluteRoot( int *outLength) { // C:\ is the only root we can generically return char *returnString = new char[3]; returnString[0] = 'C'; returnString[1] = ':'; returnString[2] = '\\'; *outLength = 3; return returnString; } char Path::isAbsolute( const char *inPathString ) { // ignore first character, which will be drive letter if( inPathString[1] == ':' && inPathString[2] == '\\' ) { return true; } else { return false; } } char *Path::extractRoot( const char *inPathString ) { if( isAbsolute( inPathString ) ){ // copy path, then trim to only three characters char *pathCopy = stringDuplicate( inPathString ); pathCopy[ 3 ] = '\0'; char *trimmedCopy = stringDuplicate( pathCopy ); delete [] pathCopy; return trimmedCopy; } else { return NULL; } } char Path::isRoot( const char *inPathString ) { // must be of form "c:\" if( strlen( inPathString ) == 3 && inPathString[1] == ':' && inPathString[2] == '\\' ) { return true; } else { return false; } } Cultivation_9+dfsg1_UnixSource/minorGems/io/file/win32/dirent.h0000640000175000017500000000327607554101513023322 0ustar pabspabs/* * Modification History * * 2002-April-7 Jason Rohrer * Added a mkdir wrapper for CodeWarrior. * * 2002-April-11 Jason Rohrer * Changed type of mode parameter to work with Visual C++. * Added missing macros. * * 2002-July-22 Jason Rohrer * Commented out mkdir replacement function to work with new MSL. * * 2002-October-13 Jason Rohrer * Re-added mkdir wrapper function, since both CW4 and VC++ need it. */ #include "minorGems/common.h" /* Declaration of POSIX directory browsing functions and types for Win32. Kevlin Henney (mailto:kevlin@acm.org), March 1997. Copyright Kevlin Henney, 1997. All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation for any purpose is hereby granted without fee, provided that this copyright and permissions notice appear in all copies and derivatives, and that no charge may be made for the software and its documentation except to cover cost of distribution. */ #ifndef DIRENT_INCLUDED #define DIRENT_INCLUDED typedef struct DIR DIR; struct dirent { char *d_name; }; DIR *opendir(const char *); int closedir(DIR *); struct dirent *readdir(DIR *); void rewinddir(DIR *); #include /** * The Metrowerks Standard Library seems * to have only a 1-parameter mkdir command in sys/stat.h. */ int mkdir( const char *pathname, unsigned int mode ); // make sure our needed macros are defined // S_IFMT and S_IFDIR seem to be defined everywhere #ifndef __S_ISTYPE #define __S_ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask)) #endif #ifndef S_ISDIR #define S_ISDIR(mode) __S_ISTYPE((mode), S_IFDIR) #endif #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/file/test/0000750000175000017500000000000011401021060021650 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/io/file/test/testPath.cpp0000640000175000017500000000112207776046015024200 0ustar pabspabs/* * Modification History * * 2002-August-1 Jason Rohrer * Created. */ #include "Path.h" #include int main() { char *pathString = "/test/this/thing"; printf( "using path string = %s\n", pathString ); printf( "Constructing path.\n" ); Path *path = new Path( pathString ); printf( "Extracting path string.\n" ); char *extractedPathString = path->getPathStringTerminated(); printf( "extracted path string = %s\n", extractedPathString ); delete [] extractedPathString; delete path; return 1; } Cultivation_9+dfsg1_UnixSource/minorGems/io/file/test/testChildFiles.cpp0000640000175000017500000000223107776046015025314 0ustar pabspabs/* * Modification History * * 2004-January-4 Jason Rohrer * Created. */ /** * A test program for the various child file functions in File.h * * @author Jason Rohrer. */ #include "minorGems/io/file/File.h" int main( int inNumArgs, char **inArgs ) { char *fileName = "linux"; if( inNumArgs > 1 ) { fileName = inArgs[1]; } File *testFile = new File( NULL, fileName ); int numChildren; File **childFiles = testFile->getChildFiles( &numChildren ); printf( "child files:\n" ); for( int i=0; igetFullFileName(); printf( " %s\n", fullName ); delete [] fullName; delete childFiles[i]; } delete [] childFiles; childFiles = testFile->getChildFilesRecursive( 10, &numChildren ); printf( "recursive child files:\n" ); for( int i=0; igetFullFileName(); printf( " %s\n", fullName ); delete [] fullName; delete childFiles[i]; } delete [] childFiles; delete testFile; return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/io/serialPort/0000750000175000017500000000000011401021060022076 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/io/serialPort/testSerialPort.cpp0000640000175000017500000000101007624272636025614 0ustar pabspabs #include "SerialPort.h" #include int main() { printf( "Constructing serial port.\n" ); SerialPort *port = new SerialPort( 4800, SerialPort::PARITY_NONE, 8, 1 ); char *line = port->receiveLine(); // specific to GPS unit port->sendLine( "ASTRAL" ); while( line != NULL ) { printf( "received: %s\n", line ); delete [] line; line = port->receiveLine(); } printf( "Deleting serial port.\n" ); delete port; return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/io/serialPort/linux/0000750000175000017500000000000011401021060023235 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/io/serialPort/linux/SerialPortLinux.cpp0000640000175000017500000001477507643326107027113 0ustar pabspabs/* * Modification History * * 2003-February-17 Jason Rohrer * Created. * * 2003-April-4 Jason Rohrer * Added function for dumping the read buffer. */ #include "minorGems/io/serialPort/SerialPort.h" #include "minorGems/util/stringUtils.h" // Much of the code in this implementation was copied // from the Serial Programming FAQ, by Gary Frefking // http://en.tldp.org/HOWTO/Serial-Programming-HOWTO/ #include #include #include #include #include #include // baudrate settings are defined in , which is // included by class LinuxSerialPortObject { public: int mFileHandle; struct termios mOldTermIO; }; SerialPort::SerialPort( int inBaud, int inParity, int inDataBits, int inStopBits ) { int fileHandle = open( "/dev/ttyS0", O_RDWR | O_NOCTTY ); if( fileHandle < 0 ) { mNativeObjectPointer = NULL; } else { LinuxSerialPortObject *serialPortObject = new LinuxSerialPortObject(); serialPortObject->mFileHandle = fileHandle; mNativeObjectPointer = (void *)serialPortObject; struct termios newTermIO; //save current serial port settings tcgetattr( fileHandle, &( serialPortObject->mOldTermIO ) ); // clear struct for new port settings bzero( &newTermIO, sizeof( newTermIO ) ); unsigned int baudRate; switch( inBaud ) { case 1200: baudRate = B1200; break; case 2400: baudRate = B2400; break; case 4800: baudRate = B4800; break; case 9600: baudRate = B9600; break; case 19200: baudRate = B19200; break; case 38400: baudRate = B38400; break; case 57600: baudRate = B57600; break; default: break; } unsigned int parity = 0; switch( inParity ) { case SerialPort::PARITY_NONE: // disable parity parity = 0; break; case SerialPort::PARITY_EVEN: // enable parity, defaults to even parity = PARENB; break; case SerialPort::PARITY_ODD: // enable parity, and set to odd parity = PARENB | PARODD; break; default: break; } unsigned int dataBits = 0; switch( inDataBits ) { case 5: dataBits = CS5; break; case 6: dataBits = CS6; break; case 7: dataBits = CS7; break; case 8: dataBits = CS8; break; default: break; } unsigned int stopBits = 0; switch( inStopBits ) { case 1: stopBits = 0; break; case 2: stopBits = CSTOPB; break; default: break; } newTermIO.c_cflag = baudRate | parity | dataBits | stopBits | CLOCAL | CREAD; /* IGNPAR : ignore bytes with parity errors ICRNL : map CR to NL (otherwise a CR input on the other computer will not terminate input) IGNCR : ignore CR, so only one line is read when other machine sends CRLF otherwise make device raw (no other input processing) */ newTermIO.c_iflag = IGNPAR | ICRNL | IGNCR; /* Raw output. */ newTermIO.c_oflag = 0; /* ICANON : enable canonical input disable all echo functionality, and don't send signals to calling program */ newTermIO.c_lflag = ICANON; /* now clean the modem line and activate the settings for the port */ tcflush( fileHandle, TCIFLUSH ); tcsetattr( fileHandle, TCSANOW, &newTermIO ); } } SerialPort::~SerialPort() { if( mNativeObjectPointer != NULL ) { LinuxSerialPortObject *serialPortObject = (LinuxSerialPortObject *)mNativeObjectPointer; int fileHandle = serialPortObject->mFileHandle; tcsetattr( fileHandle, TCSANOW, &( serialPortObject->mOldTermIO ) ); delete serialPortObject; } } int SerialPort::sendLine( char *inLine ) { if( mNativeObjectPointer != NULL ) { LinuxSerialPortObject *serialPortObject = (LinuxSerialPortObject *)mNativeObjectPointer; int fileHandle = serialPortObject->mFileHandle; int stringLength = strlen( inLine ); int numWritten = write( fileHandle, inLine, stringLength ); char *endLineString = "\n"; int numWrittenEndLine = write( fileHandle, endLineString, 1 ); if( numWritten == stringLength && numWrittenEndLine == 1 ) { return 1; } else { return -1; } } else { return -1; } } char *SerialPort::receiveLine() { if( mNativeObjectPointer != NULL ) { LinuxSerialPortObject *serialPortObject = (LinuxSerialPortObject *)mNativeObjectPointer; int fileHandle = serialPortObject->mFileHandle; char *buffer = new char[500]; int numRead = read( fileHandle, buffer, 500 ); char *returnString; if( numRead != -1 ) { buffer[ numRead ] = '\0'; returnString = stringDuplicate( buffer ); } else { returnString = NULL; } delete [] buffer; return returnString; } else { return NULL; } } void SerialPort::dumpReceiveBuffer() { if( mNativeObjectPointer != NULL ) { LinuxSerialPortObject *serialPortObject = (LinuxSerialPortObject *)mNativeObjectPointer; int fileHandle = serialPortObject->mFileHandle; // from man page: // flushes data received but not read tcflush( fileHandle, TCIFLUSH ); } } Cultivation_9+dfsg1_UnixSource/minorGems/io/serialPort/SerialPortFromFile.cpp0000640000175000017500000000266207641141423026342 0ustar pabspabs/* * Modification History * * 2003-March-28 Jason Rohrer * Created. */ #include "minorGems/io/serialPort/SerialPort.h" #include "minorGems/util/stringUtils.h" #include SerialPort::SerialPort( int inBaud, int inParity, int inDataBits, int inStopBits ) { FILE *file = fopen( "gpscap.txt", "r" ); mNativeObjectPointer = file; } SerialPort::~SerialPort() { if( mNativeObjectPointer != NULL ) { FILE *file = (FILE *)mNativeObjectPointer; fclose( file ); } } int SerialPort::sendLine( char *inLine ) { return 1; } char *SerialPort::receiveLine() { if( mNativeObjectPointer != NULL ) { FILE *file = (FILE *)mNativeObjectPointer; char *buffer = new char[500]; // read up to first newline int index = 0; char lastCharRead = (char)getc( file ); while( lastCharRead != '\n' && index < 499 ) { buffer[index] = lastCharRead; lastCharRead = (char)getc( file ); index++; } char *returnString; if( index > 0 ) { buffer[ index ] = '\0'; returnString = stringDuplicate( buffer ); } else { returnString = NULL; } delete [] buffer; return returnString; } else { return NULL; } } Cultivation_9+dfsg1_UnixSource/minorGems/io/serialPort/win32/0000750000175000017500000000000011401021060023040 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/io/serialPort/win32/COMPort.h0000640000175000017500000000672307643326110024526 0ustar pabspabs/* * Modification History * * 2003-April-4 Jason Rohrer * Added function for dumping the read buffer. */ //============================================================================= // General component library for WIN32 // Copyright (C) 2000, UAB BBDSoft ( http://www.bbdsoft.com/ ) // // This material is provided "as is", with absolutely no warranty expressed // or implied. Any use is at your own risk. // // Permission to use or copy this software for any purpose is hereby granted // without fee, provided the above notices are retained on all copies. // Permission to modify the code and to distribute modified code is granted, // provided the above notices are retained, and a notice that the code was // modified is included with the above copyright notice. // // The author of this program may be contacted at developers@bbdsoft.com //============================================================================= #ifndef _COMPORT_ #define _COMPORT_ //----------------------------------------------------------------------------- class COMPort { public: enum Parity { None = 0 , Odd , Even , Mark , Space }; enum DataBits { db4 = 4 , db5 , db6 , db7 , db8 }; enum StopBits { sb1 = 0, sb15, sb2 }; enum BitRate { br110 = 110, br300 = 300, br600 = 600, br1200 = 1200, br2400 = 2400, br4800 = 4800, br9600 = 9600, br19200 = 19200, br38400 = 38400, br56000 = 56000, br57600 = 57600, br115200 = 115200, br256000 = 256000 }; // for function getModemSignals struct MSPack { unsigned char DTR : 1; unsigned char RTS : 1; unsigned char : 2; unsigned char CTS : 1; unsigned char DSR : 1; unsigned char RI : 1; unsigned char DCD : 1; }; COMPort ( const char * const portName ); ~COMPort (); // I/O operations char read (); COMPort & write (const char inChar); unsigned long read ( void * , const unsigned long count ); unsigned long write ( const void * , const unsigned long count ); // dumps unread characters in the receive buffer void dumpReceiveBuffer(); COMPort& setBitRate ( unsigned long Param ); unsigned long bitRate () const; COMPort& setParity ( Parity Param ); Parity parity () const; COMPort& setDataBits ( DataBits Param ); DataBits dataBits () const; COMPort& setStopBits ( StopBits Param ); StopBits stopBits () const; COMPort & setHandshaking ( bool inHandshaking = true ); COMPort& setLineCharacteristics ( char * Param ); unsigned long getMaximumBitRate () const; COMPort & setxONxOFF ( bool Param = true); bool isxONxOFF () const; MSPack getModemSignals () const; COMPort& setBlockingMode ( unsigned long inReadInterval = 0 , unsigned long inReadMultiplyer = 0 , unsigned long inReadConstant = 0 ); protected: private: // disable copy constructor and assignment operator COMPort (const COMPort &); COMPort& operator= (const COMPort &); void getState () const; COMPort& setState (); unsigned thePortHandle; char * theDCB; }; // End of COMPort class declaration #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/serialPort/win32/SerialPortWin32.cpp0000640000175000017500000001036507643326111026503 0ustar pabspabs/* * Modification History * * 2003-March-24 Jason Rohrer * Created. * * 2003-March-26 Jason Rohrer * Fixed a line parsing bug (lines end with newline, not CR). * * 2003-April-4 Jason Rohrer * Added function for dumping the read buffer. */ #include "minorGems/io/serialPort/SerialPort.h" #include "minorGems/util/stringUtils.h" // For now, we are using BBDSoft's COM port code #include "COMPort.h" SerialPort::SerialPort( int inBaud, int inParity, int inDataBits, int inStopBits ) { COMPort *port = new COMPort( "COM1" ); port->setHandshaking( false ); unsigned long baudRate = COMPort::br2400; switch( inBaud ) { case 1200: baudRate = COMPort::br1200; break; case 2400: baudRate = COMPort::br2400; break; case 4800: baudRate = COMPort::br4800; break; case 9600: baudRate = COMPort::br9600; break; case 19200: baudRate = COMPort::br19200; break; case 38400: baudRate = COMPort::br38400; break; case 57600: baudRate = COMPort::br57600; break; default: break; } port->setBitRate( baudRate ); switch( inParity ) { case SerialPort::PARITY_NONE: port->setParity( COMPort::None ); break; case SerialPort::PARITY_EVEN: port->setParity( COMPort::Even ); break; case SerialPort::PARITY_ODD: port->setParity( COMPort::Odd ); break; default: port->setParity( COMPort::None ); break; } switch( inDataBits ) { case 5: port->setDataBits( COMPort::db5 ); break; case 6: port->setDataBits( COMPort::db6 ); break; case 7: port->setDataBits( COMPort::db7 ); break; case 8: port->setDataBits( COMPort::db8 ); break; default: port->setDataBits( COMPort::db8 ); break; } switch( inStopBits ) { case 1: port->setStopBits( COMPort::sb1 ); break; case 2: port->setStopBits( COMPort::sb2 ); break; default: port->setStopBits( COMPort::sb1 ); break; } mNativeObjectPointer = (void *)port; } SerialPort::~SerialPort() { if( mNativeObjectPointer != NULL ) { COMPort *port = (COMPort *)mNativeObjectPointer; delete port; } } int SerialPort::sendLine( char *inLine ) { if( mNativeObjectPointer != NULL ) { COMPort *port = (COMPort *)mNativeObjectPointer; int stringLength = strlen( inLine ); int numWritten = port->write( (void *)inLine, stringLength ); char *endLineString = "\n"; int numWrittenEndLine = port->write( (void *)endLineString, 1 ); if( numWritten == stringLength && numWrittenEndLine == 1 ) { return 1; } else { return -1; } } else { return -1; } } char *SerialPort::receiveLine() { if( mNativeObjectPointer != NULL ) { COMPort *port = (COMPort *)mNativeObjectPointer; char *buffer = new char[500]; // read up to first newline int index = 0; char lastCharRead = port->read(); while( lastCharRead != '\n' && index < 499 ) { buffer[index] = lastCharRead; lastCharRead = port->read(); index++; } char *returnString; if( index > 0 ) { buffer[ index ] = '\0'; returnString = stringDuplicate( buffer ); } else { returnString = NULL; } delete [] buffer; return returnString; } else { return NULL; } } void SerialPort::dumpReceiveBuffer() { if( mNativeObjectPointer != NULL ) { COMPort *port = (COMPort *)mNativeObjectPointer; port->dumpReceiveBuffer(); } } Cultivation_9+dfsg1_UnixSource/minorGems/io/serialPort/win32/COMPort.cpp0000640000175000017500000002471107643367544025076 0ustar pabspabs/* * Modification History * * 2003-April-4 Jason Rohrer * Added function for dumping the read buffer. */ //============================================================================= // General component library for WIN32 // Copyright (C) 2000, UAB BBDSoft ( http://www.bbdsoft.com/ ) // // This material is provided "as is", with absolutely no warranty expressed // or implied. Any use is at your own risk. // // Permission to use or copy this software for any purpose is hereby granted // without fee, provided the above notices are retained on all copies. // Permission to modify the code and to distribute modified code is granted, // provided the above notices are retained, and a notice that the code was // modified is included with the above copyright notice. // // The author of this program may be contacted at developers@bbdsoft.com //============================================================================= #ifndef _COMPORT_ #include "ComPort.h" #endif #ifndef _WINDOWS_ #define WIN32_LEAN_AND_MEAN #include #endif #ifndef _STDEXCEPT_ #include #endif using namespace std; //---------------------------------------------------------------------------- COMPort::COMPort ( const char * const portName ) : theDCB (NULL) { thePortHandle = (unsigned ) CreateFile ( portName , GENERIC_READ | GENERIC_WRITE , 0 , NULL , OPEN_EXISTING , FILE_FLAG_NO_BUFFERING , NULL ); if (thePortHandle == HFILE_ERROR) { throw runtime_error ("COMPort: failed to open."); } // endif theDCB = new char [sizeof(DCB)]; getState(); setBlockingMode(); setHandshaking(); } // end constructor //---------------------------------------------------------------------------- COMPort::~COMPort() { delete [] theDCB; // close serial port device if (CloseHandle ((HANDLE)thePortHandle) == FALSE ) { throw runtime_error ("COMPort: failed to close."); } // endif } // end destructor //---------------------------------------------------------------------------- void COMPort::getState () const { if (!GetCommState ( (HANDLE) thePortHandle , (LPDCB) theDCB ) ) { throw runtime_error ("COMPort: could not retrieve serial port state."); } // endif } // end COMPort::getState () const //---------------------------------------------------------------------------- COMPort& COMPort::setState () { if (!SetCommState ( (HANDLE) thePortHandle , (LPDCB) theDCB ) ) { throw runtime_error ("COMPort: could not modify serial port state."); } // endif return *this; } // end COMPort::setState () //----------------------------------------------------------------------------- COMPort& COMPort::setBitRate ( unsigned long Param ) { DCB & aDCB = *((LPDCB)theDCB); aDCB.BaudRate = Param; return setState(); } // end COMPort::setBitRate (..) //----------------------------------------------------------------------------- unsigned long COMPort::bitRate() const { DCB & aDCB = *((LPDCB)theDCB); return aDCB.BaudRate; } // end COMPort::bitRate () const //----------------------------------------------------------------------------- COMPort& COMPort::setLineCharacteristics( char * inConfig ) { COMMTIMEOUTS aTimeout; if ( !BuildCommDCBAndTimeouts ( inConfig , (LPDCB)theDCB , &aTimeout ) ) { throw runtime_error ("COMPort: could not set line characteristics."); } // endif if ( ! SetCommTimeouts ( (HANDLE(thePortHandle)) , &aTimeout ) ) { throw runtime_error ("COMPort: could not set line characteristics."); } // endif return setState(); } //---------------------------------------------------------------------------- char COMPort::read () { char buffer; DWORD charsRead = 0; do { if (! ReadFile ( (HANDLE(thePortHandle)) , &buffer , sizeof(char) , &charsRead , NULL ) ) { throw runtime_error ("COMPort: read failed."); } // endif } while ( !charsRead ); return buffer; } // end COMPort::read() //---------------------------------------------------------------------------- void COMPort::dumpReceiveBuffer () { PurgeComm( (HANDLE(thePortHandle)) , PURGE_RXCLEAR ); } // end COMPort::dumpReceiveBuffer() //---------------------------------------------------------------------------- unsigned long COMPort::read ( void *inBuffer , const unsigned long inCharsReq ) { DWORD charsRead = 0; if ( !ReadFile ( (HANDLE(thePortHandle)) , inBuffer , inCharsReq , &charsRead , NULL ) ) { throw runtime_error ("COMPort: read failed."); } // endif return charsRead; } // end COMPort::read (..) //---------------------------------------------------------------------------- COMPort & COMPort::write ( const char inChar ) { char buffer = inChar; DWORD charsWritten = 0; if ( !WriteFile ( (HANDLE(thePortHandle)) , &buffer , sizeof(char) , &charsWritten , NULL ) ) { throw runtime_error ("COMPort: write failed."); } // endif return *this; } // end COMPort::write (..) //---------------------------------------------------------------------------- unsigned long COMPort::write ( const void *inBuffer , const unsigned long inBufSize ) { DWORD charsWritten = 0; if ( !WriteFile ( (HANDLE(thePortHandle)) , inBuffer , inBufSize , &charsWritten , NULL ) ) { throw runtime_error ("COMPort: write failed."); } // endif return charsWritten; } // end COMPort::write() //----------------------------------------------------------------------------- COMPort& COMPort::setxONxOFF ( bool Param ) { DCB & aDCB = *((LPDCB)theDCB); aDCB.fOutX = Param ? 1 : 0; aDCB.fInX = Param ? 1 : 0; return setState(); } // end COMPort::setxONxOFF (..) //----------------------------------------------------------------------------- bool COMPort::isxONxOFF () const { DCB & aDCB = *((LPDCB)theDCB); return (aDCB.fOutX && aDCB.fInX); } // end COMPort::isxONxOFF () const //---------------------------------------------------------------------------- COMPort& COMPort::setBlockingMode ( unsigned long inReadInterval , unsigned long inReadMultiplyer , unsigned long inReadConstant ) { COMMTIMEOUTS commTimeout; if ( !GetCommTimeouts ( (HANDLE(thePortHandle)) , &commTimeout ) ) { throw runtime_error ("COMPort: failed to retrieve timeouts."); } // endif commTimeout.ReadIntervalTimeout = inReadInterval; if ( inReadInterval==MAXDWORD ) { commTimeout.ReadTotalTimeoutMultiplier = 0; commTimeout.ReadTotalTimeoutConstant = 0; } else { commTimeout.ReadTotalTimeoutMultiplier = inReadMultiplyer; commTimeout.ReadTotalTimeoutConstant = inReadConstant; } // endifelse if ( !SetCommTimeouts ( (HANDLE(thePortHandle)) , &commTimeout ) ) { throw runtime_error ("COMPort: failed to modify timeouts."); } // endif return *this; } // end COMPort::setBlockingMode (..) //----------------------------------------------------------------------------- COMPort & COMPort::setHandshaking ( bool inHandshaking ) { DCB & aDCB = *((LPDCB)theDCB); if (inHandshaking) { aDCB.fOutxCtsFlow = TRUE; aDCB.fOutxDsrFlow = FALSE; aDCB.fRtsControl = RTS_CONTROL_HANDSHAKE; } else { aDCB.fOutxCtsFlow = FALSE; aDCB.fOutxDsrFlow = FALSE; aDCB.fRtsControl = RTS_CONTROL_ENABLE; } // endifelse return setState(); } // end COMPort::setHandshaking (..) //----------------------------------------------------------------------------- unsigned long COMPort::getMaximumBitRate() const { COMMPROP aProp; if ( !GetCommProperties ( (HANDLE)thePortHandle , &aProp ) ) { throw runtime_error ("COMPort: failed to retrieve port properties."); } // endif return aProp.dwMaxBaud; } // end COMPort::getMaximumBitRate () const //----------------------------------------------------------------------------- COMPort::MSPack COMPort::getModemSignals() const { MSPack aPack; // 1 bit - DTR, 2 - bit RTS (output signals) // 4 bit - CTS, 5 bit - DSR, 6 bit - RI, 7 bit - DCD (input signals) if ( !GetCommModemStatus ( (HANDLE)thePortHandle , (LPDWORD)&aPack ) ) { throw runtime_error ("COMPort: failed to retrieve modem signals."); } // endif return aPack; } // end COMPort::getModemSignals () const //----------------------------------------------------------------------------- COMPort& COMPort::setParity ( Parity Param ) { DCB & aDCB = *((LPDCB)theDCB); aDCB.Parity = Param; return setState(); } // end COMPort::setParity (..) //----------------------------------------------------------------------------- COMPort& COMPort::setDataBits ( DataBits Param ) { DCB & aDCB = *((LPDCB)theDCB); aDCB.ByteSize = Param; return setState(); } // end COMPort::setDataBits (..) //----------------------------------------------------------------------------- COMPort& COMPort::setStopBits ( StopBits Param ) { DCB & aDCB = *((LPDCB)theDCB); aDCB.StopBits = Param; return setState(); } // end COMPort::setStopBits (..) //----------------------------------------------------------------------------- COMPort::Parity COMPort::parity () const { DCB & aDCB = *((LPDCB)theDCB); return (COMPort::Parity)aDCB.Parity; } // end COMPort::parity () const //----------------------------------------------------------------------------- COMPort::DataBits COMPort::dataBits () const { DCB & aDCB = *((LPDCB)theDCB); return (COMPort::DataBits)aDCB.ByteSize; } // end COMPort::dataBits () const //----------------------------------------------------------------------------- COMPort::StopBits COMPort::stopBits () const { DCB & aDCB = *((LPDCB)theDCB); return (COMPort::StopBits)aDCB.StopBits; } // end COMPort::stopBits () cosnt Cultivation_9+dfsg1_UnixSource/minorGems/io/serialPort/testSerialPortCompile0000750000175000017500000000011507624270527026350 0ustar pabspabsg++ -o testSerialPort -I../../.. linux/SerialPortLinux.cpp testSerialPort.cppCultivation_9+dfsg1_UnixSource/minorGems/io/serialPort/SerialPort.h0000640000175000017500000000430607643326042024364 0ustar pabspabs/* * Modification History * * 2003-February-17 Jason Rohrer * Created. * * 2003-April-4 Jason Rohrer * Added function for dumping the read buffer. */ #ifndef SERIAL_PORT_INCLUDED #define SERIAL_PORT_INCLUDED /** * Serial port. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class SerialPort { public: static const int PARITY_NONE = 0; static const int PARITY_EVEN = 1; static const int PARITY_ODD = 2; /** * Constructs a serial port. * * @param inBaud the baud rate. * @param inParity the parity, one of SerialPort:: PARITY_NONE, * PARITY_EVEN, or PARITY_ODD. * @param inDataBits the number of data bits, 5, 6, 7, or 8. * @param inStopBits the number of stop bits, 1 or 2. */ SerialPort( int inBaud, int inParity, int inDataBits, int inStopBits ); ~SerialPort(); /** * Sends a line of text through this serial port. * * @param inLine the \0-terminated line of text to send. * Should not contain newline characters. * Must be destroyed by caller if non-const. * * @return 1 if the line was sent successfully, * or -1 for a port error. */ int sendLine( char *inLine ); /** * Receives a line of text from this serial port. * * @return the read line as a \0-terminated string with end of * line characters included, or NULL for a port error. * Must be destroyed by caller if non-NULL. */ char *receiveLine(); /** * Discards all characters in the receive buffer, including * unread characters. * * Can be used to recover from buffer overflow problems. */ void dumpReceiveBuffer(); private: /** * Used for platform-specific implementations. */ void *mNativeObjectPointer; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/InputStream.h0000640000175000017500000000735010047466201022423 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-9 Jason Rohrer * Changed the "number of bytes read" parameter and return value * to longs. * * 2001-February-3 Jason Rohrer * Added a readDouble function to fix platform-specific double problems. * Also added a readLong function for completeness. Implemented * these functions, making use of the new TypeIO interface. * * 2001-February-12 Jason Rohrer * Changed to subclass Stream. * * 2002-March-9 Jason Rohrer * Added a readByte() function. * * 2002-March-31 Jason Rohrer * Made destructor virtual so it works with subclasses. * * 2004-May-9 Jason Rohrer * Added support for shorts. */ #include "minorGems/common.h" #ifndef INPUT_STREAM_CLASS_INCLUDED #define INPUT_STREAM_CLASS_INCLUDED #include "Stream.h" #include "TypeIO.h" /** * Interface for a byte input stream. * * @author Jason Rohrer */ class InputStream : public Stream { public: InputStream(); virtual ~InputStream(); /** * Reads bytes from this stream. * * @param inBuffer the buffer where read bytes will be put. * Must be pre-allocated memory space. * @param inNumBytes the number of bytes to read from the stream. * * @return the number of bytes read successfully, * or -1 for a stream error. */ virtual long read( unsigned char *inBuffer, long inNumBytes ) = 0; /** * Reads a byte from this stream. * * @param outByte pointer to where the byte should be stored. * * @return the number of bytes read successfully, or -1 for a * stream error. */ long readByte( unsigned char *outByte ); /** * Reads a double from the stream in a platform-independent way. * * @param outDouble pointer to where the double should be stored. * * @return the number of bytes read successfully, or -1 for a * stream error. */ long readDouble( double *outDouble ); /** * Reads a long from the stream in a platform-independent way. * * @param outLong pointer to where the long should be stored. * * @return the number of bytes read successfully, or -1 for a * stream error. */ long readLong( long *outLong ); /** * Reads a short from the stream in a platform-independent way. * * @param outShort pointer to where the short should be stored. * * @return the number of bytes read successfully, or -1 for a * stream error. */ long readShort( short *outShort ); private: unsigned char *mDoubleBuffer; unsigned char *mLongBuffer; unsigned char *mShortBuffer; unsigned char *mByteBuffer; }; inline InputStream::InputStream() : mDoubleBuffer( new unsigned char[8] ), mLongBuffer( new unsigned char[4] ), mShortBuffer( new unsigned char[2] ), mByteBuffer( new unsigned char[1] ) { } inline InputStream::~InputStream() { delete [] mDoubleBuffer; delete [] mShortBuffer; delete [] mLongBuffer; delete [] mByteBuffer; } inline long InputStream::readByte( unsigned char *outByte ) { int numBytes = read( mByteBuffer, 1 ); *outByte = mByteBuffer[0]; return numBytes; } inline long InputStream::readDouble( double *outDouble ) { int numBytes = read( mDoubleBuffer, 8 ); *outDouble = TypeIO::bytesToDouble( mDoubleBuffer ); return numBytes; } inline long InputStream::readLong( long *outLong ) { int numBytes = read( mLongBuffer, 4 ); *outLong = TypeIO::bytesToLong( mLongBuffer ); return numBytes; } inline long InputStream::readShort( short *outShort ) { int numBytes = read( mShortBuffer, 4 ); *outShort = TypeIO::bytesToShort( mShortBuffer ); return numBytes; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/TypeIO.h0000640000175000017500000000650610047466202021324 0ustar pabspabs/* * Modification History * * 2001-February-3 Jason Rohrer * Created. * Fixed parameter names to match convention. * * 2003-January-11 Jason Rohrer * Added missing casts. * * 2004-May-9 Jason Rohrer * Added support for shorts. */ #include "minorGems/common.h" #ifndef TYPE_IO_INCLUDED #define TYPE_IO_INCLUDED /** * Interfaces for platform-independent type input and output. * * The specification for the input/output format for types is as follows: * * Types should be output in the order and format that a big-endian Linux * outputs them by default. * * Note that minorGems/numtest.cpp can be used to test how doubles are * stored on a specific platform. * * @author Jason Rohrer */ class TypeIO { public: /** * Converts an 32-bit integer to a byte array in a * platform-independent fashion. * * @param inInt the integer to convert to a byte array. * @param outBytes preallocated array where bytes will be returned. */ static void longToBytes( long inInt, unsigned char *outBytes ); /** * Converts a 4-byte array to a 32-bit integer * platform-independent fashion. * * @param inBytes array of bytes to convert. * * @return the integer represented by the bytes. */ static long bytesToLong( unsigned char *inBytes ); /** * Converts an 16-bit integer to a byte array in a * platform-independent fashion. * * @param inInt the integer to convert to a byte array. * @param outBytes preallocated array where bytes will be returned. */ static void shortToBytes( short inInt, unsigned char *outBytes ); /** * Converts a 2-byte array to a 16-bit integer * platform-independent fashion. * * @param inBytes array of bytes to convert. * * @return the integer represented by the bytes. */ static short bytesToShort( unsigned char *inBytes ); /** * Converts an 64-bit float to a byte array in a * platform-independent fashion. * * @param inDouble the double to convert to a byte array. * @param outBytes preallocated array where bytes will be returned. */ static void doubleToBytes( double inDouble, unsigned char *outBytes ); /** * Converts a 8-byte array to a 64-bit float * platform-independent fashion. * * @param inBytes array of bytes to convert. * * @return the double represented by the bytes. */ static double bytesToDouble( unsigned char *inBytes ); }; // for now, these long IO functions can be implemented in the same way // on every platform. inline void TypeIO::longToBytes( long inInt, unsigned char *outBytes ) { // use a big-endian conversion outBytes[0] = (unsigned char)( inInt >> 24 & 0xFF ); outBytes[1] = (unsigned char)( inInt >> 16 & 0xFF ); outBytes[2] = (unsigned char)( inInt >> 8 & 0xFF ); outBytes[3] = (unsigned char)( inInt & 0xFF ); } inline long TypeIO::bytesToLong( unsigned char *inBytes ) { return (long)( inBytes[0] << 24 | inBytes[1] << 16 | inBytes[2] << 8 | inBytes[3] ); } inline void TypeIO::shortToBytes( short inInt, unsigned char *outBytes ) { // use a big-endian conversion outBytes[0] = (unsigned char)( inInt >> 8 & 0xFF ); outBytes[1] = (unsigned char)( inInt & 0xFF ); } inline short TypeIO::bytesToShort( unsigned char *inBytes ) { return (short)( inBytes[0] << 8 | inBytes[1] ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/pipedStreamTest.cpp0000640000175000017500000000305607244373534023632 0ustar pabspabs/* * Modification History * * 2001-February-19 Jason Rohrer * Created. */ #include #include "PipedStream.h" #include "minorGems/util/random/StdRandomSource.h" // test function for piped streams int main() { PipedStream *stream = new PipedStream; InputStream *inStream = stream; OutputStream *outStream = stream; StdRandomSource *randSource = new StdRandomSource(); unsigned char *bufferA = new unsigned char[10]; unsigned char *bufferB = new unsigned char[15]; int i; printf( "bufferA = \n" ); for( i=0; i<10; i++ ) { bufferA[i] = (unsigned char)( randSource->getRandomBoundedInt(0, 255) ); printf( "%d\n", bufferA[i] ); } printf( "bufferB = \n" ); for( i=0; i<15; i++ ) { bufferB[i] = (unsigned char)( randSource->getRandomBoundedInt(0, 255) ); printf( "%d\n", bufferB[i] ); } unsigned char *bufferC = new unsigned char[ 10 + 15]; outStream->write( bufferA, 10 ); outStream->write( bufferB, 15 ); inStream->read( bufferC, 10 + 10 ); char *error = inStream->getLastError(); if( error != NULL ) { printf( "Stream error %s\n", error ); delete [] error; } printf( "bufferc = \n" ); for( i=0; i<10 + 15; i++ ) { printf( "%d\n", bufferC[i] ); } inStream->read( bufferC, 10 ); error = inStream->getLastError(); if( error != NULL ) { printf( "Stream error %s\n", error ); delete [] error; } printf( "bufferc = \n" ); for( i=0; i<10 + 15; i++ ) { printf( "%d\n", bufferC[i] ); } delete [] bufferA; delete [] bufferB; delete [] bufferC; delete randSource; delete stream; } Cultivation_9+dfsg1_UnixSource/minorGems/io/linux/0000750000175000017500000000000011401021060021111 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/io/linux/TypeIOLinux.cpp0000640000175000017500000000524507473770515024053 0ustar pabspabs/* * Modification History * * 2001-February-3 Jason Rohrer * Created. * Fixed a bug in the big-endian code. * Fixed parameter names to match convention. * * 2001-February-26 Jason Rohrer * Fixed a bug in the little-endian implementation. * * 2001-August-28 Jason Rohrer * Changed to be FreeBSD compatible. * * 2002-March-13 Jason Rohrer * Started to change to work with solaris. * Finished changing to work with solaris. * * 2002-April-11 Jason Rohrer * Added a default BSD case to work with OSX. * * 2002-May-25 Jason Rohrer * Changed to use minorGems endian.h */ #include "minorGems/io/TypeIO.h" #include "minorGems/system/endian.h" /* * Linux-specific type input and output. * Note that all types are output in the order that * a big-endian linux machine outputs them with no conversion. */ #if __BYTE_ORDER == __LITTLE_ENDIAN void TypeIO::doubleToBytes( double inDouble, unsigned char *outBytes ) { unsigned char *doubleBuffer = (unsigned char*)( &inDouble ); // output second word first outBytes[0] = doubleBuffer[7]; outBytes[1] = doubleBuffer[6]; outBytes[2] = doubleBuffer[5]; outBytes[3] = doubleBuffer[4]; outBytes[4] = doubleBuffer[3]; outBytes[5] = doubleBuffer[2]; outBytes[6] = doubleBuffer[1]; outBytes[7] = doubleBuffer[0]; } double TypeIO::bytesToDouble( unsigned char *inBytes ) { double returnValue; unsigned char *doubleBuffer = (unsigned char*)( &returnValue ); // put first word at the end of this double doubleBuffer[7] = inBytes[0]; doubleBuffer[6] = inBytes[1]; doubleBuffer[5] = inBytes[2]; doubleBuffer[4] = inBytes[3]; doubleBuffer[3] = inBytes[4]; doubleBuffer[2] = inBytes[5]; doubleBuffer[1] = inBytes[6]; doubleBuffer[0] = inBytes[7]; return returnValue; } #endif #if __BYTE_ORDER == __BIG_ENDIAN void TypeIO::doubleToBytes( double inDouble, unsigned char *outBytes ) { unsigned char *doubleBuffer = (unsigned char*)( &inDouble ); // output in stored order outBytes[0] = doubleBuffer[0]; outBytes[1] = doubleBuffer[1]; outBytes[2] = doubleBuffer[2]; outBytes[3] = doubleBuffer[3]; outBytes[4] = doubleBuffer[4]; outBytes[5] = doubleBuffer[5]; outBytes[6] = doubleBuffer[6]; outBytes[7] = doubleBuffer[7]; } double TypeIO::bytesToDouble( unsigned char *inBytes ) { double returnValue; unsigned char *doubleBuffer = (unsigned char*)( &returnValue ); // store in input order doubleBuffer[0] = inBytes[0]; doubleBuffer[1] = inBytes[1]; doubleBuffer[2] = inBytes[2]; doubleBuffer[3] = inBytes[3]; doubleBuffer[4] = inBytes[4]; doubleBuffer[5] = inBytes[5]; doubleBuffer[6] = inBytes[6]; doubleBuffer[7] = inBytes[7]; return returnValue; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/OutputStream.h0000640000175000017500000000716011374544330022627 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-9 Jason Rohrer * Changed the "number of bytes written" parameter and return value * to longs. * * 2001-February-3 Jason Rohrer * Added a writeDouble function to fix platform-specific double problems. * Also added a writeLong function for completeness. Implemented * these functions, making use of the new TypeIO interface. * * 2001-February-12 Jason Rohrer * Changed to subclass Stream. * * 2002-February-25 Jason Rohrer * Added a function for writing a string. * * 2002-March-31 Jason Rohrer * Made destructor virtual so it works with subclasses. * * 2004-May-9 Jason Rohrer * Added support for shorts. * * 2010-May-18 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/common.h" #ifndef OUTPUT_STREAM_CLASS_INCLUDED #define OUTPUT_STREAM_CLASS_INCLUDED #include "Stream.h" #include "TypeIO.h" #include /** * Interface for a byte output stream. * * @author Jason Rohrer */ class OutputStream : public Stream { public: OutputStream(); virtual ~OutputStream(); /** * Writes bytes to this stream. * * @param inBuffer the buffer of bytes to send. * @param inNumBytes the number of bytes to send. * * @return the number of bytes written successfully, * or -1 for a stream error. */ virtual long write( unsigned char *inBuffer, long inNumBytes ) = 0; /** * Writes a string to this stream. * * @param inString a \0-terminated string. * Must be destroyed by caller. * @return the number of bytes written successfully, or -1 for a * stream error. */ long writeString( const char *inString ); /** * Writes a double to the stream in a platform-independent way. * * @param inDouble the double to write * * @return the number of bytes written successfully, or -1 for a * stream error. */ long writeDouble( double inDouble ); /** * Writes a long to the stream in a platform-independent way. * * @param inLong the long to write * * @return the number of bytes written successfully, or -1 for a * stream error. */ long writeLong( long inLong ); /** * Writes a short to the stream in a platform-independent way. * * @param inShort the long to write * * @return the number of bytes written successfully, or -1 for a * stream error. */ long writeShort( short inShort ); private: unsigned char *mDoubleBuffer; unsigned char *mLongBuffer; unsigned char *mShortBuffer; }; inline OutputStream::OutputStream() : mDoubleBuffer( new unsigned char[8] ), mLongBuffer( new unsigned char[4] ), mShortBuffer( new unsigned char[2] ) { } inline OutputStream::~OutputStream() { delete [] mDoubleBuffer; delete [] mLongBuffer; delete [] mShortBuffer; } inline long OutputStream::writeString( const char *inString ) { int numBytes = write( (unsigned char *)inString, strlen( inString ) ); return numBytes; } inline long OutputStream::writeDouble( double inDouble ) { TypeIO::doubleToBytes( inDouble, mDoubleBuffer ); int numBytes = write( mDoubleBuffer, 8 ); return numBytes; } inline long OutputStream::writeLong( long inLong ) { TypeIO::longToBytes( inLong, mLongBuffer ); int numBytes = write( mLongBuffer, 4 ); return numBytes; } inline long OutputStream::writeShort( short inShort ) { TypeIO::shortToBytes( inShort, mShortBuffer ); int numBytes = write( mShortBuffer, 2 ); return numBytes; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/PipedStream.h0000640000175000017500000000765107554101513022372 0ustar pabspabs/* * Modification History * * 2001-February-19 Jason Rohrer * Created. * * 2001-February-20 Jason Rohrer * Added a missing return value. */ #include "minorGems/common.h" #ifndef PIPED_STREAM_CLASS_INCLUDED #define PIPED_STREAM_CLASS_INCLUDED #include "minorGems/io/InputStream.h" #include "minorGems/io/OutputStream.h" #include "minorGems/util/SimpleVector.h" #include /** * An input/output stream that can server as a pipe between * two components that read from and write to streams. * * Buffered internally to prevent blocking, so is compatible * with non-threaded components. Note, however, that * ever buffer written to the stream is copied internally, * so max memory usage is doubled. * * IS NOT THREAD-SAFE! * * @author Jason Rohrer */ class PipedStream : public InputStream, public OutputStream { public: /** * Constructs a PipedStream. */ PipedStream(); /** * Destroys any pending unread buffers. */ ~PipedStream(); // implements the InputStream interface long read( unsigned char *inBuffer, long inNumBytes ); // implements the OutputStream interface long write( unsigned char *inBuffer, long inNumBytes ); protected: SimpleVector *mBuffers; SimpleVector *mBufferSizes; long mCurrentBufferIndex; }; inline PipedStream::PipedStream() : mBuffers( new SimpleVector() ), mBufferSizes( new SimpleVector() ), mCurrentBufferIndex( 0 ) { } inline PipedStream::~PipedStream() { int numRemaining = mBuffers->size(); for( int i=0; igetElement( i ) ); delete [] buffer; } delete mBuffers; delete mBufferSizes; } inline long PipedStream::read( unsigned char *inBuffer, long inNumBytes ) { if( mBuffers->size() == 0 ) { // none read, since no buffers available InputStream:: setNewLastErrorConst( "No data available on piped stream read." ); return 0; } else { unsigned char *currentBuffer = *( mBuffers->getElement( 0 ) ); long currentBufferSize = *( mBufferSizes->getElement( 0 ) ); long outputIndex = 0; while( outputIndex < inNumBytes ) { long bytesRemaining = inNumBytes - outputIndex; long bytesRemainingInCurrent = currentBufferSize - mCurrentBufferIndex; // if current buffer isn't big enough to fill output if( bytesRemaining >= bytesRemainingInCurrent ) { // copy all of current buffer into inBuffer memcpy( &( inBuffer[outputIndex] ), &( currentBuffer[mCurrentBufferIndex] ), bytesRemainingInCurrent ); outputIndex += bytesRemainingInCurrent; // delete the current buffer mBuffers->deleteElement( 0 ); mBufferSizes->deleteElement( 0 ); delete [] currentBuffer; mCurrentBufferIndex = 0; if( outputIndex != inNumBytes && mBuffers->size() == 0 ) { // partial read, since no more buffers available InputStream::setNewLastErrorConst( "Partial data available on piped stream read." ); return outputIndex; } if( mBuffers->size() != 0 ) { // get the new current buffer currentBuffer = *( mBuffers->getElement( 0 ) ); currentBufferSize = *( mBufferSizes->getElement( 0 ) ); } } else { // current buffer is bigger memcpy( &( inBuffer[outputIndex] ), &( currentBuffer[mCurrentBufferIndex] ), bytesRemaining ); mCurrentBufferIndex += bytesRemaining; outputIndex += bytesRemaining; } } // end while // if we made it out of the while loop, we read all bytes return inNumBytes; } // end else } inline long PipedStream::write( unsigned char *inBuffer, long inNumBytes ) { // add a copy of the buffer to the vector of buffers unsigned char *copy = new unsigned char[ inNumBytes ]; memcpy( copy, inBuffer, inNumBytes ); mBuffers->push_back( copy ); mBufferSizes->push_back( inNumBytes ); return inNumBytes; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/Stream.h0000640000175000017500000000513707554101513021405 0ustar pabspabs/* * Modification History * * 2001-February-12 Jason Rohrer * Created. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-March-31 Jason Rohrer * Made destructor virtual so it works with subclasses. * Fixed several bugs in deletion of mLastError. */ #include "minorGems/common.h" #ifndef STREAM_CLASS_INCLUDED #define STREAM_CLASS_INCLUDED #include #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Base class for all streams. * * @author Jason Rohrer */ class Stream { public: /** * Constructs a stream. */ Stream(); virtual ~Stream(); /** * Gets the last error associated with this stream. * Calling this function clears the error until * another error occurs. * * @return the last stream error in human-readable form. * Must be destroyed by caller. Returns NULL if * there is no error. Note that this string is '\0' terminated. */ char *getLastError(); protected: /** * Called by subclasses to specify a new last error. Useful * when error doesn't contain information specifiable by * a constant string. * * @param inString human-readable string representing the error. * Note that this string must be '\0' terminated. * Will be destroyed by this class. */ void setNewLastError( char *inString ); /** * Called by subclasses to specify a new last error. Useful * when the error can be described by a constant string * * @param inString human-readable constant string representing * the error. * Note that this string must be '\0' terminated. */ void setNewLastErrorConst( const char *inString ); private: // set to NULL when there is no error char *mLastError; }; inline Stream::Stream() : mLastError( NULL ) { } inline Stream::~Stream() { if( mLastError != NULL ) { delete [] mLastError; } } inline char *Stream::getLastError() { char *returnString = mLastError; mLastError = NULL; return returnString; } inline void Stream::setNewLastError( char *inString ) { if( mLastError != NULL ) { delete [] mLastError; } mLastError = inString; } inline void Stream::setNewLastErrorConst( const char *inString ) { int length = 0; char lastChar = 'a'; while( lastChar != '\0' ) { lastChar = inString[length]; length++; } // add one more to length to accommodate '\0' terminination length++; if( mLastError != NULL ) { delete [] mLastError; } mLastError = new char[ length ]; memcpy( mLastError, inString, length ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/io/win32/0000750000175000017500000000000011401021060020714 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/io/win32/TypeIOWin32.cpp0000640000175000017500000000247107237354514023452 0ustar pabspabs/* * Modification History * * 2001-February-3 Jason Rohrer * Created. * Fixed parameter names to match convention. * * 2001-February-4 Jason Rohrer * Fixed a byte-order bug. */ #include "minorGems/io/TypeIO.h" /* * Win32-specific type input and output. * Note that all types are output in the order that * a big-endian linux machine outputs them with no conversion. */ // windows machines are all little-endian void TypeIO::doubleToBytes( double inDouble, unsigned char *outBytes ) { unsigned char *doubleBuffer = (unsigned char*)( &inDouble ); // output second word first outBytes[0] = doubleBuffer[7]; outBytes[1] = doubleBuffer[6]; outBytes[2] = doubleBuffer[5]; outBytes[3] = doubleBuffer[4]; outBytes[4] = doubleBuffer[3]; outBytes[5] = doubleBuffer[2]; outBytes[6] = doubleBuffer[1]; outBytes[7] = doubleBuffer[0]; } double TypeIO::bytesToDouble( unsigned char *inBytes ) { double returnValue; unsigned char *doubleBuffer = (unsigned char*)( &returnValue ); // put first word at the end of this double doubleBuffer[7] = inBytes[0]; doubleBuffer[6] = inBytes[1]; doubleBuffer[5] = inBytes[2]; doubleBuffer[4] = inBytes[3]; doubleBuffer[3] = inBytes[4]; doubleBuffer[2] = inBytes[5]; doubleBuffer[1] = inBytes[6]; doubleBuffer[0] = inBytes[7]; return returnValue; } Cultivation_9+dfsg1_UnixSource/minorGems/formats/0000750000175000017500000000000011401021057021024 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/formats/xml/0000750000175000017500000000000011401021057021624 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/formats/xml/XMLUtils.cpp0000640000175000017500000000470207540235637024041 0ustar pabspabs/* * Modification History * * 2002-September-12 Jason Rohrer * Created. */ #include "XMLUtils.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/SimpleVector.h" #include char *XMLUtils::escapeDisallowedCharacters( char *inString ) { SimpleVector *returnStringVector = new SimpleVector(); int stringLength = strlen( inString ); int i; for( i=0; ipush_back( '&' ); returnStringVector->push_back( 'a' ); returnStringVector->push_back( 'm' ); returnStringVector->push_back( 'p' ); returnStringVector->push_back( ';' ); break; case '<': returnStringVector->push_back( '&' ); returnStringVector->push_back( 'l' ); returnStringVector->push_back( 't' ); returnStringVector->push_back( ';' ); break; case '>': returnStringVector->push_back( '&' ); returnStringVector->push_back( 'g' ); returnStringVector->push_back( 't' ); returnStringVector->push_back( ';' ); break; case '\"': returnStringVector->push_back( '&' ); returnStringVector->push_back( 'q' ); returnStringVector->push_back( 'u' ); returnStringVector->push_back( 'o' ); returnStringVector->push_back( 't' ); returnStringVector->push_back( ';' ); break; case '\'': returnStringVector->push_back( '&' ); returnStringVector->push_back( 'a' ); returnStringVector->push_back( 'p' ); returnStringVector->push_back( 'o' ); returnStringVector->push_back( 's' ); returnStringVector->push_back( ';' ); break; default: returnStringVector->push_back( inString[i] ); break; } } int numChars = returnStringVector->size(); char *returnString = new char[ numChars + 1 ]; for( i=0; igetElement( i ) ); } returnString[ numChars ] = '\0'; delete returnStringVector; return returnString; } Cultivation_9+dfsg1_UnixSource/minorGems/formats/xml/XMLUtils.h0000640000175000017500000000134107540235637023502 0ustar pabspabs/* * Modification History * * 2002-September-12 Jason Rohrer * Created. */ #ifndef XML_UTILS_INCLUDED #define XML_UTILS_INCLUDED /** * Utilities for processing XML. * * @author Jason Rohrer */ class XMLUtils { public: /** * Escapes characters disallowed in XML character data. * * @param the string to escape as a \0-terminated string. * Must be destroyed by caller if non-const. * * @return string with characters escaped as a newly allocated * \0-terminated string. * Must be destroyed by caller. */ static char *escapeDisallowedCharacters( char *inString ); }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/formats/html/0000750000175000017500000000000011401021057021770 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/formats/html/HTMLUtils.cpp0000640000175000017500000000215707540240755024310 0ustar pabspabs/* * Modification History * * 2002-September-12 Jason Rohrer * Created. */ #include "HTMLUtils.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/SimpleVector.h" #include char *HTMLUtils::removeAllTags( char *inString ) { SimpleVector *returnStringVector = new SimpleVector(); int stringLength = strlen( inString ); int i = 0; while( i < stringLength ) { if( inString[i] == '<' ) { // the start of a tag // skip all until (and including) close of tag while( i < stringLength && inString[i] != '>' ) { // do nothing i++; } } else { returnStringVector->push_back( inString[i] ); } i++; } int numChars = returnStringVector->size(); char *returnString = new char[ numChars + 1 ]; for( i=0; igetElement( i ) ); } returnString[ numChars ] = '\0'; delete returnStringVector; return returnString; } Cultivation_9+dfsg1_UnixSource/minorGems/formats/html/HTMLUtils.h0000640000175000017500000000131007540240755023743 0ustar pabspabs/* * Modification History * * 2002-September-12 Jason Rohrer * Created. */ #ifndef HTML_UTILS_INCLUDED #define HTML_UTILS_INCLUDED /** * Utilities for processing HTML. * * @author Jason Rohrer */ class HTMLUtils { public: /** * Removes all HTML tags from an HTML string. * * @param the HTML data as a \0-terminated string. * Must be destroyed by caller if non-const. * * @return data with all HTML tags removed as a newly allocated * \0-terminated string. * Must be destroyed by caller. */ static char *removeAllTags( char *inString ); }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/formats/encodingUtilsTestCompile0000750000175000017500000000021007733613350025743 0ustar pabspabsg++ -g -I../.. -o encodingUtilsTest encodingUtilsTest.cpp encodingUtils.cpp ../util/stringUtils.cpp ../util/StringBufferOutputStream.cppCultivation_9+dfsg1_UnixSource/minorGems/formats/encodingUtils.h0000640000175000017500000000423207733613350024026 0ustar pabspabs/* * Modification History * * 2003-August-22 Jason Rohrer * Created. * * 2003-September-22 Jason Rohrer * Added base64 encoding. */ #ifndef ENCODING_UTILS_INCLUDED #define ENCODING_UTILS_INCLUDED /** * A collection of functions for representing data in various encoding formats. * * @author Jason Rohrer */ /** * Encodes data as a ASCII hexidecimal string. * * @param inData the data to encode. * Must be destroyed by caller. * @param inDataLength the length of inData in bytes. * * @return a \0-terminated ASCII hexidecimal string containing * characters in the range [0-9] and [A-F]. * Will be twice as long as inData. * Must be destroyed by caller. */ char *hexEncode( unsigned char *inData, int inDataLength ); /** * Decodes raw data from an ASCII hexidecimal string. * * @param inHexString a \0-terminated hexidecimal string. * Must be destroyed by caller. * * @return an array of raw data, or NULL if decoding fails. * Will be half as long as inHexString. * Must be destroyed by caller if non-NULL. */ unsigned char *hexDecode( char *inHexString ); /** * Encodes data as a ASCII base64 string. * * @param inData the data to encode. * Must be destroyed by caller. * @param inDataLength the length of inData in bytes. * @param inBreakLines set to true to break lines every 76 characters, * or false to produce an unbroken base64 string. * * @return a \0-terminated ASCII base64 string containing * characters in the range [0-9], [A-Z], [a-z], and [+,/,=]. * Must be destroyed by caller. */ char *base64Encode( unsigned char *inData, int inDataLength, char inBreakLines = true ); /** * Decodes raw data from an ASCII base64 string. * * @param inBase64String a \0-terminated base64 string. Can optionally contain * linebreaks. * Must be destroyed by caller. * @param outDataLength pointer to where the length of the decoded data * should be returned. * * @return an array of raw data, or NULL if decoding fails. * Must be destroyed by caller if non-NULL. */ unsigned char *base64Decode( char *inBase64String, int *outDataLength ); #endif Cultivation_9+dfsg1_UnixSource/minorGems/formats/encodingUtils.cpp0000640000175000017500000003143510027336105024354 0ustar pabspabs/* * Modification History * * 2003-August-22 Jason Rohrer * Created. * * 2003-September-22 Jason Rohrer * Added base64 encoding. * * 2004-March-21 Jason Rohrer * Fixed a variable scoping and redefinition bug pointed out by Benjamin Meyer. */ #include "encodingUtils.h" #include "minorGems/util/SimpleVector.h" #include #include char fourBitIntToHex( int inInt ) { char outChar[2]; if( inInt < 10 ) { sprintf( outChar, "%d", inInt ); } else { switch( inInt ) { case 10: outChar[0] = 'A'; break; case 11: outChar[0] = 'B'; break; case 12: outChar[0] = 'C'; break; case 13: outChar[0] = 'D'; break; case 14: outChar[0] = 'E'; break; case 15: outChar[0] = 'F'; break; default: outChar[0] = '0'; break; } } return outChar[0]; } // returns -1 if inHex is not a valid hex character int hexToFourBitInt( char inHex ) { int returnInt; switch( inHex ) { case '0': returnInt = 0; break; case '1': returnInt = 1; break; case '2': returnInt = 2; break; case '3': returnInt = 3; break; case '4': returnInt = 4; break; case '5': returnInt = 5; break; case '6': returnInt = 6; break; case '7': returnInt = 7; break; case '8': returnInt = 8; break; case '9': returnInt = 9; break; case 'A': case 'a': returnInt = 10; break; case 'B': case 'b': returnInt = 11; break; case 'C': case 'c': returnInt = 12; break; case 'D': case 'd': returnInt = 13; break; case 'E': case 'e': returnInt = 14; break; case 'F': case 'f': returnInt = 15; break; default: returnInt = -1; break; } return returnInt; } char *hexEncode( unsigned char *inData, int inDataLength ) { char *resultHexString = new char[ inDataLength * 2 + 1 ]; int hexStringIndex = 0; for( int i=0; i> 4 ); int lowBits = 0xF & ( currentByte ); resultHexString[ hexStringIndex ] = fourBitIntToHex( highBits ); hexStringIndex++; resultHexString[ hexStringIndex ] = fourBitIntToHex( lowBits ); hexStringIndex++; } resultHexString[ hexStringIndex ] = '\0'; return resultHexString; } unsigned char *hexDecode( char *inHexString ) { int hexLength = strlen( inHexString ); if( hexLength % 2 != 0 ) { // hex strings must be even in length return NULL; } int dataLength = hexLength / 2; unsigned char *rawData = new unsigned char[ dataLength ]; for( int i=0; i *encodingVector = new SimpleVector(); int numInLine = 0; // take groups of 3 data bytes and map them to 4 base64 digits for( int i=0; i> 18 ); unsigned int digitB = 0x3F & ( block >> 12 ); unsigned int digitC = 0x3F & ( block >> 6 ); unsigned int digitD = 0x3F & ( block ); encodingVector->push_back( binaryToAscii[ digitA ] ); encodingVector->push_back( binaryToAscii[ digitB ] ); encodingVector->push_back( binaryToAscii[ digitC ] ); encodingVector->push_back( binaryToAscii[ digitD ] ); numInLine += 4; if( inBreakLines && numInLine == 76 ) { // break the line encodingVector->push_back( '\r' ); encodingVector->push_back( '\n' ); numInLine = 0; } } else { // at end int numLeft = inDataLength - i; switch( numLeft ) { case 0: // no padding break; case 1: { // two digits, two pads unsigned int block = inData[i] << 16 | 0; unsigned int digitA = 0x3F & ( block >> 18 ); unsigned int digitB = 0x3F & ( block >> 12 ); encodingVector->push_back( binaryToAscii[ digitA ] ); encodingVector->push_back( binaryToAscii[ digitB ] ); encodingVector->push_back( '=' ); encodingVector->push_back( '=' ); break; } case 2: { // three digits, one pad unsigned int block = inData[i] << 16 | inData[i+1] << 8 | 0; // base64 digits, with digitA at left unsigned int digitA = 0x3F & ( block >> 18 ); unsigned int digitB = 0x3F & ( block >> 12 ); unsigned int digitC = 0x3F & ( block >> 6 ); encodingVector->push_back( binaryToAscii[ digitA ] ); encodingVector->push_back( binaryToAscii[ digitB ] ); encodingVector->push_back( binaryToAscii[ digitC ] ); encodingVector->push_back( '=' ); break; } default: break; } // done with all data i = inDataLength; } } char *returnString = encodingVector->getElementString(); delete encodingVector; return returnString; } unsigned char *base64Decode( char *inBase64String, int *outDataLength ) { SimpleVector *decodedVector = new SimpleVector(); int encodingLength = strlen( inBase64String ); SimpleVector *binaryEncodingVector = new SimpleVector(); int i; for( i=0; ipush_back( currentBinary ); } } int binaryEncodingLength = binaryEncodingVector->size(); unsigned char *binaryEncoding = binaryEncodingVector->getElementArray(); delete binaryEncodingVector; int blockCount = binaryEncodingLength / 4; if( binaryEncodingLength % 4 != 0 ) { // extra, 0-padded block blockCount += 1; } // take groups of 4 encoded digits and map them to 3 data bytes for( i=0; i> 16 ); unsigned int digitB = 0xFF & ( block >> 8 ); unsigned int digitC = 0xFF & ( block ); decodedVector->push_back( digitA ); decodedVector->push_back( digitB ); decodedVector->push_back( digitC ); } else { // at end int numLeft = binaryEncodingLength - i; switch( numLeft ) { case 0: // no padding break; case 1: { // impossible break; } case 2: { // two base64 digits, one data byte unsigned int block = binaryEncoding[i] << 18 | binaryEncoding[i+1] << 12 | 0; // data byte digits, with digitA at left unsigned int digitA = 0xFF & ( block >> 16 ); decodedVector->push_back( digitA ); break; } case 3: { // three base64 digits, two data bytes unsigned int block = binaryEncoding[i] << 18 | binaryEncoding[i+1] << 12 | binaryEncoding[i+2] << 6 | 0; // data byte digits, with digitA at left unsigned int digitA = 0xFF & ( block >> 16 ); unsigned int digitB = 0xFF & ( block >> 8 ); decodedVector->push_back( digitA ); decodedVector->push_back( digitB ); break; } default: break; } // done with all data i = binaryEncodingLength; } } delete [] binaryEncoding; *outDataLength = decodedVector->size(); unsigned char* returnData = decodedVector->getElementArray(); delete decodedVector; return returnData; } Cultivation_9+dfsg1_UnixSource/minorGems/formats/encodingUtilsTest.cpp0000640000175000017500000000232107733613350025216 0ustar pabspabs/* * Modification History * * 2003-September-22 Jason Rohrer * Created. */ #include "encodingUtils.h" #include #include int main() { const char *dataString = "*#$&$(@KFI#*$(SDBM@#*!(@%" "*#$&$(@KFI#*$(SDBM@#*!(@%" "*#$&$(@KFI#*$(SDBM@#*!(@F" "*#$&$(@KFI#*$(SDBM@#*!(@F" "*#$&$(@KFI#*$(SDBM@#*!(@F" "*#$&$(@KFI#*$(SDBM@#*!(@%a"; printf( "base64 encoding the string: %s\n", dataString ); char *encoding = base64Encode( (unsigned char *)dataString, strlen( dataString ), true ); printf( "Encoded as:\n%s\n", encoding ); int decodedLength; unsigned char *decoding = base64Decode( encoding, &decodedLength ); char *buffer = new char[ decodedLength + 1 ]; memcpy( (void *)buffer, (void *)decoding, decodedLength ); buffer[ decodedLength ] = '\0'; printf( "Decoded as: %s\n", buffer ); if( strcmp( buffer, dataString ) == 0 ) { printf( "Test passed\n" ); } else { printf( "Test failed\n" ); } delete [] buffer; delete [] decoding; delete [] encoding; return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/util/0000750000175000017500000000000011401021107020322 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/util/development/0000750000175000017500000000000011462022731022657 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/util/development/leakTracer/0000750000175000017500000000000011401021107024721 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/util/development/leakTracer/LeakCheckAnalyze0000750000175000017500000000033110612151771030020 0ustar pabspabs#!/bin/sh CHECKER=`dirname $0`/LeakCheck" $@" ANALYZER=`dirname $0`/leak-analyze" $1 leak.out" echo "Checking with: $CHECKER" echo "" $CHECKER echo "" echo "Analyzing with: $ANALYZER" echo "" $ANALYZER 2>&1 | more Cultivation_9+dfsg1_UnixSource/minorGems/util/development/leakTracer/leak-analyze0000750000175000017500000000636410002231613027236 0ustar pabspabs#!/usr/bin/perl # # Modification History # # 2004-January-17 Jason Rohrer # Fixed regexps to match both A-F and a-f for hex address strings. # # Erwin S. Andreasen # Henner Zeller # # Homepage: http://www.andreasen.org/LeakTracer/ # This program is Public Domain use IO::Handle; die "You must supply at least one argument.\n" unless $#ARGV >= 0; $ExeFile = shift @ARGV; $LeaksFile = $#ARGV >= 0 ? shift @ARGV : "leak.out"; open (LEAKS, $LeaksFile) or die "Could not open leaks data file $LeaksFile: $!"; if ($#ARGV >= 0) { $BreakOn = shift @ARGV; # Rest in @ARGV are program arguments } $n = $u = 0; while () { chop; next if (m/^\s*#/); # 1 2 3 4 5 6 7 #if (/^\s*L\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)\s+(\d+)/) { # Allocations, which have not been freed or deallocations which have not # been allocated. # 1 2 3 if (/^\s*L\s+(0x)?([0-9a-fA-F]+)\s+(\d+)/) { $addr="$2"; # ",$4,$6"; $u++ if not exists $Type{$addr}; $Count{$addr}++; $Size{$addr} += $3; # $7; $Type{$addr} = "Leak"; $n++; } elsif (/^\s*D\s+(0x)?([0-9a-fA-F]+)/) { $addr="$2"; # ",$4,$6"; $u++ if not exists $Type{$addr}; $Count{$addr}++; $Type{$addr} = "delete on not allocated memory"; $n++; } # allocations/deallocations with other errornous conditions # 1 2 3 4 5 elsif (/^\s*([SO])\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)/) { $addrs = "$3,$5,$1"; $AllocDealloc{$addrs} = ("$1" =~ m/S/) ? "Different allocation schemes" : "This Memory was overwritten"; } } print STDERR "Gathered $n ($u unique) points of data.\n"; close (LEAKS); # Instead of using -batch, we just run things as usual. with -batch, # we quit on the first error, which we don't want. open (PIPE, "|gdb -q $ExeFile") or die "Cannot start gdb"; #open (PIPE, "|cat"); # Change set listsize 2 to something else to show more lines print PIPE "set prompt\nset complaints 1000\nset height 0\n"; # Optionally, run the program if (defined($BreakOn)) { print PIPE "break $BreakOn\n"; print PIPE "run ", join(" ", @ARGV), " \n"; } print PIPE "set listsize 2\n"; foreach (sort keys %AllocDealloc) { print PIPE "echo \\n#-- Alloc: $AllocDealloc{$_}\\nalloc here :\n"; @addrs = split(/,/,$_); print PIPE "l *0x" . (shift @addrs) . "\necho ..free here :\n"; print PIPE "set listsize 1\n"; print PIPE "l *0x" . (shift @addrs) . "\n"; } foreach (sort keys %Type) { print PIPE "echo \\n#-- $Type{$_}: counted $Count{$_}x"; if ($Size{$_} > 0) { print PIPE " / total Size: $Size{$_}"; } print PIPE "\\n\n"; @addrs = split(/,/,$_); print PIPE "set listsize 2\n"; print PIPE "l *0x" . (shift @addrs) . "\n"; #print PIPE "echo ..called from :\n"; #print PIPE "set listsize 1\n"; # gdb bails out, if it cannot find an address. #print PIPE "l *0x" . (shift @addrs) . "\necho ..called from :\n"; #print PIPE "l *0x" . (shift @addrs) . "\n"; } if (defined($BreakOn)) { print PIPE "kill\n"; } print PIPE "quit\n"; PIPE->flush(); wait(); close (PIPE); Cultivation_9+dfsg1_UnixSource/minorGems/util/development/leakTracer/VERSION0000640000175000017500000000000410001775461026002 0ustar pabspabs2.3 Cultivation_9+dfsg1_UnixSource/minorGems/util/development/leakTracer/LeakCheck0000750000175000017500000000064010001775461026477 0ustar pabspabs#!/bin/sh if [ $# -lt 1 ] ; then echo "Usage: $0 " exit 1 fi # this looks in the same directory, this # LeakCheck script resides; modify to your # needs: SHLIB=`dirname $0`/LeakTracer.so if [ ! -x $SHLIB ] ; then echo "$SHLIB not found" exit 1 fi if [ -z "$LEAKTRACE_FILE" ] ; then rm -f leak.out else rm -f "$LEAKTRACE_FILE" fi export LD_PRELOAD=$SHLIB exec $@ Cultivation_9+dfsg1_UnixSource/minorGems/util/development/leakTracer/test.cc0000640000175000017500000000054110001775461026226 0ustar pabspabs/* * Modification History * * 2002-March-31 Jason Rohrer * Added test of strdup support. */ #include // Small leaky test program void foo() { int *x = new int; } int main() { char *str = strdup( "Test String" ); int *z = new int[10]; foo(); foo(); delete z; delete z; // delete value twice } Cultivation_9+dfsg1_UnixSource/minorGems/util/development/leakTracer/README.html0000640000175000017500000002270510001775461026571 0ustar pabspabs

Table of contents

Introduction

LeakTracer is a small tool I wrote when checking a C++ program for memory
leaks. I couldn't get dmalloc to display what I wanted, and I just saw the
__builtin_return_address gcc-extension mentioned.

To use LeakTracer, run your program using the provided LeakCheck script. It
uses the LD_PRELOAD feature to "overlay" some functions on top of your
functions (no recompile needed). If your platform does not support LD_PRELOAD,
you can add the LeakTracer.o object file to the objects in your Makefile and
run your application. 

LeakTracer uses gdb to print out the exact line where the memory was allocated
and not freed - this of course means you have to free all dynamically
allocated data. LeakTracer also overrides the global operator new and operator
delete - this will give problems if you override them as well.

LeakTracer traces only new/new[] and delete calls - it does not look at
malloc/free/realloc.

Here is some example output:

Gathered 8 (8 unique) points of data.
(gdb)
Allocations: 1 / Size: 36
0x80608e6 is in NullArcableInstance::NullArcableInstance(void) (Machine.cc:40).
39      public:
40          NullArcableInstance() : ArcableInstance(new NullArcable) {}

Allocations: 1 / Size: 8
0x8055b02 is in init_types(void) (Type.cc:119).
118     void init_types() {
119         Type::Integer = new IntegerType;

Allocations: 1 / Size: 132 (new[])
0x805f4ab is in Hashtable::Hashtable(unsigned int) (ea/h/Hashtable.h:15).
14          Hashtable (uint _size = 32) : size(_size), count(0) {
15              table = new List [size];

[...]

Requirements

You need Perl5 and gdb installed to run the leak-analyzer. You need gcc -- I
currently use 2.95 but have used it with previous older versions without
problems.
You also need to run this on an architecture which supports
__builtin_return_address arguments that are greater than 0 - there may be
some problems on MIPS there. 

So far this code has been tested under Linux 2.2, x86 system, Solaris and
HP-UX.

Installation

Just type make. There is no install target; you should put LeakTracer
some place you can remember.

Since version 2.0, it is possible to preload the LeakTracer object on
architectures that support LD_PRELOAD (this is at least Linux and probably
others -- please report success/failure). This means it is much easier to use
the program: you do not need to relink your program with LeakTracer.o.

In case your platform does not support LD_PRELOAD, you can use LeakTracer in
the old pre 2.0 way: add LeakTracer.o to your object files -- at the very end
of them (also after -llibrary lines).

In any case your application must also be compiled with debugging enabled
(i.e. -g).

Running with LeakTracer

If you are using the shared object, run the LeakCheck script. This script
should stay in the directory where you install LeakCheck -- it will search for
LeakTracer.so file there and load it. E.g.:

~/src/LeakTracer/LeakCheck yourApplication

(if you put LeakTracer in ~/src/LeakTracer/)

Run your application as normal, performing tasks that you want to be traced
for memory leaks. While the application runs, LeakTracer will write data about
memory allocation to the file "leak.out" in the current directory. You can
override the location of that file by setting the LEAKTRACE_FILE environment
variable.

If you cannot use LD_PRELOAD, just run your application as normal after
relinking it. It will also produce a "leak.out" file when it finishes.

Detectable errors

LeakTracer is capable to detect the following problems with your program

  1) memory which is allocated but not freed
  2) (limited support for) overwritten memory at the end of the allocated
     block  ( reason = 1 )
  3) memory which is tried to be deleted but which is not allocated
     (either because of a garbage pointer or twice deletion)
     (reason = 2)
  4) memory which is allocated with new[] but deleted with simple delete
     and vice versa (reason = 4)

For the last three problems, LeakTracer can abort() your program if you
tell it so; the resulting core-dump allows to debug the problem. By default,
only the overwrite memory condition results in an abort of the program
because it is inherently critical. The two other conditions are not critical.
You can influence what LeakTracer does with the environment variable
   LT_ABORTREASON
which you can set to some numeric value which is the result of the
sum of the reasons you find in the parentesis in the enumeration above.
To abort on any reason, for example, you would set LT_ABORTREASON to 7.

Analyzing output

You should then run leak-analyze, since looking at the raw leak.out file will
not help you much. To run leak-analyze, you need Perl as well as gdb
installed (any version of gdb will do). For example:

leak-analyze myprog leak.out

You don't have to specify the leak.out filename if you just use the default
one. leak-analyze will run gdb on the file, sending it a number of commands
that will show the source lines with the memory leaks.

leak-analyze should show you something like this:

Gathered 2 (2 unique) points of data.

#-- Alloc: Different allocation schemes
alloc here :0x80485b7 is in main (test.cc:6).
5
6               int *wrong = new int[10];
..free here :0x80485d9 is in main (test.cc:11).
11              delete wrong;

#-- Leak: Allocations: 1 / Size: 168 
0x8048593 is in main (test.cc:3).
2       int main() {
3               int *array = new int [42] ;

#-- Leak: Allocations: 1 / Size: 4 
0x80485a5 is in main (test.cc:4).
3               int *array = new int [42] ;
4               int *foo = new int;
This means that total of two allocations happened, in two different places.

First a delete error is shown: you allocated some memory using new[] but you
freed it using delete. leak-analyze will show where you allocated the memory and where you freed it.

Afterwards each allocation is shown in turn. There was 1 allocation from this
line of code (test.cc:3), and it was 168 bytes in size. Note that of the two
lines of code shown, it's the bottom one that created the allocation.

That's all there is to it - now you should find those memory leaks, fix them
and rerun Leak tracer.

Shared libraries and objects

If you want to analyze the leaks in shared libraries in your file, it may be
necessary to make leak-analyze run your program and thus load the shared
libraries before searching for addresses.

To do that, run leak-analyze with the program name, leak name AND another
argument which is where to set the breakpoint, e.g.:

leak-analyze myprog leak.out main

This will make leak-analyze tell gdb to set a breakpoint on "main" and then
run the program. After the analysis is complete, the program will be killed.

If you want to load some shared libraries, you can set a breakpoint on a
different location, e.g. main.cc:42 if you know that once line 42 is reached,
all shared objects have been loaded.

If your program needs some command line arguments, supply them after "main".

Licensing

LeakTracer is public domain (i.e. do with it whatever you feel like).

Credits

Initial version of LeakTracer was written by Erwin Andreasen. Henner Zeller
(foobar@to.com) contributed a rewrite of the code which
introduced dynamic loading of LeakTracer and more.

Revision history

February 21, 1999       v1.0 - only tested internally
February 23, 1999       v1.1 - added operator new[] / delete[]
February 23, 1999           v1.2 - Oops, forgot to free() the memory..
February 26, 1999       v1.3 - allow delete 0
March 27, 1999          v1.4 - Allow %p format without leading 0x for non-GNU 
                                       libc. Option to leak-analyze to run the program.
July 21, 1999               v1.5 - Fix for the above suggested by Alan Gonzalez
August 21, 2000         v1.6 - use a destructor instead of 
                                       __attribute__(destructor)
November 19, 2000               v2.0 - Rewrite by Henner Zeller introduces LD_PRELOAD
                                       and much more
February 27, 2001               v2.1 - Further update by Henner: optional thread safety,
                                       choose what should make LeakTracer abort(), better
                                       tracing of delete on non-new'ed pointers
March 2, 2001                   v2.2 - Another updated by Henner: hash table to increase
                                       performance with many allocations
June 13, 2001                   v2.3 - Made LT more resistant to being called before init
                                       and after destruction

Authors:    Erwin Andreasen 
        Henner Zeller 
Homepage:   http://www.andreasen.org/LeakTracer/

Cultivation_9+dfsg1_UnixSource/minorGems/util/development/leakTracer/Makefile0000640000175000017500000000262510001777407026407 0ustar pabspabs# # Modification History # # 2004-January-16 Jason Rohrer # Switched to use minorGems platform-independed mutexes. # CC = g++ # Source files SRC := LeakTracer.cc ROOT_PATH = ../../../.. # Switch comment to select a platform # PLATFORM_MUTEX = $(ROOT_PATH)/minorGems/system/win32/MutexLockWin32.cpp PLATFORM_MUTEX = $(ROOT_PATH)/minorGems/system/linux/MutexLockLinux.cpp -pthread # Comment both of these out to disable thread safetly C_THREAD=-DTHREAD_SAVE -D_REENTRANT -D_THREAD_SAFE O_THREAD = $(PLATFORM_MUTEX) # Common flags C_FLAGS = -g -pipe -Wall -W -I$(ROOT_PATH) $(C_THREAD) O_FLAGS = $(C_FLAGS) $(O_THREAD) # Object files OBJ_DIR = . OBJ := $(patsubst %.cc,$(OBJ_DIR)/%.o,$(SRC)) SHOBJ := $(patsubst %.o,$(OBJ_DIR)/%.so,$(OBJ)) .PHONY: all clean tidy distrib test all: $(OBJ) $(SHOBJ) clean: tidy rm -f $(OBJ) leak.out tidy: rm -f *~ *orig *bak *rej tags: $(SRC) $(INCL) ctags $(SRC) $(INCL) distrib: clean all README.html (cd .. && tar cvfz /root/drylock/LeakTracer/LeakTracer.tar.gz --exclude LeakTracer/CVS --exclude LeakTracer/old --exclude LeakTracer/test LeakTracer/) $(OBJ_DIR)/%.o: %.cc $(CC) -fPIC -c $(C_FLAGS) $< -o $@ $(OBJ_DIR)/%.so : $(OBJ_DIR)/%.o $(CC) $(O_FLAGS) -shared -o $@ $< README.html: README /root/ed/mcl/util/htmlize.pl README test: $(CC) $(C_FLAGS) test.cc -o test ./test ./LeakCheck ./test ./leak-analyze ./test # ./compare-test test.template test.result Cultivation_9+dfsg1_UnixSource/minorGems/util/development/leakTracer/README0000640000175000017500000002130110001775461025615 0ustar pabspabsIntroduction ------------ LeakTracer is a small tool I wrote when checking a C++ program for memory leaks. I couldn't get dmalloc to display what I wanted, and I just saw the __builtin_return_address gcc-extension mentioned. To use LeakTracer, run your program using the provided LeakCheck script. It uses the LD_PRELOAD feature to "overlay" some functions on top of your functions (no recompile needed). If your platform does not support LD_PRELOAD, you can add the LeakTracer.o object file to the objects in your Makefile and run your application. LeakTracer uses gdb to print out the exact line where the memory was allocated and not freed - this of course means you have to free all dynamically allocated data. LeakTracer also overrides the global operator new and operator delete - this will give problems if you override them as well. LeakTracer traces only new/new[] and delete calls - it does not look at malloc/free/realloc. Here is some example output: Gathered 8 (8 unique) points of data. (gdb) Allocations: 1 / Size: 36 0x80608e6 is in NullArcableInstance::NullArcableInstance(void) (Machine.cc:40). 39 public: 40 NullArcableInstance() : ArcableInstance(new NullArcable) {} Allocations: 1 / Size: 8 0x8055b02 is in init_types(void) (Type.cc:119). 118 void init_types() { 119 Type::Integer = new IntegerType; Allocations: 1 / Size: 132 (new[]) 0x805f4ab is in Hashtable::Hashtable(unsigned int) (ea/h/Hashtable.h:15). 14 Hashtable (uint _size = 32) : size(_size), count(0) { 15 table = new List [size]; [...] Requirements ------------ You need Perl5 and gdb installed to run the leak-analyzer. You need gcc -- I currently use 2.95 but have used it with previous older versions without problems. You also need to run this on an architecture which supports __builtin_return_address arguments that are greater than 0 - there may be some problems on MIPS there. So far this code has been tested under Linux 2.2, x86 system, Solaris and HP-UX. Installation ------------ Just type make. There is no install target; you should put LeakTracer some place you can remember. Since version 2.0, it is possible to preload the LeakTracer object on architectures that support LD_PRELOAD (this is at least Linux and probably others -- please report success/failure). This means it is much easier to use the program: you do not need to relink your program with LeakTracer.o. In case your platform does not support LD_PRELOAD, you can use LeakTracer in the old pre 2.0 way: add LeakTracer.o to your object files -- at the very end of them (also after -llibrary lines). In any case your application must also be compiled with debugging enabled (i.e. -g). Running with LeakTracer ----------------------- If you are using the shared object, run the LeakCheck script. This script should stay in the directory where you install LeakCheck -- it will search for LeakTracer.so file there and load it. E.g.: ~/src/LeakTracer/LeakCheck yourApplication (if you put LeakTracer in ~/src/LeakTracer/) Run your application as normal, performing tasks that you want to be traced for memory leaks. While the application runs, LeakTracer will write data about memory allocation to the file "leak.out" in the current directory. You can override the location of that file by setting the LEAKTRACE_FILE environment variable. If you cannot use LD_PRELOAD, just run your application as normal after relinking it. It will also produce a "leak.out" file when it finishes. Detectable errors ----------------- LeakTracer is capable to detect the following problems with your program 1) memory which is allocated but not freed 2) (limited support for) overwritten memory at the end of the allocated block ( reason = 1 ) 3) memory which is tried to be deleted but which is not allocated (either because of a garbage pointer or twice deletion) (reason = 2) 4) memory which is allocated with new[] but deleted with simple delete and vice versa (reason = 4) For the last three problems, LeakTracer can abort() your program if you tell it so; the resulting core-dump allows to debug the problem. By default, only the overwrite memory condition results in an abort of the program because it is inherently critical. The two other conditions are not critical. You can influence what LeakTracer does with the environment variable LT_ABORTREASON which you can set to some numeric value which is the result of the sum of the reasons you find in the parentesis in the enumeration above. To abort on any reason, for example, you would set LT_ABORTREASON to 7. Analyzing output ---------------- You should then run leak-analyze, since looking at the raw leak.out file will not help you much. To run leak-analyze, you need Perl as well as gdb installed (any version of gdb will do). For example: leak-analyze myprog leak.out You don't have to specify the leak.out filename if you just use the default one. leak-analyze will run gdb on the file, sending it a number of commands that will show the source lines with the memory leaks. leak-analyze should show you something like this: Gathered 2 (2 unique) points of data. #-- Alloc: Different allocation schemes alloc here :0x80485b7 is in main (test.cc:6). 5 6 int *wrong = new int[10]; ..free here :0x80485d9 is in main (test.cc:11). 11 delete wrong; #-- Leak: Allocations: 1 / Size: 168 0x8048593 is in main (test.cc:3). 2 int main() { 3 int *array = new int [42] ; #-- Leak: Allocations: 1 / Size: 4 0x80485a5 is in main (test.cc:4). 3 int *array = new int [42] ; 4 int *foo = new int; This means that total of two allocations happened, in two different places. First a delete error is shown: you allocated some memory using new[] but you freed it using delete. leak-analyze will show where you allocated the memory and where you freed it. Afterwards each allocation is shown in turn. There was 1 allocation from this line of code (test.cc:3), and it was 168 bytes in size. Note that of the two lines of code shown, it's the bottom one that created the allocation. That's all there is to it - now you should find those memory leaks, fix them and rerun Leak tracer. Shared libraries and objects ---------------------------- If you want to analyze the leaks in shared libraries in your file, it may be necessary to make leak-analyze run your program and thus load the shared libraries before searching for addresses. To do that, run leak-analyze with the program name, leak name AND another argument which is where to set the breakpoint, e.g.: leak-analyze myprog leak.out main This will make leak-analyze tell gdb to set a breakpoint on "main" and then run the program. After the analysis is complete, the program will be killed. If you want to load some shared libraries, you can set a breakpoint on a different location, e.g. main.cc:42 if you know that once line 42 is reached, all shared objects have been loaded. If your program needs some command line arguments, supply them after "main". Licensing --------- LeakTracer is public domain (i.e. do with it whatever you feel like). Credits ------- Initial version of LeakTracer was written by Erwin Andreasen. Henner Zeller (foobar@to.com) contributed a rewrite of the code which introduced dynamic loading of LeakTracer and more. Revision history ---------------- February 21, 1999 v1.0 - only tested internally February 23, 1999 v1.1 - added operator new[] / delete[] February 23, 1999 v1.2 - Oops, forgot to free() the memory.. February 26, 1999 v1.3 - allow delete 0 March 27, 1999 v1.4 - Allow %p format without leading 0x for non-GNU libc. Option to leak-analyze to run the program. July 21, 1999 v1.5 - Fix for the above suggested by Alan Gonzalez August 21, 2000 v1.6 - use a destructor instead of __attribute__(destructor) November 19, 2000 v2.0 - Rewrite by Henner Zeller introduces LD_PRELOAD and much more February 27, 2001 v2.1 - Further update by Henner: optional thread safety, choose what should make LeakTracer abort(), better tracing of delete on non-new'ed pointers March 2, 2001 v2.2 - Another updated by Henner: hash table to increase performance with many allocations June 13, 2001 v2.3 - Made LT more resistant to being called before init and after destruction Authors: Erwin Andreasen Henner Zeller Homepage: http://www.andreasen.org/LeakTracer/ Cultivation_9+dfsg1_UnixSource/minorGems/util/development/leakTracer/LeakTracer.cc0000640000175000017500000003332011331117720027260 0ustar pabspabs/* * Modification History * * 2002-March-31 Jason Rohrer * Added support for strdup. * Fixed bug in strdup. * * 2003-October-3 Jason Rohrer * Added printout of leak contents in report. * * 2004-January-16 Jason Rohrer * Switched to use minorGems platform-independed mutexes. * Changed to use simpler fopen call to open report. */ /* * Homepage: * * Authors: * Erwin S. Andreasen * Henner Zeller * * This program is Public Domain */ #ifdef THREAD_SAVE #define _THREAD_SAVE //#include #include "minorGems/system/MutexLock.h" #endif #include #include #include #include #include #include #include #include #include #include /* * underlying allocation, de-allocation used within * this tool */ #define LT_MALLOC malloc #define LT_FREE free #define LT_REALLOC realloc /* * prime number for the address lookup hash table. * if you have _really_ many memory allocations, use a * higher value, like 343051 for instance. */ #define SOME_PRIME 35323 #define ADDR_HASH(addr) ((unsigned long) addr % SOME_PRIME) /** * Filedescriptor to write to. This should not be a low number, * because these often have special meanings (stdin, out, err) * and may be closed by the program (daemons) * So choose an arbitrary higher FileDescriptor .. e.g. 42 */ #define FILEDESC 42 /** * allocate a bit more memory in order to check if there is a memory * overwrite. Either 0 or more than sizeof(unsigned int). Note, you can * only detect memory over_write_, not _reading_ beyond the boundaries. Better * use electric fence for these kind of bugs * */ typedef unsigned long magic_t; #define MAGIC ((magic_t) 0xAABBCCDDLu) /** * this may be more than sizeof(magic_t); if you want more, then * sepecify it like #define SAVESIZE (sizeof(magic_t) + 12) */ #define SAVESIZE (sizeof(magic_t) + 0) /** * on 'new', initialize the memory with this value. * if not defined - uninitialized. This is very helpful because * it detects if you initialize your classes correctly .. if not, * this helps you faster to get the segmentation fault you're * implicitly asking for :-). * * Set this to some value which is likely to produce a * segmentation fault on your platform. */ #define SAVEVALUE 0xAA /** * on 'delete', clean memory with this value. * if not defined - no memory clean. * * Set this to some value which is likely to produce a * segmentation fault on your platform. */ #define MEMCLEAN 0xEE /** * Initial Number of memory allocations in our list. * Doubles for each re-allocation. */ #define INITIALSIZE 32768 static class LeakTracer { struct Leak { const void *addr; size_t size; const void *allocAddr; bool type; int nextBucket; }; int newCount; // how many memory blocks do we have int leaksCount; // amount of entries in the leaks array int firstFreeSpot; // Where is the first free spot in the leaks array? int currentAllocated; // currentAllocatedMemory int maxAllocated; // maximum Allocated unsigned long totalAllocations; // total number of allocations. stats. unsigned int abortOn; // resons to abort program (see abortReason_t) /** * Have we been initialized yet? We depend on this being * false before constructor has been called! */ bool initialized; bool destroyed; // Has our destructor been called? FILE *report; // filedescriptor to write to /** * pre-allocated array of leak info structs. */ Leak *leaks; /** * fast hash to lookup the spot where an allocation is * stored in case of an delete. map */ int *leakHash; // fast lookup #ifdef THREAD_SAVE MutexLock mutex; #endif enum abortReason_t { OVERWRITE_MEMORY = 0x01, DELETE_NONEXISTENT = 0x02, NEW_DELETE_MISMATCH = 0x04 }; public: LeakTracer() { initialize(); } void initialize() { // Unfortunately we might be called before our constructor has actualy fired if (initialized) return; // fprintf(stderr, "LeakTracer::initialize()\n"); initialized = true; newCount = 0; leaksCount = 0; firstFreeSpot = 1; // index '0' is special currentAllocated = 0; maxAllocated = 0; totalAllocations = 0; abortOn = OVERWRITE_MEMORY; // only _severe_ reason report = 0; leaks = 0; leakHash = 0; char uniqFilename[256]; const char *filename = getenv("LEAKTRACE_FILE") ? : "leak.out"; struct stat dummy; if (stat(filename, &dummy) == 0) { sprintf(uniqFilename, "%s.%d", filename, getpid()); fprintf(stderr, "LeakTracer: file exists; using %s instead\n", uniqFilename); } else { sprintf(uniqFilename, "%s", filename); } // not sure why this "open" code is here // (it doesn't open the file properly in MinGW on win32) /* int reportfd = open(uniqFilename, O_WRONLY|O_CREAT|O_TRUNC,S_IREAD|S_IWRITE); if (reportfd < 0) { fprintf(stderr, "LeakTracer: cannot open %s: %m\n", filename); report = stderr; } else { int dupfd = dup2(reportfd, FILEDESC); close(reportfd); report = fdopen(dupfd, "w"); if (report == NULL) { report = stderr; } } */ // simpler version using only fopen report = fopen( uniqFilename, "w" ); if( report == NULL ) { fprintf( stderr, "LeakTracer: cannot open %s\n", uniqFilename ); report = stderr; } time_t t = time(NULL); fprintf (report, "# starting %s", ctime(&t)); leakHash = (int*) LT_MALLOC(SOME_PRIME * sizeof(int)); memset ((void*) leakHash, 0x00, SOME_PRIME * sizeof(int)); #ifdef MAGIC fprintf (report, "# memory overrun protection of %d Bytes " "with magic 0x%4lX\n", SAVESIZE, MAGIC); #endif #ifdef SAVEVALUE fprintf (report, "# initializing new memory with 0x%2X\n", SAVEVALUE); #endif #ifdef MEMCLEAN fprintf (report, "# sweeping deleted memory with 0x%2X\n", MEMCLEAN); #endif if (getenv("LT_ABORTREASON")) { abortOn = atoi(getenv("LT_ABORTREASON")); } #define PRINTREASON(x) if (abortOn & x) fprintf(report, "%s ", #x); fprintf (report, "# aborts on "); PRINTREASON( OVERWRITE_MEMORY ); PRINTREASON( DELETE_NONEXISTENT ); PRINTREASON( NEW_DELETE_MISMATCH ); fprintf (report, "\n"); #undef PRINTREASON #ifdef THREAD_SAVE fprintf (report, "# thread save\n"); /* * create default, non-recursive ('fast') mutex * to lock our datastructure where we keep track of * the user's new/deletes */ /*if (pthread_mutex_init(&mutex, NULL) < 0) { fprintf(report, "# couldn't init mutex ..\n"); fclose(report); _exit(1); }*/ #else fprintf(report, "# not thread save; if you use threads, recompile with -DTHREAD_SAVE\n"); #endif fflush(report); } /* * the workhorses: */ void *registerAlloc(size_t size, bool type); void registerFree (void *p, bool type); /** * write a hexdump of the given area. */ void hexdump(const unsigned char* area, int size); /** * Terminate current running progam. */ void progAbort(abortReason_t reason) { if (abortOn & reason) { fprintf(report, "# abort; DUMP of current state\n"); writeLeakReport(); fclose(report); abort(); } else fflush(report); } /** * write a Report over leaks, e.g. still pending deletes */ void writeLeakReport(); ~LeakTracer() { // fprintf(stderr, "LeakTracer::destroy()\n"); time_t t = time(NULL); fprintf (report, "# finished %s", ctime(&t)); writeLeakReport(); fclose(report); free(leaks); #ifdef THREAD_SAVE //pthread_mutex_destroy(&mutex); #endif destroyed = true; } } leakTracer; void* LeakTracer::registerAlloc (size_t size, bool type) { initialize(); // fprintf(stderr, "LeakTracer::registerAlloc()\n"); if (destroyed) { fprintf(stderr, "Oops, registerAlloc called after destruction of LeakTracer (size=%d)\n", size); return LT_MALLOC(size); } void *p = LT_MALLOC(size + SAVESIZE); // Need to call the new-handler if (!p) { fprintf(report, "LeakTracer malloc %m\n"); _exit (1); } #ifdef SAVEVALUE /* initialize with some defined pattern */ memset(p, SAVEVALUE, size + SAVESIZE); #endif #ifdef MAGIC /* * the magic value is a special pattern which does not need * to be uniform. */ if (SAVESIZE >= sizeof(magic_t)) { magic_t *mag; mag = (magic_t*)((char*)p + size); *mag = MAGIC; } #endif #ifdef THREAD_SAVE //pthread_mutex_lock(&mutex); mutex.lock(); #endif ++newCount; ++totalAllocations; currentAllocated += size; if (currentAllocated > maxAllocated) maxAllocated = currentAllocated; for (;;) { for (int i = firstFreeSpot; i < leaksCount; i++) if (leaks[i].addr == NULL) { leaks[i].addr = p; leaks[i].size = size; leaks[i].type = type; leaks[i].allocAddr=__builtin_return_address(1); firstFreeSpot = i+1; // allow to lookup our index fast. int *hashPos = &leakHash[ ADDR_HASH(p) ]; leaks[i].nextBucket = *hashPos; *hashPos = i; #ifdef THREAD_SAVE //pthread_mutex_unlock(&mutex); mutex.unlock(); #endif return p; } // Allocate a bigger array // Note that leaksCount starts out at 0. int new_leaksCount = (leaksCount == 0) ? INITIALSIZE : leaksCount * 2; leaks = (Leak*)LT_REALLOC(leaks, sizeof(Leak) * new_leaksCount); if (!leaks) { fprintf(report, "# LeakTracer realloc failed: %m\n"); _exit(1); } else { fprintf(report, "# internal buffer now %d\n", new_leaksCount); fflush(report); } memset(leaks+leaksCount, 0x00, sizeof(Leak) * (new_leaksCount-leaksCount)); leaksCount = new_leaksCount; } } void LeakTracer::hexdump(const unsigned char* area, int size) { fprintf(report, "# "); for (int j=0; j < size ; ++j) { fprintf (report, "%02x ", *(area+j)); if (j % 16 == 15) { fprintf(report, " "); for (int k=-15; k < 0 ; k++) { char c = (char) *(area + j + k); fprintf (report, "%c", isprint(c) ? c : '.'); } fprintf(report, "\n# "); } } fprintf(report, "\n"); } void LeakTracer::registerFree (void *p, bool type) { initialize(); if (p == NULL) return; if (destroyed) { fprintf(stderr, "Oops, allocation destruction of LeakTracer (p=%p)\n", p); return; } #ifdef THREAD_SAVE //pthread_mutex_lock(&mutex); mutex.lock(); #endif int *lastPointer = &leakHash[ ADDR_HASH(p) ]; int i = *lastPointer; while (i != 0 && leaks[i].addr != p) { lastPointer = &leaks[i].nextBucket; i = *lastPointer; } if (leaks[i].addr == p) { *lastPointer = leaks[i].nextBucket; // detach. newCount--; leaks[i].addr = NULL; currentAllocated -= leaks[i].size; if (i < firstFreeSpot) firstFreeSpot = i; if (leaks[i].type != type) { fprintf(report, "S %10p %10p # new%s but delete%s " "; size %d\n", leaks[i].allocAddr, __builtin_return_address(1), ((!type) ? "[]" : " normal"), ((type) ? "[]" : " normal"), leaks[i].size); progAbort( NEW_DELETE_MISMATCH ); } #ifdef MAGIC if ((SAVESIZE >= sizeof(magic_t)) && *((magic_t*)((char*)p + leaks[i].size)) != MAGIC) { fprintf(report, "O %10p %10p " "# memory overwritten beyond allocated" " %d bytes\n", leaks[i].allocAddr, __builtin_return_address(1), leaks[i].size); fprintf(report, "# %d byte beyond area:\n", SAVESIZE); hexdump((unsigned char*)p+leaks[i].size, SAVESIZE); progAbort( OVERWRITE_MEMORY ); } #endif #ifdef THREAD_SAVE # ifdef MEMCLEAN int allocationSize = leaks[i].size; # endif //pthread_mutex_unlock(&mutex); mutex.unlock(); #else #define allocationSize leaks[i].size #endif #ifdef MEMCLEAN // set it to some garbage value. memset((unsigned char*)p, MEMCLEAN, allocationSize + SAVESIZE); #endif LT_FREE(p); return; } #ifdef THREAD_SAVE //pthread_mutex_unlock(&mutex); mutex.unlock(); #endif fprintf(report, "D %10p # delete non alloc or twice pointer %10p\n", __builtin_return_address(1), p); progAbort( DELETE_NONEXISTENT ); } void LeakTracer::writeLeakReport() { initialize(); if (newCount > 0) { fprintf(report, "# LeakReport\n"); fprintf(report, "# %10s | %9s # Pointer Addr\n", "from new @", "size"); } for (int i = 0; i < leaksCount; i++) if (leaks[i].addr != NULL) { // This ought to be 64-bit safe? char *memContents = (char *)LT_MALLOC( leaks[i].size + 1 ); memcpy( (void *)memContents, (void *)( leaks[i].addr ), leaks[i].size ); memContents[ leaks[i].size ] = '\0'; fprintf(report, "L %10p %9ld # %p \"%s\"\n", leaks[i].allocAddr, (long) leaks[i].size, leaks[i].addr, memContents ); LT_FREE( memContents ); } fprintf(report, "# total allocation requests: %6ld ; max. mem used" " %d kBytes\n", totalAllocations, maxAllocated / 1024); fprintf(report, "# leak %6d Bytes\t:-%c\n", currentAllocated, (currentAllocated == 0) ? ')' : '('); if (currentAllocated > 50 * 1024) { fprintf(report, "# .. that is %d kByte!! A lot ..\n", currentAllocated / 1024); } } /** -- The actual new/delete operators -- **/ void* operator new(size_t size) { return leakTracer.registerAlloc(size,false); } void* operator new[] (size_t size) { return leakTracer.registerAlloc(size,true); } void operator delete (void *p) { leakTracer.registerFree(p,false); } void operator delete[] (void *p) { leakTracer.registerFree(p,true); } // added by Jason Rohrer char *strdup( const char *inString ) { char *outString = (char*)leakTracer.registerAlloc( strlen( inString ) + 1, true ); strcpy( outString, inString ); return outString; } /* Emacs: * Local variables: * c-basic-offset: 8 * End: * vi:set tabstop=8 shiftwidth=8 nowrap: */ Cultivation_9+dfsg1_UnixSource/minorGems/util/development/memory/0000750000175000017500000000000011401021107024154 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/util/development/memory/debugMemory.cpp0000640000175000017500000000421307554553735027177 0ustar pabspabs/* * Modification History * * 2002-October-17 Jason Rohrer * Created. * * 2002-October-18 Jason Rohrer * Added static initialization counting class for MemoryTrack. * * 2002-October-19 Jason Rohrer * Added more detail to error message. * Improved printing behavior. * Moved include of debugMemory.h to work better with IDE compilers. * Fixed to deal with differences between malloc and new[] on some platforms. * * 2002-October-20 Jason Rohrer * Removed delete macro trick that was causing crashes in tinyxml. * Removed function that was no longer being used. */ #include "minorGems/util/development/memory/debugMemory.h" #ifdef DEBUG_MEMORY #include "minorGems/util/development/memory/MemoryTrack.h" #include "stdlib.h" #include "stdio.h" void *debugMemoryNew( unsigned int inSize, const char *inFileName, int inLine ) { void *allocatedPointer = (void *)malloc( inSize ); MemoryTrack::addAllocation( allocatedPointer, inSize, SINGLE_ALLOCATION, inFileName, inLine ); return allocatedPointer; } void *debugMemoryNewArray( unsigned int inSize, const char *inFileName, int inLine ) { unsigned int mallocSize = inSize; if( inSize == 0 ) { // always allocate at least one byte to circumvent differences // between malloc and new[] on some platforms // (new int[0] returns a pointer to an array of length 0, while // malloc( 0 ) can return NULL on some platforms) mallocSize = 1; } void *allocatedPointer = (void *)malloc( mallocSize ); MemoryTrack::addAllocation( allocatedPointer, inSize, ARRAY_ALLOCATION, inFileName, inLine ); return allocatedPointer; } void debugMemoryDelete( void *inPointer ) { MemoryTrack::addDeallocation( inPointer, SINGLE_ALLOCATION ); free( inPointer ); } void debugMemoryDeleteArray( void *inPointer ) { MemoryTrack::addDeallocation( inPointer, ARRAY_ALLOCATION ); free( inPointer ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/development/memory/MemoryTrack.cpp0000640000175000017500000001620507554553734027160 0ustar pabspabs/* * Modification History * * 2002-October-17 Jason Rohrer * Created. * * 2002-October-18 Jason Rohrer * Changed to use custom list instead of SimpleVector because SimpleVector * uses debugMemory. * Added static initialization counting class. * Changed to use struct and malloc for AllocationList to avoid * circular new and delete calls. * * 2002-October-19 Jason Rohrer * Fixed a bug in adding to the alloc list. * Improved printing behavior. * Added support for clearing memory on allocation and deallocation. * Fixed to ignore deallocation of our own static lock. * Fixed bug in allocation count. * Added message for NULL pointer deallocation. * Put locks in place around print statements, which are not atomic on win32. * Changed to use hex notation when printing pointers. * * 2002-October-19 Jason Rohrer * Added ifdef for DEBUG_MEMORY. * * 2002-October-20 Jason Rohrer * Removed file and line arguments from deallocation calls. */ #ifdef DEBUG_MEMORY #include "minorGems/util/development/memory/MemoryTrack.h" #include #include #include int MemoryTrackStaticInitCounter::mCount = 0; MutexLock *MemoryTrack::mLock; AllocationList *MemoryTrack::mListHead; char MemoryTrack::mTracking = false; int MemoryTrack::mTotalAllocationSize = 0; int MemoryTrack::mTotalDeallocationSize = 0; int MemoryTrack::mNumberOfAllocations = 0; void MemoryTrack::addAllocation( void *inPointer, unsigned int inAllocationSize, int inAllocationType, const char *inFileName, int inLineNumber ) { mLock->lock(); if( !mTracking ) { printf( "Tracking off on allocation (0x%x) [%d bytes] %s:%d.\n", (unsigned int)inPointer, inAllocationSize, inFileName, inLineNumber ); mLock->unlock(); return; } // insert after head of list AllocationList *element = (AllocationList *)malloc( sizeof( AllocationList ) ); element->mPrevious = (void *)mListHead; element->mNext = mListHead->mNext; mListHead->mNext = (void *)element; AllocationList *nextElement = (AllocationList *)( element->mNext ); if( nextElement != NULL ) { nextElement->mPrevious = (void *)element; } element->mPointer = inPointer; element->mAllocationSize = inAllocationSize; element->mAllocationType = inAllocationType; element->mFileName = inFileName; element->mLineNumber = inLineNumber; mTotalAllocationSize += inAllocationSize; mNumberOfAllocations ++; // wipe this block of memory clearMemory( inPointer, inAllocationSize ); mLock->unlock(); } int MemoryTrack::addDeallocation( void *inPointer, int inDeallocationType ) { mLock->lock(); if( inPointer == NULL ) { printf( "NULL pointer (0x%x) deallocated\n", (unsigned int)inPointer ); } if( inPointer == (void *)mLock ) { // we're seeing the deallocation of our own static lock as // the system exits // ignore it mLock->unlock(); return 0; } if( !mTracking ) { printf( "Tracking off on deallocation (0x%x)\n", (unsigned int)inPointer ); mLock->unlock(); return 0; } AllocationList *element = (AllocationList *)( mListHead->mNext ); while( element != NULL ) { void *pointer = element->mPointer; if( pointer == inPointer ) { unsigned int allocationSize = element->mAllocationSize; int allocationType = element->mAllocationType; const char *allocFileName = element->mFileName; int allocLineNumber = element->mLineNumber; // remove from list, whether or not types match AllocationList *previousElement = (AllocationList *)( element->mPrevious ); AllocationList *nextElement = (AllocationList *)( element->mNext ); // patch list previousElement->mNext = (void *)( nextElement ); if( nextElement != NULL ) { nextElement->mPrevious = (void *)( previousElement ); } free( element ); mTotalDeallocationSize += allocationSize; if( allocationType == inDeallocationType ) { // found and types match mLock->unlock(); // wipe this block of memory clearMemory( inPointer, allocationSize ); return 0; } else { // allocation types don't match printf( "Attempt to deallocate (0x%x) [%d bytes] with wrong" " delete form\n" " %s:%d (location of original allocation)\n", (unsigned int)inPointer, allocationSize, allocFileName, allocLineNumber ); mLock->unlock(); return 2; } } element = (AllocationList *)( element->mNext ); } // not found (delete of unallocated memory) printf( "Attempt to deallocate (0x%x) unallocated memory\n", (unsigned int)inPointer ); mLock->unlock(); return 1; } void MemoryTrack::printLeaks() { mLock->lock(); printf( "\n\n---- debugMemory report ----\n" ); printf( "Number of Allocations: %d\n", mNumberOfAllocations ); printf( "Total allocations: %d bytes\n", mTotalAllocationSize ); printf( "Total deallocations: %d bytes\n", mTotalDeallocationSize ); int leakEstimate = mTotalAllocationSize - mTotalDeallocationSize; AllocationList *element = (AllocationList *)( mListHead->mNext ); if( element == NULL ) { printf( "No leaks detected.\n" ); } else { printf( "Leaks detected:\n" ); } int leakSum = 0; while( element != NULL ) { printf( "Not deallocated (0x%x) [%d bytes]\n" " %s:%d (location of original allocation)\n", (unsigned int)( element->mPointer ), element->mAllocationSize, element->mFileName, element->mLineNumber ); leakSum += element->mAllocationSize; element = (AllocationList *)( element->mNext ); } if( leakSum != leakEstimate ) { printf( "Warning: Leak sum does not equal leak estimate.\n" ); } printf( "Leaked memory: %d bytes\n", leakSum ); printf( "---- END debugMemory report ----\n\n" ); mLock->unlock(); } void MemoryTrack::clearMemory( void *inPointer, unsigned int inSize ) { unsigned char *charArray = (unsigned char *)inPointer; for( unsigned int i=0; i #include #define SINGLE_ALLOCATION 0 #define ARRAY_ALLOCATION 1 /** * Linked list of memory allocations. * * @author Jason Rohrer */ typedef struct { void *mPrevious; void *mNext; void *mPointer; unsigned int mAllocationSize; int mAllocationType; const char *mFileName; int mLineNumber; } AllocationList; /** * Class that tracks memory allocations and deallocations. * * @author Jason Rohrer */ class MemoryTrack { public: /** * Adds an allocation to this tracker and clears the allocated * memory block. * * @param inPointer a pointer to the allocated memory. * @param inAllocationType the type of allocation, * either SINGLE_ALLOCATION or ARRAY_ALLOCATION. * @param inAllocationSize the size of the allocation in bytes. * @param inFileName the name of the source file in which the * allocation took place. * @param inLineNumber the line number in the source file * on which the allocation took place. */ static void addAllocation( void *inPointer, unsigned int inAllocationSize, int inAllocationType, const char *inFileName, int inLineNumber ); /** * Adds a deallocation to this tracker and clears the block * to be deallocated. * Must be called *before* the memory is deallocated * * @param inPointer a pointer to the memory being deallocated. * @param inDeallocationType the type of deallocation, * either SINGLE_ALLOCATION or ARRAY_ALLOCATION. * @return 0 if the deallocation deallocates * an allocated block of memory, or 1 if it * deallocates a block of memory that is not currently allocated, * and 2 if it is the wrong deallocation type for the specified * block. */ static int addDeallocation( void *inPointer, int inDeallocationType ); /** * Prints a list of all memory leaks (allocations that have never * been deallocated). */ static void printLeaks(); // these are public so initializer can get to them static MutexLock *mLock; // dummy place holder for list head static AllocationList *mListHead; // true if we're tracking static char mTracking; static int mTotalAllocationSize; static int mTotalDeallocationSize; static int mNumberOfAllocations; protected: /** * Clears memory so that reading from it will not produce * anything useful. Good for checking for reads to memory that * has been deallocated. * * @param inPointer pointer to the memory to clear. * @Param inSize the number of bytes to clear starting at inPointer. */ static void clearMemory( void *inPointer, unsigned int inSize ); }; /** * Class that initializes MemoryTrack's static members. * * *All* files that use MemoryTrack will instantiate a static * instance of this class (see static instance below). * * This class counts how many static instantiations have happened so * far, making sure to init/destroy MemoryTrack's static members only once. * * Adapted from: * http://www.hlrs.de/organization/par/services/tools/docu/kcc/ * tutorials/static_initialization.html */ class MemoryTrackStaticInitCounter { public: MemoryTrackStaticInitCounter() { if( mCount == 0 ) { // allocate static members MemoryTrack::mLock = new MutexLock(); MemoryTrack::mListHead = (AllocationList *) malloc( sizeof( AllocationList ) ); MemoryTrack::mListHead->mPrevious = NULL; MemoryTrack::mListHead->mNext = NULL; MemoryTrack::mTotalAllocationSize = 0; MemoryTrack::mTotalDeallocationSize = 0; MemoryTrack::mNumberOfAllocations = 0; MemoryTrack::mTracking = true; } mCount++; } ~MemoryTrackStaticInitCounter() { mCount--; if( mCount == 0 ) { // print leaks... we should only get here after // all static members of classes that use MemoryTrack // have been destroyed. MemoryTrack::printLeaks(); MemoryTrack::mTracking = false; // deallocate static members free( MemoryTrack::mListHead ); delete MemoryTrack::mLock; } } private: // only allocate/deallocate when mCount == 0 static int mCount; }; // This will be included in *every* file that includes MemoryTrack.h static MemoryTrackStaticInitCounter memoryTrackInitializer; #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/development/memory/testDebugMemory.cpp0000640000175000017500000000267707554421343030040 0ustar pabspabs/* * Modification History * * 2002-October-17 Jason Rohrer * Created. * * 2002-October-18 Jason Rohrer * Added static initialization counting class for MemoryTrack. * * 2002-October-19 Jason Rohrer * Removed call to debugMemoryPrintLeaksAndStopTracking. * Made test cases more interesting. * Added test cases for deleting NULL pointers. */ #include "minorGems/util/development/memory/debugMemory.h" #include class TestClass { public: TestClass() { mX = new int[5]; } ~TestClass() { delete [] mX; } int *mX; }; int main() { int *x = new int[5]; printf( "array contents before initializing elements:\n" "%d, %d, %d, %d, %d\n\n", x[0], x[1], x[2], x[3], x[4] ); x[0] = 1; x[1] = 2; x[2] = 3; x[3] = 4; x[4] = 5; printf( "array contents before deleting:\n" "%d, %d, %d, %d, %d\n\n", x[0], x[1], x[2], x[3], x[4] ); delete [] x; printf( "array contents after deleting:\n" "%d, %d, %d, %d, %d\n\n", x[0], x[1], x[2], x[3], x[4] ); int *y = new int[4]; y[0] = 1; TestClass *t = new TestClass(); delete t; int *z = new int[7]; delete z; //delete t; int *badPointer = NULL; delete badPointer; int *badPointer2 = NULL; delete [] badPointer2; return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/util/SettingsManager.h0000640000175000017500000002037411373324323023613 0ustar pabspabs/* * Modification History * * 2002-September-16 Jason Rohrer * Created. * Fixed a memory leak. * * 2002-September-25 Jason Rohrer * Added a setSetting function that takes a string value. * * 2002-September-26 Jason Rohrer * Added functions for integer values. * * 2003-August-24 Jason Rohrer * Fixed to remove 499-character limit for a setting value. * * 2009-February-11 Jason Rohrer * Made getSettingsFile public to support arbitry binary data in settings. * * 2009-February-12 Jason Rohrer * Added support for secure hashing. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/common.h" #ifndef SETTINGS_MANAGER_INCLUDED #define SETTINGS_MANAGER_INCLUDED #include "minorGems/util/SimpleVector.h" #include "minorGems/system/MutexLock.h" #include // utility class for dealing with static member dealocation class SettingsManagerStaticMembers; /** * Class that manages program settings. * * @author Jason Rohrer */ class SettingsManager { public: /** * Sets the directory name where settings are stored. * * @param inName the name of the directory (relative to the * program's working directory). * Must be destroyed by caller if non-const. */ static void setDirectoryName( const char *inName ); /** * Gets the directory name where settings are stored. * * @return the name of the directory (relative to the * program's working directory). * Must be destroyed by caller. */ static char *getDirectoryName(); /** * Sets the salt to be used when hashing settings. * * @param inSalt the salt as an ASCII string. * Must be destroyed by caller if non-const. */ static void setHashSalt( const char *inSalt ); /** * Gets the salt to be used when hashing settings. * * @return the hash salt. * Must be destroyed by caller. */ static char *getHashSalt(); /** * Turns hashing on or off. If off, manager will reject * settings in folder that do not have proper hashes. * * @param inOn true to turn hashing on. */ static void setHashingOn( char inOn ); /** * Gets a setting. * * @param inSettingName the name of the setting to get. * Must be destroyed by caller if non-const. * * @return a vector of strings representing the setting value. * The vector and the strings it contains must be destroyed * by the caller. */ static SimpleVector *getSetting( const char *inSettingName ); /** * Gets a string setting. * * @param inSettingName the name of the setting to get. * Must be destroyed by caller if non-const. * * @return the setting value string, or NULL if no setting * value can be read. * Must be destroyed by caller if non-NULL. */ static char *getStringSetting( const char *inSettingName ); /** * Gets a float setting. * * @param inSettingName the name of the setting to get. * Must be destroyed by caller if non-const. * @param outValueFound pointer to where flag should be returned * indicating whether or not the value was found. * Set to true if the value was found, false otherwise. * * @return the setting value. */ static float getFloatSetting( const char *inSettingName, char *outValueFound ); /** * Gets an integer setting. * * @param inSettingName the name of the setting to get. * Must be destroyed by caller if non-const. * @param outValueFound pointer to where flag should be returned * indicating whether or not the value was found. * Set to true if the value was found, false otherwise. * * @return the setting value. */ static int getIntSetting( const char *inSettingName, char *outValueFound ); /** * Sets a setting. * * @param inSettingName the name of the setting to set. * Must be destroyed by caller if non-const. * @param inSettingVector a vector of strings representing the * setting value. * The vector and the strings it contains must be destroyed * by the caller. */ static void setSetting( const char *inSettingName, SimpleVector *inSettingVector ); /** * Sets a setting to a single float value. * * @param inSettingName the name of the setting to set. * Must be destroyed by caller if non-const. * @param inSettingValue the value to set. */ static void setSetting( const char *inSettingName, float inSettingValue ); /** * Sets a setting to a single int value. * * @param inSettingName the name of the setting to set. * Must be destroyed by caller if non-const. * @param inSettingValue the value to set. */ static void setSetting( const char *inSettingName, int inSettingValue ); /** * Sets a setting to a single string value. * * @param inSettingName the name of the setting to set. * Must be destroyed by caller if non-const. * @param inSettingValue the value to set. * Must be destroyed by caller. */ static void setSetting( const char *inSettingName, const char *inSettingValue ); /** * Gets the file for a setting name. * * @param inSettingName the name of the setting. * Must be destroyed by caller if non-const. * @param inReadWriteFlags the flags to pass into the * fopen call. For example, "r" or "w". * * @return the file descriptor, or NULL if the open failed. * Must be fclose()'d by caller if non-NULL. */ static FILE *getSettingsFile( const char *inSettingName, const char *inReadWriteFlags ); protected: static SettingsManagerStaticMembers mStaticMembers; static char mHashingOn; /** * Gets the file name for a setting with the default ini extension. * The .ini extension is added automatically by this call. * * @param inSettingName the name of the setting. * Must be destroyed by caller if non-const. * * @return the name of the settings file. * Must be destroyed by caller. */ static char *getSettingsFileName( const char *inSettingName ); /** * Gets the file name for a setting with a custom extension. * The extension should be passed in without a period in it. * * @param inSettingName the name of the setting. * Must be destroyed by caller if non-const. * @param inExtension the extension to add to the name. * Example: "ini" * Must be destroyed by caller if non-const. * * @return the name of the settings file. * Must be destroyed by caller. */ static char *getSettingsFileName( const char *inSettingName, const char *inExtension ); }; /** * Container for static members to allow for their proper destruction * on program termination. * * @author Jason Rohrer */ class SettingsManagerStaticMembers { public: SettingsManagerStaticMembers(); ~SettingsManagerStaticMembers(); char *mDirectoryName; char *mHashSalt; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/CircularBuffer.h0000640000175000017500000000614107554101514023413 0ustar pabspabs/* * Modification History * * 2001-Janary-11 Jason Rohrer * Created. * * 2001-Janary-12 Jason Rohrer * Added canRead and canWrite functions. * * 2001-February-24 Jason Rohrer * Fixed incorrect delete usage. */ #include "minorGems/common.h" #ifndef CIRCULAR_BUFFER_INCLUDED #define CIRCULAR_BUFFER_INCLUDED #include "minorGems/system/Semaphore.h" /** * Thread-safe circular buffer. * * @author Jason Rohrer */ class CircularBuffer { public: /** * Constructs a CircularBuffer. * * @param inSize the number of objects in this buffer. */ CircularBuffer( unsigned long inSize ); ~CircularBuffer(); /** * Writes an object into the next free position in the buffer. * Blocks if no free positions are available. * * @param inObject the object pointer to write. */ void writeObject( void *inObject ); /** * Reads the next available object from the buffer. * Blocks if now objects are available. * * @return the object pointer read. */ void *readNextObject(); /** * Returns true if an object can be read from this buffer * without blocking. */ char canRead(); /** * Returns true if an object can be written to this buffer * without blocking. */ char canWrite(); private: unsigned long mBufferSize; void **mObjects; unsigned long mReadIndex, mWriteIndex; // initialized to 0 Semaphore *mReadSemaphore; // initialized to mBufferSize; Semaphore *mWriteSemaphore; MutexLock *mLock; }; inline CircularBuffer::CircularBuffer( unsigned long inSize ) : mBufferSize( inSize ), mObjects( new void*[inSize] ), mReadIndex( 0 ), mWriteIndex( 0 ), mReadSemaphore( new Semaphore( 0 ) ), mWriteSemaphore( new Semaphore( inSize ) ), mLock( new MutexLock() ) { } inline CircularBuffer::~CircularBuffer() { delete [] mObjects; delete mReadSemaphore; delete mWriteSemaphore; delete mLock; } // note that in this implementation, no mutex is needed, since // reader and writer are never accessing the same data members // simultaneously inline void CircularBuffer::writeObject( void *inObject ) { // wait to for a space to write into mWriteSemaphore->wait(); // write, and increment our write location mObjects[ mWriteIndex ] = inObject; mWriteIndex++; mWriteIndex = mWriteIndex % mBufferSize; // signal the reader that an new object is ready mReadSemaphore->signal(); } inline void *CircularBuffer::readNextObject() { void *returnObject; // wait for an object to read mReadSemaphore->wait(); // read the object, and increment our read location returnObject = mObjects[ mReadIndex ]; mReadIndex++; mReadIndex = mReadIndex % mBufferSize; // signal the writer mWriteSemaphore->signal(); // now return the object that we retrieved from the buffer return returnObject; } inline char CircularBuffer::canRead() { // can read if the read semaphore won't block return ! mReadSemaphore->willBlock(); } inline char CircularBuffer::canWrite() { // can write if the write semaphore won't block return ! mWriteSemaphore->willBlock(); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/SettingsManager.cpp0000640000175000017500000002127211374544330024147 0ustar pabspabs/* * Modification History * * 2002-September-16 Jason Rohrer * Created. * Fixed a memory leak. * * 2002-September-25 Jason Rohrer * Added a setSetting function that takes a string value. * * 2002-September-26 Jason Rohrer * Added functions for integer values. * * 2003-January-11 Jason Rohrer * Added default values for float and int settings. * * 2003-August-24 Jason Rohrer * Fixed to remove 499-character limit for a setting value. * * 2009-February-12 Jason Rohrer * Added support for secure hashing. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "SettingsManager.h" #include "minorGems/util/stringUtils.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/Path.h" #include "minorGems/crypto/hashes/sha1.h" // will be destroyed automatically at program termination SettingsManagerStaticMembers SettingsManager::mStaticMembers; char SettingsManager::mHashingOn = false; void SettingsManager::setDirectoryName( const char *inName ) { delete [] mStaticMembers.mDirectoryName; mStaticMembers.mDirectoryName = stringDuplicate( inName ); } char *SettingsManager::getDirectoryName() { return stringDuplicate( mStaticMembers.mDirectoryName ); } void SettingsManager::setHashSalt( const char *inSalt ) { delete [] mStaticMembers.mHashSalt; mStaticMembers.mHashSalt = stringDuplicate( inSalt ); } char *SettingsManager::getHashSalt() { return stringDuplicate( mStaticMembers.mHashSalt ); } void SettingsManager::setHashingOn( char inOn ) { mHashingOn = inOn; } SimpleVector *SettingsManager::getSetting( const char *inSettingName ) { char *fileName = getSettingsFileName( inSettingName ); File *settingsFile = new File( NULL, fileName ); delete [] fileName; char *fileContents = settingsFile->readFileContents(); delete settingsFile; if( fileContents == NULL ) { // return empty vector return new SimpleVector(); } if( mHashingOn ) { char *hashFileName = getSettingsFileName( inSettingName, "hash" ); File *hashFile = new File( NULL, hashFileName ); delete [] hashFileName; char *savedHash = hashFile->readFileContents(); delete hashFile; if( savedHash == NULL ) { printf( "Hash missing for setting %s\n", inSettingName ); delete [] fileContents; return new SimpleVector(); } // compute hash char *stringToHash = autoSprintf( "%s%s", fileContents, mStaticMembers.mHashSalt ); char *hash = computeSHA1Digest( stringToHash ); delete [] stringToHash; int difference = strcmp( hash, savedHash ); delete [] hash; delete [] savedHash; if( difference != 0 ) { printf( "Hash mismatch for setting %s\n", inSettingName ); delete [] fileContents; return new SimpleVector(); } } // else tokenize the file contents SimpleVector *returnVector = tokenizeString( fileContents ); delete [] fileContents; return returnVector; } char *SettingsManager::getStringSetting( const char *inSettingName ) { char *value = NULL; SimpleVector *settingsVector = getSetting( inSettingName ); int numStrings = settingsVector->size(); if( numStrings >= 1 ) { char *firstString = *( settingsVector->getElement( 0 ) ); value = stringDuplicate( firstString ); } for( int i=0; igetElement( i ) ); delete [] nextString; } delete settingsVector; return value; } float SettingsManager::getFloatSetting( const char *inSettingName, char *outValueFound ) { char valueFound = false; float value = 0; char *stringValue = getStringSetting( inSettingName ); if( stringValue != NULL ) { int numRead = sscanf( stringValue, "%f", &value ); if( numRead == 1 ) { valueFound = true; } delete [] stringValue; } *outValueFound = valueFound; return value; } int SettingsManager::getIntSetting( const char *inSettingName, char *outValueFound ) { char valueFound = false; int value = 0; char *stringValue = getStringSetting( inSettingName ); if( stringValue != NULL ) { int numRead = sscanf( stringValue, "%d", &value ); if( numRead == 1 ) { valueFound = true; } delete [] stringValue; } *outValueFound = valueFound; return value; } void SettingsManager::setSetting( const char *inSettingName, SimpleVector *inSettingVector ) { char **settingParts = inSettingVector->getElementArray(); char *settingString = join( settingParts, inSettingVector->size(), "\n" ); delete [] settingParts; if( mHashingOn ) { // compute hash char *stringToHash = autoSprintf( "%s%s", settingString, mStaticMembers.mHashSalt ); char *hash = computeSHA1Digest( stringToHash ); delete [] stringToHash; char *hashFileName = getSettingsFileName( inSettingName, "hash" ); FILE *file = fopen( hashFileName, "w" ); delete [] hashFileName; if( file != NULL ) { fprintf( file, "%s", hash ); fclose( file ); } delete [] hash; } FILE *file = getSettingsFile( inSettingName, "w" ); if( file != NULL ) { fprintf( file, "%s", settingString ); fclose( file ); } delete [] settingString; // else do nothing } void SettingsManager::setSetting( const char *inSettingName, float inSettingValue ) { char *valueString = new char[ 15 ]; sprintf( valueString, "%f", inSettingValue ); setSetting( inSettingName, valueString ); delete [] valueString; } void SettingsManager::setSetting( const char *inSettingName, int inSettingValue ) { char *valueString = new char[ 15 ]; sprintf( valueString, "%d", inSettingValue ); setSetting( inSettingName, valueString ); delete [] valueString; } void SettingsManager::setSetting( const char *inSettingName, const char *inSettingValue ) { SimpleVector *settingsVector = new SimpleVector( 1 ); settingsVector->push_back( (char *)inSettingValue ); setSetting( inSettingName, settingsVector ); delete settingsVector; } FILE *SettingsManager::getSettingsFile( const char *inSettingName, const char *inReadWriteFlags ) { char *fullFileName = getSettingsFileName( inSettingName ); FILE *file = fopen( fullFileName, inReadWriteFlags ); delete [] fullFileName; return file; } char *SettingsManager::getSettingsFileName( const char *inSettingName ) { return getSettingsFileName( inSettingName, "ini" ); } char *SettingsManager::getSettingsFileName( const char *inSettingName, const char *inExtension ) { char **pathSteps = new char*[1]; pathSteps[0] = mStaticMembers.mDirectoryName; char *fileName = new char[ strlen( inSettingName ) + strlen( inExtension ) + 2 ]; sprintf( fileName, "%s.%s", inSettingName, inExtension ); File *settingsFile = new File( new Path( pathSteps, 1, false ), fileName ); delete [] fileName; // pathSteps copied internally by Path constructor delete [] pathSteps; char *fullFileName = settingsFile->getFullFileName(); delete settingsFile; return fullFileName; } SettingsManagerStaticMembers::SettingsManagerStaticMembers() : mDirectoryName( stringDuplicate( "settings" ) ), mHashSalt( stringDuplicate( "default_salt" ) ) { } SettingsManagerStaticMembers::~SettingsManagerStaticMembers() { delete [] mDirectoryName; delete [] mHashSalt; } Cultivation_9+dfsg1_UnixSource/minorGems/util/random/0000750000175000017500000000000011401021107021602 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/util/random/Noise.h0000640000175000017500000000473207554101514023056 0ustar pabspabs// Jason Rohrer // Noise.h /** * * Noise generation interface * * * Created 11-3-99 * Mods: * Jason Rohrer 12-20-2000 Added a fractal noise function * that fills a double array. * */ #include "minorGems/common.h" #ifndef NOISE_INCLUDED #define NOISE_INCLUDED #include #include #include "RandomSource.h" // fills 2d image with RGBA noise void genRandNoise2d(unsigned long *buff, int buffHigh, int buffWide); // returns a random floating point between 0 and 1 inline float floatRand() { float invRandMax = 1 / ((float)RAND_MAX); return (float)(rand()) * invRandMax; } // fills 2d image with ARGB fractal noise void genFractalNoise2d(unsigned long *buff, int buffHigh, int buffWide); /** * Fills a 2d array with 1/f fractal noise. * * @param inBuffer a pre-allocated buffer to fill. * @param inWidth the width and height of the 2d array * contained in the buffer. * Must be a power of 2. * @param inMaxFrequency the maximum frequency of noise modulation to * include, in [2,inWidth]. Lower values produce more "blurry" or * blocky noise. * @param inFPower power to raise F to whene generating noise. * Amplitude of modulation = 1 / (F^inFPower). * @param inInterpolate set to true to perform interpolation of * each frequency modulation. Setting to false produces a "blockier" * noise, while setting to true makes the noise more cloud-like. * @param inRandSource the source to use for random numbers. */ void genFractalNoise2d( double *inBuffer, int inWidth, int inMaxFrequency, double inFPower, char inInterpolate, RandomSource *inRandSource ); /** * Fills a 1d array with 1/f fractal noise. * * @param inBuffer a pre-allocated buffer to fill. * @param inWidth the width of the 2d array * contained in the buffer. * Must be a power of 2. * @param inMaxFrequency the maximum frequency of noise modulation to * include, in [2,inWidth]. Lower values produce more "blurry" or * blocky noise. * @param inFPower power to raise F to whene generating noise. * Amplitude of modulation = 1 / (F^inFPower). * @param inInterpolate set to true to perform interpolation of * each frequency modulation. Setting to false produces a "blockier" * noise, while setting to true makes the noise more cloud-like. * @param inRandSource the source to use for random numbers. */ void genFractalNoise( double *inBuffer, int inWidth, int inMaxFrequency, double inFPower, char inInterpolate, RandomSource *inRandSource ); #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/random/testRandom.cpp0000640000175000017500000001141311142365151024443 0ustar pabspabs#include "RandomSource.h" #include // perform tests on a random source // tests found here: // NIST FIPS 140-1 U.S. Federal Standard // http://csrc.nist.gov/publications/fips/fips140-1/fips1401.pdf void testRandom( RandomSource *inSource ) { int bitsPerStep = 1; // 20000 bits // must be a multiple of bitsPerStep int numBits = 20000; int numSteps = numBits / bitsPerStep; unsigned int *drawnNumbers = new unsigned int[ numSteps ]; unsigned int *drawnBits = new unsigned int[ numBits ]; unsigned int bitSum = 0; // 1 or 0 when we're in a run unsigned int currentRunType = 2; unsigned int currentRunLength = 0; unsigned int longestRunLength = 0; #define maxRunLengthToTrack 100 unsigned int runLengthCounts[2][ maxRunLengthToTrack ]; int r; for( r=0; rgetRandomBoundedInt( 0, 1 ); //drawnNumbers[i] = inSource->getRandomBoolean(); for( int b=0; b> b & 0x01 ); bitSum += bit; drawnBits[bitIndex] = bit; bitIndex++; if( bit == currentRunType ) { currentRunLength++; if( currentRunLength > longestRunLength ) { longestRunLength = currentRunLength; } } else { // end of run if( currentRunLength > 0 && currentRunLength < maxRunLengthToTrack ) { // count it runLengthCounts[currentRunType][ currentRunLength ] ++; } currentRunType = bit; currentRunLength = 1; } } } float mean = (float)bitSum / numBits; printf( "Mean = %f\n", mean ); float varSum = 0; for( i=0; i 2326 && Z[t] < 2674 ) { } else { failed = true; //printf( "autocorrelation test failed for Z[%d] = %d\n", t, Z[t] ); } } if( !failed ) { printf( "Autocorrelation test passed.\n" ); } delete [] drawnNumbers; delete [] drawnBits; } #include "CustomRandomSource.h" #include "StdRandomSource.h" int main() { CustomRandomSource randSource( 11234258 ); //StdRandomSource randSource( 11 ); testRandom( &randSource ); return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/util/random/CustomRandomSource.h0000640000175000017500000001146611175630012025571 0ustar pabspabs/* * Modification History * * 2009-January-14 Jason Rohrer * Created. * * 2009-February-5 Jason Rohrer * Added support for restoring from saved state. * */ #ifndef CUSTOM_RANDOM_SOURCE_INCLUDED #define CUSTOM_RANDOM_SOURCE_INCLUDED #include #include #include "RandomSource.h" /** * Implementation of RandomSource that does not depend on platform or library. * * Maintains its own internal state. */ class CustomRandomSource : public RandomSource { public: // seeds itself with current time CustomRandomSource(); // specify the seed CustomRandomSource( unsigned int inSeed ); // save for rewind later void saveState(); void rewindState(); // can be used to save state to disk unsigned int getSavedState(); void restoreFromSavedState( unsigned int inSavedState ); void reseed( unsigned int inSeed ); // implements these functions float getRandomFloat(); // in interval [0,1.0] double getRandomDouble(); // in interval [0,1.0] unsigned int getRandomInt(); // in interval [0,MAX] unsigned int getIntMax(); // returns MAX int getRandomBoundedInt( int inRangeStart, int inRangeEnd ); double getRandomBoundedDouble( double inRangeStart, double inRangeEnd ); char getRandomBoolean(); private: double mInvMAXPlusOne; // 1 / ( MAX + 1 ) unsigned int mState; unsigned int mSavedState; // returns next number and updates state unsigned int genRand32(); }; inline CustomRandomSource::CustomRandomSource() { MAX = 4294967295U; mState = (unsigned)( time(NULL) ); invMAX = (float)1.0 / ((float)MAX); invDMAX = 1.0 / ((double)MAX); mInvMAXPlusOne = 1.0 / ( ( (float)MAX ) + 1.0 ); saveState(); } inline CustomRandomSource::CustomRandomSource( unsigned int inSeed ) { MAX = 4294967295U; mState = inSeed; invMAX = (float)1.0 / ((float)MAX); invDMAX = 1.0 / ((double)MAX); mInvMAXPlusOne = 1.0 / ( ( (double)MAX ) + 1.0 ); saveState(); } inline void CustomRandomSource::saveState() { mSavedState = mState; } inline void CustomRandomSource::rewindState() { mState = mSavedState; } inline unsigned int CustomRandomSource::getSavedState() { return mSavedState; } inline void CustomRandomSource::restoreFromSavedState( unsigned int inSavedState) { mState = inSavedState; } inline void CustomRandomSource::reseed( unsigned int inSeed ) { mState = inSeed; } // from Cultivation/Passage's landscape.cpp // faster as a set of macros #define CustNum1( inSeed ) \ ( ( inSeed * 0xFEA09B9DU ) + 1 ) #define CustNum2( inSeed ) \ ( ( ( inSeed ^ CustNum1( inSeed ) ) * 0x9C129511U ) + 1 ) #define CustNum3( inSeed ) \ ( ( inSeed * 0x2512CFB8U ) + 1 ) #define CustNum4( inSeed ) \ ( ( ( inSeed ^ CustNum3( inSeed ) ) * 0xB89C8895U ) + 1 ) #define CustNum5( inSeed ) \ ( ( inSeed * 0x6BF962C1U ) + 1 ) #define CustNum6( inSeed ) \ ( ( ( inSeed ^ CustNum5( inSeed ) ) * 0x4BF962C1U ) + 1 ) inline unsigned int CustomRandomSource::genRand32() { mState = CustNum2( mState ) ^ (CustNum4( mState ) >> 11) ^ (CustNum6( mState ) >> 22); return mState; } inline float CustomRandomSource::getRandomFloat() { return (float)(genRand32()) * invMAX; } inline double CustomRandomSource::getRandomDouble() { return (double)(genRand32()) * invDMAX; } inline unsigned int CustomRandomSource::getRandomInt() { return genRand32(); } inline unsigned int CustomRandomSource::getIntMax() { return MAX; } inline int CustomRandomSource::getRandomBoundedInt( int inRangeStart, int inRangeEnd ) { // float in range [0,1) double randFloat = (double)( genRand32() ) * mInvMAXPlusOne; int onePastRange = inRangeEnd + 1; int magnitude = (int)( randFloat * ( onePastRange - inRangeStart ) ); return magnitude + inRangeStart; } inline double CustomRandomSource::getRandomBoundedDouble( double inRangeStart, double inRangeEnd ) { // double in range [0,1] double randDouble = getRandomDouble(); double magnitude = randDouble * ( inRangeEnd - inRangeStart ); return magnitude + inRangeStart; } inline char CustomRandomSource::getRandomBoolean() { // float in range [0,1] double randFloat = getRandomFloat(); if( randFloat < 0.5 ) { return true; } else { return false; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/random/RandomSource.h0000640000175000017500000000401411133471650024372 0ustar pabspabs// Jason Rohrer // RandomSource.h /** * * abstract interface for random number generation * * Can be implemented by: * --stdlib rand() function calls * --seed file full of random numbers * * Created 12-7-99 * Mods: * Jason Rohrer 9-28-2000 Added a getRandomBoundedInt() * interface to faciliate retrieving * an integer in a given range [a,b] * where each integer in the range * has the same probability of being * returned. * Jason Rohrer 12-16-2000 Added a getRandomDouble() interface. * Jason Rohrer 11-21-2005 Added a virtual destructor. * Jason Rohrer 07-09-2006 Added a getRandomBoundedDouble interface. * Jason Rohrer 07-27-2006 Added a getRandomBoolean interface. */ #include "minorGems/common.h" #ifndef RANDOM_SOURCE_INCLUDED #define RANDOM_SOURCE_INCLUDED class RandomSource { public: // pure virtual functions implemented by inheriting classes virtual float getRandomFloat() = 0; // in interval [0,1.0] virtual double getRandomDouble() = 0; // in interval [0,1.0] virtual unsigned int getRandomInt() = 0; // in interval [0,MAX] virtual unsigned int getIntMax() = 0; // returns MAX /** * Returns a random integer in [rangeStart,rangeEnd] * where each integer in the range has an equal * probability of occuring. */ virtual int getRandomBoundedInt( int inRangeStart, int inRangeEnd ) = 0; /** * Returns a random double in [rangeStart,rangeEnd]. */ virtual double getRandomBoundedDouble( double inRangeStart, double inRangeEnd ) = 0; /** * Gets a random true/false value. */ virtual char getRandomBoolean() = 0; virtual ~RandomSource(); protected: unsigned int MAX; // maximum integer random number float invMAX; // floating point inverse of MAX double invDMAX; // double invers of MAX }; inline RandomSource::~RandomSource() { // does nothing // exists to ensure that subclass destructors are called } #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/random/StdRandomSource.h0000640000175000017500000000641311133471650025052 0ustar pabspabs// Jason Rohrer // StdRandomSource.h /** * * Implementation of random number generation that uses stdlib calls * * * Created 12-7-99 * Mods: * Jason Rohrer 9-28-2000 Added a getRandomBoundedInt() * implementation * Jason Rohrer 12-7-2000 Overloaded constructor to support * specifying a seed. * Jason Rohrer 12-16-2000 Added a getRandomDouble() function. * Jason Rohrer 12-17-2000 Fixed bug in initialization of invDMAX * in default constructor. * Jason Rohrer 9-13-2001 Fixed a bug in getRandomBoundedInt() * as floats were being used, and they * don't provide enough resolution. * Jason Rohrer 10-11-2002 Fixed some type casting warnings. * Jason Rohrer 07-09-2006 Added getRandomBoundedDouble. * Jason Rohrer 07-27-2006 Added getRandomBoolean. */ #include "minorGems/common.h" #ifndef STD_RANDOM_SOURCE_INCLUDED #define STD_RANDOM_SOURCE_INCLUDED #include #include #include "RandomSource.h" class StdRandomSource : public RandomSource { public: StdRandomSource(); // needed to seed stdlib generator // specify the seed for the stdlib generator StdRandomSource( unsigned int inSeed ); // implements these functions float getRandomFloat(); // in interval [0,1.0] double getRandomDouble(); // in interval [0,1.0] unsigned int getRandomInt(); // in interval [0,MAX] unsigned int getIntMax(); // returns MAX int getRandomBoundedInt( int inRangeStart, int inRangeEnd ); double getRandomBoundedDouble( double inRangeStart, double inRangeEnd ); char getRandomBoolean(); private: double mInvMAXPlusOne; // 1 / ( MAX + 1 ) }; inline StdRandomSource::StdRandomSource() { MAX = RAND_MAX; srand( (unsigned)time(NULL) ); invMAX = (float)1.0 / ((float)MAX); invDMAX = 1.0 / ((double)MAX); mInvMAXPlusOne = 1.0 / ( ( (float)MAX ) + 1.0 ); } inline StdRandomSource::StdRandomSource( unsigned int inSeed ) { MAX = RAND_MAX; srand( inSeed ); invMAX = (float)1.0 / ((float)MAX); invDMAX = 1.0 / ((double)MAX); mInvMAXPlusOne = 1.0 / ( ( (double)MAX ) + 1.0 ); } inline float StdRandomSource::getRandomFloat() { return (float)(rand()) * invMAX; } inline double StdRandomSource::getRandomDouble() { return (double)(rand()) * invDMAX; } inline unsigned int StdRandomSource::getRandomInt() { return rand(); } inline unsigned int StdRandomSource::getIntMax() { return MAX; } inline int StdRandomSource::getRandomBoundedInt( int inRangeStart, int inRangeEnd ) { // float in range [0,1) double randFloat = (double)( rand() ) * mInvMAXPlusOne; int onePastRange = inRangeEnd + 1; int magnitude = (int)( randFloat * ( onePastRange - inRangeStart ) ); return magnitude + inRangeStart; } inline double StdRandomSource::getRandomBoundedDouble( double inRangeStart, double inRangeEnd ) { // double in range [0,1] double randDouble = getRandomDouble(); double magnitude = randDouble * ( inRangeEnd - inRangeStart ); return magnitude + inRangeStart; } inline char StdRandomSource::getRandomBoolean() { // float in range [0,1] double randFloat = getRandomFloat(); if( randFloat < 0.5 ) { return true; } else { return false; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/random/Noise.cpp0000640000175000017500000001625407220517221023407 0ustar pabspabs// Jason Rohrer // Noise.cpp /** * * Noise generation implementation * * * Created 11-3-99 * Mods: * Jason Rohrer 12-20-2000 Changed genFractalNoise2d function to make * it less blocky. * */ #include "Noise.h" // fills 2d image with ARGB noise void genRandNoise2d(unsigned long *buff, int buffHigh, int buffWide) { int *yOffset = new int[buffHigh]; // precalc y offsets for( int y=0; y> 24 & 0xFF) + alpha; int buffRed = (buffARGB >> 16 & 0xFF) + red; int buffGreen = (buffARGB >> 8 & 0xFF) + green; int buffBlue = (buffARGB & 0xFF) + blue; if( buffAlpha < 0) buffAlpha = 0; if( buffRed < 0) buffRed = 0; if( buffGreen < 0) buffGreen = 0; if( buffBlue < 0) buffBlue = 0; if( buffAlpha > 255) buffAlpha = 255; if( buffRed > 255) buffRed = 255; if( buffGreen > 255) buffGreen = 255; if( buffBlue > 255) buffBlue = 255; buff[ yOffset[buffY] + buffX] = buffBlue | buffGreen << 8 | buffRed << 16 | buffAlpha << 24; buffX++; blockX++; if( buffX >= buffWide ) blockX = blockWide; // if this block hangs outside buffer } buffY++; blockY++; if( buffY >= buffHigh ) blockY = blockHigh; // if this block hangs outside buffer } buffX = startX + blockWide; } buffY = startY + blockHigh; } } delete [] yOffset; } void genFractalNoise2d( double *inBuffer, int inWidth, int inMaxFrequency, double inFPower, char inInterpolate, RandomSource *inRandSource ) { RandomSource *r = inRandSource; int w = inWidth; int i, x, y, f; int numPoints = w * w; // first, fill surface with a uniform 0.5 value for( i=0; igetRandomDouble() - 1 ) * weight; } // now walk though 2d array and perform // bilinear interpolation between blocks for( y=0; y 1.0 ) { inBuffer[y * w + x] = 1.0; } else if( inBuffer[y * w + x] < 0.0 ) { inBuffer[y * w + x] = 0.0; } } } delete [] blockValues; } } void genFractalNoise( double *inBuffer, int inWidth, int inMaxFrequency, double inFPower, char inInterpolate, RandomSource *inRandSource ) { RandomSource *r = inRandSource; int w = inWidth; int i, x, f; // first, fill array with uniform 0.5 values for( i=0; igetRandomDouble() - 1 ) * weight; } // now walk though array and perform linear interpolation between blocks for( x=0; x 1.0 ) { inBuffer[x] = 1.0; } else if( inBuffer[x] < 0.0 ) { inBuffer[x] = 0.0; } } delete [] blockValues; } } Cultivation_9+dfsg1_UnixSource/minorGems/util/stringUtils.h0000640000175000017500000002153311373326622023051 0ustar pabspabs/* * Modification History * * 2002-April-6 Jason Rohrer * Created. * * 2002-April-8 Jason Rohrer * Fixed multiple inclusion bug * * 2002-May-7 Jason Rohrer * Added functions for case-insensitive substring finding and case * conversion. * * 2002-May-9 Jason Rohrer * Fixed a bug when string not found. * * 2002-May-26 Jason Rohrer * Added a function for string comparison ignoring cases. * * 2003-March-28 Jason Rohrer * Added split function. * * 2003-May-1 Jason Rohrer * Added replacement functions. * * 2003-May-4 Jason Rohrer * Added list replacement function. * * 2003-May-10 Jason Rohrer * Moved implementations into a .cpp file. This will break several * projects. * Added a tokenization function. * * 2003-June-14 Jason Rohrer * Added a join function. * * 2003-June-22 Jason Rohrer * Added an autoSprintf function. * * 2003-August-12 Jason Rohrer * Added a concatonate function. * * 2003-September-7 Jason Rohrer * Improved split comment. * * 2006-June-2 Jason Rohrer * Added a stringStartsWith function. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/common.h" #include "minorGems/util/SimpleVector.h" #ifndef STRING_UTILS_INCLUDED #define STRING_UTILS_INCLUDED // ANSI does not support strdup, strcasestr, or strcasecmp #include #include #include /** * Duplicates a string into a newly allocated string. * * @param inString the \0-terminated string to duplicate. * Must be destroyed by caller if non-const. * * @return a \0-terminated duplicate of inString. * Must be destroyed by caller. */ inline char *stringDuplicate( const char *inString ) { char *returnBuffer = new char[ strlen( inString ) + 1 ]; strcpy( returnBuffer, inString ); return returnBuffer; } /** * Converts a string to lower case. * * @param inString the \0-terminated string to convert. * Must be destroyed by caller if non-const. * * @return a newly allocated \0-terminated string * that is a lowercase version of inString. * Must be destroyed by caller. */ char *stringToLowerCase( const char *inString ); /** * Searches for the first occurrence of one string in another. * * @param inHaystack the \0-terminated string to search in. * Must be destroyed by caller if non-const. * @param inNeedle the \0-terminated string to search for. * Must be destroyed by caller if non-const. * * @return a string pointer into inHaystack where the * first occurrence of inNeedle starts, or NULL if inNeedle is not found. */ char *stringLocateIgnoreCase( const char *inHaystack, const char *inNeedle ); /** * Compares two strings, ignoring case. * * @param inStringA the first \0-terminated string. * Must be destroyed by caller if non-const. * @param inStringB the second \0-terminated string. * Must be destroyed by caller if non-const. * * @return an integer less than, equal to, or greater than zero if * inStringA is found, respectively, to be less than, to match, or be * greater than inStringB. */ int stringCompareIgnoreCase( const char *inStringA, const char *inStringB ); /** * Checks if a string starts with a given prefix string. * * @param inString a \0-terminated string. * Must be destroyed by caller if non-const. * @param inPrefix the prefix to look for as a \0-terminated string. * Must be destroyed by caller if non-const. * * @return true if inString begins with inPrefix, or false otherwise. */ char stringStartsWith( const char *inString, const char *inPrefix ); /** * Splits a string into parts around a separator string. * * Note that splitting strings that start and/or end with the separator * results in "empty" strings being returned at the start and/or end * of the parts array. * * For example, splitting "a#short#test" around the "#" separator will * result in the array { "a", "short", "test" }, but splitting * "#another#short#test#" will result in the array * { "", "another", "short", "test", "" }. * * This differs somewhat from the perl version of split, but it gives the * caller more information about the string being split. * * @param inString the string to split. * Must be destroyed by caller if non-const. * @param inSeparator the separator string. * Must be destroyed by caller if non-const. * @param outNumParts pointer to where the the number of parts (the length of * the returned array) should be returned. * * @return an array of split parts. * Must be destroyed by caller. */ char **split( const char *inString, const char *inSeparator, int *outNumParts ); /** * Joins a collection of strings using a separator string. * * @param inStrings the strings to join. * Array and strings must be destroyed by caller. * @param inNumParts the number of strings to join. * @param inSeparator the separator string. * Must be destroyed by caller if non-const. * * @return the joined string. * Must be destroyed by caller. */ char *join( char **inStrings, int inNumParts, const char *inGlue ); /** * Concatonates two strings. * * @param inStringA the first string in the concatonation. * Must be destroyed by caller if non-const. * @param inStringB the second string in the concatonation. * Must be destroyed by caller if non-const. * * @return the concatonation. * Must be destroyed by caller. */ char *concatonate( const char *inStringA, const char *inStringB ); /** * Replaces the first occurrence of a target string with * a substitute string. * * All parameters and return value must be destroyed by caller. * * @param inHaystack the string to search for inTarget in. * @param inTarget the string to search for. * @param inSubstitute the string to replace the first occurrence * of the target with. * @param outFound a pre-allocated character which will be filled * with true if the target is found, and filled with false * otherwise. * * @return a newly allocated string with the substitution performed. */ char *replaceOnce( const char *inHaystack, const char *inTarget, const char *inSubstitute, char *outFound ); /** * Replaces the all occurrences of a target string with * a substitute string. * * Note that this function is not self-insertion-safe: * If inSubstitute contains inTarget, this function will * enter an infinite loop. * * All parameters and return value must be destroyed by caller. * * @param inHaystack the string to search for inTarget in. * @param inTarget the string to search for. * @param inSubstitute the string to replace the all occurrences * of the target with. * @param outFound a pre-allocated character which will be filled * with true if the target is found at least once, * and filled with false otherwise. * * @return a newly allocated string with the substitutions performed. */ char *replaceAll( const char *inHaystack, const char *inTarget, const char *inSubstitute, char *outFound ); /** * Replaces the all occurrences of each target string on a list with * a corresponding substitute string. * * Note that this function is not self-insertion-safe: * If inSubstituteVector contains elements from inTargetVector, * this function will enter an infinite loop. * * All parameters and return value must be destroyed by caller. * * @param inHaystack the string to search for targets in. * @param inTargetVector the list of strings to search for. * Vector and contained strings must be destroyed by caller. * @param inSubstituteVector the corresponding list of strings to * replace the all occurrences of the targets with. * Vector and contained strings must be destroyed by caller. * * @return a newly allocated string with the substitutions performed. */ char *replaceTargetListWithSubstituteList( const char *inHaystack, SimpleVector *inTargetVector, SimpleVector *inSubstituteVector ); /** * Split a string into tokens using whitespace as separators. * * @param inString the string to tokenize. * Must be destroyed by caller. * * @return a vector of extracted strings. * Vector and strings must be destroyed by caller. */ SimpleVector *tokenizeString( const char *inString ); /** * Prints formatted data elements into a newly allocated string buffer. * * Similar to sprintf, except that buffer sizing is automatic (and therefore * safer than manual sizing). * * @param inFormatString the format string to print from. * @param variable argument list data values to fill in the format string * with (uses same conventions as printf). * * @return a newly allocated buffer containing the \0-terminated printed * string. * Must be destroyed by caller. */ char *autoSprintf( const char* inFormatString, ... ); #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/vectorTest.cpp0000640000175000017500000000425711325144546023223 0ustar pabspabs #include "SimpleVector.h" #include class A { public: A() { mV.push_back( 50 ); mV.push_back( 60 ); } ~A() { printf( "A's destructor invoked\n" ); } SimpleVector mV; void print() { printf( "A{ " ); for( int i=0; i v; A a; v.push_back( a ); v.push_back( a ); // push more back to force vector expansion for( int i=0; i<10; i++ ) { v.push_back( a ); } /* printf( "making array\n" ); A *array = new A[100]; printf( "deleting array\n" ); delete [] array; printf( "About to exit\n" ); */ printf( "making w\n" ); SimpleVector w; w.push_back( a ); w.push_back( a ); // push more back to force vector expansion for( int i=0; i<10; i++ ) { w.push_back( a ); } printf( "deleting from w\n" ); // delete a few to test for leak w.deleteElement( 0 ); w.deleteElement( 0 ); { // copy constructor SimpleVector clone = v; printf( "clone = " ); for( int i=0; iprint(); } printf( "\n" ); // assignment operator clone = w; printf( "clone = " ); for( int i=0; iprint(); } printf( "\n" ); // clone deleted here } // v still okay? printf( "v = " ); for( int i=0; iprint(); } printf( "\n" ); // w still okay? printf( "w = " ); for( int i=0; iprint(); } printf( "\n" ); } Cultivation_9+dfsg1_UnixSource/minorGems/util/log/0000750000175000017500000000000011401021107021103 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/util/log/PrintLog.h0000640000175000017500000000422611373324323023035 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-April-8 Jason Rohrer * Changed to be thread-safe. * * 2010-April-5 Jason Rohrer * Printf-like functionality. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/common.h" #ifndef PRINT_LOG_INCLUDED #define PRINT_LOG_INCLUDED #include "Log.h" #include "minorGems/system/MutexLock.h" /** * A console-based implementation of the Log interface. * * @author Jason Rohrer */ class PrintLog : public Log { public: /** * Constructs a print log. */ PrintLog(); virtual ~PrintLog(); // implement the Log interface virtual void setLoggingLevel( int inLevel ); virtual int getLoggingLevel(); virtual void logString( const char *inString, int inLevel ); virtual void logString( const char *inLoggerName, const char *inString, int inLevel ); virtual void logPrintf( int inLevel, const char* inFormatString, ... ); virtual void logNPrintf( const char *inLoggerName, int inLevel, const char* inFormatString, ... ); protected: int mLoggingLevel; static const char *mDefaultLoggerName; MutexLock *mLock; /** * Generates a string representation of a log message. * * @param inLoggerName the name of the logger * as a \0-terminated string. * Must be destroyed by caller. * @param inString the string to log as a \0-terminated string. * Must be destroyed by caller. * @param inLevel the level to log inString at. * * @return the log message as a \0-terminated string. * Must be destroyed by caller. */ char *generateLogMessage( const char *inLoggerName, const char *inString, int inLevel ); }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/log/AppLog.h0000640000175000017500000000715111373324323022461 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-March-30 Jason Rohrer * Wrapped our dynamically allocated static member in a statically * allocated class to avoid memory leaks at program termination. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/common.h" #ifndef APP_LOG_INCLUDED #define APP_LOG_INCLUDED #include "Log.h" #include "PrintLog.h" // internally used class class LogPointerWrapper; /** * A singleton wrapper for implementations of the Log interface. * * This class should be the interface for log-access for applications. * * @author Jason Rohrer */ class AppLog { public: /** * These log errors at various levels. * * All char* parameters must be \0-terminated and destroyed by caller. */ static void criticalError( const char *inString ); static void criticalError( const char *inLoggerName, const char *inString ); static void error( const char *inString ); static void error( const char *inLoggerName, const char *inString ); static void warning( const char *inString ); static void warning( const char *inLoggerName, const char *inString ); static void info( const char *inString ); static void info( const char *inLoggerName, const char *inString ); static void detail( const char *inString ); static void detail( const char *inLoggerName, const char *inString ); static void trace( const char *inString ); static void trace( const char *inLoggerName, const char *inString ); /** * Sets the log to use. * Note that this call destroys the current log. * * @param inLog the log to use. * Will be destroyed by this class. */ static void setLog( Log *inLog ); /** * Gets the log being used. * * @return the log being used. * Will be destroyed by this class. */ static Log *getLog(); /** * Sets the logging level of the current log. * * Messages with levels above the current level will not be logged. * * @param inLevel one of the defined logging levels. */ static void setLoggingLevel( int inLevel ); /** * Gets the logging level of the current log. * * Messages with levels above the current level will not be logged. * * @return one of the defined logging levels. */ static int getLoggingLevel(); protected: // note that all static objects // are destroyed at program termination //static Log *mLog; static LogPointerWrapper mLogPointerWrapper; }; /** * Wrapper for static member pointer to ensure * deletion of static member object at program termination. */ class LogPointerWrapper { public: /** * Constructor allows specification of the object * to wrap and destroy at program termination. * * @param inLog the log object to wrap. * Will be destroyed at program termination if non-NULL. */ LogPointerWrapper( Log *inLog ); /** * Destructor will get called at program termination * if the object is statically allocated. */ ~LogPointerWrapper(); Log *mLog; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/log/FileLog.h0000640000175000017500000000354211373324323022620 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-November-5 Jason Rohrer * Added support for backing up logs and deleting old log data. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/common.h" #ifndef FILE_LOG_INCLUDED #define FILE_LOG_INCLUDED #include "PrintLog.h" #include /** * A file-based implementation of the Log interface. * * @author Jason Rohrer */ class FileLog : public PrintLog { public: /** * Constructs a file log. * * @param inFileName the name of the file to write log messages to. * Must be destroyed by caller. * @param inSecondsBetweenBackups the number of seconds to wait * before making a backup of the current log file (deleting any * old backups), clearing the current log file, and starting * a fresh log in the current log file. Defaults to 3600 * seconds (one hour). Backup logs are saved to inFileName.bakup */ FileLog( const char *inFileName, unsigned long inSecondsBetweenBackups = 3600 ); virtual ~FileLog(); /** * Makes a backup of the current log file, deletes old backups, * and clears the current log file. */ void makeBackup(); // overrides PrintLog::logString virtual void logString( const char *inLoggerName, const char *inString, int inLevel ); protected: FILE *mLogFile; char *mLogFileName; unsigned long mSecondsBetweenBackups; unsigned long mTimeOfLastBackup; static const char *mDefaultLogFileName; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/log/Makefile0000640000175000017500000000235707436570640022603 0ustar pabspabs# # Modification History # # 2002-February-25 Jason Rohrer # Created. # Changed to be more succinct. # GXX=g++ ROOT_PATH = ../../.. DEBUG_ON_FLAG = -g DEBUG_OFF_FLAG = DEBUG_FLAG = ${DEBUG_OFF_FLAG} TIME_PLATFORM_PATH = unix TIME_PLATFORM = Unix TIME_O = ${ROOT_PATH}/minorGems/system/${TIME_PLATFORM_PATH}/Time${TIME_PLATFORM}.o TIME_H = ${ROOT_PATH}/minorGems/system/Time.h TIME_CPP = ${ROOT_PATH}/minorGems/system/${TIME_PLATFORM_PATH}/Time${TIME_PLATFORM}.cpp testLog: AppLog.o FileLog.o PrintLog.o Log.o testLog.o ${TIME_O} ${GXX} ${DEBUG_FLAG} -o testLog AppLog.o FileLog.o PrintLog.o Log.o testLog.o ${TIME_O} clean: rm -f *.o ${TIME_O} testLog.o: testLog.cpp AppLog.h FileLog.h Log.h ${GXX} ${DEBUG_FLAG} -o testLog.o -c testLog.cpp AppLog.o: AppLog.h AppLog.cpp Log.h PrintLog.h ${GXX} ${DEBUG_FLAG} -o AppLog.o -c AppLog.cpp PrintLog.o: PrintLog.h PrintLog.cpp Log.h PrintLog.h ${TIME_H} ${GXX} ${DEBUG_FLAG} -I${ROOT_PATH} -o PrintLog.o -c PrintLog.cpp FileLog.o: PrintLog.h FileLog.cpp FileLog.h Log.h ${GXX} ${DEBUG_FLAG} -o FileLog.o -c FileLog.cpp Log.o: Log.h Log.cpp ${GXX} ${DEBUG_FLAG} -o Log.o -c Log.cpp ${TIME_O}: ${TIME_H} ${TIME_CPP} ${GXX} ${DEBUG_FLAG} -I${ROOT_PATH} -o ${TIME_O} -c ${TIME_CPP} Cultivation_9+dfsg1_UnixSource/minorGems/util/log/FileLog.cpp0000640000175000017500000000613111373324323023150 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-March-13 Jason Rohrer * Added a flush after every write. * * 2002-April-8 Jason Rohrer * Changed to be thread-safe. * * 2002-November-5 Jason Rohrer * Added support for backing up logs and deleting old log data. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "FileLog.h" #include "PrintLog.h" #include "minorGems/util/stringUtils.h" #include "minorGems/io/file/File.h" #include #include const char *FileLog::mDefaultLogFileName = "default.log"; FileLog::FileLog( const char *inFileName, unsigned long inSecondsBetweenBackups ) : mLogFile( NULL ), mLogFileName( stringDuplicate( inFileName ) ), mSecondsBetweenBackups( inSecondsBetweenBackups ), mTimeOfLastBackup( time( NULL ) ) { mLogFile = fopen( mLogFileName, "a" ); if( mLogFile == NULL ) { printf( "Log file %s failed to open.\n", mLogFileName ); printf( "Writing log to default file: %s\n", mDefaultLogFileName ); // switch to default log file name delete [] mLogFileName; mLogFileName = stringDuplicate( mDefaultLogFileName ); mLogFile = fopen( mLogFileName, "a" ); if( mLogFile == NULL ) { printf( "Default log file %s failed to open.\n", mLogFileName ); } } } FileLog::~FileLog() { if( mLogFile != NULL ) { fclose( mLogFile ); } delete [] mLogFileName; } void FileLog::logString( const char *inLoggerName, const char *inString, int inLevel ) { if( mLogFile != NULL ) { if( inLevel <= mLoggingLevel ) { char *message = PrintLog::generateLogMessage( inLoggerName, inString, inLevel ); mLock->lock(); fprintf( mLogFile, "%s\n", message ); fflush( mLogFile ); if( time( NULL ) - mTimeOfLastBackup > mSecondsBetweenBackups ) { makeBackup(); } mLock->unlock(); delete [] message; } } } void FileLog::makeBackup() { fclose( mLogFile ); File *currentLogFile = new File( NULL, mLogFileName ); char *backupFileName = new char[ strlen( mLogFileName ) + 10 ]; sprintf( backupFileName, "%s.backup", mLogFileName ); File *backupLogFile = new File( NULL, backupFileName ); delete [] backupFileName; // copy into backup log file, which will overwrite it currentLogFile->copy( backupLogFile ); delete currentLogFile; delete backupLogFile; // clear main log file and start writing to it again mLogFile = fopen( mLogFileName, "w" ); if( mLogFile == NULL ) { printf( "Log file %s failed to open.\n", mLogFileName ); } mTimeOfLastBackup = time( NULL ); } Cultivation_9+dfsg1_UnixSource/minorGems/util/log/AppLog.cpp0000640000175000017500000000603211373324323023011 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-March-30 Jason Rohrer * Wrapped our dynamically allocated static member in a statically * allocated class to avoid memory leaks at program termination. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "AppLog.h" #include "Log.h" #include // wrap our static member in a statically allocated class LogPointerWrapper AppLog::mLogPointerWrapper( new PrintLog ); LogPointerWrapper::LogPointerWrapper( Log *inLog ) : mLog( inLog ) { } LogPointerWrapper::~LogPointerWrapper() { if( mLog != NULL ) { delete mLog; } } void AppLog::criticalError( const char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::CRITICAL_ERROR_LEVEL ); } void AppLog::criticalError( const char *inLoggerName, const char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::CRITICAL_ERROR_LEVEL ); } void AppLog::error( const char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::ERROR_LEVEL ); } void AppLog::error( const char *inLoggerName, const char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::ERROR_LEVEL ); } void AppLog::warning( const char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::WARNING_LEVEL ); } void AppLog::warning( const char *inLoggerName, const char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::WARNING_LEVEL ); } void AppLog::info( const char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::INFO_LEVEL ); } void AppLog::info( const char *inLoggerName, const char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::INFO_LEVEL ); } void AppLog::detail( const char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::DETAIL_LEVEL ); } void AppLog::detail( const char *inLoggerName, const char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::DETAIL_LEVEL ); } void AppLog::trace( const char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::TRACE_LEVEL ); } void AppLog::trace( const char *inLoggerName, const char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::TRACE_LEVEL ); } void AppLog::setLog( Log *inLog ) { int currentLoggingLevel = getLoggingLevel(); if( inLog != mLogPointerWrapper.mLog ) { delete mLogPointerWrapper.mLog; } mLogPointerWrapper.mLog = inLog; setLoggingLevel( currentLoggingLevel ); } Log *AppLog::getLog() { return mLogPointerWrapper.mLog; } void AppLog::setLoggingLevel( int inLevel ) { mLogPointerWrapper.mLog->setLoggingLevel( inLevel ); } int AppLog::getLoggingLevel() { return mLogPointerWrapper.mLog->getLoggingLevel(); } Cultivation_9+dfsg1_UnixSource/minorGems/util/log/Log.cpp0000640000175000017500000000057107436570021022355 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. */ #include "Log.h" const int Log::DEACTIVATE_LEVEL = 0; const int Log::CRITICAL_ERROR_LEVEL = 1; const int Log::ERROR_LEVEL = 2; const int Log::WARNING_LEVEL = 3; const int Log::INFO_LEVEL = 4; const int Log::DETAIL_LEVEL = 5; const int Log::TRACE_LEVEL = 6; Log::~Log() { } Cultivation_9+dfsg1_UnixSource/minorGems/util/log/PrintLog.cpp0000640000175000017500000001116611373324323023371 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-March-11 Jason Rohrer * Added a missing include. * * 2002-April-8 Jason Rohrer * Fixed a casting, dereferencing Win32 compile bug. * Changed to be thread-safe. * Changed to use thread-safe printing function. * * 2002-April-8 Jason Rohrer * Fixed a signed-unsigned mismatch. * * 2004-January-11 Jason Rohrer * Added lock around asctime call. * Increased scope of lock. * * 2004-January-29 Jason Rohrer * Changed to use ctime instead of localtime and asctime. * Improved locking scope. * Changed to use autoSprintf. * * 2010-April-5 Jason Rohrer * Printf-like functionality. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "PrintLog.h" #include "minorGems/system/Time.h" #include "minorGems/util/printUtils.h" #include "minorGems/util/stringUtils.h" #include #include #include #include const char *PrintLog::mDefaultLoggerName = "general"; PrintLog::PrintLog() : mLoggingLevel( Log::TRACE_LEVEL ), mLock( new MutexLock() ) { } PrintLog::~PrintLog() { delete mLock; } void PrintLog::setLoggingLevel( int inLevel ) { mLock->lock(); mLoggingLevel = inLevel; mLock->unlock(); } int PrintLog::getLoggingLevel() { mLock->lock(); int level = mLoggingLevel; mLock->unlock(); return level; } void PrintLog::logString( const char *inString, int inLevel ) { logString( (char *)mDefaultLoggerName, inString, inLevel ); } void PrintLog::logString( const char *inLoggerName, const char *inString, int inLevel ) { // not thread-safe to read mLoggingLevel here // without synchronization. // However, we want logging calls that are above // our level to execute with nearly no overhead. // mutex might be too much overhead.... // Besides, not being thread-safe in this case might // (worst case) result in a missed log entry or // an extra log entry... but setting the logging level should be rare. if( inLevel <= mLoggingLevel ) { char *message = generateLogMessage( inLoggerName, inString, inLevel ); threadPrintF( "%s\n", message ); delete [] message; } } void PrintLog::logPrintf( int inLevel, const char* inFormatString, ... ) { unsigned int bufferSize = 200; va_list argList; va_start( argList, inFormatString ); char *buffer = new char[ bufferSize ]; int stringLength = vsnprintf( buffer, bufferSize, inFormatString, argList ); va_end( argList ); if( stringLength == -1 || stringLength >= (int)bufferSize ) { // too long! delete [] buffer; buffer = stringDuplicate( "Message too long" ); } logString( (char *)mDefaultLoggerName, buffer, inLevel ); delete [] buffer; } void PrintLog::logNPrintf( const char *inLoggerName, int inLevel, const char* inFormatString, ... ) { unsigned int bufferSize = 200; va_list argList; va_start( argList, inFormatString ); char *buffer = new char[ bufferSize ]; int stringLength = vsnprintf( buffer, bufferSize, inFormatString, argList ); va_end( argList ); if( stringLength == -1 || stringLength >= (int)bufferSize ) { // too long! delete [] buffer; buffer = stringDuplicate( "Message too long" ); } logString( inLoggerName, buffer, inLevel ); delete [] buffer; } char *PrintLog::generateLogMessage( const char *inLoggerName, const char *inString, int inLevel ) { unsigned long seconds, milliseconds; Time::getCurrentTime( &seconds, &milliseconds ); // lock around ctime call, since it returns a static buffer mLock->lock(); char *dateString = stringDuplicate( ctime( (time_t *)( &seconds ) ) ); // done with static buffer, since we made a copy mLock->unlock(); // this date string ends with a newline... // get rid of it dateString[ strlen(dateString) - 1 ] = '\0'; char *messageBuffer = autoSprintf( "L%d | %s (%ld ms) | %s | %s", inLevel, dateString, milliseconds, inLoggerName, inString ); delete [] dateString; return messageBuffer; } Cultivation_9+dfsg1_UnixSource/minorGems/util/log/testLog.cpp0000640000175000017500000000147207436570021023256 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. */ #include "AppLog.h" #include "FileLog.h" #include "Log.h" int main() { AppLog::warning( "A test warning" ); AppLog::warning( "Another test warning" ); AppLog::warning( "mainApp", "And Another test warning" ); AppLog::setLoggingLevel( Log::ERROR_LEVEL ); AppLog::warning( "Yet Another test warning" ); AppLog::error( "mainApp", "A test error" ); AppLog::setLog( new FileLog( "test.log" ) ); // this should be skipped AppLog::warning( "A second test warning" ); // this should not be AppLog::criticalError( "A critical error" ); AppLog::setLog( new FileLog( "test2.log" ) ); // this should be skipped AppLog::warning( "A third test warning" ); return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/util/log/Log.h0000640000175000017500000000546311373324323022024 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2010-April-5 Jason Rohrer * Printf-like functionality. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/common.h" #ifndef LOG_INCLUDED #define LOG_INCLUDED #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * An interface for a class that can perform logging functions. * * @author Jason Rohrer */ class Log { public: // These levels were copied from the JLog framework // by Todd Lauinger static const int DEACTIVATE_LEVEL; static const int CRITICAL_ERROR_LEVEL; static const int ERROR_LEVEL; static const int WARNING_LEVEL; static const int INFO_LEVEL; static const int DETAIL_LEVEL; static const int TRACE_LEVEL; // provided so that virtual deletes work properly virtual ~Log(); /** * Sets the logging level of the current log. * * Messages with levels above the current level will not be logged. * * @param inLevel one of the defined logging levels. */ virtual void setLoggingLevel( int inLevel ) = 0; /** * Gets the logging level of the current log. * * Messages with levels above the current level will not be logged. * * @return one of the defined logging levels. */ virtual int getLoggingLevel() = 0; /** * Logs a string in this log under the default logger name. * * @param inString the string to log as a \0-terminated string. * Must be destroyed by caller. * @param inLevel the level to log inString at. */ virtual void logString( const char *inString, int inLevel ) = 0; /** * Logs a string in this log, specifying a logger name. * * @param inLoggerName the name of the logger * as a \0-terminated string. * Must be destroyed by caller. * @param inString the string to log as a \0-terminated string. * Must be destroyed by caller. * @param inLevel the level to log inString at. */ virtual void logString( const char *inLoggerName, const char *inString, int inLevel ) = 0; virtual void logPrintf( int inLevel, const char* inFormatString, ... ) = 0; virtual void logNPrintf( const char *inLoggerName, int inLevel, const char* inFormatString, ... ) = 0; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/StringBufferOutputStream.h0000640000175000017500000000277210047451312025513 0ustar pabspabs/* * Modification History * * 2002-August-1 Jason Rohrer * Created. * * 2004-May-9 Jason Rohrer * Added function for getting data as a byte array. */ #include "minorGems/common.h" #ifndef STRING_BUFFER_OUTPUT_STREAM_INCLUDED #define STRING_BUFFER_OUTPUT_STREAM_INCLUDED #include "minorGems/util/SimpleVector.h" #include "minorGems/io/OutputStream.h" /** * An output stream that fills a string buffer. * * @author Jason Rohrer */ class StringBufferOutputStream : public OutputStream { public: StringBufferOutputStream(); ~StringBufferOutputStream(); /** * Gets the data writen to this stream since construction as a * \0-terminated string. * * @return a string containing all data written to this stream. * Must be destroyed by caller. */ char *getString(); /** * Gets the data writen to this stream since construction as a * byte array. * * @param outNumBytes pointer to where the array length should be * returned. * * @return an array containingdata written to this stream. * Must be destroyed by caller. */ unsigned char *getBytes( int *outNumBytes ); // implements the OutputStream interface long write( unsigned char *inBuffer, long inNumBytes ); protected: SimpleVector *mCharacterVector; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/TranslationManager.cpp0000640000175000017500000002244111374544330024644 0ustar pabspabs/* * Modification History * * 2004-October-7 Jason Rohrer * Created. * * 2006-February-19 Jason Rohrer * Fixed an inconsistency in memory management. * * 2008-September-17 Jason Rohrer * Support for setting language data directly. * * 2010-March-21 Jason Rohrer * Fixed crash if default language file not found. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "TranslationManager.h" #include #include "minorGems/io/file/File.h" // will be destroyed automatically at program termination TranslationManagerStaticMembers TranslationManager::mStaticMembers; void TranslationManager::setDirectoryName( const char *inName ) { mStaticMembers.setDirectoryAndLanguage( inName, mStaticMembers.mLanguageName ); } char *TranslationManager::getDirectoryName() { return stringDuplicate( mStaticMembers.mDirectoryName ); } char **TranslationManager::getAvailableLanguages( int *outNumLanguages ) { File *languageDirectory = new File( NULL, mStaticMembers.mDirectoryName ); if( languageDirectory->exists() && languageDirectory->isDirectory() ) { int numChildFiles; File **childFiles = languageDirectory->getChildFiles( &numChildFiles ); if( childFiles != NULL ) { SimpleVector *languageNames = new SimpleVector(); for( int i=0; igetFileName(); char *extensionPointer = strstr( name, ".txt" ); if( extensionPointer != NULL ) { // terminate string, cutting off extension extensionPointer[0] = '\0'; languageNames->push_back( stringDuplicate( name ) ); } delete [] name; delete childFiles[i]; } delete [] childFiles; char **returnArray = languageNames->getElementArray(); *outNumLanguages = languageNames->size(); delete languageNames; delete languageDirectory; return returnArray; } } delete languageDirectory; // default, if we didn't return above *outNumLanguages = 0; return new char*[0]; } void TranslationManager::setLanguage( const char *inLanguageName ) { mStaticMembers.setDirectoryAndLanguage( mStaticMembers.mDirectoryName, inLanguageName ); } void TranslationManager::setLanguageData( const char *inData ) { mStaticMembers.setTranslationData( inData ); } const char *TranslationManager::translate( const char *inTranslationKey ) { char *translatedString = NULL; SimpleVector *keys = mStaticMembers.mTranslationKeys; SimpleVector *naturalLanguageStrings = mStaticMembers.mNaturalLanguageStrings; if( keys != NULL ) { int numKeys = keys->size(); for( int i=0; igetElement( i ) ) ) == 0 ) { // keys match translatedString = *( naturalLanguageStrings->getElement( i ) ); } } } if( translatedString == NULL ) { // no translation exists // the translation for this key is the key itself // add it to our translation table char *key = stringDuplicate( inTranslationKey ); char *value = stringDuplicate( inTranslationKey ); keys->push_back( key ); naturalLanguageStrings->push_back( value ); // thus, we return a value from our table, just as if a translation // had existed for this string translatedString = value; } return translatedString; } TranslationManagerStaticMembers::TranslationManagerStaticMembers() : mDirectoryName( NULL ), mLanguageName( NULL ), mTranslationKeys( NULL ), mNaturalLanguageStrings( NULL ) { // default setDirectoryAndLanguage( "languages", "English" ); } TranslationManagerStaticMembers::~TranslationManagerStaticMembers() { if( mDirectoryName != NULL ) { delete [] mDirectoryName; } if( mLanguageName != NULL ) { delete [] mLanguageName; } if( mTranslationKeys != NULL ) { int numKeys = mTranslationKeys->size(); for( int i=0; igetElement( i ) ); } delete mTranslationKeys; } if( mNaturalLanguageStrings != NULL ) { int numKeys = mNaturalLanguageStrings->size(); for( int i=0; igetElement( i ) ); } delete mNaturalLanguageStrings; } } void TranslationManagerStaticMembers::setDirectoryAndLanguage( const char *inDirectoryName, const char *inLanguageName ) { // save temp copies first to allow caller to pass our own members in to us char *tempDirectoryName = stringDuplicate( inDirectoryName ); char *tempLanguageName = stringDuplicate( inLanguageName ); if( mDirectoryName != NULL ) { delete [] mDirectoryName; } if( mLanguageName != NULL ) { delete [] mLanguageName; } mDirectoryName = tempDirectoryName; mLanguageName = tempLanguageName; char dataSet = false; File *directoryFile = new File( NULL, mDirectoryName ); if( directoryFile->exists() && directoryFile->isDirectory() ) { char *languageFileName = autoSprintf( "%s.txt", mLanguageName ); File *languageFile = directoryFile->getChildFile( languageFileName ); delete [] languageFileName; if( languageFile != NULL ) { char *languageData = languageFile->readFileContents(); if( languageData != NULL ) { dataSet = true; setTranslationData( languageData ); delete [] languageData; } delete languageFile; } } delete directoryFile; if( !dataSet ) { // failed to open file... // set blank data to ensure that vectors are created setTranslationData( "" ); } } static inline char *stringSkip( const char *inString, int inNumChars ) { return (char *)( &( inString[ inNumChars ] ) ); } void TranslationManagerStaticMembers::setTranslationData( const char *inData ) { // clear the old translation table if( mTranslationKeys != NULL ) { int numKeys = mTranslationKeys->size(); for( int i=0; igetElement( i ) ); } delete mTranslationKeys; } if( mNaturalLanguageStrings != NULL ) { int numKeys = mNaturalLanguageStrings->size(); for( int i=0; igetElement( i ) ); } delete mNaturalLanguageStrings; } // now read in the translation table mTranslationKeys = new SimpleVector(); mNaturalLanguageStrings = new SimpleVector(); char readError = false; while( ! readError ) { char *key = new char[ 100 ]; int numRead = sscanf( inData, "%99s", key ); if( numRead == 1 ) { inData = stringSkip( inData, strlen( key ) ); // skip the first " int readChar = ' '; while( readChar != '"' && readChar != '\0' ) { readChar = inData[0]; inData = stringSkip( inData, 1 ); } if( readChar != EOF ) { char *naturalLanguageString = new char[1000]; // scan a string of up to 999 characters, stopping // at the first " character numRead = sscanf( inData, "%999[^\"]", naturalLanguageString ); if( numRead == 1 ) { inData = stringSkip( inData, strlen( naturalLanguageString ) ); // trim the key and string and save them mTranslationKeys->push_back( stringDuplicate( key ) ); mNaturalLanguageStrings->push_back( stringDuplicate( naturalLanguageString ) ); } else { readError = true; } delete [] naturalLanguageString; // skip the trailing " readChar = ' '; while( readChar != '"' && readChar != '\0' ) { readChar = inData[0]; inData = stringSkip( inData, 1 ); } } else { readError = true; } } else { readError = true; } delete [] key; } } Cultivation_9+dfsg1_UnixSource/minorGems/util/StringBufferOutputStream.cpp0000640000175000017500000000227410047451312026043 0ustar pabspabs/* * Modification History * * 2002-August-1 Jason Rohrer * Created. * * 2004-May-9 Jason Rohrer * Added function for getting data as a byte array. */ #include "minorGems/util/StringBufferOutputStream.h" StringBufferOutputStream::StringBufferOutputStream() : mCharacterVector( new SimpleVector() ) { } StringBufferOutputStream::~StringBufferOutputStream() { delete mCharacterVector; } char *StringBufferOutputStream::getString() { int numChars = mCharacterVector->size(); char *returnArray = new char[ numChars + 1 ]; for( int i=0; igetElement( i ) ) ); } returnArray[ numChars ] = '\0'; return returnArray; } unsigned char *StringBufferOutputStream::getBytes( int *outNumBytes ) { *outNumBytes = mCharacterVector->size(); return mCharacterVector->getElementArray(); } long StringBufferOutputStream::write( unsigned char *inBuffer, long inNumBytes ) { for( int i=0; ipush_back( inBuffer[ i ] ); } return inNumBytes; } Cultivation_9+dfsg1_UnixSource/minorGems/util/stringUtils.cpp0000640000175000017500000002625711373326622023414 0ustar pabspabs/* * Modification History * * 2003-May-10 Jason Rohrer * Created. * Added a tokenization function. * * 2003-June-14 Jason Rohrer * Added a join function. * * 2003-June-22 Jason Rohrer * Added an autoSprintf function. * * 2003-July-27 Jason Rohrer * Fixed bugs in autoSprintf return values for certain cases. * * 2003-August-12 Jason Rohrer * Added a concatonate function. * * 2003-September-7 Jason Rohrer * Changed so that split returns last part, even if it is empty. * * 2004-January-15 Jason Rohrer * Added work-around for MinGW vsnprintf bug. * * 2006-June-2 Jason Rohrer * Added a stringStartsWith function. * * 2009-September-7 Jason Rohrer * Fixed int types. * Switched away from StringBufferOutputStream to new function in SimpleVector. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "stringUtils.h" #include char *stringToLowerCase( const char *inString ) { unsigned int length = strlen( inString ); char *returnString = stringDuplicate( inString ); for( unsigned int i=0; i stringLength ) { return false; } else { for( unsigned int i=0; i *parts = new SimpleVector(); char *workingString = stringDuplicate( inString ); char *workingStart = workingString; unsigned int separatorLength = strlen( inSeparator ); char *foundSeparator = strstr( workingString, inSeparator ); while( foundSeparator != NULL ) { // terminate at separator foundSeparator[0] = '\0'; parts->push_back( stringDuplicate( workingString ) ); // skip separator workingString = &( foundSeparator[ separatorLength ] ); foundSeparator = strstr( workingString, inSeparator ); } // add the remaining part, even if it is the empty string parts->push_back( stringDuplicate( workingString ) ); delete [] workingStart; *outNumParts = parts->size(); char **returnArray = parts->getElementArray(); delete parts; return returnArray; } char *join( char **inStrings, int inNumParts, const char *inGlue ) { SimpleVector result; for( int i=0; i *inTargetVector, SimpleVector *inSubstituteVector ) { int numTargets = inTargetVector->size(); char *newHaystack = stringDuplicate( inHaystack ); char tagFound; for( int i=0; igetElement( i ) ), *( inSubstituteVector->getElement( i ) ), &tagFound ); delete [] newHaystack; newHaystack = newHaystackWithReplacements; } return newHaystack; } SimpleVector *tokenizeString( const char *inString ) { char *tempString = stringDuplicate( inString ); char *restOfString = tempString; SimpleVector *foundTokens = new SimpleVector(); SimpleVector *currentToken = new SimpleVector(); while( restOfString[0] != '\0' ) { // characters remain // skip whitespace char nextChar = restOfString[0]; while( nextChar == ' ' || nextChar == '\n' || nextChar == '\r' || nextChar == '\t' ) { restOfString = &( restOfString[1] ); nextChar = restOfString[0]; } if( restOfString[0] != '\0' ) { // a token while( nextChar != ' ' && nextChar != '\n' && nextChar != '\r' && nextChar != '\t' && nextChar != '\0' ) { // still not whitespace currentToken->push_back( nextChar ); restOfString = &( restOfString[1] ); nextChar = restOfString[0]; } // reached end of token foundTokens->push_back( currentToken->getElementString() ); currentToken->deleteAll(); } } delete [] tempString; delete currentToken; return foundTokens; } char *autoSprintf( const char* inFormatString, ... ) { unsigned int bufferSize = 50; va_list argList; va_start( argList, inFormatString ); char *buffer = new char[ bufferSize ]; int stringLength = vsnprintf( buffer, bufferSize, inFormatString, argList ); va_end( argList ); if( stringLength != -1 ) { // follows C99 standard... // stringLength is the length of the string that would have been // written if the buffer was big enough // not room for string and terminating \0 in bufferSize bytes if( (unsigned int)stringLength >= bufferSize ) { // need to reprint with a bigger buffer delete [] buffer; bufferSize = (unsigned int)( stringLength + 1 ); va_list argList; va_start( argList, inFormatString ); buffer = new char[ bufferSize ]; // can simply use vsprintf now vsprintf( buffer, inFormatString, argList ); va_end( argList ); return buffer; } else { // buffer was big enough // trim the buffer to fit the string char *returnString = stringDuplicate( buffer ); delete [] buffer; return returnString; } } else { // follows old ANSI standard // -1 means the buffer was too small // Note that some buggy non-C99 vsnprintf implementations // (notably MinGW) // do not return -1 if stringLength equals bufferSize (in other words, // if there is not enough room for the trailing \0). // Thus, we need to check for both // (A) stringLength == -1 // (B) stringLength == bufferSize // below. // keep doubling buffer size until it's big enough while( stringLength == -1 || (unsigned int)stringLength == bufferSize ) { delete [] buffer; if( (unsigned int)stringLength == bufferSize ) { // only occurs if vsnprintf implementation is buggy // might as well use the information, though // (instead of doubling the buffer size again) bufferSize = bufferSize + 1; } else { // double buffer size again bufferSize = 2 * bufferSize; } va_list argList; va_start( argList, inFormatString ); buffer = new char[ bufferSize ]; stringLength = vsnprintf( buffer, bufferSize, inFormatString, argList ); va_end( argList ); } // trim the buffer to fit the string char *returnString = stringDuplicate( buffer ); delete [] buffer; return returnString; } } Cultivation_9+dfsg1_UnixSource/minorGems/util/SimpleVector.h0000640000175000017500000002702111373326622023134 0ustar pabspabs// Jason Rohrer // SimpleVector.h /** * * Simple vector template class. Supports pushing at end and random-access deletions. * Dynamically sized. * * * Created 10-24-99 * Mods: * Jason Rohrer 12-11-99 Added deleteAll function * Jason Rohrer 1-30-2000 Changed to return NULL if get called on non-existent element * Jason Rohrer 12-20-2000 Added a function for deleting a particular * element. * Jason Rohrer 12-14-2001 Added a function for getting the index of * a particular element. * Jason Rohrer 1-24-2003 Added a functions for getting an array or * string of all elements. * Jason Rohrer 7-26-2005 Added template <> to explicitly specialized * getElementString. * Jason Rohrer 1-9-2009 Added setElementString method. * Jason Rohrer 9-7-2009 Fixed int types. * Added appendElementString. * Jason Rohrer 11-11-2009 Changed second deleteElement to allow * SimpleVectors containing ints. * Jason Rohrer 12-23-2009 New push_back for arrays. * Jason Rohrer 1-5-2010 Reduced default size to conserve memory. * Jason Rohrer 1-12-2010 Added copy constructor and assignment * operator. * Jason Rohrer 1-16-2010 Fixed bugs in new constructor/operator. * Jason Rohrer 1-18-2010 Fixed memmove/memcpy bugs. * Jason Rohrer 1-28-2010 Data protected for subclass access. * Jason Rohrer 4-28-2010 Fast version of getElement. * Jason Rohrer 5-14-2010 String parameters as const to fix warnings. */ #include "minorGems/common.h" #ifndef SIMPLEVECTOR_INCLUDED #define SIMPLEVECTOR_INCLUDED #include // for memory moving functions const int defaultStartSize = 2; template class SimpleVector { public: SimpleVector(); // create an empty vector SimpleVector(int sizeEstimate); // create an empty vector with a size estimate ~SimpleVector(); // copy constructor SimpleVector( const SimpleVector &inCopy ); // assignment operator SimpleVector & operator = (const SimpleVector &inOther ); void push_back(Type x); // add x to the end of the vector // add array of elements to the end of the vector void push_back(Type *inArray, int inLength); Type *getElement(int index); // get a ptr to element at index in vector Type *getElementFast(int index); // no bounds checking int size(); // return the number of allocated elements in the vector bool deleteElement(int index); // delete element at an index in vector /** * Deletes a particular element. Deletes the first element * in vector == to inElement. * * @param inElement the element to delete. * * @return true iff an element was deleted. */ bool deleteElementEqualTo( Type inElement ); /** * Gets the index of a particular element. Gets the index of the * first element in vector == to inElement. * * @param inElement the element to get the index of. * * @return the index if inElement, or -1 if inElement is not found. */ int getElementIndex( Type inElement ); void deleteAll(); // delete all elements from vector /** * Gets the elements as an array. * * @return the a new array containing all elements in this vector. * Must be destroyed by caller, though elements themselves are * not copied. */ Type *getElementArray(); /** * Gets the char elements as a \0-terminated string. * * @return a \0-terminated string containing all elements in this * vector. * Must be destroyed by caller. */ char *getElementString(); /** * Sets the char elements as a \0-terminated string. * * @param inString a \0-terminated string containing all elements to * set this vector with. * Must be destroyed by caller. */ void setElementString( const char *inString ); /** * Appends chars from a \0-terminated string. * * @param inString a \0-terminated string containing all elements to * append to this vector. * Must be destroyed by caller. */ void appendElementString( const char *inString ); protected: Type *elements; int numFilledElements; int maxSize; int minSize; // number of allocated elements when vector is empty }; template inline SimpleVector::SimpleVector() { elements = new Type[defaultStartSize]; numFilledElements = 0; maxSize = defaultStartSize; minSize = defaultStartSize; } template inline SimpleVector::SimpleVector(int sizeEstimate) { elements = new Type[sizeEstimate]; numFilledElements = 0; maxSize = sizeEstimate; minSize = sizeEstimate; } template inline SimpleVector::~SimpleVector() { delete [] elements; } // copy constructor template inline SimpleVector::SimpleVector( const SimpleVector &inCopy ) : elements( new Type[ inCopy.maxSize ] ), numFilledElements( inCopy.numFilledElements ), maxSize( inCopy.maxSize ), minSize( inCopy.minSize ) { // if these objects contain pointers to stack, etc, this is not // going to work (not a deep copy) // because it won't invoke the copy constructors of the objects! //memcpy( elements, inCopy.elements, sizeof( Type ) * numFilledElements ); for( int i=0; i inline SimpleVector & SimpleVector::operator = ( const SimpleVector &inOther ) { // pattern found on wikipedia: // avoid self-assignment if( this != &inOther ) { // 1: allocate new memory and copy the elements Type *newElements = new Type[ inOther.maxSize ]; // again, memcpy doesn't work here, because it doesn't invoke // copy constructor on contained object /*memcpy( newElements, inOther.elements, sizeof( Type ) * inOther.numFilledElements ); */ for( int i=0; i inline int SimpleVector::size() { return numFilledElements; } template inline Type *SimpleVector::getElement(int index) { if( index < numFilledElements && index >=0 ) { return &(elements[index]); } else return NULL; } template inline Type *SimpleVector::getElementFast(int index) { return &(elements[index]); } template inline bool SimpleVector::deleteElement(int index) { if( index < numFilledElements) { // if index valid for this vector if( index != numFilledElements - 1) { // this spot somewhere in middle // memmove NOT okay here, because it leaves shallow copies // behind that cause errors when the whole element array is // destroyed. /* // move memory towards front by one spot int sizeOfElement = sizeof(Type); int numBytesToMove = sizeOfElement*(numFilledElements - (index+1)); Type *destPtr = &(elements[index]); Type *srcPtr = &(elements[index+1]); memmove( (void *)destPtr, (void *)srcPtr, (unsigned int)numBytesToMove); */ for( int i=index+1; i inline bool SimpleVector::deleteElementEqualTo( Type inElement ) { int index = getElementIndex( inElement ); if( index != -1 ) { return deleteElement( index ); } else { return false; } } template inline int SimpleVector::getElementIndex( Type inElement ) { // walk through vector, looking for first match. for( int i=0; i inline void SimpleVector::deleteAll() { numFilledElements = 0; if( maxSize > minSize ) { // free memory if vector has grown delete [] elements; elements = new Type[minSize]; // reallocate an empty vector maxSize = minSize; } } template inline void SimpleVector::push_back(Type x) { if( numFilledElements < maxSize) { // still room in vector elements[numFilledElements] = x; numFilledElements++; } else { // need to allocate more space for vector int newMaxSize = maxSize << 1; // double size // NOTE: memcpy does not work here, because it does not invoke // copy constructors on elements. // And then "delete []" below causes destructors to be invoked // on old elements, which are shallow copies of new objects. Type *newAlloc = new Type[newMaxSize]; /* unsigned int sizeOfElement = sizeof(Type); unsigned int numBytesToMove = sizeOfElement*(numFilledElements); // move into new space memcpy((void *)newAlloc, (void *) elements, numBytesToMove); */ // must use element-by-element assignment to invoke constructors for( int i=0; i inline void SimpleVector::push_back(Type *inArray, int inLength) { for( int i=0; i inline Type *SimpleVector::getElementArray() { Type *newAlloc = new Type[ numFilledElements ]; // shallow copy not good enough! /* unsigned int sizeOfElement = sizeof( Type ); unsigned int numBytesToCopy = sizeOfElement * numFilledElements; // copy into new space //memcpy( (void *)newAlloc, (void *)elements, numBytesToCopy ); */ // use assignment to ensure that constructors are invoked on element copies for( int i=0; i inline char *SimpleVector::getElementString() { char *newAlloc = new char[ numFilledElements + 1 ]; unsigned int sizeOfElement = sizeof( char ); unsigned int numBytesToCopy = sizeOfElement * numFilledElements; // memcpy fine here, since shallow copy good enough for chars // copy into new space memcpy( (void *)newAlloc, (void *)elements, numBytesToCopy ); newAlloc[ numFilledElements ] = '\0'; return newAlloc; } template <> inline void SimpleVector::appendElementString( const char *inString ) { // slow but correct unsigned int numChars = strlen( inString ); for( unsigned int i=0; i inline void SimpleVector::setElementString( const char *inString ) { deleteAll(); appendElementString( inString ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/TranslationManager.h0000640000175000017500000001217311373324323024307 0ustar pabspabs/* * Modification History * * 2004-October-7 Jason Rohrer * Created. * Copied structure from SettingsManager. * * 2006-February-19 Jason Rohrer * Fixed an inconsistency in memory management. * * 2008-September-17 Jason Rohrer * Support for setting language data directly. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #include "minorGems/common.h" #ifndef TRANSLATION_MANAGER_INCLUDED #define TRANSLATION_MANAGER_INCLUDED #include "minorGems/util/SimpleVector.h" // utility class for dealing with static member dealocation class TranslationManagerStaticMembers; /** * Class that manages natural language translation of user interface strings. * * @author Jason Rohrer */ class TranslationManager { public: /** * Sets the directory name where translation files are stored. * Defaults to "languages". * * @param inName the name of the directory (relative to the * program's working directory). * Must be destroyed by caller. */ static void setDirectoryName( const char *inName ); /** * Gets the directory name where translation files are stored. * * @return the name of the directory (relative to the * program's working directory). * Must be destroyed by caller. */ static char *getDirectoryName(); /** * Gets a list of available languages. * * @param outNumLanguages pointer to where the number of languages * should be returned. * * @return an array of language names. * Array and the strings it contains must be destroyed by caller. */ static char **getAvailableLanguages( int *outNumLanguages ); /** * Sets the natural language to translate keys into. * Defaults to "English". * * @param inLanguageName the name of the language. * This name, when .txt is appended, is the name of * the translation file. Thus, setLanguage( "English" ) would * select the English.txt language file. * Language names must not contain spaces. * Must be destroyed by caller. */ static void setLanguage( const char *inLanguageName ); // Sets the language data directly without reading it from // the file system. // Data string formated the same as a language file. // Data string destroyed by caller static void setLanguageData( const char *inData ); /** * Gets the natural language translation of a key. * * NOTE: if a translation does not exist for the key, the key * itself will be returned. (A copy of the key is returned, so * the original key passed in to translate can be destroyed by caller * if needed). * * @param inTranslationKey the translation key string. * Must be destroyed by caller if non-const. * * @return the translated natural language string. * The string MUST NOT be destroyed by the caller, as it will * be destroyed by this class upon program termination. * * This specification allows the translate function to be used * inline, whether or not a correct translation exists. Thus
         *
         * printf( "%s", translate( myKey ) );
         * delete [] myKey;
         * 
* * will always be correct, whether or not a translation exists, as * will
         *
         * printf( "%s", translate( "MY_KEY" ) );
         * 
*/ static const char *translate( const char *inTranslationKey ); protected: static TranslationManagerStaticMembers mStaticMembers; }; /** * Container for static members to allow for their proper destruction * on program termination. * * @author Jason Rohrer */ class TranslationManagerStaticMembers { public: TranslationManagerStaticMembers(); ~TranslationManagerStaticMembers(); /** * Sets the directory name and language name to use, and reads * the translation table from file. * * @param inDirectoryName the directory name. * Must be destroyed by caller. * @param inLanguageName the language name. * Must be destroyed by caller. */ void setDirectoryAndLanguage( const char *inDirectoryName, const char *inLanguageName ); // sets the data from a string // string contains same contents as a language file // string destroyed by caller void setTranslationData( const char *inData ); char *mDirectoryName; char *mLanguageName; // vectors mapping keys to strings SimpleVector *mTranslationKeys; SimpleVector *mNaturalLanguageStrings; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/printUtils.h0000640000175000017500000000102707554101514022670 0ustar pabspabs/* * Modification History * * 2002-April-8 Jason Rohrer * Created. */ #include "minorGems/common.h" #ifndef PRINT_UTILS_INCLUDED #define PRINT_UTILS_INCLUDED /** * A thread-safe version of printf. * * Note that printf is already thread-safe on certain platforms, * but does not seem to be thread-safe on Win32. * * @param inFormatString the format string to use. * @param ... a variable argument list, with the same usage * pattern as printf. */ int threadPrintF( const char* inFormatString, ... ); #endif Cultivation_9+dfsg1_UnixSource/minorGems/util/printUtils.cpp0000640000175000017500000000107407455450264023236 0ustar pabspabs/* * Modification History * * 2002-April-8 Jason Rohrer * Created. * * 2002-April-11 Jason Rohrer * Added a missing return value. */ #include "printUtils.h" #include "minorGems/system/MutexLock.h" #include // for variable argument lists #include MutexLock threadPrintFLock; int threadPrintF( const char* inFormatString, ... ) { threadPrintFLock.lock(); va_list argList; va_start( argList, inFormatString ); int returnVal = vprintf( inFormatString, argList ); threadPrintFLock.unlock(); return returnVal; } Cultivation_9+dfsg1_UnixSource/minorGems/util/test/0000750000175000017500000000000011401021107021301 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/util/test/testSnprintf.mingw.out0000640000175000017500000000055610001574214025673 0ustar pabspabsPrinted string of length 3 to buffer of size 4 with snprintf. Return value = 3 Buffer was null-terminated by snprintf Printed string of length 4 to buffer of size 4 with snprintf. Return value = 4 Buffer was NOT null-terminated by snprintf Printed string of length 5 to buffer of size 4 with snprintf. Return value = -1 Buffer was NOT null-terminated by snprintf Cultivation_9+dfsg1_UnixSource/minorGems/util/test/testSnprintf.cpp0000640000175000017500000000232410001572273024524 0ustar pabspabs/* * Modification History * * 2004-January-15 Jason Rohrer * Created. */ /** * A test program for snprintf behavior. */ #include #include #include int main() { int numStrings = 3; // test strings of length 3, 4, and 5 const char *testStrings[3] = { "tst", "test", "testt" }; int result; // a buffer of length 4, which IS NOT large // enough to hold the last two testStrings char *buffer = (char*)( malloc( 4 ) ); int i; for( i=0; i * * Modified by Aaron D. Gifford * * NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN * * The original unmodified version is available at: * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) 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 AUTHOR(S) 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 "sha1.h" #include #include // for hex encoding #include "minorGems/formats/encodingUtils.h" #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define blk0(i) (block->l[i] = (rol(block->l[i],24)&(sha1_quadbyte)0xFF00FF00) \ |(rol(block->l[i],8)&(sha1_quadbyte)0x00FF00FF)) #else #define blk0(i) block->l[i] #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); typedef union _BYTE64QUAD16 { sha1_byte c[64]; sha1_quadbyte l[16]; } BYTE64QUAD16; /* Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1_Transform(sha1_quadbyte state[5], sha1_byte buffer[64]) { sha1_quadbyte a, b, c, d, e; BYTE64QUAD16 *block; block = (BYTE64QUAD16*)buffer; /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /* SHA1_Init - Initialize new context */ void SHA1_Init(SHA_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ void SHA1_Update(SHA_CTX *context, sha1_byte *data, unsigned int len) { unsigned int i, j; j = (context->count[0] >> 3) & 63; if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; context->count[1] += (len >> 29); if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64-j)); SHA1_Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { SHA1_Transform(context->state, &data[i]); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } /* Add padding and return the message digest. */ void SHA1_Final(sha1_byte digest[SHA1_DIGEST_LENGTH], SHA_CTX *context) { sha1_quadbyte i, j; sha1_byte finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (sha1_byte)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } SHA1_Update(context, (sha1_byte *)"\200", 1); while ((context->count[0] & 504) != 448) { SHA1_Update(context, (sha1_byte *)"\0", 1); } /* Should cause a SHA1_Transform() */ SHA1_Update(context, finalcount, 8); for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { digest[i] = (sha1_byte) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } /* Wipe variables */ i = j = 0; memset(context->buffer, 0, SHA1_BLOCK_LENGTH); memset(context->state, 0, SHA1_DIGEST_LENGTH); memset(context->count, 0, 8); memset(&finalcount, 0, 8); } unsigned char *computeRawSHA1Digest( unsigned char *inData, int inDataLength ) { SHA_CTX context; SHA1_Init( &context ); // copy into buffer, since this SHA1 implementation seems to overwrite // parts of the data buffer. unsigned char *buffer = new unsigned char[ inDataLength ]; memcpy( (void *)buffer, (void *)inData, inDataLength ); SHA1_Update( &context, buffer, inDataLength ); delete [] buffer; unsigned char *digest = new unsigned char[ SHA1_DIGEST_LENGTH ]; SHA1_Final( digest, &context ); return digest; } unsigned char *computeRawSHA1Digest( char *inString ) { SHA_CTX context; SHA1_Init( &context ); // copy into buffer, since this SHA1 implementation seems to overwrite // parts of the data buffer. int dataLength = strlen( inString ); unsigned char *buffer = new unsigned char[ dataLength ]; memcpy( (void *)buffer, (void *)inString, dataLength ); SHA1_Update( &context, buffer, strlen( inString ) ); delete [] buffer; unsigned char *digest = new unsigned char[ SHA1_DIGEST_LENGTH ]; SHA1_Final( digest, &context ); return digest; } char *computeSHA1Digest( char *inString ) { unsigned char *digest = computeRawSHA1Digest( inString ); char *digestHexString = hexEncode( digest, SHA1_DIGEST_LENGTH ); delete [] digest; return digestHexString; } char *computeSHA1Digest( unsigned char *inData, int inDataLength ) { unsigned char *digest = computeRawSHA1Digest( inData, inDataLength ); char *digestHexString = hexEncode( digest, SHA1_DIGEST_LENGTH ); delete [] digest; return digestHexString; } Cultivation_9+dfsg1_UnixSource/minorGems/crypto/hashes/sha1sumCompile0000750000175000017500000000011610053166076024777 0ustar pabspabsg++ -I../../.. -o sha1sum sha1sum.cpp sha1.cpp ../../formats/encodingUtils.cppCultivation_9+dfsg1_UnixSource/minorGems/crypto/hashes/sha1sum.cpp0000640000175000017500000000356111262434343024252 0ustar pabspabs/* * Modification History * * 2004-May-20 Jason Rohrer * Created. */ #include "sha1.h" #include "minorGems/formats/encodingUtils.h" #include #include /** * Prints usage message and exits. * * @param inAppName the name of the app. */ void usage( char *inAppName ); int main( int inNumArgs, char **inArgs ) { if( inNumArgs != 2 ) { usage( inArgs[0] ); } FILE *file = fopen( inArgs[1], "rb" ); if( file == NULL ) { printf( "Failed to open file %s for reading\n\n", inArgs[1] ); usage( inArgs[0] ); } SHA_CTX shaContext; SHA1_Init( &shaContext ); int bufferSize = 5000; unsigned char *buffer = new unsigned char[ bufferSize ]; int numRead = bufferSize; char error = false; // read bytes from file until we run out while( numRead == bufferSize && !error ) { numRead = fread( buffer, 1, bufferSize, file ); if( numRead > 0 ) { SHA1_Update( &shaContext, buffer, numRead ); } else{ error = true; } } fclose( file ); delete [] buffer; if( error ) { printf( "Error reading from file %s\n", inArgs[1] ); } else { unsigned char *rawDigest = new unsigned char[ SHA1_DIGEST_LENGTH ]; SHA1_Final( rawDigest, &shaContext ); // else hash is correct char *digestHexString = hexEncode( rawDigest, SHA1_DIGEST_LENGTH ); printf( "%s %s\n", digestHexString, inArgs[1] ); delete [] rawDigest; delete [] digestHexString; } return 0; } void usage( char *inAppName ) { printf( "Usage:\n\n" ); printf( "\t%s file_to_sum\n", inAppName ); printf( "example:\n" ); printf( "\t%s test.txt\n", inAppName ); exit( 1 ); } Cultivation_9+dfsg1_UnixSource/minorGems/crypto/hashes/sha1.h0000640000175000017500000000765711330667201023201 0ustar pabspabs/* * Modification History * * 2002-May-25 Jason Rohrer * Changed to use minorGems endian.h * Added a function for hashing an entire string to a hex digest. * Added a function for getting a raw digest. * * 2003-September-15 Jason Rohrer * Added support for hashing raw (non-string) data. * Removed legacy C code. */ /* * sha.h * * Originally taken from the public domain SHA1 implementation * written by by Steve Reid * * Modified by Aaron D. Gifford * * NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN * * The original unmodified version is available at: * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) 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 AUTHOR(S) 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 __SHA1_H__ #define __SHA1_H__ #include "minorGems/system/endian.h" /* Make sure you define these types for your architecture: */ typedef unsigned int sha1_quadbyte; /* 4 byte type */ typedef unsigned char sha1_byte; /* single byte type */ /* * Be sure to get the above definitions right. For instance, on my * x86 based FreeBSD box, I define LITTLE_ENDIAN and use the type * "unsigned long" for the quadbyte. On FreeBSD on the Alpha, however, * while I still use LITTLE_ENDIAN, I must define the quadbyte type * as "unsigned int" instead. */ #define SHA1_BLOCK_LENGTH 64 #define SHA1_DIGEST_LENGTH 20 /* The SHA1 structure: */ typedef struct _SHA_CTX { sha1_quadbyte state[5]; sha1_quadbyte count[2]; sha1_byte buffer[SHA1_BLOCK_LENGTH]; } SHA_CTX; // BIG NOTE: // These overwrite the data! // copy your data to a temporary buffer before passing it through // (the "nicer" functions below these 3 do not overwrite data) void SHA1_Init(SHA_CTX *context); void SHA1_Update(SHA_CTX *context, sha1_byte *data, unsigned int len); void SHA1_Final(sha1_byte digest[SHA1_DIGEST_LENGTH], SHA_CTX* context); /** * Computes a unencoded 20-byte digest from data. * * @param inData the data to hash. * Must be destroyed by caller. * @param inDataLength the length of the data to hash. * Must be destroyed by caller. * * @return the digest as a byte array of length 20. * Must be destroyed by caller. */ unsigned char *computeRawSHA1Digest( unsigned char *inData, int inDataLength ); /** * Computes a unencoded 20-byte digest from an arbitrary string message. * * @param inString the message as a \0-terminated string. * Must be destroyed by caller. * * @return the digest as a byte array of length 20. * Must be destroyed by caller. */ unsigned char *computeRawSHA1Digest( char *inString ); /** * Computes a hex-encoded string digest from data. * * @param inData the data to hash. * Must be destroyed by caller. * @param inDataLength the length of the data to hash. * Must be destroyed by caller. * * @return the digest as a \0-terminated string. * Must be destroyed by caller. */ char *computeSHA1Digest( unsigned char *inData, int inDataLength ); /** * Computes a hex-encoded string digest from an arbitrary string message. * * @param inString the message as a \0-terminated string. * Must be destroyed by caller. * * @return the digest as a \0-terminated string. * Must be destroyed by caller. */ char *computeSHA1Digest( char *inString ); #endif Cultivation_9+dfsg1_UnixSource/minorGems/crypto/hashes/sha1Test.cpp0000640000175000017500000000476707733372451024407 0ustar pabspabs/* * Modification History * * 2002-May-25 Jason Rohrer * Created. * * 2003-September-21 Jason Rohrer * Added test of long string. */ #include "sha1.h" #include #include /** * All parameters must be destroyed by caller. */ void checkHash( char *inString, char *inTestName, char *inCorrectHash ); int main() { /* * Test vectors: * * "abc" * A999 3E36 4706 816A BA3E 2571 7850 C26C 9CD0 D89D * * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" * 8498 3E44 1C3B D26E BAAE 4AA1 F951 29E5 E546 70F1 * * A million repetitions of "a" * 34AA 973C D4C4 DAA4 F61E EB2B DBAD 2731 6534 016F */ char *abc = "abc"; char *correctABCHash = "A9993E364706816ABA3E25717850C26C9CD0D89D"; char *mixedAlpha = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; char *correctMixedAlphaHash = "84983E441C3BD26EBAAE4AA1F95129E5E54670F1"; char *correctMillionHash = "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F"; int oneMillion = 1000000; char *millionAs = new char[ oneMillion + 1 ]; for( int i=0; i getKeyDown( int vKeyCode ) { return ((GetAsyncKeyState(vKeyCode) & 0x8000) ? true : false); } getKeyUp( int vKeyCode ) { return ((GetAsyncKeyState(vKeyCode) & 0x8000) ? false : true); } Sample Mac implementation #include getKeyDown( int vKeyCode ) { KeyMapByteArray keyArray; GetKeys( keyArray ); int arrayInd = vKeyCode >> 3; // divide by 8 to get start array index of key code unsigned char neededByte = keyArray[ arrayInd ]; return (neededByte >> vKeyCode % 8) && 0x01; // trim off bit needed } getKeyUp( int vKeyCode ) { KeyMapByteArray keyArray; GetKeys( keyArray ); int arrayInd = vKeyCode >> 3; // divide by 8 to get start array index of key code unsigned char neededByte = keyArray[ arrayInd ]; return !((neededByte >> vKeyCode % 8) && 0x01); // trim off bit needed, and invert } */Cultivation_9+dfsg1_UnixSource/minorGems/graphics/filters/0000750000175000017500000000000011401021057022621 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/graphics/filters/InvertFilter.h0000640000175000017500000000123307220517106025420 0ustar pabspabs/* * Modification History * * 2000-December-21 Jason Rohrer * Created. */ #ifndef INVERT_FILTER_INCLUDED #define INVERT_FILTER_INCLUDED #include "minorGems/graphics/ChannelFilter.h" /** * Filter that inverts the values in a channel. * * @author Jason Rohrer */ class InvertFilter : public ChannelFilter { public: // implements the ChannelFilter interface void apply( double *inChannel, int inWidth, int inHeight ); }; inline void InvertFilter::apply( double *inChannel, int inWidth, int inHeight ) { int numPixels = inWidth * inHeight; for( int i=0; i *mFilterVector; }; inline MultiFilter::MultiFilter() : mFilterVector( new SimpleVector() ) { } inline MultiFilter::~MultiFilter() { delete mFilterVector; } inline void MultiFilter::apply( double *inChannel, int inWidth, int inHeight ) { int numFilters = mFilterVector->size(); for( int i=0; igetElement( i ) ); thisFilter->apply( inChannel, inWidth, inHeight ); } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/filters/MedianFilter.h0000640000175000017500000000672407301027016025354 0ustar pabspabs/* * Modification History * * 2001-May-15 Jeremy Tavan * Created. * * 2001-May-16 Jeremy Tavan * Modified to use the much faster quick_select * median-finding algorithm. * * 2001-May-17 Jaosn Rohrer * Tried to optimize by moving stuff out of the inner-inner loop. * It helped a bit, but not much. * */ #ifndef MEDIAN_FILTER_INCLUDED #define MEDIAN_FILTER_INCLUDED #include "minorGems/graphics/ChannelFilter.h" #include "quickselect.h" int medianFilterCompareInt( const void *x, const void *y ); /** * Median convolution filter. * * @author Jeremy Tavan */ class MedianFilter : public ChannelFilter { public: /** * Constructs a median filter. * * @param inRadius the radius of the box in pixels. */ MedianFilter( int inRadius ); /** * Sets the box radius. * * @param inRadius the radius of the box in pixels. */ void setRadius( int inRadius ); /** * Gets the box radius. * * @return the radius of the box in pixels. */ int getRadius(); // implements the ChannelFilter interface void apply( double *inChannel, int inWidth, int inHeight ); private: int mRadius; }; inline MedianFilter::MedianFilter( int inRadius ) : mRadius( inRadius ) { } inline void MedianFilter::setRadius( int inRadius ) { mRadius = inRadius; } inline int MedianFilter::getRadius() { return mRadius; } inline void MedianFilter::apply( double *inChannel, int inWidth, int inHeight ) { // pre-compute an integer version of the channel for the // median alg to use int numPixels = inWidth * inHeight; int *intChannel = new int[ numPixels ]; for( int p=0; p= inHeight ) { endBoxY = inHeight - 1; } int boxSizeY = endBoxY - startBoxY + 1; for( int x=0; x= inWidth ) { endBoxX = inWidth - 1; } int boxSizeX = endBoxX - startBoxX + 1; int *buffer = new int[boxSizeX * boxSizeY]; // sum all pixels in the box around this pixel for( int boxY = startBoxY; boxY<=endBoxY; boxY++ ) { int yBoxIndexContrib = boxY * inWidth; int yBoxContrib = boxSizeX * ( boxY-startBoxY ); for( int boxX = startBoxX; boxX<=endBoxX; boxX++ ) { //buffer[boxSizeX*(boxY-startBoxY)+(boxX-startBoxX)] = (int)(1000.0 * inChannel[yBoxIndexContrib + boxX]); buffer[ yBoxContrib + ( boxX-startBoxX ) ] = intChannel[ yBoxIndexContrib + boxX ]; } } medianChannel[ yIndexContrib + x ] = (double)quick_select( buffer, boxSizeX*boxSizeY ) / 1000.0; delete [] buffer; } } // copy blurred image back into passed-in image memcpy( inChannel, medianChannel, sizeof(double) * inWidth * inHeight ); delete [] medianChannel; delete [] intChannel; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/filters/quickselect.h0000640000175000017500000000320607300520500025310 0ustar pabspabs/* * This Quickselect routine is based on the algorithm described in * "Numerical recipies in C", Second Edition, * Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5 */ #define ELEM_SWAP(a,b) { register int t=(a);(a)=(b);(b)=t; } int quick_select(int arr[], int n) { int low, high; int median; int middle, ll, hh; low = 0 ; high = n-1 ; median = (low + high) / 2; for (;;) { if (high <= low) /* One element only */ return arr[median] ; if (high == low + 1) { /* Two elements only */ if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ; return arr[median] ; } /* Find median of low, middle and high items; swap into position low */ middle = (low + high) / 2; if (arr[middle] > arr[high]) ELEM_SWAP(arr[middle], arr[high]) ; if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ; if (arr[middle] > arr[low]) ELEM_SWAP(arr[middle], arr[low]) ; /* Swap low item (now in position middle) into position (low+1) */ ELEM_SWAP(arr[middle], arr[low+1]) ; /* Nibble from each end towards middle, swapping items when stuck */ ll = low + 1; hh = high; for (;;) { do ll++; while (arr[low] > arr[ll]) ; do hh--; while (arr[hh] > arr[low]) ; if (hh < ll) break; ELEM_SWAP(arr[ll], arr[hh]) ; } /* Swap middle item (in position low) back into correct position */ ELEM_SWAP(arr[low], arr[hh]) ; /* Re-set active partition */ if (hh <= median) low = ll; if (hh >= median) high = hh - 1; } } #undef ELEM_SWAP Cultivation_9+dfsg1_UnixSource/minorGems/graphics/filters/BoxBlurFilter.h0000640000175000017500000001367510500113717025536 0ustar pabspabs/* * Modification History * * 2000-December-21 Jason Rohrer * Created. * * 2001-January-6 Jason Rohrer * Added a getRadius function for completeness. * * 2006-August-22 Jason Rohrer * Fixed major bug: sum for box must start at zero. * * 2006-September-7 Jason Rohrer * Optimized inner loop. * Optimized more, avoiding summing over entire box for each pixel. */ #ifndef BOX_BLUR_FILTER_INCLUDED #define BOX_BLUR_FILTER_INCLUDED #include "minorGems/graphics/ChannelFilter.h" /** * Blur convolution filter that uses a box for averaging. * * @author Jason Rohrer */ class BoxBlurFilter : public ChannelFilter { public: /** * Constructs a box filter. * * @param inRadius the radius of the box in pixels. */ BoxBlurFilter( int inRadius ); /** * Sets the box radius. * * @param inRadius the radius of the box in pixels. */ void setRadius( int inRadius ); /** * Gets the box radius. * * @return the radius of the box in pixels. */ int getRadius(); // implements the ChannelFilter interface void apply( double *inChannel, int inWidth, int inHeight ); private: int mRadius; }; inline BoxBlurFilter::BoxBlurFilter( int inRadius ) : mRadius( inRadius ) { } inline void BoxBlurFilter::setRadius( int inRadius ) { mRadius = inRadius; } inline int BoxBlurFilter::getRadius() { return mRadius; } inline void BoxBlurFilter::apply( double *inChannel, int inWidth, int inHeight ) { double *blurredChannel = new double[ inWidth * inHeight ]; // optimization: // The sum for a given pixel's box in row N is the sum for the same pixel // in row (N-1) minus one box-row of pixels and plus a box-row of pixels // We don't have to loop over the entire box for each pixel (instead, // we just add the new pixels and subtract the old as the box moves along) char lastRowSumsSet = false; double *lastRowSums = new double[ inWidth ]; // track the sums from single rows of boxes that we have already seen // Thus, when we need to "subtract a row" from our box sum, we can use // the precomputed version double *singleRowBoxSums = new double[ inWidth * inHeight ]; for( int y=0; y= inHeight ) { endBoxY = inHeight - 1; // box hanging over bottom edge // no rows to add to sum addARow = false; } int boxSizeY = endBoxY - startBoxY + 1; for( int x=0; x= inWidth ) { endBoxX = inWidth - 1; } int boxSizeX = endBoxX - startBoxX + 1; // sum all pixels in the box around this pixel double sum = 0; if( ! lastRowSumsSet ) { // do the "slow way" for the first row for( int boxY = startBoxY; boxY<=endBoxY; boxY++ ) { int yBoxIndexContrib = boxY * inWidth; double rowSum = 0; for( int boxX = startBoxX; boxX<=endBoxX; boxX++ ) { rowSum += inChannel[ yBoxIndexContrib + boxX ]; } sum += rowSum; // store row sum for future use singleRowBoxSums[ yBoxIndexContrib + x ] = rowSum; } } else { // we have sum for this pixel from the previous row // use it to avoid looping over entire box again sum = lastRowSums[ x ]; if( addARow ) { // add pixels from row at endBoxY int yBoxIndexContrib = endBoxY * inWidth; double rowSum = 0; for( int boxX = startBoxX; boxX<=endBoxX; boxX++ ) { rowSum += inChannel[ yBoxIndexContrib + boxX ]; } sum += rowSum; // store it for later when we will need to subtract it singleRowBoxSums[ yBoxIndexContrib + x ] = rowSum; } if( subtractARow ) { // subtract pixels from startBoxY of previous row's box int yBoxIndexContrib = (startBoxY - 1) * inWidth; // use pre-computed sum for the row we're subtracting sum -= singleRowBoxSums[ yBoxIndexContrib + x ]; } } // divide by number of pixels to complete the average blurredChannel[ pixelIndex ] = sum / (boxSizeX * boxSizeY); // save to use when computing box sum for next row lastRowSums[ x ] = sum; } // we now have valid last row sums that we can use for // all the rest of the rows lastRowSumsSet = true; } // copy blurred image back into passed-in image memcpy( inChannel, blurredChannel, sizeof(double) * inWidth * inHeight ); delete [] blurredChannel; delete [] lastRowSums; delete [] singleRowBoxSums; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/filters/ThresholdFilter.h0000640000175000017500000000265107220517106026112 0ustar pabspabs/* * Modification History * * 2000-December-21 Jason Rohrer * Created. */ #ifndef THRESHOLD_FILTER_INCLUDED #define THRESHOLD_FILTER_INCLUDED #include "minorGems/graphics/ChannelFilter.h" /** * Threshold filter. * * @author Jason Rohrer */ class ThresholdFilter : public ChannelFilter { public: /** * Constructs a threshold filter. * * @param inThreshold threshold value. Channel values * above or equal to inThreshold are set to 1, while * all other values are set to 0. */ ThresholdFilter( double inThreshold ); /** * Sets the threshold. * * @param inThreshold threshold value. Channel values * above or equal to inThreshold are set to 1, while * all other values are set to 0. */ void setThreshold( double inThreshold ); // implements the ChannelFilter interface void apply( double *inChannel, int inWidth, int inHeight ); private: double mThreshold; }; inline ThresholdFilter::ThresholdFilter( double inThreshold ) : mThreshold( inThreshold ) { } inline void ThresholdFilter::setThreshold( double inThreshold ) { mThreshold = inThreshold; } inline void ThresholdFilter::apply( double *inChannel, int inWidth, int inHeight ) { int numPixels = inWidth * inHeight; for( int i=0; i= mThreshold ) { inChannel[i] = 1.0; } else { inChannel[i] = 0.0; } } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/filters/SeamlessFilter.h0000640000175000017500000000740510506006133025726 0ustar pabspabs/* * Modification History * * 2001-January-19 Jason Rohrer * Created. * * 2006-September-25 Jason Rohrer * Added control over blending curve. * Reorderd loop for efficiency. */ #ifndef SEAMLESS_FILTER_INCLUDED #define SEAMLESS_FILTER_INCLUDED #include "minorGems/graphics/ChannelFilter.h" #include /** * Filter that turns any image into a seamless tile. * * @author Jason Rohrer */ class SeamlessFilter : public ChannelFilter { public: /** * Constructs a filter. * * @param inBlendCurveExponent the exponent of the blending curve * as we near the edge of the image. Defaults to 1 for a linear * blending curve. Setting to 2 would result in a parabolic * curve which would push the blended area closer to the edge * and leave more high-contrast, unblended image data near * the middle. */ SeamlessFilter( double inBlendCurveExponent = 1 ); // implements the ChannelFilter interface virtual void apply( double *inChannel, int inWidth, int inHeight ); private: double mBlendingCurveExponent; }; inline SeamlessFilter::SeamlessFilter( double inBlendCurveExponent ) : mBlendingCurveExponent( inBlendCurveExponent ) { } inline void SeamlessFilter::apply( double *inChannel, int inWidth, int inHeight ) { int numPixels = inWidth * inHeight; int halfHigh = inHeight/2; int halfWide = inWidth/2; // mix the texture with itself. // as we move closer and closer to the texture edge, we should // start mixing in more and more of the center values double *channelCopy = new double[ numPixels ]; // first, create an image with no seams if tiled horizontally // (getting rid of vertical seams) for( int x=0; x #include "IconMap.h" class GraphicBuffer { public: GraphicBuffer( unsigned long *buff, int buffW, int buffH ); ~GraphicBuffer(); // draw a solid image into the buffer void drawImage( unsigned long *image, int *imageYOffset, int xPos, int yPos, int wide, int high ); // draw a transparent image into the buffer void drawImageAlpha( unsigned long *image, int *imageYOffset, int xPos, int yPos, int wide, int high ); // replace a rectangle in the buffer with bgColor void eraseImage( int xPos, int yPos, int wide, int high, Color &bgColor ); // draw a solid IconMap into the buffer void drawIconMap( IconMap *icon, int xPos, int yPos ); // draw a transparent image into the buffer void drawIconMapAlpha( IconMap *icon, int xPos, int yPos ); // replace a rectangle (over icon) in the buffer with bgColor void eraseIconMap( IconMap *icon, int xPos, int yPos, Color &bgColor ); // set buffer to a new buffer of the same size void setBuffer( unsigned long *buff ); // get the pixel buffer unsigned long *getBuffer(); int getWidth(); int getHeight(); // fill buffer with a color void fill( Color &fillC ); // take a screen shot, save to disc void screenShot( FILE *f ); private: unsigned long *buffer; int bufferHigh; int bufferWide; int *buffYOffset; float invChannelMax; Color utilColor; // color for using composite functions }; inline GraphicBuffer::GraphicBuffer( unsigned long *buff, int buffW, int buffH) : utilColor(0,0,0,0) { invChannelMax = 1 / 255.0; bufferHigh = buffH; bufferWide = buffW; buffer = buff; // precalc'ed y contributions to linear indexing of buffer buffYOffset = new int[bufferHigh]; for( int y=0; y bufferHigh ) { maxY = bufferHigh; } int minX = xPos; if( minX < 0 ) { minX = 0; } int maxX = xPos + wide; if( maxX > bufferWide ) { maxX = bufferWide; } for( int y=minY; y bufferHigh ) { maxY = bufferHigh; } int minX = xPos; if( minX < 0 ) { minX = 0; } int maxX = xPos + wide; if( maxX > bufferWide ) { maxX = bufferWide; } unsigned long composite = bgColor.composite; for( int y=minY; y bufferHigh ) { maxY = bufferHigh; } int minX = xPos; if( minX < 0 ) { minX = 0; } int maxX = xPos + wide; if( maxX > bufferWide ) { maxX = bufferWide; } for( int y=minY; y> 24) * invChannelMax; float oneMinusAlpha = 1-alpha; unsigned long buffARGB = buffer[ buffYContrib + x ]; argb = utilColor.getWeightedComposite( argb, alpha ); buffARGB = utilColor.getWeightedComposite( buffARGB, oneMinusAlpha ); unsigned long sum = utilColor.sumComposite( argb, buffARGB ); buffer[ buffYContrib + x ] = sum; } } } inline void GraphicBuffer::drawIconMap( IconMap *icon, int xPos, int yPos ) { drawImage( icon->imageMap, icon->yOffset, xPos, yPos, icon->wide, icon->high ); } inline void GraphicBuffer::drawIconMapAlpha( IconMap *icon, int xPos, int yPos ) { drawImageAlpha( icon->imageMap, icon->yOffset, xPos, yPos, icon->wide, icon->high ); } inline void GraphicBuffer::eraseIconMap( IconMap *icon, int xPos, int yPos, Color &bgColor ) { eraseImage( xPos, yPos, icon->wide, icon->high, bgColor ); } inline void GraphicBuffer::fill( Color &fillC ) { unsigned long composite = fillC.composite; for( int y=0; y #include #include "Image.h" /** * An RGBA extension of Image. * * @author Jason Rohrer */ class RGBAImage : public Image { public: /** * Constructs an RGBA image. All channels are initialized to 0. * * @param inWidth width of image in pixels. * @param inHeight height of image in pixels. */ RGBAImage( int inWidth, int inHeight ); /** * Constructs an RGBAImage by copying a given image. * The image data is truncated or expaned (with black color channels * and white alpha) to fit the 4 channel RGBA model, * and any selection in inImage is ignored. * * @param inImage the image to copy. Copied internally, so must be * destroyed by the caller. */ RGBAImage( Image *inImage ); /** * Gets the pixel data from this image as a byte array. * * @return a byte array containing pixel data for this image. * Stored in row-major order, with each pixel represented * by 4 bytes in the order RGBA. * Must be destroyed by caller. */ virtual unsigned char *getRGBABytes(); // get RGBA a bytes from an image without converting it to an RGBA // image first (faster---found with profiler) // // MUST be a 3- or 4-channel image static unsigned char *getRGBABytes( Image *inImage ); // overrides the Image::filter function virtual void filter( ChannelFilter *inFilter ); virtual void filter( ChannelFilter *inFilter, int inChannel ); // overrides the Image::copy function // since the function is non-virtual in the parent class, // the pointer type should determine which function gets called. RGBAImage *copy(); }; inline RGBAImage::RGBAImage( int inWidth, int inHeight ) : Image( inWidth, inHeight, 4 ) { } inline RGBAImage::RGBAImage( Image *inImage ) // Only init our image's channels to black of inImage doesn't have enough // channels. // This saves time if inImage has 4 or more channels. // Optimization found with profiler. : Image( inImage->getWidth(), inImage->getHeight(), 4, ( inImage->getNumChannels() < 4 ) ) { int minNumChannels = 4; if( inImage->getNumChannels() < minNumChannels ) { minNumChannels = inImage->getNumChannels(); } // if inImage does not have at least 4 channels, // leave our additional channels black // if inImage has extra channels, skip them // copy channels from inImage for( int c=0; cgetChannel( c ); memcpy( mChannels[c], inChannel, mNumPixels * sizeof( double ) ); } if( minNumChannels < 4 ) { // no alpha channel in inImage // set a white one double *alpha = mChannels[3]; for( int i=0; igetWidth() * inImage->getHeight(); int numChannels = inImage->getNumChannels(); int numBytes = numPixels * 4; unsigned char *bytes = new unsigned char[ numBytes ]; double *channelZero = inImage->getChannel( 0 ); double *channelOne = inImage->getChannel( 1 ); double *channelTwo = inImage->getChannel( 2 ); // double-coded branch here to make it faster // (avoid a switch inside the loop if( numChannels > 3 ) { double *channelThree = inImage->getChannel( 3 ); register int i = 0; for( int p=0; ppaste( this ); return copiedImage; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/0000750000175000017500000000000011462022731022344 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/NoLightingGL.h0000640000175000017500000000121007220267641025005 0ustar pabspabs/* * Modification History * * 2000-December-20 Jason Rohrer * Created. */ #ifndef NO_LIGHTING_GL_INCLUDED #define NO_LIGHTING_GL_INCLUDED #include "LightingGL.h" /** * LightingGL implementation that uses a constant white light everywhere. * * @author Jason Rohrer */ class NoLightingGL : public LightingGL{ public: // implements LightingGL interface void getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ); }; inline void NoLightingGL::getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ) { outColor->r = 1.0; outColor->g = 1.0; outColor->b = 1.0; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/RadiosGLView.cpp0000640000175000017500000004061307205611501025353 0ustar pabspabs// Jason Rohrer // RadiosGLView.cpp /** * * OpenGL viewer for RadiosGL rendered scenes * * Created 1-14-2000 * Mods: * Jason Rohrer 1-15-2000 Added timer function to make keyboard * interaction smoother and more intuitive * * Jason Rohrer 1-18-2000 Moved SetMouse call from within mouse handler * to inside re-display function * SetMouse was posting a new mouse event too * quickly, putting the delta-angles back to 0 * before the posted redisplay happened. * * This made view motion jerky on the PC * Fixed it by only centering mouse when it leaves window * (or hits window border) * Added mouse capture to window on PC so that * mouse events outside window border are captured * * Added "o" key control to switch into fullscreen mode */ #define APP_NAME "RadiosGLView" #define SETTINGS_FILE_NAME "RadiosGLView.ini" #define FORWARD_MOTION_TIMER 200 #include #include #include #include #include "gl.h" #include "glut.h" #include "RadiosityConstants.h" #include "Vertex.h" #include "Dither.h" #include "UniversalFileIO.h" // function for force-setting mouse position #include "SetMouse.h" // prototypes void cleanup(); int loadSettings(); void initModel(); void glutMotion(int x, int y); void draw (void); void glutDisplay (void); void glutResize (int w, int h); void glutKeyboard (unsigned char key, int x, int y); void glInit (void); void glutTimer( int value ); void glutIdle( void ); void glutEntry( int state ); // vector typedef float vec3_t[3]; // these are loaded from the ini file int screenCenterX; int screenCenterY; float invScreenCenterX; float invScreenCenterY; int dithering; int invMouse; int fullscreen; char *modelFileName = new char[99]; // window width and height int winW; int winH; long numPatches; Vertex *triangles[3]; Vertex *textAnchors[3]; int startTextID = 13; float translationX = 0; float translationY = 0; float translationZ = 0; // position for view float positionX = 0; float positionY = 25; float positionZ = 0; float speed = 0.5; // speed of motion float rotationSpeed = 1.0; // speed of view rotation // two vectors specifying view direction float lookX = 0; float lookY = 0; float lookZ = 1; float upX = 0; float upY = 1; float upZ = 0; // vector pointing to left to help with rotations float leftX = -1; float leftY = 0; float leftZ = 0; // change in position float deltaPosition = 0; // direction of motion char forwardMode = false; char backwardMode = false; // is mouse engaged for view angle movement char mouseEngaged = false; int lastMouseX = screenCenterX; int lastMouseY = screenCenterY; // set to true during recentering of mouse char ignoreEvent = false; // amount to rotate about axis float rotate = 0.0f; // vector which describes the axis to rotate about vec3_t axis = {1.0, 0.0, 0.0}; // global delta rotation, for use with the mouse vec3_t gRot = {0,0,0}; // global total rotation vec3_t gRotNet = {0,0,0}; FILE *logFile; void draw (void) { glPushMatrix (); glRotatef (-rotate, axis[0], axis[1], axis[2]); glTranslatef (-15.0, 10.0, 5.0); // draws radiosity polygons int textID = startTextID; for( int p=0; p -halfPI && gRotNet[0] + gRot[0] < halfPI ) { float oldUpX = upX; float oldUpY = upY; float oldUpZ = upZ; upX += lookX * gRot[0]; upY += lookY * gRot[0]; upZ += lookZ * gRot[0]; // normalize up float invLength = 1.0 / sqrt(upX*upX + upY*upY + upZ*upZ); upX = upX * invLength; upY = upY * invLength; upZ = upZ * invLength; lookX += oldUpX * -gRot[0]; lookY += oldUpY * -gRot[0]; lookZ += oldUpZ * -gRot[0]; // normalize look invLength = 1.0 / sqrt(lookX*lookX + lookY*lookY + lookZ*lookZ); lookX *= invLength; lookY *= invLength; lookZ *= invLength; gRotNet[0] += gRot[0]; } // now rotate left-right // this should by done by rotating all three vectors around y-axis // only X and Z components will change float cosTheta = cos( gRot[1] ); float sinTheta = sin( gRot[1] ); // first, look direction float oldLookX = lookX; float oldLookZ = lookZ; lookX = cosTheta * oldLookX + sinTheta * oldLookZ; lookZ = -sinTheta * oldLookX + cosTheta * oldLookZ; // then, up direction float oldUpX = upX; float oldUpZ = upZ; upX = cosTheta * oldUpX + sinTheta * oldUpZ; upZ = -sinTheta * oldUpX + cosTheta * oldUpZ; gRotNet[1] += gRot[1]; lastUpX = upX; lastUpY = upY; lastUpZ = upZ; lastLookX = lookX; lastLookY = lookY; lastLookZ = lookZ; } gluLookAt( positionX, positionY, positionZ, positionX + lookX, positionY + lookY, positionZ + lookZ, upX, upY, upZ ); draw (); glutSwapBuffers(); } void glutIdle( void ) { glutPostRedisplay(); } char timing = false; // are we timing interval between key strokes? char keyPress = false; // are we in the middle of a key-down session? void glutTimer( int value ) { // NOTE: This seems to rely on keyboard repeat speed.... // would like to find a better method of using keyboard to move // forward and backward in a 3d environment // Want a key-up, key-down event handler, but this is not available in GLUT!!! if( !timing && (backwardMode || forwardMode) ) { timing = true; // wait one more timer cycle glutTimerFunc(FORWARD_MOTION_TIMER, glutTimer, 1); // reset timer return; } if( timing ) { // expire timers and current motion if( backwardMode ) { backwardMode = false; } if( forwardMode ) { forwardMode = false; } timing = false; keyPress = false; } glutPostRedisplay(); } // called on app start and on resize. void glutResize (int w, int h) { winW = w; winH = h; screenCenterX = winW / 2; screenCenterY = winH / 2; invScreenCenterX = 1/ (float)screenCenterX; invScreenCenterY = 1/ (float)screenCenterY; glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glViewport( 0, 0, winW, winH ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( 90, winW / winH, 1, 9999 ); glutPostRedisplay(); } // Keyboard handler void glutKeyboard (unsigned char key, int x, int y) { switch (key){ case 'q': case 'Q': exit (1); break; case 'o': case 'O': glutFullScreen(); break; case 'e': case 'E': // move forward forwardMode = true; backwardMode = false; if( !keyPress ) { // start of a new key down keyPress = true; glutTimerFunc(FORWARD_MOTION_TIMER, glutTimer, 1); // set timer } timing = false; // reset key timer break; case 'd': case 'D': // move backward //translationZ -= 1; forwardMode = false; backwardMode = true; if( !keyPress ) { // start of a new key down keyPress = true; glutTimerFunc(FORWARD_MOTION_TIMER, glutTimer, 1); // set timer } timing = false; // reset key timer break; case 'a': case 'A': // increase speed speed += 0.1; break; case 's': case 'S': // decrease speed if( speed >= 0.1 ) speed -= 0.1; else speed = 0; break; case 'z': case 'Z': // increase rotation speed rotationSpeed += 0.1; break; case 'x': case 'X': // decrease rotation speed if( rotationSpeed >= 0.1 ) rotationSpeed -= 0.1; else rotationSpeed = 0; break; case 'm': case 'M': if( mouseEngaged ) { glutSetCursor( GLUT_CURSOR_LEFT_ARROW ); // mouse back on to normal mode mouseEngaged = false; ReleaseMouse(); // stop rotation on mouse disengage gRot[0] = 0; gRot[1] = 0; } else { glutSetCursor( GLUT_CURSOR_NONE ); // cursor off, mouse in view control mode mouseEngaged = true; ignoreEvent = true; CaptureMouse(); SetMouse( screenCenterX, screenCenterY ); } break; break; } glutPostRedisplay (); } // Called when the mouse moves in our app area. void glutMotion(int x, int y) { if( mouseEngaged ) { // only change view if mouse in view mode if( ignoreEvent ) { lastMouseX = x; lastMouseY = y; // make sure mouse back in screen area if( x < winW-5 && x > 5 && y < winH-5 && y > 5 ) { ignoreEvent = false; } else { // try centering again, user might be fiesty SetMouse( screenCenterX, screenCenterY ); } return; } gRot[0] = (lastMouseY - y)*rotationSpeed * invScreenCenterY * invMouse; gRot[1] = (lastMouseX - x)*rotationSpeed * invScreenCenterX; lastMouseX = x; lastMouseY = y; glutPostRedisplay (); // watch for mouse hitting edges of screen if( x >= winW-5 || x <= 5 || y >= winH-5 || y <= 5 ) { ignoreEvent = true; SetMouse( screenCenterX, screenCenterY ); } } } void glutEntry( int state ) { // reign mouse in if it leaves window while engaged if( state == GLUT_LEFT && mouseEngaged ) { ignoreEvent = true; SetMouse( screenCenterX, screenCenterY ); } } // Sets up various OpenGL stuff. void glInit (void) { glEnable (GL_DEPTH_TEST); glEnable (GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CW); } int main (void) { logFile = fopen("RadiosGLView.log", "w"); if( loadSettings() != 0 ) { fprintf( logFile, "Settings file %s failed to open\n", SETTINGS_FILE_NAME ); cleanup(); return 2; // settings didn't load } if( fullscreen ) { glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH |GLUT_FULLSCREEN ); glutFullScreen(); } else { glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH |GLUT_MULTISAMPLE); } glutInitWindowSize( winW, winH ); glutCreateWindow( APP_NAME ); glutKeyboardFunc( glutKeyboard ); glutDisplayFunc( glutDisplay ); glutReshapeFunc( glutResize ); glutPassiveMotionFunc( glutMotion ); glutIdleFunc( glutIdle ); glutEntryFunc( glutEntry ); glInit (); initModel(); glutSetCursor( GLUT_CURSOR_NONE ); // cursor off, mouse in view control mode mouseEngaged = true; ignoreEvent = true; CaptureMouse(); SetMouse( screenCenterX, screenCenterY ); glutMainLoop( ); cleanup(); } void cleanup() { fclose(logFile); delete [] modelFileName; } int loadSettings() { FILE *settingsFile = fopen( SETTINGS_FILE_NAME, "r" ); if( settingsFile == NULL ) { return -1; } char *stringBuffer = new char[99]; // assumes that all parameters are in a specific order // read FULLSCREEN setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%d", &fullscreen ); // read DITHER setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%d", &dithering ); // read SCREENWIDE setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%d", &winW ); // read SCREENHIGH setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%d", &winH ); screenCenterX = winW / 2; screenCenterY = winH / 2; invScreenCenterX = 1/ (float)screenCenterX; invScreenCenterY = 1/ (float)screenCenterY; // read MODELFILE setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%s", modelFileName ); // read INVERTMOUSE setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%d", &invMouse ); // convert 0/1 switch to -1/1 needed for inverting mouse if( invMouse == 0 ) invMouse = 1; else invMouse = -1; fclose( settingsFile ); return 0; } void initModel() { UniversalFileIO fileIO; FILE *sceneFile = fopen( modelFileName, "rb" ); if( sceneFile != NULL ) { fprintf( logFile, "Scene file opened.\n"); } else { return; } numPatches = fileIO.freadLong( sceneFile ); fprintf( logFile, "num patches = %d\n", numPatches); for( int v=0; v<3; v++ ) { triangles[v] = new Vertex[numPatches]; textAnchors[v] = new Vertex[numPatches]; } int textID = startTextID; for( int p=0; p #include #include "TextureGL.h" #include "LightingGL.h" #include "minorGems/math/geometry/Transform3D.h" #include "minorGems/graphics/RGBAImage.h" #include "minorGems/graphics/3d/Primitive3D.h" #include "minorGems/graphics/openGL/NoLightingGL.h" // try and include extensions if multitexture isn't // built-in to gl.h #ifndef GL_ARB_multitexture #include #endif /** * OpenGL primitive object. * * Comprised of a triangle mesh, texture map, and anchor points. * * @author Jason Rohrer */ class PrimitiveGL { public: /** * Constructs a PrimitiveGL. * * No parameters are copied, so they should not be destroyed * or re-accessed by caller. All are destroyed when the * primitive is destroyed. * * @param inPrimitive the primitive to base this GL version on. */ PrimitiveGL( Primitive3D *inPrimitive ); ~PrimitiveGL(); /** * Draws the primitive (assumes an active openGL screen). * * @param inTransform the transform to perform on the primitive * before drawing it. * @param inLighting the lighting to use when drawing the primitive. */ void draw( Transform3D *inTransform, LightingGL *inLighting ); /** * Gets the number of parameters associated with this primitive. * * @return the number of parameters. */ virtual int getNumParameters(); /** * Gets the number of animations associated with this primitive. * * @return the number of animations. */ virtual int getNumAnimations(); /** * Sets a parameter for this primative. * * @param inParameterIndex the index of the parameter to set. * If an index for a non-existing parameter is specified, * this call has no effect. * @param inValue the value to set the parameter to, in [-1, 1]. * The default value for all parameters is 0. */ virtual void setParameter( int inParameterIndex, double inValue ); /** * Gets a parameter for this primative. * * @param inParameterIndex the index of the parameter to get. * If an index for a non-existing parameter is specified, * 0 is returned. * * @return the value of the parameter, in [-1, 1]. * The default value for all parameters is 0. */ virtual double getParameter( int inParameterIndex ); /** * Steps this primitive forward in time. * * @param inStepSize the size of the timestep to take. */ virtual void step( double inStepSize ); /** * Starts a temporal animation of this primitive. * The animation progresses as step() is called repeatedly. * If called for a non-existent animation or for one that is * already running, this function has no effect. */ virtual void startAnimation( int inAnimationIndex ); /** * Stops a temporal animation of this primitive. If called * for a non-existent animation or for one that is not currently * running, this function has no effect. */ virtual void stopAnimation( int inAnimationIndex ); protected: Primitive3D *mPrimitive; TextureGL *mTextureGL; // Equivalent to the public draw(), but does manual multi-texturing // by multi-pass rendering. Does not depend on ARB calls. void drawNoMultitexture( Transform3D *inTransform, LightingGL *inLighting ); }; inline PrimitiveGL::PrimitiveGL( Primitive3D *inPrimitive ) : mPrimitive( inPrimitive ) { int numTextures = mPrimitive->mNumTextures; mTextureGL = new TextureGL( numTextures ); for( int i=0; imTexture[i]->getRGBABytes(); mTextureGL->setTextureData( i, rgba, mPrimitive->mTexture[i]->getWidth(), mPrimitive->mTexture[i]->getHeight() ); delete [] rgba; } } inline PrimitiveGL::~PrimitiveGL() { delete mPrimitive; delete mTextureGL; } // this is the ARB version of draw inline void PrimitiveGL::draw( Transform3D *inTransform, LightingGL *inLighting ) { // check for multi-texture availability before proceeding if( !TextureGL::isMultiTexturingSupported() ) { drawNoMultitexture( inTransform, inLighting ); return; } // first, copy the vertices and translate/rotate/scale them Vector3D **worldVertices = new Vector3D*[ mPrimitive->mNumVertices ]; Vector3D **worldNormals = new Vector3D*[ mPrimitive->mNumVertices ]; for( int i=0; imNumVertices; i++ ) { // copy worldVertices[i] = new Vector3D( mPrimitive->mVertices[i] ); worldNormals[i] = new Vector3D( mPrimitive->mNormals[i] ); // translate/rotate/scale inTransform->apply( worldVertices[i] ); // only rotate normals inTransform->applyNoTranslation( worldNormals[i] ); // normalize to get rid of any scaling worldNormals[i]->normalize(); } // now draw vertices as triangle strips mTextureGL->enable(); int numTextureLayers = mTextureGL->getNumLayers(); int t; if( mPrimitive->isTransparent() ) { glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } else { glDisable( GL_BLEND ); } if( mPrimitive->isBackVisible() ) { glDisable( GL_CULL_FACE ); } else { glEnable( GL_CULL_FACE ); glCullFace( GL_BACK ); glFrontFace( GL_CCW ); } Color *lightColor = new Color( 0, 0, 0, 1.0 ); // for each strip of triangles for( int y=0; ymHigh-1; y++ ) { // note that we skip the last row of verts, since they are the bottom // of the last strip. int thisRow = y * mPrimitive->mWide; int nextRow = ( y + 1 ) * mPrimitive->mWide; glBegin( GL_TRIANGLE_STRIP ); // draw first triangle // first vert in next row int index = nextRow + 0; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = thisRow + 0; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = nextRow + 1; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); // draw next vertex to complete first "rectangle" index = thisRow + 1; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); // then add rest of vertices as part of strip for( int x=2; xmWide; x++ ) { // every additional pair of vertices completes // another "rectangle" index = nextRow + x; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = thisRow + x; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); } glEnd(); } mTextureGL->disable(); // cleanup for( int i=0; imNumVertices; i++ ) { delete worldVertices[i]; delete worldNormals[i]; } delete [] worldVertices; delete [] worldNormals; delete lightColor; } // this is the non-ARB version of draw inline void PrimitiveGL::drawNoMultitexture( Transform3D *inTransform, LightingGL *inLighting ) { // first, copy the vertices and translate/rotate/scale them Vector3D **worldVertices = new Vector3D*[ mPrimitive->mNumVertices ]; Vector3D **worldNormals = new Vector3D*[ mPrimitive->mNumVertices ]; for( int i=0; imNumVertices; i++ ) { // copy worldVertices[i] = new Vector3D( mPrimitive->mVertices[i] ); worldNormals[i] = new Vector3D( mPrimitive->mNormals[i] ); // translate/rotate/scale inTransform->apply( worldVertices[i] ); // only rotate normals inTransform->applyNoTranslation( worldNormals[i] ); // normalize to get rid of any scaling worldNormals[i]->normalize(); } Color *lightColor = new Color( 0, 0, 0, 1.0 ); // now draw vertices as triangle strips mTextureGL->enable(); if( mPrimitive->isTransparent() ) { glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } else { glDisable( GL_BLEND ); } if( mPrimitive->isBackVisible() ) { glDisable( GL_CULL_FACE ); } else { glEnable( GL_CULL_FACE ); glCullFace( GL_BACK ); glFrontFace( GL_CCW ); } // for each strip of triangles for( int y=0; ymHigh-1; y++ ) { // note that we skip the last row of verts, since they are the bottom // of the last strip. int thisRow = y * mPrimitive->mWide; int nextRow = ( y + 1 ) * mPrimitive->mWide; glBegin( GL_TRIANGLE_STRIP ); // draw first triangle // first vert in next row int index = nextRow + 0; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = thisRow + 0; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = nextRow + 1; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); // draw next vertex to complete first "rectangle" index = thisRow + 1; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); // then add rest of vertices as part of strip for( int x=2; xmWide; x++ ) { // every additional pair of vertices completes // another "rectangle" index = nextRow + x; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = thisRow + x; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); } glEnd(); } mTextureGL->disable(); // cleanup for( int i=0; imNumVertices; i++ ) { delete worldVertices[i]; delete worldNormals[i]; } delete [] worldVertices; delete [] worldNormals; delete lightColor; } // these all just wrap the functions of the underlying Primitive3D inline int PrimitiveGL::getNumParameters() { return mPrimitive->getNumParameters(); } inline int PrimitiveGL::getNumAnimations() { return mPrimitive->getNumAnimations(); } inline void PrimitiveGL::setParameter( int inParameterIndex, double inValue ) { mPrimitive->setParameter( inParameterIndex, inValue ); } inline double PrimitiveGL::getParameter( int inParameterIndex ) { return mPrimitive->getParameter( inParameterIndex ); } inline void PrimitiveGL::step( double inStepSize ) { mPrimitive->step( inStepSize ); } inline void PrimitiveGL::startAnimation( int inAnimationIndex ) { mPrimitive->startAnimation( inAnimationIndex ); } inline void PrimitiveGL::stopAnimation( int inAnimationIndex ) { mPrimitive->stopAnimation( inAnimationIndex ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/AmbientLightingGL.h0000640000175000017500000000243107232125073026011 0ustar pabspabs/* * Modification History * * 2001-January-17 Jason Rohrer * Created. */ #ifndef AMBIENT_LIGHTING_GL_INCLUDED #define AMBIENT_LIGHTING_GL_INCLUDED #include "LightingGL.h" /** * LightingGL implementation that uses constant, specifiable * light values everywhere. Most useful when combined with * directional or point lightings using MultiLighting. * * Setting color to full white makes this lighting equivalent * to a NoLightingGL. * * @author Jason Rohrer */ class AmbientLightingGL : public LightingGL{ public: /** * Constructs an AmbientLighting. * * @param inColor color and intensity of lighting. * Is not copied, so cannot be accessed again by caller. */ AmbientLightingGL( Color *inColor ); ~AmbientLightingGL(); // implements LightingGL interface void getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ); private: Color *mColor; }; inline AmbientLightingGL::AmbientLightingGL( Color *inColor ) : mColor( inColor ) { } inline AmbientLightingGL::~AmbientLightingGL() { delete mColor; } inline void AmbientLightingGL::getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ) { outColor->r = mColor->r; outColor->g = mColor->g; outColor->b = mColor->b; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/SingleTextureGL.cpp0000640000175000017500000001533211363364665026121 0ustar pabspabs/* * Modification History * * 2005-February-21 Jason Rohrer * Created. * * 2005-August-24 Jason Rohrer * Added control over texture wrapping. * * 2009-September-9 Jason Rohrer * Added direct RGBA construction. * * 2009-November-5 Jason Rohrer * Added texture data replacement. * * 2010-April-20 Jason Rohrer * Reload all SingleTextures after GL context change. */ #include "SingleTextureGL.h" SimpleVector SingleTextureGL::sAllLoadedTextures; void SingleTextureGL::contextChanged() { // tell all textures to reload int numTextures = sAllLoadedTextures.size(); printf( "Reloading %d textures due to OpenGL context change\n", numTextures ); for( int i=0; ireloadFromBackup(); } } void SingleTextureGL::reloadFromBackup() { if( mRGBABackup != NULL ) { glGenTextures( 1, &mTextureID ); int error = glGetError(); if( error != GL_NO_ERROR ) { // error printf( "Error generating new texture ID, error = %d, \"%s\"\n", error, glGetString( error ) ); } setTextureData( mRGBABackup, mWidthBackup, mHeightBackup ); } } SingleTextureGL::SingleTextureGL( Image *inImage, char inRepeat ) : mRepeat( inRepeat ), mRGBABackup( NULL ) { glGenTextures( 1, &mTextureID ); int error = glGetError(); if( error != GL_NO_ERROR ) { // error printf( "Error generating new texture ID, error = %d, \"%s\"\n", error, glGetString( error ) ); } setTextureData( inImage ); sAllLoadedTextures.push_back( this ); } SingleTextureGL::SingleTextureGL( unsigned char *inRGBA, unsigned int inWidth, unsigned int inHeight, char inRepeat ) : mRepeat( inRepeat ), mRGBABackup( NULL ) { glGenTextures( 1, &mTextureID ); int error = glGetError(); if( error != GL_NO_ERROR ) { // error printf( "Error generating new texture ID, error = %d, \"%s\"\n", error, glGetString( error ) ); } setTextureData( inRGBA, inWidth, inHeight ); sAllLoadedTextures.push_back( this ); } SingleTextureGL::~SingleTextureGL() { sAllLoadedTextures.deleteElementEqualTo( this ); glDeleteTextures( 1, &mTextureID ); if( mRGBABackup != NULL ) { delete [] mRGBABackup; mRGBABackup = NULL; } } void SingleTextureGL::setTextureData( Image *inImage ) { // first, convert our image to an RGBAImage RGBAImage *rgbaImage = new RGBAImage( inImage ); if( inImage->getNumChannels() < 4 ) { // we should fill in 1.0 for the alpha channel // since the input image doesn't have an alpha channel double *channel = rgbaImage->getChannel( 3 ); int numPixels = inImage->getWidth() * inImage->getHeight(); for( int i=0; igetRGBABytes(); setTextureData( textureData, rgbaImage->getWidth(), rgbaImage->getHeight() ); delete rgbaImage; delete [] textureData; } void SingleTextureGL::setTextureData( unsigned char *inRGBA, unsigned int inWidth, unsigned int inHeight ) { if( inRGBA != mRGBABackup ) { // clear old backup if( mRGBABackup != NULL ) { delete [] mRGBABackup; mRGBABackup = NULL; } mRGBABackup = new unsigned char[ inWidth * inHeight * 4 ]; memcpy( mRGBABackup, inRGBA, inWidth * inHeight * 4 ); mWidthBackup = inWidth; mHeightBackup = inHeight; } int error; GLenum texFormat = GL_RGBA; glBindTexture( GL_TEXTURE_2D, mTextureID ); error = glGetError(); if( error != GL_NO_ERROR ) { // error printf( "Error binding to texture id %d, error = %d\n", (int)mTextureID, error ); } glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); if( mRepeat ) { glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); } else { glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); } glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glTexImage2D( GL_TEXTURE_2D, 0, texFormat, inWidth, inHeight, 0, texFormat, GL_UNSIGNED_BYTE, inRGBA ); error = glGetError(); if( error != GL_NO_ERROR ) { // error printf( "Error setting texture data for id %d, error = %d, \"%s\"\n", (int)mTextureID, error, glGetString( error ) ); printf( "Perhaps texture image width or height is not a power of 2\n" "Width = %u, Height = %u\n", inWidth, inHeight ); } } void SingleTextureGL::replaceTextureData( unsigned char *inRGBA, unsigned int inWidth, unsigned int inHeight ) { if( inRGBA != mRGBABackup ) { // clear old backup if( mRGBABackup != NULL ) { delete [] mRGBABackup; mRGBABackup = NULL; } mRGBABackup = new unsigned char[ inWidth * inHeight * 4]; memcpy( mRGBABackup, inRGBA, inWidth * inHeight * 4 ); mWidthBackup = inWidth; mHeightBackup = inHeight; } int error; GLenum texFormat = GL_RGBA; glBindTexture( GL_TEXTURE_2D, mTextureID ); error = glGetError(); if( error != GL_NO_ERROR ) { // error printf( "Error binding to texture id %d, error = %d\n", (int)mTextureID, error ); } glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, // offset inWidth, inHeight, texFormat, GL_UNSIGNED_BYTE, inRGBA ); error = glGetError(); if( error != GL_NO_ERROR ) { // error printf( "Error replacing texture data for id %d, error = %d, \"%s\"\n", (int)mTextureID, error, glGetString( error ) ); printf( "Perhaps texture image width or height is not a power of 2\n" "Width = %u, Height = %u\n", inWidth, inHeight ); } } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/MouseHandlerGL.h0000640000175000017500000000265711356745745025364 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2010-April-6 Jason Rohrer * Blocked event passing to handlers that were added by event. */ #ifndef MOUSE_HANDLER_GL_INCLUDED #define MOUSE_HANDLER_GL_INCLUDED /** * Interface for an object that can field OpenGL mouse events. * * @author Jason Rohrer */ class MouseHandlerGL { public: virtual ~MouseHandlerGL() { } /** * Callback function for when mouse moves. * * @param inX x position of mouse. * @param inY y position of mouse. */ virtual void mouseMoved( int inX, int inY ) = 0; /** * Callback function for when mouse moves with button depressed. * * @param inX x position of mouse. * @param inY y position of mouse. */ virtual void mouseDragged( int inX, int inY ) = 0; /** * Callback function for when the mouse button is depressed. * * @param inX x position of mouse. * @param inY y position of mouse. */ virtual void mousePressed( int inX, int inY ) = 0; /** * Callback function for when the mouse button is released. * * @param inX x position of mouse. * @param inY y position of mouse. */ virtual void mouseReleased( int inX, int inY ) = 0; char mHandlerFlagged; protected: MouseHandlerGL() : mHandlerFlagged( false ) { } }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/ScreenGL.h0000640000175000017500000003223311373324322024165 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-January-15 Jason Rohrer * Added compile instructions for Linux to the comments. * * 2001-February-4 Jason Rohrer * Added a function for getting the view angle. * Added support for keyboard up functions. * Added support for redraw listeners. * Added a missing destructor. * * 2001-August-29 Jason Rohrer * Added support for adding and removing mouse, keyboard, and scene handlers. * * 2001-August-30 Jason Rohrer * Fixed some comments. * * 2001-September-15 Jason Rohrer * Added functions for accessing the screen size. * * 2001-October-13 Jason Rohrer * Added a function for applying the view matrix transformation. * * 2001-October-29 Jason Rohrer * Added a private function that checks for focused keyboard handlers. * * 2004-June-11 Jason Rohrer * Added functions for getting/setting view position. * * 2004-June-14 Jason Rohrer * Added comment about need for glutInit call. * * 2006-December-21 Jason Rohrer * Added functions for changing window size and switching to full screen. * * 2008-September-12 Jason Rohrer * Changed to use glutEnterGameMode for full screen at startup. * Added a 2D graphics mode. * * 2008-September-29 Jason Rohrer * Enabled ignoreKeyRepeat. * * 2008-October-27 Jason Rohrer * Prepared for alternate implementations besides GLUT. * Switched to implementation-independent keycodes. * Added support for setting viewport size separate from screen size. * * 2010-April-20 Jason Rohrer * Alt-Enter to leave fullscreen mode. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #ifndef SCREEN_GL_INCLUDED #define SCREEN_GL_INCLUDED #include "MouseHandlerGL.h" #include "KeyboardHandlerGL.h" #include "SceneHandlerGL.h" #include "RedrawListenerGL.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/util/SimpleVector.h" // prototypes void callbackResize( int inW, int inH ); void callbackKeyboard( unsigned char inKey, int inX, int inY ); void callbackKeyboardUp( unsigned char inKey, int inX, int inY ); void callbackSpecialKeyboard( int inKey, int inX, int inY ); void callbackSpecialKeyboardUp( int inKey, int inX, int inY ); void callbackMotion( int inX, int inY ); void callbackPassiveMotion( int inX, int inY ); void callbackMouse( int inButton, int inState, int inX, int inY ); void callbackDisplay(); void callbackIdle(); /** * Object that handles general initialization of an OpenGL screen. * * @author Jason Rohrer */ class ScreenGL { public: /** * Constructs a ScreenGL. * * GLUT implementation only: * Before calling this constructor, glutInit must be called with * the application's command-line arguments. For example, if * the application's main function looks like: * * int main( int inNumArgs, char **inArgs ) { ... } * * Then you must call glutInit( &inNumArgs, inArgs ) before * constructing a screen. * * SDL implementation: * Must call SDL_Init() with at least SDL_INIT_VIDEO * as a parameter. * * @param inWide width of screen. * @param inHigh height of screen. * @param inFullScreen set to true for full screen mode. * NOTE that full screen mode requires inWide and inHigh to match * an available screen resolution. * @param inWindowName name to be displayed on title bar of window. * @param inKeyHandler object that will receive keyboard events. * NULL specifies no handler (defaults to NULL). * Must be destroyed by caller. * @param inMouseHandler object that will receive mouse events. * NULL specifies no handler (defaults to NULL). * Must be destroyed by caller. * @param inSceneHandler object that will be called to draw * the scene during in response to redraw events. * NULL specifies no handler (defaults to NULL). * Must be destroyed by caller. */ ScreenGL( int inWide, int inHigh, char inFullScreen, const char *inWindowName, KeyboardHandlerGL *inKeyHandler = NULL, MouseHandlerGL *inMouseHandler = NULL, SceneHandlerGL *inSceneHandler = NULL ); /** * Destructor closes and releases the screen. */ ~ScreenGL(); /** * Starts the GLUT main loop. * * Note that this function call never returns. */ void start(); /** * Switches to 2D mode, where no view transforms are applied * * Must be called before start(); */ void switchTo2DMode(); /** * Moves the view position. * * @param inPositionChange directional vector describing motion. * Must be destroyed by caller. */ void moveView( Vector3D *inPositionChange ); /** * Rotates the view. * * @param inOrientationChange angle to rotate view by. * Must be destroyed by caller. */ void rotateView( Angle3D *inOrientationChange ); /** * Gets the angle of the current view direction. * * @return the angle of the current view direction. * Not a copy, so shouldn't be modified or destroyed by caller. */ Angle3D *getViewOrientation(); /** * Gets the current view position. * * @return the position of the current view. * Must be destroyed by caller. */ Vector3D *getViewPosition(); /** * Sets the current view position. * * @param inPosition the new position. * Must be destroyed by caller. */ void setViewPosition( Vector3D *inPosition ); /** * Gets the width of the screen. * * @return the width of the screen, in pixels. */ int getWidth(); /** * Gets the height of the screen. * * @return the height of the screen, in pixels. */ int getHeight(); /** * Switches into full screen mode. * * Use changeWindowSize to switch back out of full screen mode. */ void setFullScreen(); /** * Sets the size of the viewport image in the window. * * Defaults to window size. * * Must be called before screen is started. * * @param inWidth, inHeight the new dimensions, in pixels. */ void setImageSize( int inWidth, int inHeight ); /** * Gets the width of the viewport image. * * @return the width of the viewport, in pixels. */ int getImageWidth(); /** * Gets the height of the viewport image. * * @return the height of the viewport, in pixels. */ int getImageHeight(); /** * Change the window size. * * @param inWidth, inHeight the new dimensions, in pixels. */ void changeWindowSize( int inWidth, int inHeight ); /** * Adds a mouse handler. * * @param inHandler the handler to add Must * be destroyed by caller. * * Must not be called after calling start(). */ void addMouseHandler( MouseHandlerGL *inHandler ); /** * Removes a mouse handler. * * @param inHandler the handler to remove. Must * be destroyed by caller. * * Must not be called after calling start(). */ void removeMouseHandler( MouseHandlerGL *inHandler ); /** * Adds a keyboard handler. * * @param inHandler the handler to add Must * be destroyed by caller. * * Must not be called after calling start(). */ void addKeyboardHandler( KeyboardHandlerGL *inHandler ); /** * Removes a keyboard handler. * * @param inHandler the handler to remove. Must * be destroyed by caller. * * Must not be called after calling start(). */ void removeKeyboardHandler( KeyboardHandlerGL *inHandler ); /** * Adds a scene handler. * * @param inHandler the handler to add Must * be destroyed by caller. * * Must not be called after calling start(). */ void addSceneHandler( SceneHandlerGL *inHandler ); /** * Removes a scene handler. * * @param inHandler the handler to remove. Must * be destroyed by caller. * * Must not be called after calling start(). */ void removeSceneHandler( SceneHandlerGL *inHandler ); /** * Adds a redraw listener. * * @param inListener the listener to add. Must * be destroyed by caller. * * Must not be called after calling start(). */ void addRedrawListener( RedrawListenerGL *inListener ); /** * Removes a redraw listener. * * @param inListener the listener to remove. Must * be destroyed by caller. * * Must not be called after calling start(). */ void removeRedrawListener( RedrawListenerGL *inListener ); /** * Applies the current view matrix transformation * to the matrix at the top of the GL_PROJECTION stack. */ void applyViewTransform(); /** * Access the various handlers. */ //KeyboardHandlerGL *getKeyHandler(); //MouseHandlerGL *getMouseHandler(); //SceneHandlerGL *getSceneHandler(); private : void setupSurface(); // used by various implementations // callbacks (external C functions that can access private members) friend void callbackResize( int inW, int inH ); friend void callbackKeyboard( unsigned char inKey, int inX, int inY ); friend void callbackKeyboardUp( unsigned char inKey, int inX, int inY ); friend void callbackSpecialKeyboard( int inKey, int inX, int inY ); friend void callbackSpecialKeyboardUp( int inKey, int inX, int inY ); friend void callbackMotion( int inX, int inY ); friend void callbackPassiveMotion( int inX, int inY ); friend void callbackMouse( int inButton, int inState, int inX, int inY ); friend void callbackDisplay(); friend void callbackIdle(); // our private members int mWide; int mHigh; // for an viewport image that can be smaller than our screen char mImageSizeSet; int mImageWide; int mImageHigh; char mFullScreen; // only allow ALT-Enter to toggle fullscreen if it started there char mStartedFullScreen; char m2DMode; Vector3D *mViewPosition; // orientation of 0,0,0 means looking in the direction (0,0,1) // with an up direction of (0,1,0) Angle3D *mViewOrientation; // vectors of handlers and listeners SimpleVector *mMouseHandlerVector; SimpleVector *mKeyboardHandlerVector; SimpleVector *mSceneHandlerVector; SimpleVector *mRedrawListenerVector; /** * Gets whether at least one of our keyboard handlers is focused. * * @return true iff at least one keyboard handler is focused. */ char isKeyboardHandlerFocused(); }; inline void ScreenGL::moveView( Vector3D *inPositionChange ) { mViewPosition->add( inPositionChange ); } inline void ScreenGL::rotateView( Angle3D *inOrientationChange ) { mViewOrientation->add( inOrientationChange ); } inline Angle3D *ScreenGL::getViewOrientation() { return mViewOrientation; } inline Vector3D *ScreenGL::getViewPosition() { return new Vector3D( mViewPosition ); } inline void ScreenGL::setViewPosition( Vector3D *inPosition ) { delete mViewPosition; mViewPosition = new Vector3D( inPosition ); } inline int ScreenGL::getWidth() { return mWide; } inline int ScreenGL::getHeight() { return mHigh; } inline void ScreenGL::setImageSize( int inWidth, int inHeight ) { mImageSizeSet = true; mImageWide = inWidth; mImageHigh = inHeight; } inline int ScreenGL::getImageWidth() { return mImageWide; } inline int ScreenGL::getImageHeight() { return mImageHigh; } inline void ScreenGL::addRedrawListener( RedrawListenerGL *inListener ) { mRedrawListenerVector->push_back( inListener ); } inline void ScreenGL::removeRedrawListener( RedrawListenerGL *inListener ) { mRedrawListenerVector->deleteElementEqualTo( inListener ); } inline void ScreenGL::addMouseHandler( MouseHandlerGL *inListener ) { mMouseHandlerVector->push_back( inListener ); } inline void ScreenGL::removeMouseHandler( MouseHandlerGL *inListener ) { mMouseHandlerVector->deleteElementEqualTo( inListener ); } inline void ScreenGL::addKeyboardHandler( KeyboardHandlerGL *inListener ) { mKeyboardHandlerVector->push_back( inListener ); } inline void ScreenGL::removeKeyboardHandler( KeyboardHandlerGL *inListener ) { mKeyboardHandlerVector->deleteElementEqualTo( inListener ); } inline void ScreenGL::addSceneHandler( SceneHandlerGL *inListener ) { mSceneHandlerVector->push_back( inListener ); } inline void ScreenGL::removeSceneHandler( SceneHandlerGL *inListener ) { mSceneHandlerVector->deleteElementEqualTo( inListener ); } inline char ScreenGL::isKeyboardHandlerFocused() { for( int h=0; hsize(); h++ ) { KeyboardHandlerGL *handler = *( mKeyboardHandlerVector->getElement( h ) ); if( handler->isFocused() ) { return true; } } // else none were focused return false; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/testNavigatorGL.cpp0000640000175000017500000000102707343255271026140 0ustar pabspabs/* * Modification History * * 2001-August-29 Jason Rohrer * Created. */ #include "TestSceneHandlerGL.h" #include "SceneNavigatorDisplayGL.h" #include "minorGems/util/random/StdRandomSource.h" // simple test function int main() { StdRandomSource *randSource = new StdRandomSource( 2 ); TestSceneHandlerGL *handler = new TestSceneHandlerGL(); char *name = "test window"; SceneNavigatorDisplayGL *screen = new SceneNavigatorDisplayGL( 200, 200, false, name, handler ); screen->start(); return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/SceneNavigatorDisplayGL.h0000640000175000017500000002046107673013511027207 0ustar pabspabs/* * Modification History * * 2001-August-29 Jason Rohrer * Created. * * 2001-August-30 Jason Rohrer * Fixed some comments. * * 2001-October-29 Jason Rohrer * Changed to move and rotate view at a fixed * time rate, regardless of framerate. * * 2003-June-14 Jason Rohrer * Fixed namespace for exit call. * Added M_PI backup definition. */ #ifndef SCENE_NAVIGATOR_DISPLAY_GL_INCLUDED #define SCENE_NAVIGATOR_DISPLAY_GL_INCLUDED #include "ScreenGL.h" #include "MouseHandlerGL.h" #include "KeyboardHandlerGL.h" #include "SceneHandlerGL.h" #include "RedrawListenerGL.h" #include "minorGems/system/Time.h" #include #include // make sure M_PI is defined #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /** * Subclass of ScreenGL that handles general-purpose 3D scene navigation * rendered through OpenGL. * * Motion is unlimited, though forward-backward motion is confinded * to the x-z plane (the user can move up and down explicitly, however). * * To constrict motion, subclass SceneNavigatorDisplayGL and override * the moveView() function (a member of ScreenGL). * * Compile note: For Linux, add these library flags: * -lGL -lglut -lGLU -L/usr/X11R6/lib * Be sure to include ScreenGL.cpp in the file list passed to the compiler. * * @author Jason Rohrer */ class SceneNavigatorDisplayGL : public ScreenGL, public MouseHandlerGL, public KeyboardHandlerGL, public RedrawListenerGL { public: /** * Constructs a navigator. * * @param inWide the width of the screen in pixels. * @param inHigh the height of the screen in pixels. * @param inFullScreen true iff the display should * fill the screen. * @param inWindowName the name of the display window. * @param inSceenHandler the handler responsible for * drawing the scene. * Must be destroyed by caller. * @param inMoveUnitsPerSecond the number of world * units to move per second when the user makes a move. * Defaults to 1.0 units per second. * @param inRotateRadiansPerSecond the number of * radians to rotate per second when the user makes a rotation. * Defaults to 1.0 radians per second. */ SceneNavigatorDisplayGL( int inWide, int inHigh, char inFullScreen, char *inWindowName, SceneHandlerGL *inSceneHandler, double inMoveUnitsPerSecond = 1.0, double inRotateRadiansPerSecond = 1.0 ); ~SceneNavigatorDisplayGL(); // implements the MouseHandlerGL interface virtual void mouseMoved( int inX, int inY ); virtual void mouseDragged( int inX, int inY ); virtual void mousePressed( int inX, int inY ); virtual void mouseReleased( int inX, int inY ); // implements the KeyboardHandlerGL interface virtual void keyPressed( unsigned char inKey, int inX, int inY ); virtual void specialKeyPressed( int inKey, int inX, int inY ); virtual void keyReleased( unsigned char inKey, int inX, int inY ); virtual void specialKeyReleased( int inKey, int inX, int inY ); // implements the RedrawListener interface virtual void fireRedraw(); private : int mLastMouseX, mLastMouseY; // amount to move and rotate view during each redraw Vector3D *mMoveDirection; Angle3D *mRotationOffset; // keep y movement separate from view direction. double mYMovement; // true if a moving key is currently depressed char mMoving; // true if a rotation key is currently depressed char mRotating; unsigned long mTimeLastRedrawS; unsigned long mTimeLastRedrawMS; double mMoveUnitsPerSecond; double mRotateRadiansPerSecond; }; inline SceneNavigatorDisplayGL::SceneNavigatorDisplayGL( int inWide, int inHigh, char inFullScreen, char *inWindowName, SceneHandlerGL *inSceneHandler, double inMoveUnitsPerSecond, double inRotateRadiansPerSecond ) : ScreenGL( inWide, inHigh, inFullScreen, inWindowName, NULL, NULL, inSceneHandler ), mMoveDirection( new Vector3D( 0, 0, 0 ) ), mRotationOffset( new Angle3D( 0, 0, 0 ) ), mMoving( false ), mRotating( false ), mYMovement( 0 ), mMoveUnitsPerSecond( inMoveUnitsPerSecond ), mRotateRadiansPerSecond( inRotateRadiansPerSecond ) { // add ourself to the superclass' listener lists addMouseHandler( this ); addKeyboardHandler( this ); addRedrawListener( this ); Time::getCurrentTime( &mTimeLastRedrawS, &mTimeLastRedrawMS ); } inline void SceneNavigatorDisplayGL::mouseMoved( int inX, int inY ) { //printf( "mouse moved to (%d, %d)\n", inX, inY ); } inline void SceneNavigatorDisplayGL::mouseDragged( int inX, int inY ) { int delX = inX - mLastMouseX; int delY = inY - mLastMouseY; Angle3D *rotAngle = new Angle3D( delY / 50, delX/50, 0 ); //mScreen->rotateView( rotAngle ); delete rotAngle; mLastMouseX = inX; mLastMouseY = inY; } inline void SceneNavigatorDisplayGL::mousePressed( int inX, int inY ) { } inline void SceneNavigatorDisplayGL::mouseReleased( int inX, int inY ) { } inline void SceneNavigatorDisplayGL::keyPressed( unsigned char inKey, int inX, int inY ) { if( inKey == 'y' || inKey == 'Y' ) { // turn view downwards mRotationOffset->mX = M_PI/20; } else if( inKey == 'h' || inKey == 'H' ) { // turn view upwards mRotationOffset->mX = -M_PI/20; } if( inKey == 's' || inKey == 'S' ) { // turn to the left mRotationOffset->mY = M_PI/20; } else if( inKey == 'f' || inKey == 'F' ) { // turn to the right mRotationOffset->mY = -M_PI/20; } if( inKey == 'i' || inKey == 'I' ) { // move upwards mYMovement = 1; } else if( inKey == 'k' || inKey == 'K' ) { // move downwards mYMovement = -1; } if( inKey == 'j' || inKey == 'J' ) { // strafe to the left mMoveDirection->mX = 1; } else if( inKey == 'l' || inKey == 'L' ) { // strafe to the right mMoveDirection->mX = -1; } if( inKey == 'e' || inKey == 'E' ) { // move forward mMoveDirection->mZ = 1; } else if( inKey == 'd' || inKey == 'D' ) { // move backward mMoveDirection->mZ = -1; } else if( inKey == 'q' || inKey == 'Q' ) { // quit ::exit( 0 ); } } inline void SceneNavigatorDisplayGL::keyReleased( unsigned char inKey, int inX, int inY ) { if( inKey == 'e' || inKey == 'E' ) { mMoveDirection->mZ = 0; } else if( inKey == 'd' || inKey == 'D' ) { mMoveDirection->mZ = 0; } if( inKey == 'j' || inKey == 'J' ) { mMoveDirection->mX = 0; } else if( inKey == 'l' || inKey == 'L' ) { mMoveDirection->mX = 0; } if( inKey == 'i' || inKey == 'I' ) { mYMovement = 0; } else if( inKey == 'k' || inKey == 'K' ) { mYMovement = 0; } if( inKey == 's' || inKey == 'S' ) { mRotationOffset->mY = 0; } else if( inKey == 'f' || inKey == 'F' ) { mRotationOffset->mY = 0; } if( inKey == 'y' || inKey == 'Y' ) { mRotationOffset->mX = 0; } else if( inKey == 'h' || inKey == 'H' ) { mRotationOffset->mX = 0; } } inline void SceneNavigatorDisplayGL::specialKeyPressed( int inKey, int inX, int inY ) { } inline void SceneNavigatorDisplayGL::specialKeyReleased( int inKey, int inX, int inY ) { } inline void SceneNavigatorDisplayGL::fireRedraw() { unsigned long currentTimeS; unsigned long currentTimeMS; Time::getCurrentTime( ¤tTimeS, ¤tTimeMS ); unsigned long deltaMS = Time::getMillisecondsSince( mTimeLastRedrawS, mTimeLastRedrawMS ); mTimeLastRedrawS = currentTimeS; mTimeLastRedrawMS = currentTimeMS; // compute the number of units to move to maintain // a constant speed of mUnitsPerSecond double unitsToMove = mMoveUnitsPerSecond * ( deltaMS / 1000.0 ); double radiansToRotate = mRotateRadiansPerSecond * ( deltaMS / 1000.0 ); // scale our rotation offset Angle3D *scaledRotationOffset = new Angle3D( mRotationOffset ); scaledRotationOffset->scale( radiansToRotate ); // rotate the view in the super class rotateView( scaledRotationOffset ); delete scaledRotationOffset; Vector3D *move = new Vector3D( mMoveDirection ); // scale our move direction move->scale( unitsToMove ); Angle3D *rotation = new Angle3D( getViewOrientation() ); // only rotate movement direction about y axis // we don't want view y direction to affect the direction of movement rotation->mX = 0; rotation->mZ = 0; move->rotate( rotation ); delete rotation; // now add in y movement, scaled move->mY = mYMovement * unitsToMove; // move the view in the superclass moveView( move ); delete move; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/SingleTextureGL.h0000640000175000017500000001117011363364665025562 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * * 2005-February-21 Jason Rohrer * Added comments about channel ordering. * Created a cpp file for static init and longer function definitions. * * 2005-August-24 Jason Rohrer * Added control over texture wrapping. * * 2006-September-20 Jason Rohrer * Changed type of mTextureID to avoid compile warnings on MacOSX. * * 2009-September-9 Jason Rohrer * Added direct RGBA construction. * * 2009-November-5 Jason Rohrer * Added texture data replacement. * * 2010-April-20 Jason Rohrer * Reload all SingleTextures after GL context change. */ #ifndef SINGLE_TEXTURE_GL_INCLUDED #define SINGLE_TEXTURE_GL_INCLUDED #include #include #include #include "minorGems/graphics/Image.h" #include "minorGems/graphics/RGBAImage.h" #include "minorGems/util/SimpleVector.h" /** * A single-layer OpenGL 32-bit texture map. * * This is essentially a simplified version of TextureGL. * * @author Jason Rohrer */ class SingleTextureGL { public: /** * Constructs a texture map, specifying texture data. * * Note that this constructor can only be called * after a GL context has been setup. * * @param inImage the image to use as a source * for texture data. The image should have * 4 channels. Any extra channels are ignored, * and black is set into additional texture * channels if inImage has too few channels. * Must be destroyed by caller, and can be * destroyed as soon as this constructor returns. * Channels are ordered as RGBA. * Must be destroyed by caller. * @param inRepeat true to repeat (tile) texture. * Defaults to true. * * Note that width and height must each be powers of 2 and * not larger than 256 (for 3DFX compatability). Additionally, * for 3DFX compatability, the aspect ratio should not exceed 8:1. */ SingleTextureGL( Image *inImage, char inRepeat = true ); /** * Specifies texture data as rgba bytes. */ SingleTextureGL( unsigned char *inRGBA, unsigned int inWidth, unsigned int inHeight, char inRepeat = true ); /** * The OpenGL texture is deleted when the SingleTextureGL object is * destroyed. */ ~SingleTextureGL(); /** * Replaces data in a texture that's already been set. * Dimensions must match original dimensions. */ void replaceTextureData( unsigned char *inRGBA, unsigned int inWidth, unsigned int inHeight ); /** * Sets the data for this texture. * * @param inImage the image to use as a source * for texture data. The image should have * 4 channels. Any extra channels are ignored, * and black is set into additional texture * channels if inImage has too few channels. * Must be destroyed by caller, and can be * destroyed as soon as this constructor returns. * Channels are ordered as RGBA. * Must be destroyed by caller. * * Note that width and height must each be powers of 2 and * not larger than 256 (for 3DFX compatability). Additionally, * for 3DFX compatability, the aspect ratio should not exceed 8:1. */ void setTextureData( Image *inImage ); /** * Sets texture data as rgba bytes. */ void setTextureData( unsigned char *inRGBA, unsigned int inWidth, unsigned int inHeight ); /** * Enables this texture. */ void enable(); /** * Disables this texture and all of its layers. */ void disable(); // tell all textures about a GL context change so they can reload // int texture memory static void contextChanged(); private: char mRepeat; GLuint mTextureID; // in case of context switch where we need to reload our texture unsigned char *mRGBABackup; unsigned int mWidthBackup; unsigned int mHeightBackup; void reloadFromBackup(); static SimpleVector sAllLoadedTextures; }; inline void SingleTextureGL::enable() { glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, mTextureID ); int error = glGetError(); if( error != GL_NO_ERROR ) { // error printf( "Error binding texture id %d, error = %d\n", (int)mTextureID, error ); } } inline void SingleTextureGL::disable() { glDisable( GL_TEXTURE_2D ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/ScreenGL_SDL.cpp0000640000175000017500000007260011375561763025242 0ustar pabspabs/* * Modification History * * 2008-October-27 Jason Rohrer * Created. Copied structure from GLUT version (ScreenGL.cpp). * * 2009-March-19 Jason Rohrer * UNICODE support for keyboard input of symbols. * * 2010-March-17 Jason Rohrer * Support for Ctrl, Alt, and Meta. * * 2010-April-6 Jason Rohrer * Blocked event passing to handlers that were added by event. * * 2010-April-8 Jason Rohrer * Added post-redraw events. * Don't treat wheel events as mouse clicks. * * 2010-April-9 Jason Rohrer * Checks for closest matching resolution. * * 2010-April-20 Jason Rohrer * Alt-Enter to leave fullscreen mode. * Reload all SingleTextures after GL context change. * * 2010-May-7 Jason Rohrer * Mapped window close button to ESC key. * Extended ASCII. * * 2010-May-12 Jason Rohrer * Use full 8 lower bits of unicode to support extended ASCII. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. * * 2010-May-19 Jason Rohrer * Added support for 1-bit stencil buffer. * * 2010-May-21 Jason Rohrer * Mapped ctrl-q and alt-q to ESC key. */ #include "ScreenGL.h" #include "SingleTextureGL.h" #include #include #include #include #include #include #include "minorGems/util/stringUtils.h" #include "minorGems/util/log/AppLog.h" /* ScreenGL to be accessed by callback functions. * * Note that this is a bit of a hack, but the callbacks * require a C-function (not a C++ member) and have fixed signatures, * so there's no way to pass the current screen into the functions. * * This hack prevents multiple instances of the ScreenGL class from * being used simultaneously. * * Even worse for SLD, because this hack is carried over from GLUT. * SDL doesn't even require C callbacks (you provide the event loop). */ ScreenGL *currentScreenGL; // maps SDL-specific special (non-ASCII) key-codes (SDLK) to minorGems key // codes (MG_KEY) int mapSDLSpecialKeyToMG( int inSDLKey ); // for ascii key char mapSDLKeyToASCII( int inSDLKey ); // prototypes /* void callbackResize( int inW, int inH ); void callbackKeyboard( unsigned char inKey, int inX, int inY ); void callbackMotion( int inX, int inY ); void callbackPassiveMotion( int inX, int inY ); void callbackMouse( int inButton, int inState, int inX, int inY ); void callbackDisplay(); void callbackIdle(); */ ScreenGL::ScreenGL( int inWide, int inHigh, char inFullScreen, const char *inWindowName, KeyboardHandlerGL *inKeyHandler, MouseHandlerGL *inMouseHandler, SceneHandlerGL *inSceneHandler ) : mWide( inWide ), mHigh( inHigh ), mImageWide( inWide ), mImageHigh( inHigh ), mFullScreen( inFullScreen ), m2DMode( false ), mViewPosition( new Vector3D( 0, 0, 0 ) ), mViewOrientation( new Angle3D( 0, 0, 0 ) ), mMouseHandlerVector( new SimpleVector() ), mKeyboardHandlerVector( new SimpleVector() ), mSceneHandlerVector( new SimpleVector() ), mRedrawListenerVector( new SimpleVector() ) { // add handlers if NULL (the default) was not passed in for them if( inMouseHandler != NULL ) { addMouseHandler( inMouseHandler ); } if( inKeyHandler != NULL ) { addKeyboardHandler( inKeyHandler ); } if( inSceneHandler != NULL ) { addSceneHandler( inSceneHandler ); } mStartedFullScreen = mFullScreen; setupSurface(); SDL_WM_SetCaption( inWindowName, NULL ); // turn off repeat SDL_EnableKeyRepeat( 0, 0 ); SDL_EnableUNICODE( true ); } ScreenGL::~ScreenGL() { delete mViewPosition; delete mViewOrientation; delete mRedrawListenerVector; delete mMouseHandlerVector; delete mKeyboardHandlerVector; delete mSceneHandlerVector; } char screenGLStencilBufferSupported = false; void ScreenGL::setupSurface() { SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); int flags = SDL_OPENGL; if( mFullScreen ) { flags = flags | SDL_FULLSCREEN; } // check for available modes SDL_Rect** modes; AppLog::getLog()->logPrintf( Log::INFO_LEVEL, "Checking if requested video mode (%dx%d) is available", mWide, mHigh ); // Get available fullscreen/hardware modes modes = SDL_ListModes( NULL, flags); // Check if there are any modes available if( modes == NULL ) { AppLog::criticalError( "ERROR: No video modes available"); exit(-1); } // Check if our resolution is restricted if( modes == (SDL_Rect**)-1 ) { AppLog::info( "All resolutions available" ); } else{ // Print valid modes char match = false; AppLog::info( "Available video modes:" ); for( int i=0; modes[i]; ++i ) { AppLog::getLog()->logPrintf( Log::DETAIL_LEVEL, " %d x %d\n", modes[i]->w, modes[i]->h ); if( mWide == modes[i]->w && mHigh == modes[i]->h ) { AppLog::info( " ^^^^ this mode matches requested mode" ); match = true; } } if( !match ) { AppLog::warning( "Warning: No match for requested video mode" ); AppLog::info( "Trying to find the closest match" ); int bestDistance = 99999999; int bestIndex = -1; for( int i=0; modes[i]; ++i ) { int distance = (int)( fabs( modes[i]->w - mWide ) + fabs( modes[i]->h - mHigh ) ); if( distance < bestDistance ) { bestIndex = i; bestDistance = distance; } } AppLog::getLog()->logPrintf( Log::INFO_LEVEL, "Picking closest available resolution, %d x %d\n", modes[bestIndex]->w, modes[bestIndex]->h ); mWide = modes[bestIndex]->w; mHigh = modes[bestIndex]->h; } } // 1-bit stencil buffer SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 1 ); // current color depth SDL_Surface *screen = SDL_SetVideoMode( mWide, mHigh, 0, flags); if ( screen == NULL ) { printf( "Couldn't set %dx%d GL video mode: %s\n", mWide, mHigh, SDL_GetError() ); } int setStencilSize; SDL_GL_GetAttribute( SDL_GL_STENCIL_SIZE, &setStencilSize ); if( setStencilSize > 0 ) { // we have a stencil buffer screenGLStencilBufferSupported = true; } glEnable( GL_DEPTH_TEST ); glEnable( GL_CULL_FACE ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glCullFace( GL_BACK ); glFrontFace( GL_CCW ); } void ScreenGL::start() { currentScreenGL = this; // call our resize callback (GLUT used to do this for us when the // window was created) callbackResize( mWide, mHigh ); // main loop while( true ) { callbackDisplay(); // handle all pending events SDL_Event event; while( SDL_PollEvent( &event ) ) { SDLMod mods = SDL_GetModState(); // alt-enter, toggle fullscreen (but only if we started there, // to prevent window content centering issues due to mWidth and // mHeight changes mid-game) if( mStartedFullScreen && event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_RETURN && ( ( mods & KMOD_META ) || ( mods & KMOD_ALT ) ) ) { printf( "Toggling fullscreen\n" ); mFullScreen = !mFullScreen; setupSurface(); callbackResize( mWide, mHigh ); // reload all textures into OpenGL SingleTextureGL::contextChanged(); } // map CTRL-q to ESC // 17 is "DC1" which is ctrl-z on some platforms else if( event.type == SDL_KEYDOWN && ( ( event.key.keysym.sym == SDLK_q && ( ( mods & KMOD_META ) || ( mods & KMOD_ALT ) || ( mods & KMOD_CTRL ) ) ) || event.key.keysym.unicode & 0xFF == 17 ) ) { // map to 27, escape int mouseX, mouseY; SDL_GetMouseState( &mouseX, &mouseY ); callbackKeyboard( 27, mouseX, mouseY ); } else { switch( event.type ) { case SDL_QUIT: { // map to 27, escape int mouseX, mouseY; SDL_GetMouseState( &mouseX, &mouseY ); callbackKeyboard( 27, mouseX, mouseY ); } break; case SDL_KEYDOWN: case SDL_KEYUP: { int mouseX, mouseY; SDL_GetMouseState( &mouseX, &mouseY ); // check if special key int mgKey = mapSDLSpecialKeyToMG( event.key.keysym.sym ); if( mgKey != 0 ) { if( event.type == SDL_KEYDOWN ) { callbackSpecialKeyboard( mgKey, mouseX, mouseY ); } else { callbackSpecialKeyboardUp( mgKey, mouseX, mouseY ); } } else { unsigned char asciiKey; // try unicode first, if 8-bit clean (extended ASCII) if( ( event.key.keysym.unicode & 0xFF00 ) == 0 ) { asciiKey = event.key.keysym.unicode & 0xFF; } else { // else unicode-to-ascii failed // fall back asciiKey = mapSDLKeyToASCII( event.key.keysym.sym ); } if( asciiKey != 0 ) { // shift and caps cancel each other if( ( ( event.key.keysym.mod & KMOD_SHIFT ) && !( event.key.keysym.mod & KMOD_CAPS ) ) || ( !( event.key.keysym.mod & KMOD_SHIFT ) && ( event.key.keysym.mod & KMOD_CAPS ) ) ) { asciiKey = toupper( asciiKey ); } if( event.type == SDL_KEYDOWN ) { callbackKeyboard( asciiKey, mouseX, mouseY ); } else { callbackKeyboardUp( asciiKey, mouseX, mouseY ); } } } } break; case SDL_MOUSEMOTION: if( event.motion.state & SDL_BUTTON( 1 ) || event.motion.state & SDL_BUTTON( 2 ) || event.motion.state & SDL_BUTTON( 3 ) ) { callbackMotion( event.motion.x, event.motion.y ); } else { callbackPassiveMotion( event.motion.x, event.motion.y ); } break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: callbackMouse( event.button.button, event.button.state, event.button.x, event.button.y ); break; } } } } } void ScreenGL::switchTo2DMode() { m2DMode = true; } void ScreenGL::setFullScreen() { //glutFullScreen(); } void ScreenGL::changeWindowSize( int inWidth, int inHeight ) { //glutReshapeWindow( inWidth, inHeight ); } void ScreenGL::applyViewTransform() { // compute view angle // default angle is 90, but we want to force a 1:1 aspect ratio to avoid // distortion. // If our screen's width is different than its height, we need to decrease // the view angle so that the angle coresponds to the smaller dimension. // This keeps the zoom factor constant in the smaller dimension. // Of course, because of the way perspective works, only one Z-slice // will have a constant zoom factor // The zSlice variable sets the distance of this slice from the picture // plane double zSlice = .31; double maxDimension = mWide; if( mHigh > mWide ) { maxDimension = mHigh; } double aspectDifference = fabs( mWide / 2 - mHigh / 2 ) / maxDimension; // default angle is 90 degrees... half the angle is PI/4 double angle = atan( tan( M_PI / 4 ) + aspectDifference / zSlice ); // double it to get the full angle angle *= 2; // convert to degrees angle = 360 * angle / ( 2 * M_PI ); // set up the projection matrix glMatrixMode( GL_PROJECTION ); glLoadIdentity(); //gluPerspective( 90, mWide / mHigh, 1, 9999 ); gluPerspective( angle, 1, 1, 9999 ); // set up the model view matrix glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); // create default view and up vectors, // then rotate them by orientation angle Vector3D *viewDirection = new Vector3D( 0, 0, 1 ); Vector3D *upDirection = new Vector3D( 0, 1, 0 ); viewDirection->rotate( mViewOrientation ); upDirection->rotate( mViewOrientation ); // get a point out in front of us in the view direction viewDirection->add( mViewPosition ); // look at takes a viewer position, // a point to look at, and an up direction gluLookAt( mViewPosition->mX, mViewPosition->mY, mViewPosition->mZ, viewDirection->mX, viewDirection->mY, viewDirection->mZ, upDirection->mX, upDirection->mY, upDirection->mZ ); delete viewDirection; delete upDirection; } void callbackResize( int inW, int inH ) { ScreenGL *s = currentScreenGL; s->mWide = inW; s->mHigh = inH; int bigDimension = s->mImageWide; if( bigDimension < s->mImageHigh ) { bigDimension = s->mImageHigh; } int excessW = s->mWide - bigDimension; int excessH = s->mHigh - bigDimension; // viewport is square of biggest image dimension, centered on screen glViewport( excessW / 2, excessH / 2, bigDimension, bigDimension ); } void callbackKeyboard( unsigned char inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); int h; // flag those that exist right now // because handlers might remove themselves or add new handlers, // and we don't want to fire to those that weren't present when // callback was called for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); handler->mHandlerFlagged = true; } // fire to all handlers for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); if( handler->mHandlerFlagged ) { // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->keyPressed( inKey, inX, inY ); } } } // deflag for next time for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); handler->mHandlerFlagged = false; } } void callbackKeyboardUp( unsigned char inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); int h; // flag those that exist right now // because handlers might remove themselves or add new handlers, // and we don't want to fire to those that weren't present when // callback was called for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); handler->mHandlerFlagged = true; } // fire to all handlers for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); if( handler->mHandlerFlagged ) { // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->keyReleased( inKey, inX, inY ); } } } // deflag for next time for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); handler->mHandlerFlagged = false; } } void callbackSpecialKeyboard( int inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); int h; // flag those that exist right now // because handlers might remove themselves or add new handlers, // and we don't want to fire to those that weren't present when // callback was called for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); handler->mHandlerFlagged = true; } // fire to all handlers for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); if( handler->mHandlerFlagged ) { // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->specialKeyPressed( inKey, inX, inY ); } } } // deflag for next time for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); handler->mHandlerFlagged = false; } } void callbackSpecialKeyboardUp( int inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); int h; // flag those that exist right now // because handlers might remove themselves or add new handlers, // and we don't want to fire to those that weren't present when // callback was called for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); handler->mHandlerFlagged = true; } // fire to all handlers for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); if( handler->mHandlerFlagged ) { // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->specialKeyReleased( inKey, inX, inY ); } } } // deflag for next time for( h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); handler->mHandlerFlagged = false; } } void callbackMotion( int inX, int inY ) { // fire to all handlers int h; // flag those that exist right now // because handlers might remove themselves or add new handlers, // and we don't want to fire to those that weren't present when // callback was called for( h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mHandlerFlagged = true; } for( h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); if( handler->mHandlerFlagged ) { handler->mouseDragged( inX, inY ); } } // deflag for next time for( h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mHandlerFlagged = false; } } void callbackPassiveMotion( int inX, int inY ) { // fire to all handlers int h; // flag those that exist right now // because handlers might remove themselves or add new handlers, // and we don't want to fire to those that weren't present when // callback was called for( h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mHandlerFlagged = true; } for( h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); if( handler->mHandlerFlagged ) { handler->mouseMoved( inX, inY ); } } // deflag for next time for( h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mHandlerFlagged = false; } } void callbackMouse( int inButton, int inState, int inX, int inY ) { // ignore wheel events if( inButton == SDL_BUTTON_WHEELUP || inButton == SDL_BUTTON_WHEELDOWN ) { return; } // fire to all handlers int h; // flag those that exist right now // because handlers might remove themselves or add new handlers, // and we don't want to fire to those that weren't present when // callbackMouse was called for( h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mHandlerFlagged = true; } for( h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); if( handler->mHandlerFlagged ) { handler->mouseMoved( inX, inY ); if( inState == SDL_PRESSED ) { handler->mousePressed( inX, inY ); } else if( inState == SDL_RELEASED ) { handler->mouseReleased( inX, inY ); } else { printf( "Error: Unknown mouse state received from SDL\n" ); } } } // deflag for next time for( h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mHandlerFlagged = false; } } void callbackDisplay() { ScreenGL *s = currentScreenGL; glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // fire to all redraw listeners // do this first so that they can update our view transform // this makes control much more responsive for( int r=0; rmRedrawListenerVector->size(); r++ ) { RedrawListenerGL *listener = *( s->mRedrawListenerVector->getElement( r ) ); listener->fireRedraw(); } if( ! s->m2DMode ) { // apply our view transform s->applyViewTransform(); } // fire to all handlers for( int h=0; hmSceneHandlerVector->size(); h++ ) { SceneHandlerGL *handler = *( currentScreenGL->mSceneHandlerVector->getElement( h ) ); handler->drawScene(); } for( int r=0; rmRedrawListenerVector->size(); r++ ) { RedrawListenerGL *listener = *( s->mRedrawListenerVector->getElement( r ) ); listener->postRedraw(); } SDL_GL_SwapBuffers(); } void callbackIdle() { //glutPostRedisplay(); } int mapSDLSpecialKeyToMG( int inSDLKey ) { switch( inSDLKey ) { case SDLK_F1: return MG_KEY_F1; case SDLK_F2: return MG_KEY_F2; case SDLK_F3: return MG_KEY_F3; case SDLK_F4: return MG_KEY_F4; case SDLK_F5: return MG_KEY_F5; case SDLK_F6: return MG_KEY_F6; case SDLK_F7: return MG_KEY_F7; case SDLK_F8: return MG_KEY_F8; case SDLK_F9: return MG_KEY_F9; case SDLK_F10: return MG_KEY_F10; case SDLK_F11: return MG_KEY_F11; case SDLK_F12: return MG_KEY_F12; case SDLK_LEFT: return MG_KEY_LEFT; case SDLK_UP: return MG_KEY_UP; case SDLK_RIGHT: return MG_KEY_RIGHT; case SDLK_DOWN: return MG_KEY_DOWN; case SDLK_PAGEUP: return MG_KEY_PAGE_UP; case SDLK_PAGEDOWN: return MG_KEY_PAGE_DOWN; case SDLK_HOME: return MG_KEY_HOME; case SDLK_END: return MG_KEY_END; case SDLK_INSERT: return MG_KEY_INSERT; case SDLK_RSHIFT: return MG_KEY_RSHIFT; case SDLK_LSHIFT: return MG_KEY_LSHIFT; case SDLK_RCTRL: return MG_KEY_RCTRL; case SDLK_LCTRL: return MG_KEY_LCTRL; case SDLK_RALT: return MG_KEY_RALT; case SDLK_LALT: return MG_KEY_LALT; case SDLK_RMETA: return MG_KEY_RMETA; case SDLK_LMETA: return MG_KEY_LMETA; default: return 0; } } char mapSDLKeyToASCII( int inSDLKey ) { // map world keys (SDLK_WORLD_X) directly to ASCII if( inSDLKey >= 160 && inSDLKey <=255 ) { return inSDLKey; } switch( inSDLKey ) { case SDLK_UNKNOWN: return 0; case SDLK_BACKSPACE: return 8; case SDLK_TAB: return 9; case SDLK_CLEAR: return 12; case SDLK_RETURN: return 13; case SDLK_PAUSE: return 19; case SDLK_ESCAPE: return 27; case SDLK_SPACE: return ' '; case SDLK_EXCLAIM: return '!'; case SDLK_QUOTEDBL: return '"'; case SDLK_HASH: return '#'; case SDLK_DOLLAR: return '$'; case SDLK_AMPERSAND: return '&'; case SDLK_QUOTE: return '\''; case SDLK_LEFTPAREN: return '('; case SDLK_RIGHTPAREN: return ')'; case SDLK_ASTERISK: return '*'; case SDLK_PLUS: return '+'; case SDLK_COMMA: return ','; case SDLK_MINUS: return '-'; case SDLK_PERIOD: return '.'; case SDLK_SLASH: return '/'; case SDLK_0: return '0'; case SDLK_1: return '1'; case SDLK_2: return '2'; case SDLK_3: return '3'; case SDLK_4: return '4'; case SDLK_5: return '5'; case SDLK_6: return '6'; case SDLK_7: return '7'; case SDLK_8: return '8'; case SDLK_9: return '9'; case SDLK_COLON: return ':'; case SDLK_SEMICOLON: return ';'; case SDLK_LESS: return '<'; case SDLK_EQUALS: return '='; case SDLK_GREATER: return '>'; case SDLK_QUESTION: return '?'; case SDLK_AT: return '@'; case SDLK_LEFTBRACKET: return '['; case SDLK_BACKSLASH: return '\\'; case SDLK_RIGHTBRACKET: return ']'; case SDLK_CARET: return '^'; case SDLK_UNDERSCORE: return '_'; case SDLK_BACKQUOTE: return '`'; case SDLK_a: return 'a'; case SDLK_b: return 'b'; case SDLK_c: return 'c'; case SDLK_d: return 'd'; case SDLK_e: return 'e'; case SDLK_f: return 'f'; case SDLK_g: return 'g'; case SDLK_h: return 'h'; case SDLK_i: return 'i'; case SDLK_j: return 'j'; case SDLK_k: return 'k'; case SDLK_l: return 'l'; case SDLK_m: return 'm'; case SDLK_n: return 'n'; case SDLK_o: return 'o'; case SDLK_p: return 'p'; case SDLK_q: return 'q'; case SDLK_r: return 'r'; case SDLK_s: return 's'; case SDLK_t: return 't'; case SDLK_u: return 'u'; case SDLK_v: return 'v'; case SDLK_w: return 'w'; case SDLK_x: return 'x'; case SDLK_y: return 'y'; case SDLK_z: return 'z'; case SDLK_DELETE: return 127; case SDLK_WORLD_0: default: return 0; } } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/TestSceneHandlerGL.h0000640000175000017500000000275007343452732026153 0ustar pabspabs/* * Modification History * * 2001-August-29 Jason Rohrer * Created. * * 2001-August-30 Jason Rohrer * Added random triangels for testing. */ #ifndef TEST_SCENE_HANDLER_GL_INCLUDED #define TEST_SCENE_HANDLER_GL_INCLUDED #include #include "SceneHandlerGL.h" #include "minorGems/util/random/StdRandomSource.h" /** * A test scene implementation. * * @author Jason Rohrer */ class TestSceneHandlerGL : public SceneHandlerGL { public: // implements the SceneHandlerGL interface virtual void drawScene(); }; void TestSceneHandlerGL::drawScene() { glDisable( GL_TEXTURE_2D ); glDisable( GL_CULL_FACE ); StdRandomSource source( 0 ); glBegin( GL_TRIANGLES ); { for( int i=0; i<10; i++ ) { glColor4f( source.getRandomFloat(), source.getRandomFloat(), source.getRandomFloat(), 1.0 ); glVertex3f( source.getRandomFloat() * 100, source.getRandomFloat() * 100, source.getRandomFloat() * 100 ); glColor4f( source.getRandomFloat(), source.getRandomFloat(), source.getRandomFloat(), 1.0 ); glVertex3f( source.getRandomFloat() * 100, source.getRandomFloat() * 100, source.getRandomFloat() * 100 ); glColor4f( source.getRandomFloat(), source.getRandomFloat(), source.getRandomFloat(), 1.0 ); glVertex3f( source.getRandomFloat() * 100, source.getRandomFloat() * 100, source.getRandomFloat() * 100 ); } } glEnd(); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/ObjectGL.h0000640000175000017500000001172507253777626024204 0ustar pabspabs/* * Modification History * * 2000-December-20 Jason Rohrer * Created. * * 2001-January-6 Jason Rohrer * Added some missing includes. * * 2001-January-16 Jason Rohrer * Changed to use a Transform3D instead of Vectors, Angles, and scales for * each primitive in the object. Changed draw() function to take a Transform3D. * Fixed a memory leak in the draw() function. * * 2001-March-14 Jason Rohrer * Added support for paramatization and temporal animations. */ #ifndef OBJECT_GL_INCLUDED #define OBJECT_GL_INCLUDED #include #include "PrimitiveGL.h" #include "LightingGL.h" #include "minorGems/math/geometry/Transform3D.h" #include "minorGems/graphics/3d/Object3D.h" /** * OpenGL object. * * Comprised of a collection of primitives. * * @author Jason Rohrer */ class ObjectGL { public: /** * Constructs an Object. * * No parameters are copied, so they should not be destroyed * or re-accessed by caller. All are destroyed when the * object is destroyed. * * @param inObject the Object3D to base this GL version on. */ ObjectGL( Object3D *inObject ); ~ObjectGL(); /** * Draws the object. * * @param inTransform the transform to apply to the object before * drawing it. * @param inLighting the lighting to use when drawing the object. */ void draw( Transform3D *inTransform, LightingGL *inLighting ); /** * Gets the number of parameters associated with this object. * * @return the number of parameters. */ virtual int getNumParameters(); /** * Gets the number of animations associated with this object. * * @return the number of animations. */ virtual int getNumAnimations(); /** * Sets a parameter for this object. * * @param inParameterIndex the index of the parameter to set. * If an index for a non-existing parameter is specified, * this call has no effect. * @param inValue the value to set the parameter to, in [-1, 1]. * The default value for all parameters is 0. */ virtual void setParameter( int inParameterIndex, double inValue ); /** * Gets a parameter for this object. * * @param inParameterIndex the index of the parameter to get. * If an index for a non-existing parameter is specified, * 0 is returned. * * @return the value of the parameter, in [-1, 1]. * The default value for all parameters is 0. */ virtual double getParameter( int inParameterIndex ); /** * Steps this object forward in time. * * @param inStepSize the size of the timestep to take. */ virtual void step( double inStepSize ); /** * Starts a temporal animation of this object. * The animation progresses as step() is called repeatedly. * If called for a non-existent animation or for one that is * already running, this function has no effect. */ virtual void startAnimation( int inAnimationIndex ); /** * Stops a temporal animation of this object. If called * for a non-existent animation or for one that is not currently * running, this function has no effect. */ virtual void stopAnimation( int inAnimationIndex ); private: Object3D *mObject; // GL versions of the primitives PrimitiveGL **mPrimitives; }; inline ObjectGL::ObjectGL( Object3D *inObject ) : mObject( inObject ) { mPrimitives = new PrimitiveGL*[ mObject->mNumPrimitives ]; for( int i=0; imNumPrimitives; i++ ) { mPrimitives[i] = new PrimitiveGL( mObject->mPrimitives[i] ); } } inline ObjectGL::~ObjectGL() { for( int i=0; imNumPrimitives; i++ ) { delete mPrimitives[i]; } delete [] mPrimitives; delete mObject; } inline void ObjectGL::draw( Transform3D *inTransform, LightingGL *inLighting ) { for( int i=0; imNumPrimitives; i++ ) { // need to transform the position of primitive within object // copy the transform for this primitive Transform3D *primTransform = new Transform3D( mObject->mTransform[i] ); // add the object transform to the end of it primTransform->transform( inTransform ); mPrimitives[i]->draw( primTransform, inLighting ); delete primTransform; } } // these just pass the function call on to the underlying Object3D inline int ObjectGL::getNumParameters() { return mObject->getNumParameters(); } inline int ObjectGL::getNumAnimations() { return mObject->getNumAnimations(); } inline void ObjectGL::setParameter( int inParameterIndex, double inValue ) { mObject->setParameter( inParameterIndex, inValue ); } inline double ObjectGL::getParameter( int inParameterIndex ) { return mObject->getParameter( inParameterIndex ); } inline void ObjectGL::step( double inStepSize ) { mObject->step( inStepSize ); } inline void ObjectGL::startAnimation( int inAnimationIndex ) { mObject->startAnimation( inAnimationIndex ); } inline void ObjectGL::stopAnimation( int inAnimationIndex ) { mObject->stopAnimation( inAnimationIndex ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/testNavigatorGLcompile0000750000175000017500000000030207343255360026724 0ustar pabspabsg++ -g -o testNavigatorGL -I/usr/X11R6/include -I../../.. -L/usr/X11R6/lib -lGL -lglut -lGLU -lX11 -lXi -lXext -lXmu ScreenGL.cpp testNavigatorGL.cpp ../../../minorGems/io/linux/TypeIOLinux.cpp Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/MultiLightingGL.h0000640000175000017500000000441507220445427025535 0ustar pabspabs/* * Modification History * * 2000-December-20 Jason Rohrer * Created. */ #ifndef MULTI_LIGHTING_GL_INCLUDED #define MULTI_LIGHTING_GL_INCLUDED #include #include "LightingGL.h" #include "minorGems/util/SimpleVector.h" /** * A LightingGL implementation that contains a collection of * LightingGLs. * * @author Jason Rohrer */ class MultiLightingGL : public LightingGL { public: /** * Constructs a MultiLighting. */ MultiLightingGL(); ~MultiLightingGL(); /** * Adds a lighting to this MultiLighting. * * @param inLighting lighting to add. Is not * destroyed when the MultiLighting is destroyed. */ void addLighting( LightingGL *inLighting ); /** * Removes a lighting to this MultiLighting. * * @param inLighting lighting to remove. */ void removeLighting( LightingGL *inLighting ); // implements LightingGL interface void getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ); private: SimpleVector *mLightingVector; }; inline MultiLightingGL::MultiLightingGL() : mLightingVector( new SimpleVector() ) { } inline MultiLightingGL::~MultiLightingGL() { delete mLightingVector; } inline void MultiLightingGL::addLighting( LightingGL *inLighting ) { mLightingVector->push_back( inLighting ); } inline void MultiLightingGL::removeLighting( LightingGL *inLighting ) { mLightingVector->deleteElement( inLighting ); } inline void MultiLightingGL::getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ) { int numLightings = mLightingVector->size(); outColor->r = 0; outColor->g = 0; outColor->b = 0; // sum lighting contributions from each lighting for( int i=0; igetElement( i ) ); thisLighting->getLighting( inPoint, inNormal, tempColor ); outColor->r += tempColor->r; outColor->g += tempColor->g; outColor->b += tempColor->b; delete tempColor; } // clip color components: if( outColor->r > 1.0 ) { outColor->r = 1.0; } if( outColor->g > 1.0 ) { outColor->g = 1.0; } if( outColor->b > 1.0 ) { outColor->b = 1.0; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/DirectionLightingGL.h0000640000175000017500000000331007220445427026354 0ustar pabspabs/* * Modification History * * 2000-December-20 Jason Rohrer * Created. */ #ifndef DIRECTION_LIGHTING_GL_INCLUDED #define DIRECTION_LIGHTING_GL_INCLUDED #include #include "LightingGL.h" /** * A LightingGL implementation with a single point light source * at distance infinity. * * @author Jason Rohrer */ class DirectionLightingGL : public LightingGL { public: /** * Constructs a DirectionLighting. * * @param inColor color of lighting for a surface * that is facing directly into the light source. * Is not copied, so cannot be accessed again by caller. * @param inDirection incoming direction of light source. * Is not copied, so cannot be accessed again by caller. */ DirectionLightingGL( Color *inColor, Vector3D *inDirection ); ~DirectionLightingGL(); // implements LightingGL interface void getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ); private: Color *mColor; // direction pointing *towards* light source Vector3D *mDirection; }; inline DirectionLightingGL::DirectionLightingGL( Color *inColor, Vector3D *inDirection ) : mColor( inColor ), mDirection( inDirection ) { // reverse the passed-in direction mDirection->scale( -1 ); } inline DirectionLightingGL::~DirectionLightingGL() { delete mColor; delete mDirection; } inline void DirectionLightingGL::getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ) { double dot = inNormal->dot( mDirection ); if( dot > 0 ) { outColor->r = mColor->r * dot; outColor->g = mColor->g * dot; outColor->b = mColor->b * dot; } else { outColor->r = 0; outColor->g = 0; outColor->b = 0; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/KeyboardHandlerGL.h0000640000175000017500000001053511356745745026026 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-February-4 Jason Rohrer * Added key release functions. * * 2001-October-29 Jason Rohrer * Added fuction for querying a handler's focus. * * 2008-October-27 Jason Rohrer * Switched to implementation-independent keycodes. * * 2010-March-17 Jason Rohrer * Support for Ctrl, Alt, and Meta. * * 2010-April-6 Jason Rohrer * Blocked event passing to handlers that were added by event. */ #ifndef KEYBOARD_HANDLER_GL_INCLUDED #define KEYBOARD_HANDLER_GL_INCLUDED // special key codes #define MG_KEY_F1 0x0001 #define MG_KEY_F2 0x0002 #define MG_KEY_F3 0x0003 #define MG_KEY_F4 0x0004 #define MG_KEY_F5 0x0005 #define MG_KEY_F6 0x0006 #define MG_KEY_F7 0x0007 #define MG_KEY_F8 0x0008 #define MG_KEY_F9 0x0009 #define MG_KEY_F10 0x000A #define MG_KEY_F11 0x000B #define MG_KEY_F12 0x000C #define MG_KEY_LEFT 0x0064 #define MG_KEY_UP 0x0065 #define MG_KEY_RIGHT 0x0066 #define MG_KEY_DOWN 0x0067 #define MG_KEY_PAGE_UP 0x0068 #define MG_KEY_PAGE_DOWN 0x0069 #define MG_KEY_HOME 0x006A #define MG_KEY_END 0x006B #define MG_KEY_INSERT 0x006C #define MG_KEY_RSHIFT 0x0070 #define MG_KEY_LSHIFT 0x0071 #define MG_KEY_RCTRL 0x0072 #define MG_KEY_LCTRL 0x0073 #define MG_KEY_RALT 0x0074 #define MG_KEY_LALT 0x0075 #define MG_KEY_RMETA 0x0076 #define MG_KEY_LMETA 0x0077 /** * Interface for an object that can field OpenGL keystrokes. * * @author Jason Rohrer */ class KeyboardHandlerGL { public: virtual ~KeyboardHandlerGL() { } /** * Gets whether this handler is focused (in other words, * whether this handler wants to reserve keyboard * events for itself). * * If no registered handler is focused, then all * registered handlers receive keyboard events. However, * if some handlers are focused, then only focused handlers * receive keyboard events. * * Note that in general, handlers should be unfocused. * A default implementation is included in this interface, * so handlers that do not care about focus can ignore * this function. * * @return true iff this handler is focused. */ virtual char isFocused(); /** * Callback function for when an ASCII-representable key is pressed. * * @param inKey ASCII representation of the pressed key. * @param inX x position of mouse when key was pressed. * @param inY y position of mouse when key was pressed. */ virtual void keyPressed( unsigned char inKey, int inX, int inY ) = 0; /** * Callback function for when an ASCII-representable key is released. * * @param inKey ASCII representation of the released key. * @param inX x position of mouse when key was released. * @param inY y position of mouse when key was released. */ virtual void keyReleased( unsigned char inKey, int inX, int inY ) = 0; /** * Callback function for when an special key is pressed. * * @param inKey integer constant representation of the pressed key. * @param inX x position of mouse when key was pressed. * @param inY y position of mouse when key was pressed. */ virtual void specialKeyPressed( int inKey, int inX, int inY ) = 0; /** * Callback function for when an special key is released. * * @param inKey integer constant representation of the released key. * @param inX x position of mouse when key was released. * @param inY y position of mouse when key was released. */ virtual void specialKeyReleased( int inKey, int inX, int inY ) = 0; char mHandlerFlagged; protected: KeyboardHandlerGL() : mHandlerFlagged( false ) { } }; inline char KeyboardHandlerGL::isFocused() { return false; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/testScreenGL.cpp0000640000175000017500000000113607236561234025426 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-February-2 Jason Rohrer * Fixed the random seed for testing. */ #include "TestHandlerGL.h" #include "ScreenGL.h" #include "minorGems/util/random/StdRandomSource.h" // simple test function int main() { StdRandomSource *randSource = new StdRandomSource( 2 ); TestHandlerGL *handler = new TestHandlerGL( randSource, 30 ); char *name = "test window"; ScreenGL *screen = new ScreenGL( 200, 200, false, name, handler, handler, handler ); handler->setupPrimitives(); screen->start(); } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/0000750000175000017500000000000011401021060023113 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/glGUITest.cpp0000640000175000017500000000755407351520325025463 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * Added support for testing ButtonGL. * * 2001-September-16 Jason Rohrer * Added support for testing StickyButtonGL. * Added support for testing MultiButtonGL. * Added support for testing MouseCursorRegionGL. * * 2001-September-16 Jason Rohrer * Added support for testing SliderGL. */ #include "SliderGL.h" #include "MouseCursorRegionGL.h" #include "MultiButtonGL.h" #include "GUIPanelGL.h" #include "GUIContainerGL.h" #include "GUITranslatorGL.h" #include "ButtonGL.h" #include "StickyButtonGL.h" #include "minorGems/graphics/Color.h" #include "minorGems/graphics/openGL/ScreenGL.h" #include "minorGems/graphics/converters/BMPImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" // prototype /** * Loads an image from a BMP file. * * @param inFileName the name of the file to load from. * * @return the loaded image, or NULL if reading from the file fails. */ Image *loadBMPImage( char *inFileName ); // a test program for gl-based guis int main() { Image *buttonUpImage = loadBMPImage( "test.bmp" ); if( buttonUpImage == NULL ) { printf( "image loading failed.\n" ); return 1; } Image *buttonDownImage = loadBMPImage( "test2.bmp" ); if( buttonUpImage == NULL ) { printf( "image loading failed.\n" ); return 1; } Image *sliderImage = loadBMPImage( "test3.bmp" ); if( sliderImage == NULL ) { printf( "image loading failed.\n" ); return 1; } // construct the screen first so that other GL calls work ScreenGL *screen = new ScreenGL( 400, 300, false, "Test GL GUI" ); // construct a button with these images ButtonGL *button = new ButtonGL( 0.45, 0.45, 0.1, 0.1, buttonUpImage, buttonDownImage ); StickyButtonGL *stickyButton = new StickyButtonGL( 0.45, 0.56, 0.1, 0.1, buttonUpImage->copy(), buttonDownImage->copy() ); int numMultiButton = 4; Image **pressedImages = new Image*[numMultiButton]; Image **unpressedImages = new Image*[numMultiButton]; for( int i=0; icopy(); unpressedImages[i] = buttonUpImage->copy(); } double gutterFraction = 0.125; MultiButtonGL *multiButton = new MultiButtonGL( 0.4, 0.67, 0.2, 0.2, numMultiButton, unpressedImages, pressedImages, gutterFraction ); SliderGL *slider = new SliderGL( 0.35, 0.34, 0.3, 0.1, sliderImage, 0.33333, new Color( 0, 0, 0), new Color( 1.0f, 0, 0 ), new Color( 0.5f, 0.5f, 0.5f ), new Color( 0.6f, 0.6f, 0.6f ) ); GUIContainerGL *container = new GUIContainerGL( 0, 0, 1, 1 ); GUITranslatorGL *translator = new GUITranslatorGL( container, screen ); screen->addRedrawListener( translator ); screen->addMouseHandler( translator ); GUIPanelGL *panel = new GUIPanelGL( 0.25, 0.25, 0.5, 0.7, new Color( 0.25f, 0.25f, 0.25f ) ); panel->add( button ); panel->add( stickyButton ); panel->add( multiButton ); panel->add( slider ); container->add( panel ); MouseCursorRegionGL *cursor = new MouseCursorRegionGL( 0, 0, 1.0, 1.0, 0.125, new Color( 0, 0, 1.0f ), new Color( 0, 0, .75f ) ); container->add( cursor ); GUIPanelGL *panel2 = new GUIPanelGL( 0, 0, 0.25, 1, new Color( 0, 1.0f, 0 ) ); container->add( panel2 ); container->add( cursor ); screen->start(); delete translator; // this deletes container too delete screen; return 0; } Image *loadBMPImage( char *inFileName ) { // load images for the button File *imageFile = new File( NULL, inFileName, strlen( inFileName ) ); FileInputStream *imageStream = new FileInputStream( imageFile ); BMPImageConverter *converter = new BMPImageConverter(); Image *returnImage = converter->deformatImage( imageStream ); delete imageFile; delete imageStream; delete converter; return returnImage; } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/LabelGL.h0000640000175000017500000000566011373324323024556 0ustar pabspabs/* * Modification History * * 2001-October-13 Jason Rohrer * Created. * * 2001-October-29 Jason Rohrer * Added a missing destructor. * * 2006-December-19 Jason Rohrer * Made destructor virtual. * * 2008-October-1 Jason Rohrer * Added function for getting TextGL. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #ifndef LABEL_GL_INCLUDED #define LABEL_GL_INCLUDED #include "GUIComponentGL.h" #include "TextGL.h" /** * A text label for OpenGL-based GUIs. * * @author Jason Rohrer */ class LabelGL : public GUIComponentGL { public: /** * Constructs a label. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inString the text to display in this label. * Is copied internally, so must be destroyed * by caller if not const. * @param inText the text object to use when drawing * this label. Must be destroyed by caller after * this class is destroyed. */ LabelGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, const char *inString, TextGL *inText ); virtual ~LabelGL(); /** * Sets the text displayed by this label. * * @param inString the text to display in this label. * Is copied internally, so must be destroyed * by caller if not const. */ void setText( const char *inString ); /** * Gets the text displayed by this label. * * @return the text to display in this label. * Must not be destroyed or modified by caller. */ char *getText(); /** * Gets the TextGL object used to draw this label. * * @return the TextGL object. * Must not be destroyed by caller until after this class is * destroyed. */ TextGL *getTextGL() { return mText; } // override fireRedraw in GUIComponentGL virtual void fireRedraw(); protected: TextGL *mText; char *mString; }; inline LabelGL::LabelGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, const char *inString, TextGL *inText ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mText( inText ), mString( NULL ) { setText( inString ); } inline LabelGL::~LabelGL() { if( mString != NULL ) { delete [] mString; } } inline void LabelGL::setText( const char *inString ) { if( mString != NULL ) { delete [] mString; } int length = strlen( inString ) + 1; mString = new char[ length ]; memcpy( mString, inString, length ); } inline char *LabelGL::getText() { return mString; } inline void LabelGL::fireRedraw() { mText->drawText( mString, mAnchorX, mAnchorY, mWidth, mHeight ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/GUIComponentGL.h0000640000175000017500000001551711316235753026055 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * Changed to use normalized floating point coordinates. * Fixed some compile bugs. * Changed to extend the GUIComponent class. * * 2001-October-29 Jason Rohrer * Added support for focus. * * 2006-July-4 Jason Rohrer * Added support for disabled components. * * 2008-October-1 Jason Rohrer * Added position changing and gettting functions. * * 2009-December-28 Jason Rohrer * Added support for locking focus. */ #ifndef GUI_COMPONENT_GL_INCLUDED #define GUI_COMPONENT_GL_INCLUDED #include "minorGems/graphics/openGL/RedrawListenerGL.h" #include "minorGems/ui/GUIComponent.h" // for key codes #include "minorGems/graphics/openGL/KeyboardHandlerGL.h" /** * A base class for all OpenGL GUI components. * * Note that all coordinates are specified in a normalized * [0,1.0] space, where the screen is spanned by this [0,1.0] range. * * @author Jason Rohrer */ class GUIComponentGL : public GUIComponent, public RedrawListenerGL { public: virtual ~GUIComponentGL(); /** * Sets the position of this component. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. */ void setPosition( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); // functions for getting position double getAnchorX() { return mAnchorX; } double getAnchorY() { return mAnchorY; } double getWidth() { return mWidth; } double getHeight() { return mHeight; } /** * Sets the enabled state of this component. * * @param inEnabled true to enable, or false to disable. */ virtual void setEnabled( char inEnabled ); /** * Gets the enabled state of this component. * * @return true to enable, or false to disable. */ virtual char isEnabled(); /** * Tests whether a point is inside this component. * * @param inX the x value to check. * @param inY the y value to check. * * @return true iff the point is insided this component. */ virtual char isInside( double inX, double inY ); /** * Sets this component's focus status. * * Note that this component may ignore this function call * and configure its own focus (for example, some * components are never focused). * * The default implementation ignores this call * and is never focused. * * @param inFocus true iff this component should be focused. */ virtual void setFocus( char inFocus ); /** * Locks the focus onto this component so that it keeps it * despite mouse clicks that would cause it to lose focus. * * @param inFocusLocked true to lock focus onto this component. */ virtual void lockFocus( char inFocusLocked ); virtual char isFocusLocked(); // the implementations below do nothing, but they allow // subclasses to pick which input they care about (and which // functions they want to override) // implements a normalized (to [0,1.0] version // of a MouseHandlerGL-like interface virtual void mouseMoved( double inX, double inY ); virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); // implements a normalized (to [0,1.0] version // of a KeyboardHandlerGL-like interface virtual char isFocused(); virtual void keyPressed( unsigned char inKey, double inX, double inY ); virtual void specialKeyPressed( int inKey, double inX, double inY ); virtual void keyReleased( unsigned char inKey, double inX, double inY ); virtual void specialKeyReleased( int inKey, double inX, double inY ); // implements the RedrawListenerGL interface virtual void fireRedraw(); protected: /** * Constructs a component. * * Should only be called by subclass constructors. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. */ GUIComponentGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); double mAnchorX; double mAnchorY; double mWidth; double mHeight; char mEnabled; char mFocusLocked; }; inline GUIComponentGL::GUIComponentGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : mAnchorX( inAnchorX ), mAnchorY( inAnchorY ), mWidth( inWidth ), mHeight( inHeight ), mEnabled( true ), mFocusLocked( false ) { } inline GUIComponentGL::~GUIComponentGL() { } inline void GUIComponentGL::setPosition( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) { mAnchorX = inAnchorX; mAnchorY = inAnchorY; mWidth = inWidth; mHeight = inHeight; } inline void GUIComponentGL::setEnabled( char inEnabled ) { mEnabled = inEnabled; } inline char GUIComponentGL::isEnabled() { return mEnabled; } inline char GUIComponentGL::isInside( double inX, double inY ) { if( inX >= mAnchorX && inX < mAnchorX + mWidth && inY >= mAnchorY && inY < mAnchorY + mHeight ) { return true; } else { return false; } } inline void GUIComponentGL::mouseMoved( double inX, double inY ) { } inline void GUIComponentGL::mouseDragged( double inX, double inY ) { } inline void GUIComponentGL::mousePressed( double inX, double inY ) { } inline void GUIComponentGL::mouseReleased( double inX, double inY ) { } inline void GUIComponentGL::setFocus( char inFocus ) { // default implementation ignores this call } inline void GUIComponentGL::lockFocus( char inFocusLocked ) { mFocusLocked = inFocusLocked; } inline char GUIComponentGL::isFocusLocked() { return mFocusLocked; } inline char GUIComponentGL::isFocused() { return false; } inline void GUIComponentGL::keyPressed( unsigned char inKey, double inX, double inY ) { } inline void GUIComponentGL::specialKeyPressed( int inKey, double inX, double inY ) { } inline void GUIComponentGL::keyReleased( unsigned char inKey, double inX, double inY ) { } inline void GUIComponentGL::specialKeyReleased( int inKey, double inX, double inY ) { } inline void GUIComponentGL::fireRedraw() { } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/GUIPanelGL.h0000640000175000017500000000371710500316045025135 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * * 2006-July-3 Jason Rohrer * Fixed warnings. * * 2006-September-8 Jason Rohrer * Made alpha-aware. */ #ifndef GUI_PANEL_GL_INCLUDED #define GUI_PANEL_GL_INCLUDED #include "GUIComponentGL.h" #include "GUIContainerGL.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/graphics/Color.h" #include /** * A container with a background color that is drawn * behind the components. * * @author Jason Rohrer */ class GUIPanelGL : public GUIContainerGL { public: /** * Constructs a panel. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inColor the background color for this panel. * Will be destroyed when this class is destroyed. */ GUIPanelGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Color *inColor ); virtual ~GUIPanelGL(); // override fireRedraw() in GUIComponentGL virtual void fireRedraw(); protected: Color *mColor; }; inline GUIPanelGL::GUIPanelGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Color *inColor ) : GUIContainerGL( inAnchorX, inAnchorY, inWidth, inHeight ), mColor( inColor ) { } inline GUIPanelGL::~GUIPanelGL() { delete mColor; } inline void GUIPanelGL::fireRedraw() { // draw our background color as a rectangle glColor4f( mColor->r, mColor->g, mColor->b, mColor->a ); glBegin( GL_QUADS ); { glVertex2d( mAnchorX, mAnchorY ); glVertex2d( mAnchorX + mWidth, mAnchorY ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glVertex2d( mAnchorX, mAnchorY + mHeight ); } glEnd(); // call the supercalss redraw GUIContainerGL::fireRedraw(); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/test3.bmp0000640000175000017500000003006607352744545024721 0ustar pabspabsBM606(@@0@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?BB>BB>BB?BB?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?AA>EDED?AA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?AA=GG:QP6][3fd3fd6][:QP=GG?AA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>ED9RQ2ig)‚$“$’Ž*|3ec:QP>ED?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?BB;ML3ec'‰…°«ÉÂÇÁ«¦*€}6][BB@@@@@@@@@@@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?BBBB@@@@@@@@@?@@?@@?@@?@@?@@?@@>@@>@@>@@>AA=AA=@@=@@>@@>@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>BB:NN/om£ž ÖÏñéöííäÏÈ!›—2ig;LL?BB@@@@@@@@@?@@?@@>@@>@@>@@=@@<@@;CC;GG;II:II:HG;DD@@>@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>CC9RQ-wt®¨ áÚùñúñìäÉÃ#–’3fd@@<@@;AA:DD9FF8IH7LK5RP2YX3_]2_^3YX6PO9HG@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=BB8TS,wt°« àØöíøïíäËÄ"—“2fe=GG@@@@@@?@@?BB;BB9FF7LK5RQ3XW1^\/ec*ro&‚%‰†'„*ur/ca4SR8HG;AA=@@>@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@?@@?@@>@@@@>AA=ED@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@>@@=BB9PO.ol¢ ÙÒôëùðóê ÝÕ°ª'|4ZY:GG9KK6WV2db*rp&|y#…‚ŽŠ˜“£ž°«Áº ÒËÙÒ ÌÅ­¨#ˆ„,ig3RP8EE;@@=@@>@@>@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@>@@>@@>@@>@@>@@>@@>@@>@@>@@>@@>@@@@>@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@>@@>@@>@@>@@=@@=@@=@@=@@=@@=@@=@@;AA8II2^]$‡ƒ¾¸çßöíöíêáÌŦ¡#†ƒ'{x$†ƒ¢Á» ÎÇ ÒË ÐÉ ÐÈÓÌØÐÝÖãÛèàæÞ×й³!‘,lj4QP8DD;@@<@@=@@>@@>@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@>@@=@@=@@=@@=@@<@@<@@;@@;@@<@@<@@<@@;@@9HG4XW(yw«¦ ÙÒîæôëêâ Ó̵°žš›—ª¥ÆÀáÙêâéáåÝáÙÞÖÞÖß×àØàÙÚÓÉ«¥&„/ba6LK9CC;@@<@@=@@=@@>@@>@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@=@@<@@;@@:@@9@@:@@9@@9@@:@@9@@:@@9AA7GG4TT*pn˜ËÅæÞïçêâÙÒý´¯¶°Å¿ÜÕíåðèïçêâãÛÛÔÖÎ ÑÊ ÍÆ ÊÄÁ»®©"‘,pn5TS9EE:AA<@@<@@=@@=@@>@@>@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@=@@;@@:@@8AA6CC6BC5CC5CC6AB7@B7AB8DD6JJ3WV*om™•žáÙìäêâߨ ÑÊ ÇÀ ËÅ ØÑèàòéòêëãáÙ ÖÎ Êþ¸´®®¨§¡œ—%‰…,qo4YW9GF;@@;@@<@@=@@=@@=@@>@@>@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@<@@:A@8DD4HH2LM1NO0NP0MO1IL3GJ4GJ5JL3RR0`_%zx¢ÆÀÞ×ëãìäåÝÜÕÖÏÛÔåÝïæóêîåáÚ ÒËÀº®©œ— Š$ƒ€'{x,qo1ca7UT;HG@@>@@>@@>@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@<@@8FE4ON/XX,`a*df*ch)_f*Yb,U].T[.V[-``)pnŒ‰¯ª ÍÇáÚêãìåêãåÞäÜçßíäòéñèèßÚÒ Ä½¬§’Ž%zw,ge1[Z4UT7ON:JIAA>@@>@@=@@=@@=@@>@@>@@>@@>@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@;CC6NM0^\)on#{| €…ˆy‡ s„"l}$iw&kt&ux!‡†£Ÿ¾¹ÔÍâÜëäîèíçëåëäïçóêõìòéèß×ÐÀº¥ "‰…,ml3XW9IHBB?BB?@@@@@?@@?@@>@@>@@>@@>@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@:GG3XW+qo!Šˆœž¡¨¬•¬Ž©ˆ¤†ž‡—˜¡¢·´ ËÆÛÕåßëæíéîêîéïéòëõí÷îóêêâÜÔ É°ª–’'|z/hf4WU;JJ>DD?AA@@@@@@@@@?@@>@@>@@>@@>@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>@@=@@:II2_^&~¤¢»½ ÁËºÐ°Ñ ¦Ï ¡Ì ¡Ä¤»­¸»½ ÊÇØÒàÛäáèæêéëêìëíìñíõîøðõíïçåÞÖÏÆÀµ¯£ž!‘(|y1kh6[Z;NM=FF?BB?AA>AA=@@>@@>@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@:II1a`%‰…µ²ÑÓÕâÌæ¾èµè²èµâ¼ÙÅÒÐÓØÖÞÚàßâããçäéåëæìèííîôð÷ñöïõíðççàÞ× ÕÎÍÆ»±« œ—(ƒ€0nl5^]8TS;NM;II;GG@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@;HH4[Z'ƒ€²¯ÕÕÛçÑîÅñ½ñ¼ðÄðÍé×ãßáâáàáßãÞæÞèÝêÝëÞíáíæïíðóòöð÷ðöîóëðèîæìäéá àØÐʺ´¢%Š+|y/pn3cb8WV;ML>ED?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>DD9RQ/rožšÇÆÙßÔêÈîÃðÅðÍñÙîâêèéåçßåÛæØçÖéÔêÓëÔìÖíÝïåðïñöñùòúñúñúñúñúñúòöîñéæÞ Öϼ±¬¢$‘,yv5`^;NM>CC@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@BB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?BB;KK7XW1ed)vv#†‡™®³ ÄÇÕÙàäáìÙëÑêÊëÄëÁë¾ì¾ì¾ìÀíÊîØðäñíñôñöñøñùñúñùðöîòêòêöíøïùñúñùñóëÒË"™”2ig;NM>BB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?BB=GG@@8JK+dh†‘¢¶³Ò½äÁìÂìÃíÄíÆîÉîÍîÑîÖîÜïâðçðéïéïåíâëÝç×áÍÕ ¼À§¥‹Ї–’ ›¦¡ ž™'І/pn7ZXAA>BB@@=CC;JJ:NM8RQ4VV&lq‰˜¡º°Ó¹ä½ìÁîÅïÉðÏðÕðÛðáðåðéðìðíðéïâîÚëÐçÅÞ¹Ó«À ˜§Šhl&TT-ML0NN5QP8NMDD?AA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>AA;GG5VU0ec,qo*yw(yx‡Šœ¨ ®Ã¹Ø¿åÂìÄîÆïÊðÐñØñàñæñëñîñððððéïâî×ëÊæ¾Û°Í¡¸ Ÿv‚_d&LM.CC3BB8BB;BB=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=AA:IH2]\)vu"Ž¡Ÿ­«°®°² ¸¾ÀÎÆÜÉæÉìÇîÇïÈñÎòÕòàòêñðñòñõñòðìïãîØêÉ弨­Èœ²‡™o|Y_%GJ,@@3@@9@@<@@=@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@:ED3YY'yx˜™±² ÃÅ ÐÑ×ÖÓÒÒÔÔÛÔáÓèÐìÊîÅðÃòÄóÐôÜóçòðòôñ÷ñöðïïçîÜêÏäÁױƞ±‡—oyW]$FH+@@2@@:@@<@@=@@>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@=@@@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@<@@8FF.[Z"} ¦ ¸ÄÆÙÍäÓêÚìàêäèäåáâÛâÔãÉæ¿ì¶ò»öÈôÚôçóññôîöíóëóëóîêêàåÕÛÅ̳¹ ¢„‡kl TT(DD0@@7@@<@@=@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@:BB5LM*de„‰¢®µÉ¾ÛÄæÉëÐí×éÝåÞàÛÛÔ×ÊÕÁ×¹àºèÃïÏðÛðçïíëîçîåèàéáðèïêëèâáÓÓÃà °°šš‚ig$SR-DD6@@;@@=@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@:DD3PQ(gj‚‰œ«­Å´×¹â½åÂäÉàÍØÌÐ ÇÆ ¾¾³·ª¸ «Ã¶ÓÅàÒçßëæêéæäÞÛÔÔÍÔÍâÚìåíæìåáÜÔÏÅÀ ²®œ™„!ki*SR2ED:@@<@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@:DD2QQ(ei}…’¢ ¢»©Ì­Ö¯Ø²Õ´Ì ´À°´¦¥™˜Œ†• ª¸ÀÍÒÝÞäãäáß×Ó Éø³´®ü ÜÔéáíæèßàÙÕÎÇÁ ¶° ›…'ge0NN9CC<@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@:CC3NO*`c!s{†”•« œ» Ã Ã Ÿ½œ°•¡Œ‘ &qp*fe)hiz}š  ¶¼ÎÓÛÞÞàÚÙÍË º¶¦¡š–Ÿ›º´ ÔÌãÛâÛáÙÜÔÓÌÇÀ¶°›–$xu/XW9GG<@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@;@@5II-XZ%hnyƒ…–£ª¨‰ ‘ w€'jn,^_0TT2QQ.VW'jlŒ«¯ÅËÓÚØÞÔØÇȳ²œ™Ї#ƒ ’Ž®¨ÈÁ ÐÉÖÏ×ÐÔÍÍÇ Àº©¤#„€/_]:II>AA?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@<@@7DD1OO+\`%jptz‰}xˆ q$gq*\a0QT3JJ4FF4HH0OP)bc€ƒ ¨¼ÈËÛÐàÐÛÅͳ¶›š!)pn*ro"†ƒŸš²­¿¹ ÆÀ É ƿ¼¶§¢%‚1^];II>AA?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@:@@5EE0PP,Z](ag&em&fo'ck*[b.RV2IK6DE8AA7@A6CD1LN)]a w”¤ ±ÉÁÞÈåÍäÈ× ¹À¡¢ ‚+ig0_],ec&xuŒˆœ—¨£®¨®©¨¢”*sp6VU=GG?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>@@<@@:@@6EE3KK1PR/SV/RV0OS2JK6DE7@A:@@9@@8@@7AC2IM*X_!o}Š¢ ¦Ë¶ãÀíÉíÍäÅÏ­¯ŠŠ+ih2VU2RQ0[Y)ig%zw"…Œˆމ#ˆ„)wt1`^9ML>CC@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@:@@9BB8DD7EE7EE8CC9AA:@@;@@<@@;@@9@@7AB3GK,T]#gyž ›Ê¬è¶ñÃôÒïÒÞ½¿••)nm2TS5II5JI2RQ.\Z,fd*mj+nk-hf1[Z7NM@@=@@=@@<@@<@@=@@=@@=@@=@@=@@<@@:@@8@A4EI.OX&`rw•‘Á¥ä°òÁöÕóàê ËÌžœ*sq4UT8FF8CC6FE5KJ4PO3SR4TR6ON8II@@>@@=@@<@@:@@7BE2JR+Wf"k…ƒ« œÏ®åÆðÜñäë ÏÏ +sp5UT9FF:AA9@@9CB9ED9FE:EE:CC@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@=@@<@@9@C6EJ1NY*\n#q‹Šª§ÆÇÝßéääÉÄ –’.lj7QP:DD;@@<@@<@@<@@<@@=@@=@@=@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@=@@<@@:AD7FL3NX.[i&rŒ›¯·ÆÈŪ¦(4][:KJ@@>@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@>@@=@@<@@;AB:DG8LP3W],mr&„†#“’#“*~2ec9QP=ED>@@>@@?@@?@@?@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@?@@>@@>@@=@@<@@AA>@@>@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@?@@?@@?@@>AA>EDED?AA@@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?BB>BB>BB?BB?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/ImageButtonGL.h0000640000175000017500000000647311355254605025765 0ustar pabspabs/* * Modification History * * 2006-July-3 Jason Rohrer * Created. * * 2007-March-17 Jason Rohrer * Enabled blending (why was it off?) * * 2010-March-31 Jason Rohrer * Changed glColor3f to 4f to fix driver bug on some video cards. */ #ifndef IMAGE_BUTTON_GL_INCLUDED #define IMAGE_BUTTON_GL_INCLUDED #include "ButtonGL.h" #include "minorGems/graphics/Image.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include /** * A textured button for GL-based GUIs. * * @author Jason Rohrer */ class ImageButtonGL : public ButtonGL { public: /** * Constructs a button. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inUnpressedImage the image to display * when this button is unpressed. Must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. * @param inPressedImage the image to display * when this button is pressed. Must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. */ ImageButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inUnpressedImage, Image *inPressedImage ); ~ImageButtonGL(); // implements the ButtonGL interface virtual void drawPressed(); virtual void drawUnpressed(); protected: Image *mUnpressedImage; Image *mPressedImage; SingleTextureGL *mUnpressedTexture; SingleTextureGL *mPressedTexture; SingleTextureGL *mCurrentTexture; /** * Draw this button using a specific texture. * * @param inTexture the texture. * Destroyed by caller. */ void drawWithTexture( SingleTextureGL *inTexture ); }; inline ImageButtonGL::ImageButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inUnpressedImage, Image *inPressedImage ) : ButtonGL( inAnchorX, inAnchorY, inWidth, inHeight ), mUnpressedImage( inUnpressedImage ), mPressedImage( inPressedImage ) { mUnpressedTexture = new SingleTextureGL( mUnpressedImage ); mPressedTexture = new SingleTextureGL( mPressedImage ); mCurrentTexture = mUnpressedTexture; } inline ImageButtonGL::~ImageButtonGL() { delete mUnpressedImage; delete mPressedImage; delete mUnpressedTexture; delete mPressedTexture; } inline void ImageButtonGL::drawPressed() { drawWithTexture( mPressedTexture ); } inline void ImageButtonGL::drawUnpressed() { drawWithTexture( mUnpressedTexture ); } inline void ImageButtonGL::drawWithTexture( SingleTextureGL *inTexture ) { // set our texture inTexture->enable(); glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); //glDisable( GL_BLEND ); glBegin( GL_QUADS ); { glTexCoord2f( 0, 1.0f ); glVertex2d( mAnchorX, mAnchorY ); glTexCoord2f( 1.0f, 1.0f ); glVertex2d( mAnchorX + mWidth, mAnchorY ); glTexCoord2f( 1.0f, 0 ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glTexCoord2f( 0, 0 ); glVertex2d( mAnchorX, mAnchorY + mHeight ); } glEnd(); inTexture->disable(); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/GUIContainerGL.h0000640000175000017500000002262711357636644026046 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * Changed to use normalized floating point coordinates. * Fixed some compile bugs. * Changed so that mouse released events are passed to all components. * * 2001-October-29 Jason Rohrer * Added support for focus, including mouse-release to focus. * * 2001-November-2 Jason Rohrer * Changed to send keyboard events only to focused components, * regardless of mouse position. * * 2006-July-3 Jason Rohrer * Fixed warnings. * * 2006-September-8 Jason Rohrer * Switched to passing mouse motion events to all components. * * 2006-December-14 Jason Rohrer * Added function for testing containment. * * 2009-December-22 Jason Rohrer * New SimpleVector delete call. * * 2009-December-28 Jason Rohrer * Added support for locking focus. * * 2010-April-8 Jason Rohrer * Fixed focus behavior for presses that didn't start on a component. * Only pass focus to enabled components. * * 2010-April-9 Jason Rohrer * Fixed crash when components removed by a mouse event. */ #ifndef GUI_CONTAINER_GL_INCLUDED #define GUI_CONTAINER_GL_INCLUDED #include "GUIComponentGL.h" #include "minorGems/util/SimpleVector.h" /** * A container full of gui components that delegates * redraw and ui events to the contained components. * * @author Jason Rohrer */ class GUIContainerGL : public GUIComponentGL { public: /** * Constructs a container. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. */ GUIContainerGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); virtual ~GUIContainerGL(); /** * Adds a component to this container. * * Note that the added component should lie within the bounds * of this container (or else event delegation may not function * properly). * * @param inComponent the component to add. Is destroyed * when this container is destroyed. */ virtual void add( GUIComponentGL *inComponent ); /** * Removes a component from this container. * * @param inComponent the component to remove. Must be * destroyed by the caller. * * @return true iff the component was removed successfully. */ virtual char remove( GUIComponentGL *inComponent ); /** * Gets whether this container contains a given component. * * @param inComponent the component to look for. * * @return true if inComponent is in this container. */ virtual char contains( GUIComponentGL *inComponent ); // the implementations below delegate events to appropriate // contained components // override functions in GUIComponentGL virtual void mouseMoved( double inX, double inY ); virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); virtual char isFocused(); virtual void keyPressed( unsigned char inKey, double inX, double inY ); virtual void specialKeyPressed( int inKey, double inX, double inY ); virtual void keyReleased( unsigned char inKey, double inX, double inY ); virtual void specialKeyReleased( int inKey, double inX, double inY ); virtual void fireRedraw(); protected: SimpleVector *mComponentVector; // flags to track components in which a press started SimpleVector *mPressStartedHereVector; }; inline GUIContainerGL::GUIContainerGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mComponentVector( new SimpleVector() ), mPressStartedHereVector( new SimpleVector() ) { } inline GUIContainerGL::~GUIContainerGL() { // delete each contained component for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); delete component; } delete mComponentVector; delete mPressStartedHereVector; } inline void GUIContainerGL::add( GUIComponentGL *inComponent ) { mComponentVector->push_back( inComponent ); mPressStartedHereVector->push_back( false ); } inline char GUIContainerGL::remove( GUIComponentGL *inComponent ) { int index = mComponentVector->getElementIndex( inComponent ); if( index != -1 ) { mComponentVector->deleteElement( index ); mPressStartedHereVector->deleteElement( index ); return true; } return false; } inline char GUIContainerGL::contains( GUIComponentGL *inComponent ) { int index = mComponentVector->getElementIndex( inComponent ); if( index >= 0 ) { return true; } else { return false; } } inline void GUIContainerGL::mouseMoved( double inX, double inY ) { for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); component->mouseMoved( inX, inY ); } } inline void GUIContainerGL::mouseDragged( double inX, double inY ) { // released events should be passed to all components // so that a pressed component can react if the mouse // is dragged elsewhere for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); component->mouseDragged( inX, inY ); } } inline void GUIContainerGL::mousePressed( double inX, double inY ) { // only pass pressed events to components // that contain the coordinate pressed for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); if( component->isInside( inX, inY ) ) { component->mousePressed( inX, inY ); // components MIGHT get removed by the event call, thus, our // vector might be too short for i now if( i < mComponentVector->size() ) { *( mPressStartedHereVector->getElement( i ) ) = true; } } else { // press started elsewhere *( mPressStartedHereVector->getElement( i ) ) = false; } } } inline void GUIContainerGL::mouseReleased( double inX, double inY ) { // first, unfocus all components, but skip those that are locked for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); if( !component->isFocusLocked() ) { component->setFocus( false ); } } // released events should be passed to all components // so that a pressed component can react if the mouse // is released elsewhere for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); component->mouseReleased( inX, inY ); // components MIGHT get removed by the event call, thus, our // vector might be too short for i now if( i < mComponentVector->size() ) { // focus any components hit by the release directly // IF press started there if( component->isInside( inX, inY ) && component->isEnabled() && *( mPressStartedHereVector->getElement(i) ) ) { component->setFocus( true ); } } } } inline char GUIContainerGL::isFocused() { // we are focused if any of our sub-components are focused for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); if( component->isFocused() ) { return true; } } // else, none focused return false; } inline void GUIContainerGL::keyPressed( unsigned char inKey, double inX, double inY ) { for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); // only send events to focused components if( component->isFocused() ) { component->keyPressed( inKey, inX, inY ); } } } inline void GUIContainerGL::specialKeyPressed( int inKey, double inX, double inY ) { for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); // only send events to focused components if( component->isFocused() ) { component->specialKeyPressed( inKey, inX, inY ); } } } inline void GUIContainerGL::keyReleased( unsigned char inKey, double inX, double inY ) { for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); // only send events to focused components if( component->isFocused() ) { component->keyReleased( inKey, inX, inY ); } } } inline void GUIContainerGL::specialKeyReleased( int inKey, double inX, double inY ) { for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); // only send events to focused components if( component->isFocused() ) { component->specialKeyReleased( inKey, inX, inY ); } } } inline void GUIContainerGL::fireRedraw() { for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); component->fireRedraw(); } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/test.bmp0000640000175000017500000001406607352744545024640 0ustar pabspabsBM66(@ ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ôôôÿÿÿÿÿÿôôôÚÚÚÉÉÉ«««™™™ˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠ½½½àààúúúÿÿÿÿÿÿýýýïïïÔÔÔ±±±———‹‹‹‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŽŽŽ«««ÒÒÒïïïÿÿÿÿÿÿÿÿÿÿÿÿàààÄÄÄšššŽŽŽ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡½½½éééÿÿÿÿÿÿøøøæææÎÎη··žžžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîîÔÔÔººº‰‰‰‡‡‡‡‡‡‡‡‡ŒŒŒ°°°éééÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûçççÁÁÁ   ‹‹‹‡‡‡‡‡‡‡‡‡“““ÈÈÈùùùÿÿÿÿÿÿõõõïïïïïïïïïþþþþþþðððÉÉÉ›››‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‰‰‰ºººÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿÿÿÿÿÿÿÿÿùùùáááÏÏÏÏÏÏÔÔÔãããûûû‡‡‡‡‡‡‹‹‹±±±íííÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõççççççéééõõõÿÿÿûûûèèèÁÁÁ•••‡‡‡ŠŠŠ¿¿¿úúúÿÿÿûûûÕÕÕ­­­§§§§§§§§§¿¿¿÷÷÷ÿÿÿúúúÚÚÚ¡¡¡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŒŒŒñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéééÓÓÓÏÏÏÏÏÏÿÿÿÿÿÿùùùÎÎΕ•••••ˆˆˆ‡‡‡‡‡‡åååÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØØØ—————————ŸŸŸ½½½ìììÿÿÿùùùÑÑÑŒŒŒöööÿÿÿÿÿÿýýý¿¿¿‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠ¹¹¹ÿÿÿÿÿÿüüüÒÒÒ•••‡‡‡‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿààদ¦ÿÿÿÿÿÿêêê¡¡¡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠ¶¶¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÉɇ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŒŒŒ´´´ää䨨ØöööÿÿÿÿÿÿÿÿÿØØØ™™™‡‡‡‡‡‡‡‡‡‡‡‡”””ÿÿÿÿÿÿÿÿÿõõõµµµ‡‡‡‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿººº‡‡‡‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿííí¨¨¨‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‘‘‘ÍÍÍÿÿÿÿÿÿÿÿÿÿÿÿóóó›››‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡˜˜˜«««ŠŠŠŠŠŠ¾¾¾öööÿÿÿüüüØØØ›››‡‡‡‡‡‡‡‡‡‡‡‡§§§ÿÿÿÿÿÿÿÿÿÿÿÿÊÊʇ‡‡‡‡‡‡‡‡ŒŒŒñññÿÿÿÿÿÿÿÿÿÿÿÿÀÀÀŠŠŠ‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿõõõ½½½‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡•••ÞÞÞÿÿÿÿÿÿÿÿÿÿÿÿÖÖÖ“““‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‘‘‘°°°¾¾¾¹¹¹   ‹‹‹‡‡‡‡‡‡‡‡‡ßßßÿÿÿÿÿÿÿÿÿÿÿÿÓÓÓ•••‡‡‡‡‡‡‡‡‡‹‹‹ÕÕÕÿÿÿÿÿÿÿÿÿÿÿÿÒÒÒ•••‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿýýýÒÒÒ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡———âââÿÿÿÿÿÿÿÿÿÿÿÿÇÇLJ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‰‰‰ˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡¢¢¢ÜÜÜüüüÿÿÿÿÿÿÿÿÿÿÿÿÍÍÍ‘‘‘‡‡‡‡‡‡‡‡‡‰‰‰¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿååå   ‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿÜÜÜ‘‘‘‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡–––àààÿÿÿÿÿÿÿÿÿÿÿÿÉÉÉ–––‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠ———ÇÇÇøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóó²²²‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆ©©©ÿÿÿÿÿÿÿÿÿÿÿÿòòò¨¨¨‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿçç磣£‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡’’’ÑÑÑÿÿÿÿÿÿÿÿÿÿÿÿçççÒÒÒÏÏÏÏÏÏËËË···³³³§§§™™™‹‹‹‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡³³³çççýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÍÍÍ•••‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡———úúúÿÿÿÿÿÿÿÿÿÿÿÿ¶¶¶‰‰‰‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿïïï±±±‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‹‹‹ºººÿÿÿÿÿÿÿÿÿÿÿÿùùùêêêôôôþþþÿÿÿÿÿÿûûûïïïáááÊÊʤ¤¤‹‹‹‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‘‘‘¿¿¿òòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÉÉɘ˜˜‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡“““èèèÿÿÿÿÿÿÿÿÿÿÿÿÆÆÆ‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿøøøÀÀÀ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡§§§÷÷÷ÿÿÿÿÿÿÿÿÿýýý«««›››   ÃÃÃæææÿÿÿÿÿÿÿÿÿûûûðððÐÐБ‘‘‡‡‡‡‡‡‡‡‡‡‡‡ŽŽŽ¿¿¿÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòò²²²‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿØØØ–––‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏŠŠŠ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡“““ÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÅÅÅŒŒŒ‡‡‡‡‡‡‰‰‰¥¥¥àààúúúÿÿÿÿÿÿÿÿÿäää“““‡‡‡‡‡‡‡‡‡¨¨¨òòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÊÊʇ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‹‹‹½½½ÿÿÿÿÿÿÿÿÿÿÿÿéé采‡‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÜÜÜššš‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‰‰‰®®®ðððÿÿÿÿÿÿÿÿÿëëë–––‡‡‡‡‡‡‡‡‡‡‡‡šššÔÔÔúúúÿÿÿÿÿÿÿÿÿ¼¼¼‡‡‡‡‡‡‡‡‡°°°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿããã°°°‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆ¨¨¨ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥¥¥‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿçç禦¦‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŽŽŽÁÁÁ÷÷÷ÿÿÿÿÿÿÿÿÿ¼¼¼ˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡   âââÿÿÿÿÿÿÿÿÿððð‡‡‡‡‡‡‡‡‡ªªªõõõÿÿÿÿÿÿÿÿÿßßß   ‡‡‡‡‡‡‡‡‡‰‰‰³³³‰‰‰‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡žžžóóóÿÿÿÿÿÿÿÿÿÿÿÿ···‹‹‹‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿííí­­­‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡”””ÏÏÏúúúÿÿÿÿÿÿõõõ‡‡‡‡‡‡‡‡‡‡‡‡‰‰‰ÍÍÍÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡‡‡‡‡‡‡ŸŸŸãããÿÿÿÿÿÿÿÿÿªªªˆˆˆ‡‡‡‡‡‡‡‡‡–––ÓÓÓõõõèèè®®®‰‰‰‡‡‡‡‡‡‡‡‡‡‡‡™™™åååÿÿÿÿÿÿÿÿÿÿÿÿÃÃLJ‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÉÉ———————————————•••‹‹‹‡‡‡‡‡‡‡‡‡ŒŒŒÁÁÁóóóþþþÿÿÿïï¢‹‹‹‡‡‡‡‡‡“““ÖÖÖÿÿÿÿÿÿÿÿÿõõõ‡‡‡‡‡‡‡‡‡ŠŠŠ´´´ñññÿÿÿÿÿÿ¶¶¶ŠŠŠ‡‡‡‡‡‡‹‹‹ºººÿÿÿÿÿÿÿÿÿåå凇‡‡‡‡žžž£££ÖÖÖÿÿÿÿÿÿÿÿÿÿÿÿåå墢¢———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòçççççççççççççççßßß   ‡‡‡‡‡‡‡‡‡‡‡‡‹‹‹ºººçççýýýþþþïïïÀÀÀªªª«««ËËËôôôÿÿÿÿÿÿÿÿÿººº‡‡‡‡‡‡‡‡‡‡‡‡‘‘‘¿¿¿òòòÿÿÿììì¹¹¹¢¢¢———¥¥¥çççÿÿÿÿÿÿÿÿÿââ✜œ‡‡‡‡‡‡²²²üüüîîîóóóÿÿÿÿÿÿÿÿÿÿÿÿøøøéééçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõ§§§‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠ¡¡¡ÂÂÂîîîÿÿÿüüüòòòóóóüüüÿÿÿÿÿÿøøøÕÕÕ‘‘‘‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡®®®ßßßûûûúúúêêêßßßåååÿÿÿÿÿÿÿÿÿñññ»»»ŽŽŽ‡‡‡‡‡‡™™™ÌÌÌòòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××ÿÿÿÿÿÿÿÿÿÿÿÿîî¸ŸŸŸŸŸŸŸŸŸŸŸŸ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‹‹‹ššš¸¸¸ØØØðððÿÿÿÿÿÿòòòÛÛÛ²²²“““‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆ”””­­­ÏÏÏßßßÿÿÿÿÿÿÿÿÿóóóÐÐЩ©©ŒŒŒ‡‡‡‡‡‡‡‡‡‡‡‡ŽŽŽŸŸŸÄÄÄòòòÿÿÿÿÿÿÿÿÿÿÿÿÉÉÉ¡¡¡ŠŠŠ½½½úúúÿÿÿÿÿÿÿÿÿ···‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡¥¥¥èèèÿÿÿÿÿÿÿÿÿÝÝ݇‡‡’’’×××ÿÿÿÿÿÿÿÿÿÀÀÀŒŒŒ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠºººõõõÿÿÿÿÿÿëë뇇‡ˆˆˆ¥¥¥éééÿÿÿÿÿÿÓÓÓ–––‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡“““ÒÒÒüüüÿÿÿÿÿÿ˜˜˜‡‡‡‡‡‡»»»øøøÿÿÿãããžžž‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡¥¥¥èèèÿÿÿÿÿÿ°°°‡‡‡‡‡‡‡‡‡ŽŽŽ   §§§   ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡œœœ§§§§§§“““‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/MultiButtonGL.h0000640000175000017500000001603707371145520026030 0ustar pabspabs/* * Modification History * * 2001-September-16 Jason Rohrer * Created. * * 2001-November-3 Jason Rohrer * Fixed a gutter size bug that occurred when there was * only one row or column. * Changed so that rows are filled before adding new columns. * Fixed a bug in setSelectedIndex. */ #ifndef MULTI_BUTTON_GL_INCLUDED #define MULTI_BUTTON_GL_INCLUDED #include "GUIContainerGL.h" #include "StickyButtonGL.h" #include "minorGems/graphics/Image.h" #include "minorGems/ui/event/ActionListenerList.h" #include "minorGems/ui/event/ActionListener.h" #include "minorGems/ui/GUIComponent.h" #include #include /** * A set of buttons that allows only one to be depressed * at a time. * * @author Jason Rohrer */ class MultiButtonGL : public GUIContainerGL, public ActionListener, public ActionListenerList { public: /** * Constructs a set of buttons. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inNumButtons the number of buttons in the set. * @param inUnpressedImages the images to display * when each button is unpressed. Images must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. * @param inPressedImages the images to display * when each button is pressed. Images must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. * @param inGutterFraction the fraction of each row * and column that should be taken up by the gutters * (the spaces between buttons). In [0, 1.0]. */ MultiButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, int inNumButtons, Image **inUnpressedImages, Image **inPressedImages, double inGutterFraction ); ~MultiButtonGL(); /** * Sets the currently depressed button. * * Note that if this function causes a change * in the state of the set, an action will * be fired to all registered listeners. * * @param inButtonIndex the index of the button to depress. */ void setSelectedButton( int inButtonIndex ); /** * Gets the index of the currently depressed button. * * Note that if this function causes a change * in the state of the set, an action will * be fired to all registered listeners. * * @return the index of the button that is depressed. */ int getSelectedButton(); // we don't need to override any mouse // or redraw functions, since the container // and the buttons should handle these correctly // implements the ActionListener interface virtual void actionPerformed( GUIComponent *inTarget ); protected: int mNumButtons; StickyButtonGL **mButtons; int mSelectedIndex; char mIgnoreEvents; /** * Maps a button in mButtons to its index in the * mButtons array. * * @param inButton the button to get an index for. * * @return the index of inButton in the mButtons array. */ int buttonToIndex( StickyButtonGL *inButton ); }; inline MultiButtonGL::MultiButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, int inNumButtons, Image **inUnpressedImages, Image **inPressedImages, double inGutterFraction ) : GUIContainerGL( inAnchorX, inAnchorY, inWidth, inHeight ), mNumButtons( inNumButtons ), mButtons( new StickyButtonGL*[inNumButtons] ), mSelectedIndex( 0 ), mIgnoreEvents( false ) { int numColumns = (int)( sqrt( mNumButtons ) ); int numRows = mNumButtons / numColumns; while( numRows * numColumns < mNumButtons ) { numRows++; } // determine gutter and button sizes double gutterSizeX; double gutterSizeY; if( numRows == 1 ) { gutterSizeY = 0; } else { gutterSizeY = ( inHeight * inGutterFraction ) / ( numRows - 1 ); } if( numColumns == 1 ) { gutterSizeX = 0; } else { gutterSizeX = ( inWidth * inGutterFraction ) / ( numColumns - 1 ); } double buttonSizeX = ( inWidth * ( 1 - inGutterFraction ) ) / ( numColumns ); double buttonSizeY = ( inHeight * ( 1 - inGutterFraction ) ) / ( numRows ); // setup each button int buttonIndex = 0; for( int r=0; r= mNumButtons ) { // jump out of our loop if we run out of buttons c = numColumns; r = numRows; } } } // now we've added all of our buttons // press one of them before we add any listeners mButtons[ mSelectedIndex ]->setPressed( true ); // now add ourselves as an action listener to each button for( int i=0; iaddActionListener( this ); } // delete the arrays pointing to the images, since // they are no longer needed delete [] inUnpressedImages; delete [] inPressedImages; } inline MultiButtonGL::~MultiButtonGL() { // note that we don't need to delete the buttons // themselves, since they will be deleted by GUIContainer // destructor delete [] mButtons; } inline void MultiButtonGL::setSelectedButton( int inButtonIndex ) { // simply press the appropriate button, and let the // action handlers do the rest mButtons[ inButtonIndex ]->setPressed( true ); } inline int MultiButtonGL::getSelectedButton() { return mSelectedIndex; } inline void MultiButtonGL::actionPerformed( GUIComponent *inTarget ) { if( !mIgnoreEvents ) { int buttonIndex = buttonToIndex( (StickyButtonGL*)inTarget ); // if another button is being pressed if( buttonIndex != mSelectedIndex && mButtons[ buttonIndex ]->isPressed() ) { // unpress the old button, ignoring the resulting event mIgnoreEvents = true; mButtons[ mSelectedIndex ]->setPressed( false ); mIgnoreEvents = false; mSelectedIndex = buttonIndex; fireActionPerformed( this ); } // else if our selected button is being unpressed else if( buttonIndex == mSelectedIndex && !( mButtons[ buttonIndex ]->isPressed() ) ) { // don't allow it to become unpressed // re-press it, ignoring the resulting event mIgnoreEvents = true; mButtons[ mSelectedIndex ]->setPressed( true ); mIgnoreEvents = false; // don't fire an action, since our state // has not changed } } } inline int MultiButtonGL::buttonToIndex( StickyButtonGL *inButton ) { for( int i=0; i /** * A textured button for GL-based GUIs that draws one image with a shadow * to show pressed/unpressed status. * * @author Jason Rohrer */ class ShadowImageButtonGL : public ButtonGL { public: /** * Constructs a button. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inUnpressedImage the image to display * when this button is unpressed. Must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. */ ShadowImageButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inUnpressedImage ); ~ShadowImageButtonGL(); // implements the ButtonGL interface virtual void drawPressed(); virtual void drawUnpressed(); protected: Image *mUnpressedImage; SingleTextureGL *mUnpressedTexture; /** * Draw this button with an optional z offset above it's shadow. * * @param inOffset true to draw the button above its shadow. */ void drawWithOffset( char inOffset ); }; inline ShadowImageButtonGL::ShadowImageButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inUnpressedImage ) : ButtonGL( inAnchorX, inAnchorY, inWidth, inHeight ), mUnpressedImage( inUnpressedImage ) { mUnpressedTexture = new SingleTextureGL( mUnpressedImage ); } inline ShadowImageButtonGL::~ShadowImageButtonGL() { delete mUnpressedImage; delete mUnpressedTexture; } inline void ShadowImageButtonGL::drawPressed() { drawWithOffset( false ); } inline void ShadowImageButtonGL::drawUnpressed() { drawWithOffset( true ); } inline void ShadowImageButtonGL::drawWithOffset( char inOffset ) { // calculate offset for shadow double xOffset = mWidth * 0.05; double yOffset = mHeight * 0.05; // use smaller double offset = xOffset; if( yOffset < xOffset ) { offset = yOffset; } // set our texture mUnpressedTexture->enable(); // shadow first glColor4f( 0.0, 0.0, 0.0, 0.5 ); // shadow in lower left corner glBegin( GL_QUADS ); { glTexCoord2f( 0, 1.0f ); glVertex2d( mAnchorX, mAnchorY ); glTexCoord2f( 1.0f, 1.0f ); glVertex2d( mAnchorX + mWidth - offset, mAnchorY ); glTexCoord2f( 1.0f, 0 ); glVertex2d( mAnchorX + mWidth - offset, mAnchorY + mHeight - offset ); glTexCoord2f( 0, 0 ); glVertex2d( mAnchorX, mAnchorY + mHeight - offset ); } glEnd(); // now button image glColor4f( 1.0, 1.0, 1.0, 10 ); double anchorOffset = 0; if( inOffset ) { // put it in upper right corner anchorOffset = offset; offset = 0; } glBegin( GL_QUADS ); { glTexCoord2f( 0, 1.0f ); glVertex2d( mAnchorX + anchorOffset, mAnchorY + anchorOffset ); glTexCoord2f( 1.0f, 1.0f ); glVertex2d( mAnchorX + mWidth - offset, mAnchorY + anchorOffset ); glTexCoord2f( 1.0f, 0 ); glVertex2d( mAnchorX + mWidth - offset, mAnchorY + mHeight - offset ); glTexCoord2f( 0, 0 ); glVertex2d( mAnchorX + anchorOffset, mAnchorY + mHeight - offset ); } glEnd(); mUnpressedTexture->disable(); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/TextFieldGL.h0000640000175000017500000003752111377017165025437 0ustar pabspabs/* * Modification History * * 2001-October-29 Jason Rohrer * Created. * * 2001-November-2 Jason Rohrer * Added support for cursor position. * Added support for clicking to reposition the cursor. * Fixed an array out-of-bounds bug in the keyReleased function. * * 2008-September-29 Jason Rohrer * Improved text width handling and cursor spacing. * Added length limit. * * 2008-October-27 Jason Rohrer * Switched to implementation-independent keycodes. * Switched to key press (instead of release) events. * * 2008-November-1 Jason Rohrer * Added support for forced upper case. * * 2009-December-29 Jason Rohrer * Added support for setting cursor position. * * 2010-January-1 Jason Rohrer * Does NOT fire event when text changed externally. * * 2010-January-11 Jason Rohrer * Fixed mouse-based cursor positioning. * * 2010-January-27 Jason Rohrer * Fixed bug in text editing. * * 2010-January-29 Jason Rohrer * Added parameter for border width. * * 2010-March-6 Jason Rohrer * Added support for ignoring one key-press. * * 2010-March-31 Jason Rohrer * Changed glColor3f to 4f to fix driver bug on some video cards. * * 2010-April-6 Jason Rohrer * Enlarged click area. * * 2010-April-8 Jason Rohrer * Fixed behavior for presses that didn't start on this field. * * 2010-May-7 Jason Rohrer * Extended ASCII. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. * * 2010-May-25 Jason Rohrer * Based border offset on border width (instead of 1/8 height). */ #ifndef TEXT_FIELD_GL_INCLUDED #define TEXT_FIELD_GL_INCLUDED #include "GUIComponentGL.h" #include "TextGL.h" #include "minorGems/graphics/Color.h" #include "minorGems/util/stringUtils.h" #include "minorGems/ui/event/ActionListenerList.h" /** * A text field for OpenGL-based GUIs. * Will fire an event to listeners every time text changes * * @author Jason Rohrer */ class TextFieldGL : public GUIComponentGL, public ActionListenerList { public: /** * Constructs a text field. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inBorderWidth width of the border and the cursor. * @param inString the text to display in this text field. * Is copied internally, so must be destroyed * by caller if not const. * @param inText the text object to use when drawing * this textField. Must be destroyed by caller after * this class is destroyed. * @param inBorderColor the color of this field's border. * Will be destroyed when this class is destroyed. * @param inFocusedBorderColor the color of this field's border * when the field is focused. * Will be destroyed when this class is destroyed. * @param inBackgroundColor the color of this field's background. * Will be destroyed when this class is destroyed. * @param inCursorColor the color of this field's cursor line. * Will be destroyed when this class is destroyed. * @param inLengthLimit the length limit for the entered string, * or -1 for no limit. Defaults to -1. * @param inForceUppercase true to force upper case for text entered. * Defaults to false. */ TextFieldGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, double inBorderWidth, const char *inString, TextGL *inText, Color *inBorderColor, Color *inFocusedBorderColor, Color *inBackgroundColor, Color *inCursorColor, int inLengthLimit = -1, char inForceUppercase = false ); ~TextFieldGL(); /** * Sets the text displayed by this text field. * * @param inString the text to display in this text field. * Is copied internally, so must be destroyed * by caller if not const. */ void setText( const char *inString ); /** * Gets the text displayed by this text field. * * @return the text to display in this textField. * Must not be destroyed or modified by caller. */ char *getText(); /** * Ignore next 1 key press. * * Useful for when this field becomes focused due to a key press * (like from a key-based menu selection) but we don't want that * key press passed immediately into the field. */ void ignoreNextKey(); void setCursorPosition( int inCharPosition ); // override functions in GUIComponentGL virtual void setFocus( char inFocus ); virtual char isFocused(); virtual void keyPressed( unsigned char inKey, double inX, double inY ); virtual void specialKeyPressed( int inKey, double inX, double inY ); virtual void keyReleased( unsigned char inKey, double inX, double inY ); virtual void specialKeyReleased( int inKey, double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); virtual void fireRedraw(); // override this to make click area slightly bigger // (so it includes the border) virtual char isInside( double inX, double inY ); protected: /** * Adjusts the cursor position to bring it in bounds of the * current string. */ void fixCursorPosition(); TextGL *mText; char *mString; Color *mBorderColor; Color *mFocusedBorderColor; Color *mBackgroundColor; Color *mCursorColor; Color *mCurrentBorderColor; int mLengthLimit; char mForceUppercase; char mFocused; // cursor position, in number of characters int mCursorPosition; double mBorderWidth; char mIgnoreNextKey; char mPressStartedOnUs; }; inline TextFieldGL::TextFieldGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, double inBorderWidth, const char *inString, TextGL *inText, Color *inBorderColor, Color *inFocusedBorderColor, Color *inBackgroundColor, Color *inCursorColor, int inLengthLimit, char inForceUppercase ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mText( inText ), mString( NULL ), mBorderColor( inBorderColor ), mFocusedBorderColor( inFocusedBorderColor ), mBackgroundColor( inBackgroundColor ), mCursorColor( inCursorColor ), mCurrentBorderColor( inBorderColor ), mLengthLimit( inLengthLimit ), mForceUppercase( inForceUppercase ), mFocused( false ), mCursorPosition( 0 ), mBorderWidth( inBorderWidth ), mIgnoreNextKey( false ), mPressStartedOnUs( false ) { setText( inString ); } inline TextFieldGL::~TextFieldGL() { if( mString != NULL ) { delete [] mString; } delete mBorderColor; delete mFocusedBorderColor; delete mBackgroundColor; delete mCursorColor; } inline void TextFieldGL::setText( const char *inString ) { if( mString != NULL ) { delete [] mString; } int length = strlen( inString ) + 1; mString = new char[ length ]; memcpy( mString, inString, length ); } inline char *TextFieldGL::getText() { return mString; } inline void TextFieldGL::ignoreNextKey() { mIgnoreNextKey = true; } inline void TextFieldGL::setCursorPosition( int inCharPosition ) { int maxPosition = strlen( mString ); if( inCharPosition > maxPosition ) { inCharPosition = maxPosition; } mCursorPosition = inCharPosition; } inline void TextFieldGL::setFocus( char inFocus ) { mFocused = inFocus; if( mFocused ) { mCurrentBorderColor = mFocusedBorderColor; } else { mCurrentBorderColor = mBorderColor; } } inline char TextFieldGL::isFocused() { return mFocused; } inline void TextFieldGL::keyPressed( unsigned char inKey, double inX, double inY ) { if( mIgnoreNextKey ) { mIgnoreNextKey = false; return; } //printf( "key press: %d\n", inKey ); // backspace and delete if( inKey == 127 || inKey == 8 ) { if( mCursorPosition != 0 ) { int length = strlen( mString ); if( length != 0 ) { char *newString = new char[ length ]; // copy mString up to the last char before the deleted char memcpy( newString, mString, mCursorPosition-1 ); // copy the portion of mString after the deleted char // this will include the trailing \0 memcpy( &( newString[mCursorPosition-1] ), &( mString[mCursorPosition] ), length - mCursorPosition + 1 ); setText( newString ); delete [] newString; mCursorPosition--; fireActionPerformed( this ); } } } // allowable character key, from space up to tilde, then extended ascii // ignore after length limit reached else if( ( (inKey >= 32 && inKey <= 126) || (inKey >= 160 ) ) && ( mLengthLimit < 0 || (int)strlen( mString ) < mLengthLimit ) ) { // add a character to our string int oldStringLength = strlen( mString ); int length = oldStringLength + 2; char *newString = new char[ length ]; if( mCursorPosition != 0 ) { // copy chars up to cursor position memcpy( newString, mString, mCursorPosition ); } // copy chars after cursor position, including trailing \0 memcpy( &( newString[mCursorPosition+1] ), &( mString[mCursorPosition] ), oldStringLength - mCursorPosition + 1 ); if( mForceUppercase ) { inKey = toupper( inKey ); } // now stick in the inserted char newString[ mCursorPosition ] = inKey; setText( newString ); delete [] newString; mCursorPosition++; fireActionPerformed( this ); } // else a non-valid key hit } inline void TextFieldGL::specialKeyPressed( int inKey, double inX, double inY ) { if( mIgnoreNextKey ) { mIgnoreNextKey = false; return; } switch( inKey ) { case MG_KEY_RIGHT: mCursorPosition++; break; case MG_KEY_LEFT: mCursorPosition--; default: break; } fixCursorPosition(); } inline void TextFieldGL::keyReleased( unsigned char inKey, double inX, double inY ) { } inline void TextFieldGL::specialKeyReleased( int inKey, double inX, double inY ) { } inline char TextFieldGL::isInside( double inX, double inY ) { // offset to give text room double offset = mBorderWidth;//mHeight * 0.125; // draw border quad behind background double borderOffset = offset + mBorderWidth; if( inX >= mAnchorX - borderOffset && inX < mAnchorX + mWidth + borderOffset && inY >= mAnchorY - borderOffset && inY < mAnchorY + mHeight + borderOffset ) { return true; } else { return false; } } inline void TextFieldGL::mousePressed( double inX, double inY ) { if( isEnabled() ) { // we'll only get pressed events if the mouse is pressed on us // so we don't need to check isInside status mPressStartedOnUs = true; } } inline void TextFieldGL::mouseReleased( double inX, double inY ) { if( isInside( inX, inY ) && mPressStartedOnUs ) { double offset = mHeight * 0.1; int numChars = strlen( mString ); // measure longer substrings until we find place where mouse clicked char found = false; for( int i=0; imeasureTextWidth( substring ) * mHeight + offset; if( i < numChars ) { // if click is halfway into next char, put cursor before // next char char nextChar[2] = { mString[i], '\0' }; subWidth += mText->measureTextWidth( nextChar ) * mHeight * 0.5; } delete [] substring; if( subWidth > inX - mAnchorX ) { mCursorPosition = i; found = true; } } if( !found ) { // stick cursor at very end mCursorPosition = numChars; } /* mCursorPosition = (int)( 0.5 + strlen( mString ) * ( inX - mAnchorX ) / mWidth ); */ } // reset for next time mPressStartedOnUs = false; } inline void TextFieldGL::fixCursorPosition() { if( mCursorPosition < 0 ) { mCursorPosition = 0; } else { int stringLength = strlen( mString ); if( mCursorPosition > stringLength ) { mCursorPosition = stringLength; } } } inline void TextFieldGL::fireRedraw() { // offset to give text room double offset = mBorderWidth;//mHeight * 0.125; // draw border quad behind background double borderOffset = offset + mBorderWidth; glColor4f( mCurrentBorderColor->r, mCurrentBorderColor->g, mCurrentBorderColor->b, 1.0f ); glBegin( GL_QUADS ); { glVertex2d( mAnchorX - borderOffset, mAnchorY - borderOffset ); glVertex2d( mAnchorX - borderOffset, mAnchorY + mHeight + borderOffset ); glVertex2d( mAnchorX + mWidth + borderOffset, mAnchorY + mHeight + borderOffset ); glVertex2d( mAnchorX + mWidth + borderOffset, mAnchorY - borderOffset ); } glEnd(); // draw the background glColor4f( mBackgroundColor->r, mBackgroundColor->g, mBackgroundColor->b, 1.0f ); glBegin( GL_QUADS ); { glVertex2d( mAnchorX - offset, mAnchorY - offset ); glVertex2d( mAnchorX - offset, mAnchorY + mHeight + offset ); glVertex2d( mAnchorX + mWidth + offset, mAnchorY + mHeight + offset ); glVertex2d( mAnchorX + mWidth + offset, mAnchorY - offset ); } glEnd(); double charWidth = mHeight * strlen( mString ); // draw the text mText->drawText( mString, mAnchorX, mAnchorY, charWidth, mHeight ); if( mFocused ) { // draw the cursor glColor4f( mCursorColor->r, mCursorColor->g, mCursorColor->b, 1.0f ); /* glBegin( GL_LINES ); { // make a temp sub-string and measure its length char *substring = stringDuplicate( mString ); substring[ mCursorPosition ] = '\0'; double subWidth = mText->measureTextWidth( substring ) * mHeight + offset; delete [] substring; double cursorViewX = mAnchorX + subWidth; glVertex2d( cursorViewX, mAnchorY ); glVertex2d( cursorViewX, mAnchorY + mHeight ); } glEnd(); */ // make a temp sub-string and measure its length char *substring = stringDuplicate( mString ); substring[ mCursorPosition ] = '\0'; double subWidth = mText->measureTextWidth( substring ) * mHeight ;//+ offset; delete [] substring; double cursorViewX = mAnchorX + subWidth; double cursorOffset = mBorderWidth / 2; glBegin( GL_QUADS ); { glVertex2d( cursorViewX - cursorOffset, mAnchorY ); glVertex2d( cursorViewX - cursorOffset, mAnchorY + mHeight ); glVertex2d( cursorViewX + cursorOffset, mAnchorY + mHeight ); glVertex2d( cursorViewX + cursorOffset, mAnchorY ); } glEnd(); } /* // draw the border on top of the text and cursor glColor4f( mCurrentBorderColor->r, mCurrentBorderColor->g, mCurrentBorderColor->b, 1.0f ); glBegin( GL_LINE_LOOP ); { glVertex2d( mAnchorX - offset, mAnchorY - offset ); glVertex2d( mAnchorX - offset, mAnchorY + mHeight + offset ); glVertex2d( mAnchorX + mWidth + offset, mAnchorY + mHeight + offset ); glVertex2d( mAnchorX + mWidth + offset, mAnchorY - offset ); } glEnd(); */ } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/SliderGL.h0000640000175000017500000002646011361766724024776 0ustar pabspabs/* * Modification History * * 2001-September-17 Jason Rohrer * Created. * * 2001-September-21 Jason Rohrer * Fixed a bug in thumb positioning in response to mouse position. * * 2001-September-23 Jason Rohrer * Fixed a bug in icon color clearing. * * 2009-December-25 Jason Rohrer * Added control over border/thumb width and thumb increments to fix rounding * errors. * * 2010-January-30 Jason Rohrer * Improved response when clicking. * * 2010-February-8 Jason Rohrer * Distinguish between first press and subsequent dragging. * * 2010-March-31 Jason Rohrer * Changed glColor3f to 4f to fix driver bug on some video cards. * * 2010-April-2 Jason Rohrer * Added functions for setting bar colors after construction. * * 2010-April-15 Jason Rohrer * Distinguish release event. */ #ifndef SLIDER_GL_INCLUDED #define SLIDER_GL_INCLUDED #include "GUIComponentGL.h" #include "minorGems/graphics/Image.h" #include "minorGems/graphics/Color.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include "minorGems/ui/event/ActionListenerList.h" #include /** * A textured slider for GL-based GUIs. * * @author Jason Rohrer */ class SliderGL : public GUIComponentGL, public ActionListenerList { public: /** * Constructs a slider. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inIconImage the image to display * to the left of this slider. Must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. * If set to NULL, then no icon will be drawn. * @param inIconWidthFraction the fraction of the slider's * width that should be taken up by the icon. * @param inBarStartColor the color on the left end of * the slider bar. * Will be destroyed when this class is destroyed. * @param inBarEndColor the color on the right end of * the slider bar. * Will be destroyed when this class is destroyed. * @param inThumbColor the color of the slider thumb. * Will be destroyed when this class is destroyed. * @param inBorderColor the color of the slider border. * Will be destroyed when this class is destroyed. * @param inBorderWidth width of the border. * @param inThumbWidth width of the slider thumb. * @param inMinThumbIncrement the minimum increment * at which to draw the slider thumb. */ SliderGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inIconImage, double inIconWidthFraction, Color *inBarStartColor, Color *inBarEndColor, Color *inThumbColor, Color *inBorderColor, double inBorderWidth, double inThumbWidth, double inMinThumbIncrement ); ~SliderGL(); // both are destroyed by this class void setBarStartColor( Color *inColor ); void setBarEndColor( Color *inColor ); /** * Gets the position of the slider thumb. * * @return the thumb position, in [0,1.0]. */ double getThumbPosition(); /** * Sets the position of the slider thumb. * * Note that if the slider thumb position changes * as a result of this call, then an action * will be fired to all registered listeners. * * @param inPosition the thumb position, in [0,1.0]. */ void setThumbPosition( double inPosition ); // override funxtions in GUIComponentGL virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); virtual void fireRedraw(); // is set to true for action fired from first press on a slider // adjustment, false for subsequent dragging for same adjustment char mJustPressed; // is set to true for action fired from mouse release on slider // at end of dragging char mJustReleased; // true iff the slider is currently being dragged char mDragging; protected: // common call for press and drag virtual void mouseInput( double inX, double inY ); SingleTextureGL *mIconTexture; double mIconWidthFraction; Color *mBarStartColor; Color *mBarEndColor; Color *mThumbColor; Color *mBorderColor; double mThumbPosition; double mBorderWidth; double mThumbWidth; double mMinThumbIncrement; // gets thumb position rounded to nearest increment double getPeggedThumbPosition(); }; inline SliderGL::SliderGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inIconImage, double inIconWidthFraction, Color *inBarStartColor, Color *inBarEndColor, Color *inThumbColor, Color *inBorderColor, double inBorderWidth, double inThumbWidth, double inMinThumbIncrement ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mJustPressed( false ), mJustReleased( false ), mDragging( false ), mIconWidthFraction( inIconWidthFraction ), mBarStartColor( inBarStartColor ), mBarEndColor( inBarEndColor ), mThumbColor( inThumbColor ), mBorderColor( inBorderColor ), mThumbPosition( 0.5 ), mBorderWidth( inBorderWidth ), mThumbWidth( inThumbWidth ), mMinThumbIncrement( inMinThumbIncrement ) { mThumbPosition = (int)( mThumbPosition / mMinThumbIncrement ) * mMinThumbIncrement; if( inIconImage != NULL ) { mIconTexture = new SingleTextureGL( inIconImage ); // the image is no longer needed delete inIconImage; } else { mIconTexture = NULL; } } inline SliderGL::~SliderGL() { if( mIconTexture != NULL ) { delete mIconTexture; } delete mBarStartColor; delete mBarEndColor; delete mThumbColor; delete mBorderColor; } inline void SliderGL::setBarStartColor( Color *inColor ) { delete mBarStartColor; mBarStartColor = inColor; } inline void SliderGL::setBarEndColor( Color *inColor ) { delete mBarEndColor; mBarEndColor = inColor; } inline double SliderGL::getThumbPosition() { return mThumbPosition; } inline void SliderGL::setThumbPosition( double inPosition ) { if( mThumbPosition != inPosition ) { mThumbPosition = inPosition; fireActionPerformed( this ); } } inline double SliderGL::getPeggedThumbPosition() { double barWidth = mWidth * ( 1 - mIconWidthFraction ); double thumbUsableBarWidth = barWidth - mThumbWidth; return (int)( mThumbPosition * thumbUsableBarWidth / mMinThumbIncrement ) * mMinThumbIncrement / thumbUsableBarWidth; } inline void SliderGL::mouseInput( double inX, double inY ) { if( mDragging ) { double barWidth = mWidth * ( 1 - mIconWidthFraction ); double iconEndX = mAnchorX + mWidth - barWidth; // we want the mouse centered over the thumb double thumbUsableBarWidth = barWidth - mThumbWidth; mThumbPosition = ( inX - ( iconEndX + 0.5 * mThumbWidth ) ) / thumbUsableBarWidth; if( mThumbPosition > 1 ) { mThumbPosition = 1; } else if( mThumbPosition < 0 ) { mThumbPosition = 0; } // fire to listeners fireActionPerformed( this ); } } inline void SliderGL::mouseDragged( double inX, double inY ) { mJustPressed = false; mJustReleased = false; mouseInput( inX, inY ); } inline void SliderGL::mousePressed( double inX, double inY ) { // presses passed to us only on direct hit // (use to filter mouse activity that starts outside us) mDragging = true; mJustPressed = true; mJustReleased = false; mouseInput( inX, inY ); } inline void SliderGL::mouseReleased( double inX, double inY ) { // always stop dragging on a release mJustReleased = true; // one last event mouseInput( inX, inY ); mJustPressed = false; mDragging = false; } inline void SliderGL::fireRedraw() { // these values will change below if there is an icon double barStartX = mAnchorX; double barWidth = mWidth; if( mIconTexture != NULL ){ // first, draw the icon // set our texture mIconTexture->enable(); double textureStartX = mAnchorX; double textureStartY = mAnchorY; double textureEndX = mAnchorX + mIconWidthFraction * mWidth; double textureEndY = mAnchorY + mHeight; glBegin( GL_QUADS ); { // make sure our color is set to white for our texture // (to avoid leftover colors) glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); glTexCoord2f( 0, 1.0f ); glVertex2d( textureStartX, textureStartY ); glTexCoord2f( 1.0f, 1.0f ); glVertex2d( textureEndX, textureStartY ); glTexCoord2f( 1.0f, 0 ); glVertex2d( textureEndX, textureEndY ); glTexCoord2f( 0, 0 ); glVertex2d( textureStartX, textureEndY ); } glEnd(); mIconTexture->disable(); barWidth = mWidth * ( 1 - mIconWidthFraction ); barStartX = textureEndX; } // end check for a non-NULL icon texture // now, draw the slider bar double barHeight = 0.5 * mHeight; // center the bar vertically double barStartY = mAnchorY + ( mHeight - barHeight ) * 0.5; // draw it's border rectangle underneath glColor4f( mBorderColor->r, mBorderColor->g, mBorderColor->b, 1.0f ); glBegin( GL_QUADS ); { glVertex2d( barStartX, barStartY ); glVertex2d( barStartX, barStartY + barHeight ); glVertex2d( barStartX + barWidth, barStartY + barHeight ); glVertex2d( barStartX + barWidth, barStartY ); } glEnd(); // draw its gradient-filled center, smaller glBegin( GL_QUADS ); { // start of bar glColor4f( mBarStartColor->r, mBarStartColor->g, mBarStartColor->b, 1.0f ); glVertex2d( barStartX + mBorderWidth, barStartY + mBorderWidth ); glVertex2d( barStartX + mBorderWidth, barStartY + barHeight - mBorderWidth ); // end of bar glColor4f( mBarEndColor->r, mBarEndColor->g, mBarEndColor->b, 1.0f ); glVertex2d( barStartX + barWidth - mBorderWidth, barStartY + barHeight - mBorderWidth ); glVertex2d( barStartX + barWidth - mBorderWidth, barStartY + mBorderWidth ); } glEnd(); // now draw the thumb // we don't want the thumb going off the ends of the bar double thumbUsableBarWidth = barWidth - mThumbWidth; // round to nearest increment double thumbDrawPosition = getPeggedThumbPosition(); double thumbStartX = thumbDrawPosition * thumbUsableBarWidth + barStartX; double thumbEndX = thumbStartX + mThumbWidth; // draw its border, underneath glColor4f( mBorderColor->r, mBorderColor->g, mBorderColor->b, 1.0f ); glBegin( GL_QUADS ); { glVertex2d( thumbStartX, mAnchorY ); glVertex2d( thumbStartX, mAnchorY + mHeight ); glVertex2d( thumbEndX, mAnchorY + mHeight ); glVertex2d( thumbEndX, mAnchorY ); } glEnd(); // draw fill, smaller glColor4f( mThumbColor->r, mThumbColor->g, mThumbColor->b, 1.0f ); glBegin( GL_QUADS ); { glVertex2d( thumbStartX + mBorderWidth, mAnchorY + mBorderWidth ); glVertex2d( thumbStartX + mBorderWidth, mAnchorY + mHeight - mBorderWidth ); glVertex2d( thumbEndX - mBorderWidth, mAnchorY + mHeight - mBorderWidth ); glVertex2d( thumbEndX - mBorderWidth, mAnchorY + mBorderWidth ); } glEnd(); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/TextGL.h0000640000175000017500000005175311373326622024473 0ustar pabspabs/* * Modification History * * 2001-October-13 Jason Rohrer * Created. * Changed to support alpha-blended fonts, even for 3-channel font textures. * Fixed a bug in the font coloring. * * 2001-November-3 Jason Rohrer * Fixed some missing includes. * * 2006-December-26 Jason Rohrer * Added support for variable-width characers. * * 2007-March-4 Jason Rohrer * Added support for character z coordinates. * * 2007-March-15 Jason Rohrer * Added support for pixelated characters. Fixed font spacing issue. * * 2008-September-29 Jason Rohrer * Added support for font images with non-power-of-2 dimensions. * * 2010-January-29 Jason Rohrer * Fixed text measurement. * * 2010-May-7 Jason Rohrer * Extended ASCII. * * 2010-May-12 Jason Rohrer * Function to measure text height for Extended ASCII accents. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ #ifndef TEXT_GL_INCLUDED #define TEXT_GL_INCLUDED #include #include #include #include #include "minorGems/graphics/Color.h" #include "minorGems/graphics/Image.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" /** * A class for drawing text strings to an OpenGL context. * Fonts are texture maps. * * @author Jason Rohrer */ class TextGL { public: /** * Constructs a gl text object. * * Note that this function can only be called * after an OpenGL drawing context has been created. * * @param inImage the image to use as a font texture. * If image is not square and height is greater than width, * extra height in each character is assumed to be the accent region * where characters can extend above normal vertical space. * Should have dimensions that are a * power of 2. * If dimensions are not a power of 2, then class will compensate * by padding image. * Fonts in the image are assumed to be in row-major ASCII order, * with 16 characters per row. * Must be destroyed by caller. Can be destroyed as soon as * this constructor returns. * @param inUseAlpha true iff alpha blending should * be used when rendering the font textures. If alpha * blending is enabled and the image has only 3 channels, * only the red channel (channel 0) * of the image is used: it is interpreted as the alpha * channel for a white fontset. * @param inFixedWidth true iff font should be displayed in fixed * width mode. If false, a width will be measured for each * character from the alpha channel. If inUseAlpha is false, * the font is always fixed width. Defaults to true. * @param inCharacterSpacingFraction the extra width-fraction of * spacing around each character. A fraction of a full character * width. Space is added on *both* sides of character. * Defaults to 0.1 * @param inSpaceWidthFraction the fraction of a full character width * occupied by a space. Defaults to 1.0 (a full width). */ TextGL( Image *inImage, char inUseAlpha = false, char inFixedWidth = true, double inCharacterSpacingFraction = 0.1, double inSpaceWidthFraction = 1.0 ); virtual ~TextGL(); /** * Sets the main color of the font. * * If the gradient color is non-NULL, this color will be at the * top of the fonts. Otherwise, the entire extent * of the font will be this color. * * @param inColor the main color of the font. * Will be destroyed when this class is destroyed. */ void setFontColor( Color *inColor ); /** * Gets the main color of the font. * * If the gradient color is non-NULL, this color will be at the * top of the fonts. Otherwise, the entire extent * of the font will be this color. * * @return the main color of the font. * Will be destroyed when this class is destroyed. */ Color *getFontColor(); /** * Sets the gradient color of the font. * * If non-NULL, this color will be at the * bottom of the fonts. * * @param inColor the gradient color of the font, or * NULL to disable the gradient. * Will be destroyed when this class is destroyed. */ void setGradientColor( Color *inColor ); /** * Gets the gradient color of the font. * * If non-NULL, this color will be at the * bottom of the fonts. * * @return the gradient color of the font, or * NULL if disable gradient disabled. * Will be destroyed when this class is destroyed. */ Color *getGradientColor(); /** * Sets the z coordinate for font drawing. * * Defaults to 0. * * @param inZ the z coordinate. */ void setFontZ( double inZ ); /** * Sets smothing. * * @param inSmooth true to enable smoothing of font texture (linear * interpolation) or false to show pure texture pixels (nearest * neighbor). */ void setSmoothing( char inSmooth ); /** * Draws a text string into a specified region of the * context. Text is squeezed to fit into the region. * * Uses the currently active OpenGL projection settings. * * @param inString the \0-delimited string to draw. * Must be destroyed by caller if not const. * @param inX the x coordinate of the region's lower-left * corner. * @param inY the y coordinate of the region's lower-left * corner. * @param inWidth the width of the region. * @param inHeight the height of the region. */ void drawText( const char *inString, double inX, double inY, double inWidth, double inHeight ); /** * Measures the width of a string of text. * * @inString the string to measure. Destroyed by caller. * * @return the width of the display text in character height * units. A return value of 8.5 means the string would be as * wide as 8.5 characters are high (if the string was displayed * with a 1:1 aspect ratio). */ double measureTextWidth( const char *inString ); /** * Measures the height of a string of text * * @inString the string to measure. Destroyed by caller. * * @return the height of the display text in character height * units (basic block character height, equal to block character * width, without considering special * accent space). A return value of 1.5 means the string extends * above the basic block height by 0.5. * This is the true height that a call to drawText would use * (measureTextHeight * inHeight). */ double measureTextHeight( const char *inString ); protected: double mXImageFractionUsed; double mYImageFractionUsed; // height/width of used area of whole font texture double mHWRatio; Color *mFontColor; Color *mGradientColor; double mZ; char mSmooth; SingleTextureGL *mFontTexture; char mUseAlpha; double *mStartWidthFractionMetrics; double *mEndWidthFractionMetrics; double mCharacterSpacingFraction; double *mHeightFractionMetrics; /** * Draws a character into a specified region of the * context. The character is squeezed to fit into the region. * * Uses the currently active OpenGL projection settings. * * @param inCharacter the character to draw. Unsigned to support * extended ASCII. * @param inX the x coordinate of the region's lower-left * corner. * @param inY the y coordinate of the region's lower-left * corner. * @param inWidth the width of the region. * @param inHeight the height of the region. * Note that character accents may extend above inHeight. * @param outUsedWidth pointer to where the actual width used * by this character should be returned. */ void drawCharacter( unsigned char inCharacter, double inX, double inY, double inWidth, double inHeight, double *outUsedWidth ); /** * Generates internal character width metrics from a font image. * * @param inFontImage the font image. Destroyed by caller. * @param inFixedWidth, inSpaceWidthFraction, * inCharacterSpacingFractionsame as parameters for the TextGL * constructor. */ void generateCharacterWidths( Image *inFontImage, char inFixedWidth, double inCharacterSpacingFraction, double inSpaceWidthFraction ); }; inline TextGL::TextGL( Image *inImage, char inUseAlpha, char inFixedWidth, double inCharacterSpacingFraction, double inSpaceWidthFraction ) : mXImageFractionUsed( 1.0 ), mYImageFractionUsed( 1.0 ), mFontColor( new Color( 1.0f, 1.0f, 1.0f, 1.0f ) ), mGradientColor( NULL ), mZ( 0 ), mSmooth( false ), mUseAlpha( inUseAlpha ), mStartWidthFractionMetrics( new double[ 256 ] ), mEndWidthFractionMetrics( new double[ 256 ] ), mCharacterSpacingFraction( inCharacterSpacingFraction ), mHeightFractionMetrics( new double[ 256 ] ) { // pad image to next power of 2 in each dimension int w = inImage->getWidth(); int h = inImage->getHeight(); int paddedW = w; int paddedH = h; double log2w = log( w ) / log( 2 ); double log2h = log( h ) / log( 2 ); int next2PowerW = (int)( ceil( log2w ) ); int next2PowerH = (int)( ceil( log2h ) ); if( next2PowerW != log2w ) { paddedW = (int)( pow( 2, next2PowerW ) ); mXImageFractionUsed = (double)w / (double)paddedW; } if( next2PowerH != log2h ) { paddedH = (int)( pow( 2, next2PowerH ) ); mYImageFractionUsed = (double)h / (double)paddedH; } mHWRatio = h / (double)w; // pad the image int numChannels = inImage->getNumChannels(); Image paddedImage( paddedW, paddedH, numChannels, true ); for( int c=0; cgetChannel( c ); for( int y=0; ygetChannel(3), paddedImage.getChannel(0), numPixels * sizeof( double ) ); // set other channels to white for( int c=0; c<3; c++ ) { double *channel = fontImage->getChannel( c ); for( int p=0; penable(); if( mSmooth ) { glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); } else { glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); } glBegin( GL_QUADS ); { if( mGradientColor != NULL ) { glColor4f( mGradientColor->r, mGradientColor->g, mGradientColor->b, mGradientColor->a ); } else { glColor4f( mFontColor->r, mFontColor->g, mFontColor->b, mFontColor->a ); } glTexCoord2f( textureStartX, textureStartY ); glVertex3d( charStartX, charStartY, mZ ); glTexCoord2f( textureEndX, textureStartY ); glVertex3d( charEndX, charStartY, mZ ); glColor4f( mFontColor->r, mFontColor->g, mFontColor->b, mFontColor->a ); glTexCoord2f( textureEndX, textureEndY ); glVertex3d( charEndX, charEndY, mZ ); glTexCoord2f( textureStartX, textureEndY ); glVertex3d( charStartX, charEndY, mZ ); } glEnd(); mFontTexture->disable(); } inline void TextGL::generateCharacterWidths( Image *inFontImage, char inFixedWidth, double inCharacterSpacingFraction, double inSpaceWidthFraction ) { if( mUseAlpha && ! inFixedWidth ) { double *alphaChannel = inFontImage->getChannel( 3 ); int imageWidth = inFontImage->getWidth(); int imageHeight = inFontImage->getHeight(); int pixelsPerCharacterX = (int)( mXImageFractionUsed * imageWidth ) / 16; int pixelsPerCharacterY = (int)( mYImageFractionUsed * imageHeight ) / 16; // look at each row and column in texture image for( int r=0; r<16; r++ ) { int charStartY = pixelsPerCharacterY * r; int charEndY = charStartY + pixelsPerCharacterY; for( int c=0; c<16; c++ ) { int charStartX = pixelsPerCharacterX * c; int charEndX = charStartX + pixelsPerCharacterX; // by "ink", I mean part of the image that has a non-zero // alpha // we want to find how far this character stretches to the // left and right within it's fixed-width region int smallestXWithInk = charEndX; int largestXWithInk = charStartX; // also how high (but don't care about how low) // also note that y coordinates inverted. int smallestYWithInk = charEndY; for( int x=charStartX; x 0 ) { // hit some ink if( x < smallestXWithInk ) { smallestXWithInk = x; } if( x > largestXWithInk ) { largestXWithInk = x; } if( y < smallestYWithInk ) { smallestYWithInk = y; } } } } int i = r * 16 + c; if( smallestXWithInk <= largestXWithInk ) { mStartWidthFractionMetrics[ i ] = (double)( smallestXWithInk - charStartX ) / (double)pixelsPerCharacterX; mEndWidthFractionMetrics[ i ] = (double)( largestXWithInk - charStartX ) / (double)pixelsPerCharacterX; mStartWidthFractionMetrics[ i ] -= inCharacterSpacingFraction; mEndWidthFractionMetrics[ i ] += inCharacterSpacingFraction; // note: base this on pixelsPerCharacterX, // because height of 1.0 is basic block size, not // extended (accent) size mHeightFractionMetrics[ i ] = (double)( charEndY - smallestYWithInk ) / (double)pixelsPerCharacterX; } else { // empty character image // treat as a space mStartWidthFractionMetrics[ i ] = 0; mEndWidthFractionMetrics[ i ] = inSpaceWidthFraction; mHeightFractionMetrics[ i ] = 0; } } } } else { // not using alpha (or fixed width requested) // fix width for( int i=0; i<256; i++ ) { mStartWidthFractionMetrics[ i ] = 0 - inCharacterSpacingFraction; mEndWidthFractionMetrics[ i ] = 1 + inCharacterSpacingFraction; // assume it consumes full vertical space in each block mHeightFractionMetrics[ i ] = mHWRatio; } // special case for space mStartWidthFractionMetrics[ 32 ] = 0; mEndWidthFractionMetrics[ 32 ] = inSpaceWidthFraction; } } inline double TextGL::measureTextWidth( const char *inString ) { double width = 0; int numChars = strlen( inString ); for( int i=0; i height ) { height = charHeight; } } return height; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/test2.bmp0000640000175000017500000001406607352744545024722 0ustar pabspabsBM66(@ QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ’’’™™™™™™’’’‚‚‚xxxggg\\\VVVQQQQQQQQQQQQQQQQQQSSS^^^qqq………–––™™™™™™———jjjZZZSSSQQQQQQQQQQQQQQQQQQUUUfff~~~™™™™™™™™™™™™†††uuu\\\UUUQQQQQQQQQQQQQQQQQQQQQQQQUUUqqq‹‹‹™™™™™™”””‰‰‰{{{mmm^^^™™™™™™™™™™™™™™™™™™™™™oooRRRQQQQQQQQQTTTiii‹‹‹™™™™™™™™™™™™™™™™™™™™™™™™–––ŠŠŠsss___SSSQQQQQQQQQXXXwww•••™™™™™™“““˜˜˜˜˜˜xxx]]]QQQQQQQQQQQQQQQQQQRRRooo™™™™™™™™™™™™™™™™™™™™™™™™”””™™™™™™™™™•••†††||||||ˆˆˆ–––UUUQQQQQQSSSjjjŽŽŽ™™™™™™™™™™™™™™™“““ŠŠŠŠŠŠ‹‹‹’’’™™™–––‹‹‹sssYYYQQQSSSrrr–––™™™–––€€€hhhdddddddddrrr”””™™™–––‚‚‚```QQQQQQQQQQQQQQQTTT™™™™™™™™™™™™™™™‹‹‹~~~||||||™™™™™™•••{{{YYYUUUUUUVVVYYY^^^QQQQQQQQQ^^^‰‰‰™™™™™™™™™™™™™™™^^^ZZZZZZ[[[___qqq™™™•••}}}TTTVVV“““™™™™™™˜˜˜sssQQQQQQQQQQQQRRRooo™™™™™™———~~~YYYQQQQQQQQQQQQUUU™™™™™™™™™™™™™™™†††cccVVVUUUUUU™™™™™™ŒŒŒ```QQQQQQQQQQQQQQQQQQQQQQQQSSSmmm™™™™™™™™™™™™™™™xxxTTTQQQQQQQQQQQQQQQQQQTTTlll‰‰‰TTTVVV“““™™™™™™™™™[[[QQQQQQQQQQQQYYY™™™™™™™™™’’’lllQQQQQQQQQQQQUUU™™™™™™™™™™™™™™™oooQQQQQQQQQQQQ™™™™™™ŽŽŽdddQQQQQQQQQQQQQQQQQQQQQQQQWWW{{{™™™™™™™™™™™™’’’]]]QQQQQQQQQQQQQQQQQQQQQQQQQQQ[[[fffSSSSSSqqq“““™™™———\\\QQQQQQQQQQQQddd™™™™™™™™™™™™yyyVVVQQQQQQQQQTTT™™™™™™™™™™™™sssSSSQQQQQQQQQ™™™™™™’’’pppQQQQQQQQQQQQQQQQQQQQQQQQYYY………™™™™™™™™™™™™€€€XXXQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQWWWiiirrrnnn```SSSQQQQQQQQQVVV………™™™™™™™™™™™™~~~YYYQQQQQQQQQSSS™™™™™™™™™™™™}}}YYYQQQQQQQQQ™™™™™™˜˜˜~~~QQQQQQQQQQQQQQQQQQQQQQQQZZZ‡‡‡™™™™™™™™™™™™wwwVVVQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRQQQQQQQQQQQQQQQaaaƒƒƒ———™™™™™™™™™™™™{{{WWWQQQQQQQQQRRRrrr™™™™™™™™™™™™‰‰‰```QQQQQQQQQ™™™™™™™™™ƒƒƒVVVQQQQQQQQQQQQQQQQQQQQQZZZ†††™™™™™™™™™™™™xxxZZZUUUUUUUUUQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRZZZwww•••™™™™™™™™™™™™™™™‘‘‘jjjQQQQQQQQQQQQQQQeee™™™™™™™™™™™™‘‘‘eeeQQQQQQQQQ™™™™™™™™™ŠŠŠbbbQQQQQQQQQQQQQQQQQQQQQWWW}}}™™™™™™™™™™™™ŠŠŠ~~~||||||zzzmmmkkkddd[[[SSSQQQQQQQQQQQQQQQQQQQQQQQQQQQUUUkkkŠŠŠ———™™™™™™™™™™™™™™™•••{{{YYYQQQQQQQQQQQQQQQZZZ–––™™™™™™™™™™™™mmmRRRQQQQQQ™™™™™™™™™jjjQQQQQQQQQQQQQQQQQQQQQSSSooo™™™™™™™™™™™™•••ŒŒŒ’’’˜˜˜™™™™™™–––‡‡‡xxxbbbSSSQQQQQQQQQQQQQQQQQQWWWsss‘‘‘™™™™™™™™™™™™™™™™™™“““xxx[[[QQQQQQQQQQQQQQQQQQXXX‹‹‹™™™™™™™™™™™™wwwVVVQQQQQQ™™™™™™™™™”””sssQQQQQQQQQQQQQQQQQQQQQQQQddd”””™™™™™™™™™———fff]]]```uuuŠŠŠ™™™™™™™™™———}}}WWWQQQQQQQQQQQQUUUrrr”””™™™™™™™™™™™™™™™™™™‘‘‘kkkUUUQQQQQQQQQQQQQQQQQQQQQUUU{{{™™™™™™™™™™™™ZZZQQQQQQ™™™™™™™™™™™™|||SSSQQQQQQQQQQQQQQQQQQQQQXXX|||™™™™™™™™™™™™vvvTTTQQQQQQRRRbbb†††–––™™™™™™™™™‰‰‰XXXQQQQQQQQQeee‘‘‘™™™™™™™™™™™™™™™’’’xxxUUUQQQQQQQQQQQQQQQQQQQQQQQQQQQSSSqqq™™™™™™™™™™™™ŒŒŒ^^^QQQQQQ™™™™™™™™™™™™„„„\\\QQQQQQQQQQQQQQQQQQQQQRRRhhh™™™™™™™™™ZZZQQQQQQQQQQQQ\\\–––™™™™™™™™™pppQQQQQQQQQiii™™™™™™™™™™™™™™™ˆˆˆiiiQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQddd™™™™™™™™™™™™™™™cccQQQQQQ™™™™™™™™™™™™ŠŠŠcccQQQQQQQQQQQQQQQQQQQQQQQQUUUsss”””™™™™™™™™™pppQQQQQQQQQQQQQQQ```‡‡‡™™™™™™™™™QQQQQQQQQfff“““™™™™™™™™™†††```QQQQQQQQQRRR^^^kkk]]]RRRQQQQQQQQQQQQQQQ___’’’™™™™™™™™™™™™nnnSSSQQQ™™™™™™™™™™™™ŽŽŽhhhQQQQQQQQQQQQQQQQQQQQQQQQQQQXXX{{{–––™™™™™™“““]]]QQQQQQQQQQQQRRR{{{™™™™™™™™™™™™QQQQQQQQQ___‡‡‡™™™™™™™™™fffQQQQQQQQQQQQZZZ~~~“““‹‹‹gggRRRQQQQQQQQQQQQ\\\‰‰‰™™™™™™™™™™™™uuuUUUQQQ™™™™™™™™™™™™™™™xxxZZZZZZZZZZZZZZZYYYSSSQQQQQQQQQTTTsss‘‘‘˜˜˜™™™aaaSSSQQQQQQXXX€€€™™™™™™™™™’’’QQQQQQQQQRRRkkk™™™™™™mmmSSSQQQQQQSSSooo™™™™™™™™™‰‰‰^^^QQQQQQVVV^^^aaa€€€™™™™™™™™™™™™‰‰‰aaaZZZ™™™™™™™™™™™™™™™‘‘‘ŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠ†††___QQQQQQQQQQQQSSSooo‹‹‹———˜˜˜sssffffffyyy’’’™™™™™™™™™oooQQQQQQQQQQQQWWWsss‘‘‘™™™nnn```ZZZcccŠŠŠ™™™™™™™™™‡‡‡]]]QQQQQQkkk———ŽŽŽ’’’™™™™™™™™™™™™”””ŒŒŒŠŠŠ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™“““dddQQQQQQQQQQQQQQQSSS```tttŽŽŽ™™™———‘‘‘‘‘‘———™™™™™™”””€€€WWWQQQQQQQQQQQQQQQUUUhhh………–––•••ŒŒŒ………‰‰‰™™™™™™™™™‘‘‘pppUUUQQQQQQ[[[zzz‘‘‘™™™™™™™™™™™™™™™™™™™™™™™™€€€™™™™™™™™™™™™nnn____________^^^TTTQQQQQQQQQQQQQQQQQQQQQSSS\\\nnn™™™™™™‘‘‘‚‚‚jjjXXXQQQQQQQQQQQQQQQQQQQQQQQQXXXggg{{{………™™™™™™™™™‘‘‘}}}eeeTTTQQQQQQQQQQQQUUU___uuu™™™™™™™™™™™™yyyaaaSSSqqq–––™™™™™™™™™mmmQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQccc‹‹‹™™™™™™™™™………UUUQQQWWW€€€™™™™™™™™™sssTTTQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRooo“““™™™™™™UUUQQQRRRbbb‹‹‹™™™™™™~~~YYYQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQXXX}}}———™™™™™™[[[QQQQQQTTTppp”””™™™ˆˆˆ___QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQccc‹‹‹™™™™™™jjjQQQQQQQQQUUU```ddd___TTTQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ]]]ddddddWWWQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQCultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/StickyButtonGL.h0000640000175000017500000000663707351162241026206 0ustar pabspabs/* * Modification History * * 2001-September-16 Jason Rohrer * Created. */ #ifndef STICKY_BUTTON_GL_INCLUDED #define STICKY_BUTTON_GL_INCLUDED #include "ButtonGL.h" #include "minorGems/graphics/Image.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" /** * A button that can be toggled between a pressed * and unpressed state. * * @author Jason Rohrer */ class StickyButtonGL : public ButtonGL { public: /** * Constructs a button. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inUnpressedImage the image to display * when this button is unpressed. Must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. * @param inPressedImage the image to display * when this button is pressed. Must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. */ StickyButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inUnpressedImage, Image *inPressedImage ); /** * Gets whether this button is toggled to a pressed state. * * @return true iff this button is pressed. */ virtual char isPressed(); /** * Sets this button's state. * * Note that if this function call causes * this button's state to change, then all * registered ActionListeners are notified. * * @param inPressed true iff this button should be * pressed. */ virtual void setPressed( char inPressed ); // override funxtions in GUIComponentGL virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); }; inline StickyButtonGL::StickyButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inUnpressedImage, Image *inPressedImage ) : ButtonGL( inAnchorX, inAnchorY, inWidth, inHeight, inUnpressedImage, inPressedImage ) { } inline char StickyButtonGL::isPressed() { if( mCurrentTexture == mPressedTexture ) { return true; } else { return false; } } inline void StickyButtonGL::setPressed( char inPressed ) { SingleTextureGL *lastTexture = mCurrentTexture; if( inPressed ) { mCurrentTexture = mPressedTexture; } else { mCurrentTexture = mUnpressedTexture; } // check for state change if( lastTexture != mCurrentTexture ) { fireActionPerformed( this ); } } inline void StickyButtonGL::mouseDragged( double inX, double inY ) { // do nothing until the release } inline void StickyButtonGL::mousePressed( double inX, double inY ) { // do nothing until the release } inline void StickyButtonGL::mouseReleased( double inX, double inY ) { // Note the following situation: // If the mouse is pressed on another button and then // released on this button, this button will toggle... // This is non-standard behavior, but the situation occurs // rarely. By ignoring this situation, we save a bit of coding, // but this may need to be fixed later. if( isInside( inX, inY ) ) { // toggle our state to the opposite of it's current setting setPressed( !isPressed() ); // this will fire an action automatically } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/GUITranslatorGL.h0000640000175000017500000002265111356702061026234 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * * 2001-September-16 Jason Rohrer * Changed so that translation preserves the aspect * ration of GUI components. * Fixed a bug with inverted y components of mouse coordinates. * * 2001-October-29 Jason Rohrer * Added support for focus. * * 2001-November-3 Jason Rohrer * Changed translation methods to be public. * * 2006-July-3 Jason Rohrer * Fixed warnings. Changed to be a scene handler. * Fixed problems with coordinate translation. * * 2008-October-27 Jason Rohrer * Added support for setting viewport size separate from screen size. * * 2009-December-24 Jason Rohrer * Changed behavior to be consistent with ScreenGL's square, centered viewport. * * 2009-December-25 Jason Rohrer * Added support for translated dimension ranges other than [0,1]. * * 2010-April-6 Jason Rohrer * Fixed behavior when screen image does not fill entire screen. */ #ifndef GUI_TRANSLATOR_GL_INCLUDED #define GUI_TRANSLATOR_GL_INCLUDED #include "GUIComponentGL.h" #include #include "minorGems/graphics/openGL/MouseHandlerGL.h" #include "minorGems/graphics/openGL/KeyboardHandlerGL.h" #include "minorGems/graphics/openGL/SceneHandlerGL.h" #include "minorGems/graphics/openGL/ScreenGL.h" /** * A class that translates coordinates for a gui component. * * Notes on intended use: * This class is best used as a wrapper for the entire * GL-based gui. In this case, the component passed to the * constructor would be a GUIContainerGL containing the * entire GUI. This class can be thought of as a wrapper * for interfacing size-independent GUI components to the * screen. * * @author Jason Rohrer */ class GUITranslatorGL : public MouseHandlerGL, public KeyboardHandlerGL, public SceneHandlerGL { public: /** * Constructs a translator. * * @param inComponent the component for which events * will be translated and delegated. All events will * be delegated to inComponent, regardless of whether the * events pass the inComponent->isInside() test. * Will be destroyed when this class is destroyed. * @param inScreen the screen whose size is used during * event translation. Must be destroyed by caller after * this class is destroyed. * @param inMaxDimension the translated width of the maximum * dimension of the screen. Defaults to 1.0 * The larger dimension of the screen is mapped to [0,inMaxDimension] * while the smaller dimension is mapped to a subset of this range, * centered. */ GUITranslatorGL( GUIComponentGL *inComponent, ScreenGL *inScreen, double inMaxDimension = 1.0 ); virtual ~GUITranslatorGL(); /** * Translates screen coordinates to gui coordinates. * * @param inX the x value of the screen coordinate. * @param inY the y value of the screen coordinate. * @param outX a pointer to an value in which the gui x * component will be returned. * @param outY a pointer to an value in which the gui y * component will be returned. */ void translate( int inX, int inY, double *outX, double *outY ); /** * Translates screen coordinates to gui coordinates. * * @param inX the x value of the screen coordinate. * * @return the gui x component will be returned. */ double translateX( int inX ); /** * Translates screen coordinates to gui coordinates. * * @param inX the x value of the screen coordinate. * * @return the gui x component will be returned. */ double translateY( int inY ); // implements the MouseHandlerGL interface virtual void mouseMoved( int inX, int inY ); virtual void mouseDragged( int inX, int inY ); virtual void mousePressed( int inX, int inY ); virtual void mouseReleased( int inX, int inY ); // implements the KeyboardHandlerGL interface virtual char isFocused(); virtual void keyPressed( unsigned char inKey, int inX, int inY ); virtual void specialKeyPressed( int inKey, int inX, int inY ); virtual void keyReleased( unsigned char inKey, int inX, int inY ); virtual void specialKeyReleased( int inKey, int inX, int inY ); // implements the SceneHandlerGL interface virtual void drawScene(); protected: GUIComponentGL *mComponent; ScreenGL *mScreen; double mMaxDimension; /** * Gets the factor by which screen coordinates need * to be multiplied to get gui coordinates. * * @return the translation factor to multiply by. */ double getTranslationFactor(); }; inline GUITranslatorGL::GUITranslatorGL( GUIComponentGL *inComponent, ScreenGL *inScreen, double inMaxDimension ) : mComponent( inComponent ), mScreen( inScreen ), mMaxDimension( inMaxDimension ) { } inline GUITranslatorGL::~GUITranslatorGL() { delete mComponent; } inline void GUITranslatorGL::translate( int inX, int inY, double *outX, double *outY ) { *outX = translateX( inX ); *outY = translateY( inY ); } inline double GUITranslatorGL::translateX( int inX ) { int xOffset = 0; int width = mScreen->getImageWidth(); int height = mScreen->getImageHeight(); if( width < height ) { // width is smaller, x range [0,mMaxDimension] // hangs off edge of vieport, centered xOffset = ( height - width ) / 2; } int screenWidth = mScreen->getWidth(); if( screenWidth != width ) { xOffset -= ( screenWidth - width ) / 2; } return ( inX + xOffset ) * getTranslationFactor(); } inline double GUITranslatorGL::translateY( int inY ) { int yOffset = 0; int width = mScreen->getImageWidth(); int height = mScreen->getImageHeight(); if( width > height ) { // height is smaller, y range [0,mMaxDimension] // hangs off edge of vieport, centered yOffset = ( width - height ) / 2; } int screenHeight = mScreen->getHeight(); if( screenHeight != height ) { yOffset += ( screenHeight - height ) / 2; } return ( height - inY + yOffset ) * getTranslationFactor(); } inline double GUITranslatorGL::getTranslationFactor() { double width = (double)( mScreen->getImageWidth() ); double height = (double)( mScreen->getImageHeight() ); if( width >= height ) { return mMaxDimension / width; } else { return mMaxDimension / height; } } inline void GUITranslatorGL::mouseMoved( int inX, int inY ) { mComponent->mouseMoved( translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::mouseDragged( int inX, int inY ) { mComponent->mouseDragged( translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::mousePressed( int inX, int inY ) { mComponent->mousePressed( translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::mouseReleased( int inX, int inY ) { mComponent->mouseReleased( translateX( inX ), translateY( inY ) ); } inline char GUITranslatorGL::isFocused() { return mComponent->isFocused(); } inline void GUITranslatorGL::keyPressed( unsigned char inKey, int inX, int inY ) { mComponent->keyPressed( inKey, translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::specialKeyPressed( int inKey, int inX, int inY ) { mComponent->specialKeyPressed( inKey, translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::keyReleased( unsigned char inKey, int inX, int inY ) { mComponent->keyReleased( inKey, translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::specialKeyReleased( int inKey, int inX, int inY ) { mComponent->specialKeyReleased( inKey, translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::drawScene() { // setup orthographic projection matrices before // telling our component to draw itself // (thus, the component can draw in [0.0, mMaxDimension] space // with no regard to screen size) // Ack: // some of this code was adapted from NeHe's tutorial #17, // which can be found at http://nehe.gamedev.net glMatrixMode( GL_PROJECTION ); // Select The Projection Matrix glPushMatrix(); // Store The Projection Matrix glLoadIdentity(); // Reset The Projection Matrix // setup an orthographic projection for our 2d gui // the longer screen dimension gets mapped to the range [0,mMaxDimension], // and the shorter dimension maps to a subset of [0,mMaxDimension], // centered // we don't care about z for 2d gui's, so just select // the region around 0 double zStart = -1; double zEnd = 1; // because screenGL uses a square viewport, we can accomplish the above // with a very simple ortho projection in all cases glOrtho( 0, mMaxDimension, 0, mMaxDimension, zStart, zEnd ); glMatrixMode( GL_MODELVIEW ); // Select The Modelview Matrix glPushMatrix(); // Store The Modelview Matrix // set the model view matrix to the identity glLoadIdentity(); glDisable( GL_DEPTH_TEST ); glDisable( GL_CULL_FACE ); // draw our gui mComponent->fireRedraw(); glEnable( GL_DEPTH_TEST ); glEnable( GL_CULL_FACE ); // restore the matrices glMatrixMode( GL_MODELVIEW ); // Select The Modelview Matrix glPopMatrix(); // Restore The Old Modelview Matrix glMatrixMode( GL_PROJECTION ); // Select The Projection Matrix glPopMatrix(); // Restore The Old Projection Matrix } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/glGUITestCompile0000750000175000017500000000042507351044134026202 0ustar pabspabsg++ -g -o glGUITest -I/usr/X11R6/include -I../../../.. -L/usr/X11R6/lib -lGL -lglut -lGLU -lX11 -lXi -lXext -lXmu glGUITest.cpp ../../../../minorGems/graphics/openGL/ScreenGL.cpp ../../../../minorGems/io/linux/TypeIOLinux.cpp ../../../../minorGems/io/file/linux/PathLinux.cpp Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/gui/MouseCursorRegionGL.h0000640000175000017500000000765211355254605027201 0ustar pabspabs/* * Modification History * * 2001-September-16 Jason Rohrer * Created. * * 2010-March-31 Jason Rohrer * Changed glColor3f to 4f to fix driver bug on some video cards. */ #ifndef MOUSE_CURSOR_REGION_GL_INCLUDED #define MOUSE_CURSOR_REGION_GL_INCLUDED #include "minorGems/graphics/openGL/gui/GUIComponentGL.h" #include "minorGems/graphics/Color.h" #include /** * A region in a GL-based gui where a mouse cursor * will be displayed. * * @author Jason Rohrer */ class MouseCursorRegionGL : public GUIComponentGL { public: /** * Constructs a mouse cursor region. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inCursorFraction the fraction of the * region to be filled by the cursor. * @param inUnpressedColor the color to draw the * cursor with when the mouse is unpressed. * Will be destroyed when this class is destroyed. * @param inPressedColor the color to draw the * cursor with when the mouse is pressed. * Will be destroyed when this class is destroyed. */ MouseCursorRegionGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, double inCursorFraction, Color *inUnpressedColor, Color *inPressedColor ); ~MouseCursorRegionGL(); // override functions in GUIComponentGL virtual void mouseMoved( double inX, double inY ); virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); virtual void fireRedraw(); protected: double mCursorFraction; double mMouseLocationX; double mMouseLocationY; Color *mUnpressedColor; Color *mPressedColor; Color *mCurrentColor; }; inline MouseCursorRegionGL::MouseCursorRegionGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, double inCursorFraction, Color *inUnpressedColor, Color *inPressedColor ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mCursorFraction( inCursorFraction ), mUnpressedColor( inUnpressedColor ), mPressedColor( inPressedColor ), mMouseLocationX( -1 ), mMouseLocationY( -1 ) { mCurrentColor = mUnpressedColor; } inline MouseCursorRegionGL::~MouseCursorRegionGL() { delete mUnpressedColor; delete mPressedColor; } inline void MouseCursorRegionGL::mouseMoved( double inX, double inY ) { mMouseLocationX = inX; mMouseLocationY = inY; mCurrentColor = mUnpressedColor; } inline void MouseCursorRegionGL::mouseReleased( double inX, double inY ) { mMouseLocationX = inX; mMouseLocationY = inY; mCurrentColor = mUnpressedColor; } inline void MouseCursorRegionGL::mouseDragged( double inX, double inY ) { mMouseLocationX = inX; mMouseLocationY = inY; mCurrentColor = mPressedColor; } inline void MouseCursorRegionGL::mousePressed( double inX, double inY ) { mMouseLocationX = inX; mMouseLocationY = inY; mCurrentColor = mPressedColor; } inline void MouseCursorRegionGL::fireRedraw() { if( isInside( mMouseLocationX, mMouseLocationY ) ) { // set our color glColor4f( mCurrentColor->r, mCurrentColor->g, mCurrentColor->b, 1.0f ); double cursorLineLengthX = mCursorFraction * mWidth; double cursorLineLengthY = mCursorFraction * mHeight; // horizontal line glBegin( GL_LINES ); { glVertex2d( mMouseLocationX - 0.5 * cursorLineLengthX, mMouseLocationY ); glVertex2d( mMouseLocationX + 0.5 * cursorLineLengthX, mMouseLocationY ); } glEnd(); // vertical line glBegin( GL_LINES ); { glVertex2d( mMouseLocationX, mMouseLocationY - 0.5 * cursorLineLengthY ); glVertex2d( mMouseLocationX, mMouseLocationY + 0.5 * cursorLineLengthY ); } glEnd(); } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/testScreenGLcompile0000750000175000017500000000021407220267517026214 0ustar pabspabsg++ -g -o testScreenGL -I../../.. -lGL -lglut -lGLU -L/usr/X11R6/lib ScreenGL.cpp testScreenGL.cpp ../../../minorGems/util/random/Noise.cpp Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/RadiosGLView.ini0000640000175000017500000000013207205611501025340 0ustar pabspabsFULLSCREEN 1 DITHER 1 SCREENWIDE 640 SCREENHIGH 480 MODELFILE radiosity.out INVERTMOUSE 0 Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/RedrawListenerGL.h0000640000175000017500000000153711357501142025702 0ustar pabspabs/* * Modification History * * 2001-February-4 Jason Rohrer * Created. * * 2010-April-9 Jason Rohrer * Added post-redraw events. */ #ifndef REDRAW_LISTENER_GL_INCLUDED #define REDRAW_LISTENER_GL_INCLUDED /** * Interface for an object that will be called at the beginning of * every openGL redraw. * * @author Jason Rohrer */ class RedrawListenerGL { public: virtual ~RedrawListenerGL() { } /** * Tells the redraw listener that a redraw is occuring. * * Note that the redraw operation waits for this call to return * before proceeding. */ virtual void fireRedraw() = 0; /** * Tells this redraw listener that all scenes have been drawn. * * Defaults to doing nothing. */ virtual void postRedraw() { } }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/SceneHandlerGL.h0000640000175000017500000000073511062511474025304 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. */ #ifndef SCENE_HANDLER_GL_INCLUDED #define SCENE_HANDLER_GL_INCLUDED /** * Interface for an object that can draw a scene onto a ScreenGL. * * @author Jason Rohrer */ class SceneHandlerGL { public: virtual ~SceneHandlerGL() { } /** * Draws a scene of objects into a ScreenGL. */ virtual void drawScene() = 0; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/ScreenGL.cpp0000640000175000017500000003342711377047676024550 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-January-31 Jason Rohrer * Fixed a bug in the fullscreen code. * * 2001-February-4 Jason Rohrer * Added support for keyboard up functions. * Added support for redraw listeners. * Added missing initialization. * * 2001-August-29 Jason Rohrer * Added support for vectors of mouse, keyboard, and scene handlers. * * 2001-September-15 Jason Rohrer * Fixed a bug in passing redraw events to the redraw listeners. * * 2001-October-13 Jason Rohrer * Added a function for applying the view matrix transformation. * Removed unneeded code from the glutResize function. * * 2001-October-29 Jason Rohrer * Added support for focusable keyboard handlers. * * 2001-October-29 Jason Rohrer * Added support for focusable keyboard handlers. * * 2004-August-30 Jason Rohrer * Fixed distortion issues when screen aspect ratio is not 1:1. * * 2005-February-7 Jason Rohrer * Fixed bug of incorrect GL matrix stack usage. Now fog works correctly. * * 2005-February-21 Jason Rohrer * Removed incorrect call to glPopMatrix. * * 2005-March-4 Jason Rohrer * Changed to call redraw listeners before applying view transform. * * 2006-December-21 Jason Rohrer * Added functions for changing window size and switching to full screen. * * 2008-September-12 Jason Rohrer * Changed to use glutEnterGameMode for full screen at startup. * Added a 2D graphics mode. * * 2008-September-29 Jason Rohrer * Enabled ignoreKeyRepeat. * * 2008-October-27 Jason Rohrer * Prepared for alternate implementations besides GLUT. * Switched to implementation-independent keycodes. * Added support for setting viewport size separate from screen size. * * 2010-May-14 Jason Rohrer * String parameters as const to fix warnings. */ /* * Compile note: For Linux, add these library flags: * -lGL -lglut -lGLU -L/usr/X11R6/lib */ #include "ScreenGL.h" #include #include #include #include #include "minorGems/util/stringUtils.h" /* ScreenGL to be accessed by callback functions. * * Note that this is a bit of a hack, but the callbacks * require a C-function (not a C++ member) and have fixed signatures, * so there's no way to pass the current screen into the functions. * * This hack prevents multiple instances of the ScreenGL class from * being used simultaneously. */ ScreenGL *currentScreenGL; // maps GLUT-specific special key-codes (GLUT_KEY) to minorGems key codes // (MG_KEY) int mapGLUTKeyToMG( int inGLUTKey ); ScreenGL::ScreenGL( int inWide, int inHigh, char inFullScreen, const char *inWindowName, KeyboardHandlerGL *inKeyHandler, MouseHandlerGL *inMouseHandler, SceneHandlerGL *inSceneHandler ) : mWide( inWide ), mHigh( inHigh ), mImageSizeSet( false ), mImageWide( inWide ), mImageHigh( inHigh ), mFullScreen( inFullScreen ), m2DMode( false ), mViewPosition( new Vector3D( 0, 0, 0 ) ), mViewOrientation( new Angle3D( 0, 0, 0 ) ), mMouseHandlerVector( new SimpleVector() ), mKeyboardHandlerVector( new SimpleVector() ), mSceneHandlerVector( new SimpleVector() ), mRedrawListenerVector( new SimpleVector() ) { // add handlers if NULL (the default) was not passed in for them if( inMouseHandler != NULL ) { addMouseHandler( inMouseHandler ); } if( inKeyHandler != NULL ) { addKeyboardHandler( inKeyHandler ); } if( inSceneHandler != NULL ) { addSceneHandler( inSceneHandler ); } if( mFullScreen ) { glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); //| GLUT_FULLSCREEN ); } else { glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); } if( !mFullScreen ) { glutInitWindowSize( mWide, mHigh ); glutCreateWindow( inWindowName ); } else { // use game mode for full screen char *gameMode = autoSprintf( "%dx%d:32", mWide, mHigh ); glutGameModeString( gameMode ); if( glutGameModeGet( GLUT_GAME_MODE_POSSIBLE ) ) { glutEnterGameMode(); delete [] gameMode; } else { printf( "The full-screen mode %s is not available\n", gameMode ); delete [] gameMode; exit(1); } } glutIgnoreKeyRepeat( 1 ); glutKeyboardFunc( callbackKeyboard ); glutKeyboardUpFunc( callbackKeyboardUp ); glutSpecialFunc( callbackSpecialKeyboard ); glutSpecialUpFunc( callbackSpecialKeyboardUp ); glutReshapeFunc( callbackResize ); glutMotionFunc( callbackMotion ); glutMouseFunc( callbackMouse ); glutPassiveMotionFunc( callbackPassiveMotion ); glutDisplayFunc( callbackDisplay ); glutIdleFunc( callbackIdle ); glEnable( GL_DEPTH_TEST ); glEnable( GL_CULL_FACE ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glCullFace( GL_BACK ); glFrontFace( GL_CCW ); } ScreenGL::~ScreenGL() { if( mFullScreen ) { glutLeaveGameMode(); } delete mViewPosition; delete mViewOrientation; delete mRedrawListenerVector; delete mMouseHandlerVector; delete mKeyboardHandlerVector; delete mSceneHandlerVector; } void ScreenGL::start() { currentScreenGL = this; glutMainLoop(); } void ScreenGL::switchTo2DMode() { m2DMode = true; } void ScreenGL::setFullScreen() { glutFullScreen(); } void ScreenGL::changeWindowSize( int inWidth, int inHeight ) { glutReshapeWindow( inWidth, inHeight ); } void ScreenGL::applyViewTransform() { // compute view angle // default angle is 90, but we want to force a 1:1 aspect ratio to avoid // distortion. // If our screen's width is different than its height, we need to decrease // the view angle so that the angle coresponds to the smaller dimension. // This keeps the zoom factor constant in the smaller dimension. // Of course, because of the way perspective works, only one Z-slice // will have a constant zoom factor // The zSlice variable sets the distance of this slice from the picture // plane double zSlice = .31; double maxDimension = mWide; if( mHigh > mWide ) { maxDimension = mHigh; } double aspectDifference = fabs( mWide / 2 - mHigh / 2 ) / maxDimension; // default angle is 90 degrees... half the angle is PI/4 double angle = atan( tan( M_PI / 4 ) + aspectDifference / zSlice ); // double it to get the full angle angle *= 2; // convert to degrees angle = 360 * angle / ( 2 * M_PI ); // set up the projection matrix glMatrixMode( GL_PROJECTION ); glLoadIdentity(); //gluPerspective( 90, mWide / mHigh, 1, 9999 ); gluPerspective( angle, 1, 1, 9999 ); // set up the model view matrix glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); // create default view and up vectors, // then rotate them by orientation angle Vector3D *viewDirection = new Vector3D( 0, 0, 1 ); Vector3D *upDirection = new Vector3D( 0, 1, 0 ); viewDirection->rotate( mViewOrientation ); upDirection->rotate( mViewOrientation ); // get a point out in front of us in the view direction viewDirection->add( mViewPosition ); // look at takes a viewer position, // a point to look at, and an up direction gluLookAt( mViewPosition->mX, mViewPosition->mY, mViewPosition->mZ, viewDirection->mX, viewDirection->mY, viewDirection->mZ, upDirection->mX, upDirection->mY, upDirection->mZ ); delete viewDirection; delete upDirection; } void callbackResize( int inW, int inH ) { ScreenGL *s = currentScreenGL; s->mWide = inW; s->mHigh = inH; if( ! s->mImageSizeSet ) { // keep image size same as screen size s->mImageWide = inW; s->mImageHigh = inH; } int bigDimension = s->mImageWide; if( bigDimension < s->mImageHigh ) { bigDimension = s->mImageHigh; } int excessW = s->mWide - bigDimension; int excessH = s->mHigh - bigDimension; // viewport is square of biggest image dimension, centered on screen // (to ensure a 1:1 aspect ratio) glViewport( excessW / 2, excessH / 2, bigDimension, bigDimension ); /* if( s->mWide >= s->mHigh ) { int excess = s->mWide - s->mHigh; glViewport( 0, -excess / 2, s->mWide, s->mWide ); } else { int excess = s->mHigh - s->mWide; glViewport( -excess / 2, 0, s->mHigh, s->mHigh ); } */ glutPostRedisplay(); } void callbackKeyboard( unsigned char inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); // fire to all handlers for( int h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->keyPressed( inKey, inX, inY ); } } } void callbackKeyboardUp( unsigned char inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); // fire to all handlers for( int h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->keyReleased( inKey, inX, inY ); } } } void callbackSpecialKeyboard( int inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); // fire to all handlers for( int h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->specialKeyPressed( mapGLUTKeyToMG( inKey ), inX, inY ); } } } void callbackSpecialKeyboardUp( int inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); // fire to all handlers for( int h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->specialKeyReleased( mapGLUTKeyToMG( inKey ), inX, inY ); } } } void callbackMotion( int inX, int inY ) { // fire to all handlers for( int h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mouseDragged( inX, inY ); } } void callbackPassiveMotion( int inX, int inY ) { // fire to all handlers for( int h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mouseMoved( inX, inY ); } } void callbackMouse( int inButton, int inState, int inX, int inY ) { // fire to all handlers for( int h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mouseMoved( inX, inY ); if( inState == GLUT_DOWN ) { handler->mousePressed( inX, inY ); } else if( inState == GLUT_UP ) { handler->mouseReleased( inX, inY ); } else { printf( "Error: Unknown mouse state received from OpenGL\n" ); } } } void callbackDisplay() { ScreenGL *s = currentScreenGL; glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // fire to all redraw listeners // do this first so that they can update our view transform // this makes control much more responsive for( int r=0; rmRedrawListenerVector->size(); r++ ) { RedrawListenerGL *listener = *( s->mRedrawListenerVector->getElement( r ) ); listener->fireRedraw(); } if( ! s->m2DMode ) { // apply our view transform s->applyViewTransform(); } // fire to all handlers for( int h=0; hmSceneHandlerVector->size(); h++ ) { SceneHandlerGL *handler = *( currentScreenGL->mSceneHandlerVector->getElement( h ) ); handler->drawScene(); } glutSwapBuffers(); } void callbackIdle() { glutPostRedisplay(); } int mapGLUTKeyToMG( int inGLUTKey ) { switch( inGLUTKey ) { case GLUT_KEY_F1: return MG_KEY_F1; case GLUT_KEY_F2: return MG_KEY_F2; case GLUT_KEY_F3: return MG_KEY_F3; case GLUT_KEY_F4: return MG_KEY_F4; case GLUT_KEY_F5: return MG_KEY_F5; case GLUT_KEY_F6: return MG_KEY_F6; case GLUT_KEY_F7: return MG_KEY_F7; case GLUT_KEY_F8: return MG_KEY_F8; case GLUT_KEY_F9: return MG_KEY_F9; case GLUT_KEY_F10: return MG_KEY_F10; case GLUT_KEY_F11: return MG_KEY_F11; case GLUT_KEY_F12: return MG_KEY_F12; case GLUT_KEY_LEFT: return MG_KEY_LEFT; case GLUT_KEY_UP: return MG_KEY_UP; case GLUT_KEY_RIGHT: return MG_KEY_RIGHT; case GLUT_KEY_DOWN: return MG_KEY_DOWN; case GLUT_KEY_PAGE_UP: return MG_KEY_PAGE_UP; case GLUT_KEY_PAGE_DOWN: return MG_KEY_PAGE_DOWN; case GLUT_KEY_HOME: return MG_KEY_HOME; case GLUT_KEY_END: return MG_KEY_END; case GLUT_KEY_INSERT: return MG_KEY_INSERT; default: return 0; } } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/TestHandlerGL.h0000640000175000017500000002707207343227173025200 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-January-9 Jason Rohrer * Changed to use new Primitive3D implementations. * * 2001-January-16 Jason Rohrer * Changed to use new Translate3D class for drawing primitives. * * 2001-January-30 Jason Rohrer * Updated to comply with new Primitive3D interface. * * 2001-January-31 Jason Rohrer * Added definition of M_PI if not automatically defined. * Added a quit key handler. * Added multitexturing to central quad to test multitexture implementations. * Had to recommit because of lost log message. * * 2001-February-2 Jason Rohrer * Fixed a bug in the way textures were generated. * * 2001-February-24 Jason Rohrer * Fixed incorrect delete usage. * * 2001-August-29 Jason Rohrer * Fixed to use new KeyboardHandler interface. */ #ifndef TEST_HANDLER_GL_INCLUDED #define TEST_HANDLER_GL_INCLUDED #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #include "ScreenGL.h" #include "MouseHandlerGL.h" #include "KeyboardHandlerGL.h" #include "SceneHandlerGL.h" #include "PrimitiveGL.h" #include "ObjectGL.h" #include "minorGems/graphics/3d/LathePrimitive3D.h" #include "minorGems/graphics/3d/LandscapePrimitive3D.h" #include "TextureGL.h" #include "LightingGL.h" #include "NoLightingGL.h" #include "DirectionLightingGL.h" #include "MultiLightingGL.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/graphics/Color.h" #include "minorGems/graphics/RGBAImage.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include "minorGems/graphics/filters/ThresholdFilter.h" #include "minorGems/graphics/filters/InvertFilter.h" #include "minorGems/util/random/RandomSource.h" #include "minorGems/util/random/Noise.h" #include "minorGems/graphics/Image.h" /** * Test class for ScreenGL implementation. * * Draws a simple polygon. * * @author Jason Rohrer */ class TestHandlerGL : public MouseHandlerGL, public KeyboardHandlerGL, public SceneHandlerGL { public: TestHandlerGL( RandomSource *inRandSource, int inNumTriangles ); ~TestHandlerGL(); /** * Sets up primitives. Must be called *after* OpenGL context is * constructed. */ void setupPrimitives(); // implement the MouseHandlerGL interface void mouseMoved( int inX, int inY ); void mouseDragged( int inX, int inY ); void mousePressed( int inX, int inY ); void mouseReleased( int inX, int inY ); // implement the KeyboardHandlerGL interface void keyPressed( unsigned char inKey, int inX, int inY ); void specialKeyPressed( int inKey, int inX, int inY ); void keyReleased( unsigned char inKey, int inX, int inY ); void specialKeyReleased( int inKey, int inX, int inY ); // implement the SceneHandlerGL interface void drawScene(); private: RandomSource *mRandSource; int mNumTriangles; Vector3D ***mTriangles; Color ***mColors; PrimitiveGL *mPrimitive; PrimitiveGL *mPrimitive2; PrimitiveGL *mPrimitive3; LightingGL *mLightingA; LightingGL *mLightingB; MultiLightingGL *mLighting; double mCurrentAngle; double mAngleStep; }; TestHandlerGL::TestHandlerGL( RandomSource *inRandSource, int inNumTriangles ) : mRandSource( inRandSource ), mNumTriangles( inNumTriangles ), mTriangles( new Vector3D**[inNumTriangles] ), mColors( new Color**[inNumTriangles] ) { mLightingA = new DirectionLightingGL( new Color( 1.0, 1.0, 1.0 ), new Vector3D( 0, 0, 1 ) ); mLightingB = new DirectionLightingGL( new Color( 0, 0, 1.0 ), new Vector3D( 1, 1, 0 ) ); mLighting = new MultiLightingGL(); mLighting->addLighting( mLightingA ); mLighting->addLighting( mLightingB ); for( int i=0; igetRandomDouble() * 10, mRandSource->getRandomDouble() * 10 - 5, mRandSource->getRandomDouble() * 10 ); mColors[i][j] = new Color( mRandSource->getRandomFloat(), mRandSource->getRandomFloat(), mRandSource->getRandomFloat(), mRandSource->getRandomFloat() ); } } } TestHandlerGL::~TestHandlerGL() { for( int i=0; igetChannel(0), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(1), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(2), textSize, textSize, fPower, true, mRandSource ); Image *selection = new Image( textSize, textSize, 1 ); genFractalNoise2d( selection->getChannel(0), textSize, textSize, fPower, true, mRandSource ); BoxBlurFilter *blur = new BoxBlurFilter( 10 ); ThresholdFilter *threshold = new ThresholdFilter( 0.5 ); InvertFilter *invert = new InvertFilter(); selection->filter( threshold ); noiseImage->setSelection( selection ); //noiseImage->filter(invert); delete blur; delete threshold; delete invert; noiseImage->clearSelection(); delete selection; /*for( int t=0; tcopy(); //double *alpha = imageArray[1]->getChannel(3); //int numPixels = imageArray[1]->getWidth() * imageArray[1]->getHeight(); //for( int p=0; psetTransparent( true ); // all passed in args will be destroyed when primitive is destroyed // setup second primitive Vector3D **latheCurve = new Vector3D*[4]; latheCurve[0] = new Vector3D( 0.5, 1, 0 ); latheCurve[1] = new Vector3D( 1, 0.5, 0 ); latheCurve[2] = new Vector3D( 1, -0.5, 0 ); latheCurve[3] = new Vector3D( 0.5, -1, 0 ); noiseImage = new RGBAImage( textSize, textSize ); genFractalNoise2d( noiseImage->getChannel(0), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(1), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(2), textSize, textSize, fPower, true, mRandSource ); Primitive3D *lathePrimitive3D = new LathePrimitive3D( 4, latheCurve, 15, 2 * M_PI, noiseImage ); lathePrimitive3D->setTransparent( false ); mPrimitive2 = new PrimitiveGL( lathePrimitive3D ); //mPrimitive2->setBackVisible( true ); // all passed in args will be destroyed when primitive is destroyed noiseImage = new RGBAImage( textSize, textSize ); genFractalNoise2d( noiseImage->getChannel(0), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(1), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(2), textSize, textSize, fPower, true, mRandSource ); int landSize = 20; unsigned long *intHeights = new unsigned long[ landSize * landSize ]; genFractalNoise2d( intHeights, landSize, landSize ); double *heights = new double[ landSize * landSize ]; for( int l=0; lr, mColors[p][v]->g, mColors[p][v]->b, mColors[p][v]->a ); glVertex3f( mTriangles[p][v]->mX, mTriangles[p][v]->mY, mTriangles[p][v]->mZ ); } } glEnd(); */ Vector3D *pos = new Vector3D( 0, 0, 10 ); Angle3D *rot = new Angle3D( mCurrentAngle, mCurrentAngle, 0 ); //Angle3D *rot = new Angle3D( 0, 0, 0 ); Transform3D *trans = new Transform3D(); trans->scale( 5 ); trans->rotate( rot ); trans->translate( pos ); //mPrimitive->setBackVisible( false ); mPrimitive->draw( trans, mLighting ); delete rot; delete pos; delete trans; pos = new Vector3D( 0, -10, 10 ); rot = new Angle3D( 0, mCurrentAngle, 0 ); trans = new Transform3D(); trans->scale( 20 ); trans->rotate( rot ); trans->translate( pos ); mPrimitive3->draw( trans, mLighting ); delete rot; delete pos; delete trans; pos = new Vector3D( 0, 5, 0 ); rot = new Angle3D( M_PI, mCurrentAngle, 0 ); trans = new Transform3D(); trans->scale( 20 ); trans->rotate( rot ); trans->translate( pos ); //mPrimitive3->setBackVisible( false ); //mPrimitive3->draw( trans, mLighting ); delete rot; delete pos; delete trans; mCurrentAngle += mAngleStep; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/openGL/TextureGL.h0000640000175000017500000002335507236121004024406 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-January-19 Jason Rohrer * Added support for multi-texturing. Now a TextureGL no longer has * an overall width and height (each layer can have a different size). * Also, a TextureGL no longer has an overall ID. * Got rid of an old, commented-out interface in the process. * Changed how multi-textures are blended. Settled on using MODULATE * for all layers of the texture, since nothing else produces results * that are compatible with vertex lighting. * * 2001-January-30 Jason Rohrer * Changed to check for GL version 1.2 before using 1.2 features. * Now we assume multi-texture is defined (because of the availability * of glext), but we check that it is supported at runtime. * Moved enable() below ARB function name definitions. * * 2001-January-31 Jason Rohrer * Fixed even more compile bugs. There's no need to do a multitexture * bind when setting texture data. * Removed extra checks for unneeded extensions, and fixed more compile bugs. * Fixed a bug in initMultitexture(). * Fixed a bug in disable(). */ #ifndef TEXTURE_GL_INCLUDED #define TEXTURE_GL_INCLUDED #include #include #include // try and include extensions if multitexture isn't // built-in to gl.h #ifndef GL_ARB_multitexture #include #endif /** * OpenGL 32-bit multi-layered texture map. * * @author Jason Rohrer */ class TextureGL { public: /** * Constructs a texture map. * * @param inNumLayers the number of multi-texture layers * in this texture. Currently supports a value in range [1:8]. */ TextureGL( int inNumLayers ); /** * The OpenGL texture is deleted when the TextureGL object is * destroyed. */ ~TextureGL(); /** * Sets the data for a layer of this texture. * * Blocks of 4 characters specify each pixel, and each * block must be ordered as RGBA. * * Note that width and height must each be powers of 2 and * not larger than 256 (for 3DFX compatability). Additionally, * for 3DFX compatability, the aspect ratio should not exceed 8:1. * * @param inLayer the multi-texture layer to set the data to. * @param inData a character array containing the color and alpha * components for this texture. All data is copied internally. * @param inWide width of the map in 4-byte blocks. * @param inHigh height of the map. */ void setTextureData( int inLayer, unsigned char *inData, int inWide, int inHigh ); /** * Gets the number of layers in this multi-texture. * * @return the number of layers in this multi-texture. */ int getNumLayers(); /** * Enables this texture and all of its layers. * * Note that this function only makes sense when * isMultitexturingSupported() returns true. * Otherwise, it is equivalent * to calling enable( 0 ). */ void enable(); /** * Enables a particular texture layer. * * @param inLayerNumber the layer to enable. */ void enable( int inLayerNumber ); /** * Disables this texture and all of its layers. */ void disable(); /** * Gets whether multitexturing is supported by the loaded * GL implementation. * * @return true if multitexturing is supported. */ static char isMultiTexturingSupported(); static GLenum sMultiTextureEnum[8]; private: static int sNextFreeTextureID; int mNumLayers; unsigned int *mTextureID; static const int MAX_NUM_LAYERS = 8; static char sMultiTexturingSupported; // a static initializer function static void staticInit(); static char sBeenInited; }; // initialize static members int TextureGL::sNextFreeTextureID = 13; char TextureGL::sBeenInited = false; char TextureGL::sMultiTexturingSupported = false; GLenum TextureGL::sMultiTextureEnum[MAX_NUM_LAYERS] = { GL_TEXTURE0_ARB, GL_TEXTURE1_ARB, GL_TEXTURE2_ARB, GL_TEXTURE3_ARB, GL_TEXTURE4_ARB, GL_TEXTURE5_ARB, GL_TEXTURE6_ARB, GL_TEXTURE7_ARB }; inline TextureGL::TextureGL( int inNumLayers ) : mNumLayers( inNumLayers ), mTextureID( new unsigned int[inNumLayers] ) { if( !sBeenInited ) { // call static initializer function TextureGL::staticInit(); } if( inNumLayers > MAX_NUM_LAYERS ) { printf( "TextureGL only supports 8 multi-texture layers.\n" ); printf( "The following number of layers is out of range: %d\n", inNumLayers ); } for( int i=0; i1) && string[i-1]=='\n')) { // New Extension Begins Here! other=&string[i]; pos=0; // Begin New Search while (string[i]!='\n') { // Search Whole Extension-String if (string[i]==search[pos]) pos++; // Next Position if ((pos>maxpos) && string[i+1]=='\n') return true; // We Have A Winner! i++; } } } return false; // Sorry, Not Found! } // isMultitextureSupported() Checks At Run-Time If Multitexturing Is Supported inline char initMultitexture(void) { char *extString = (char *)glGetString( GL_EXTENSIONS ); // Fetch Extension String int len = strlen( extString ); // allow for end of string character char *extensions = new char[ len + 1 ]; strcpy( extensions, extString ); for (int i=0; i #include "Color.h" class IconMap { public: IconMap( int w, int h); // construct a map of a certain width and height IconMap( int w, int h, int *offset ); // pass in precomputed y offsets into image map ~IconMap(); int wide; int high; int numPixels; int *yOffset; unsigned long *imageMap; // draw a solid IconMap into this IconMap void drawIconMap( IconMap *icon, int xPos, int yPos ); // draw a transparent IconMap into this IconMap void drawIconMapAlpha( IconMap *icon, int xPos, int yPos ); void copy( IconMap *icon ); // copy contents of another icon map into this one // does nothing if icon maps aren't the same size private: char yOffsetExternal; // is the yOffset ptr external? float invChannelMax; Color utilColor; }; inline IconMap::IconMap( int w, int h ) { invChannelMax = 1 / 255.0; wide = w; high = h; numPixels = wide * high; imageMap = new unsigned long[ wide * high ]; yOffset = new int[high]; for( int y=0; yimageMap; int *imageYOffset = icon->yOffset; int imageWide = icon->wide; int imageHigh = icon->high; // watch for buffer bounds int minY = yPos; if( minY < 0 ) { minY = 0; } int maxY = yPos + imageHigh; if( maxY > high ) { maxY = high; } int minX = xPos; if( minX < 0 ) { minX = 0; } int maxX = xPos + imageWide; if( maxX > wide ) { maxX = wide; } for( int y=minY; yimageMap; int *imageYOffset = icon->yOffset; int imageWide = icon->wide; int imageHigh = icon->high; // watch for buffer bounds int minY = yPos; if( minY < 0 ) { minY = 0; } int maxY = yPos + imageHigh; if( maxY > high ) { maxY = high; } int minX = xPos; if( minX < 0 ) { minX = 0; } int maxX = xPos + imageWide; if( maxX > wide ) { maxX = wide; } for( int y=minY; y> 24) * invChannelMax; float oneMinusAlpha = 1-alpha; unsigned long buffARGB = imageMap[ buffYContrib + x ]; argb = utilColor.getWeightedComposite( argb, alpha ); buffARGB = utilColor.getWeightedComposite( buffARGB, oneMinusAlpha ); unsigned long sum = utilColor.sumComposite( argb, buffARGB ); imageMap[ buffYContrib + x ] = sum; } } } inline void IconMap::copy( IconMap *icon ) { // make sure they are the same size if( numPixels != icon->numPixels ) { return; } // void * memcpy (void * dst, const void * src, size_t len); // each pixel is 4 bytes, so shift numPixels by 2 memcpy( (void *)(imageMap), (void *)(icon->imageMap), numPixels << 2 ); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/3d/0000750000175000017500000000000011401021057021457 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/graphics/3d/LandscapePrimitive3D.h0000640000175000017500000000776707253033271025637 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. Copied from LandscapePrimitiveGL, which this class will replace * * 2001-January-15 Jason Rohrer * Fixed a bug in the constructor. * * 2001-January-17 Jason Rohrer * Fliped how x and y in height map correspond to x and z in the terrain. * * 2001-January-19 Jason Rohrer * Changed to support multi-texturing. * * 2001-January-30 Jason Rohrer * Fixed a bug that occurs when the class defaults to no detail texture. * * 2001-March-11 Jason Rohrer * Fixed a bug in the texture map anchor points. */ #ifndef LANDSCAPE_PRIMITIVE_3D_INCLUDED #define LANDSCAPE_PRIMITIVE_3D_INCLUDED #include "Primitive3D.h" /** * Primitive 3D lanscape object. * * Made from a height map. Mesh generated spans both x and z between -1 and * 1, and can vary in height (y) from 0 to 1. * * @author Jason Rohrer */ class LandscapePrimitive3D : public Primitive3D { public: /** * Constructs a LandscapePrimitive with a multi-layer texture. * * @param inWide width of mesh in number of vertices. * Must be even and at least 2. * @param inHigh height of mesh in number of vertices. * @param inHeights array of heights for each vertex, each in * [0,1]. Must be destroyed by caller. * @param inTexture the texture to map onto the lanscape. * Texture is anchored by its corners to the corners of the * lanscape. inTexture is destroyed when primitive is destroyed. * @param inDetailTexture the second layer in the multi-texture. * The alpha channel of inDetailTexture will be used as * the blending factor to mix the detail with the global texture. * Set to NULL to use only a single layer. * @param inDetailScale the scale factor of the detailed texture. * Setting to 1.0 will anchor the detail at the corners of the * mesh (just like inTexture). Setting to 0.25 will cause * the detail texture to cycle 4 times across the surface of the * mesh. */ LandscapePrimitive3D( int inWide, int inHigh, double *inHeights, RGBAImage *inTexture, RGBAImage *inDetailTexture = NULL, double inDetailScale = 1.0 ); }; inline LandscapePrimitive3D::LandscapePrimitive3D( int inWide, int inHigh, double *inHeights, RGBAImage *inTexture, RGBAImage *inDetailTexture, double inDetailScale ) { // first, set Primitve3D members mHigh = inHigh; mWide = inWide; mNumVertices = mHigh * mWide; if( inDetailTexture == NULL ) { mNumTextures = 1; mTexture = new RGBAImage*[1]; mTexture[0] = inTexture; mAnchorX = new double*[1]; mAnchorY = new double*[1]; mAnchorX[0] = new double[mNumVertices]; mAnchorY[0] = new double[mNumVertices]; } else { mNumTextures = 2; mTexture = new RGBAImage*[2]; mTexture[0] = inTexture; mTexture[1] = inDetailTexture; mAnchorX = new double*[2]; mAnchorY = new double*[2]; mAnchorX[0] = new double[mNumVertices]; mAnchorY[0] = new double[mNumVertices]; mAnchorX[1] = new double[mNumVertices]; mAnchorY[1] = new double[mNumVertices]; } mVertices = new Vector3D*[mNumVertices]; // anchor at texture corners, and step linearly through texture double anchorYStep = 1.0 / ( mHigh - 1 ); double anchorXStep = 1.0 / ( mWide - 1 ); double detailAnchorYStep = 1.0 / ( ( mHigh - 1 ) * inDetailScale ); double detailAnchorXStep = 1.0 / ( ( mWide - 1 ) * inDetailScale ); for( int y=0; y #include "minorGems/graphics/RGBAImage.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/io/Serializable.h" /** * 3D primitive object. * * Comprised of a triangle mesh, texture map, and anchor points. * * @author Jason Rohrer */ class Primitive3D : public Serializable { public: /** * Constructs a Primitive. * * No parameters are copied, so they should not be destroyed * or re-accessed by caller. All are destroyed when the * primitive is destroyed. * * @param inWide width of mesh in number of vertices. * Must be even and at least 2. * @param inHigh height of mesh in number of vertices. * @param inVertices vertices in row-major order. * @param inNumTextures number of multi-texture layers for * this primitive. * @param inTexture array of textures to map onto mesh. * Note that no RGBAImage in this array should have a * selection in it. * @param inAnchorX x texture anchors for each texture and * each vertex (indexed as inAnchorX[textNum][vertNum]), * in range [0,1] (outside this range, texture will wrap). * @param inAnchorY y texture anchors for each texture and * each vertex (indexed as inAnchorY[textNum][vertNum]), * in range [0,1] (outside this range, texture will wrap). */ Primitive3D( long inWide, long inHigh, Vector3D **inVertices, long inNumTextures, RGBAImage **inTexture, double **inAnchorX, double **inAnchorY ); // construct without initializing any members // for use by subclasses and for deserialization. Primitive3D(); char mMembersAllocated; ~Primitive3D(); /** * Sets whether this primitive is transparent or not. Default * is not transparent. * * @param inTransparent true if this primitive is transparent. */ void setTransparent( char inTransparent ); /** * Gets whether this primitive is transparent or not. * * @return true iff this primitive is transparent. */ char isTransparent(); /** * Sets whether this primitive's back face is visible. Default * is not visible. * * @param inIsVisible true if the back face is visible. */ void setBackVisible( char inIsVisible ); /** * Gets whether this primitive's back face is visible or not. * * @return true iff the back face is visible. */ char isBackVisible(); /** * Gets a new instance of the derived class type. * * Should be equivalent to calling the default constructor * for the derived class type. * * Should be overridden by all derived classes that * have data members beyond those provided by Primitive3D. * Note that if the extra data members require copying, * then the copy() function should be overridden instead * of simply overriding this function. * * Primitive3D::copy() will use this function to produce * a class instance before doing a copy. * * Note that this functionality SHOULD be provided by * the built-in RTTI, but it doesn't seem to be. * * @return an instance of the derived class. */ virtual Primitive3D *getNewInstance(); /** * Copies this primitive. * * @return a copy of this primitive, which must be destroyed * by the caller. */ virtual Primitive3D *copy(); /** * Gets the number of parameters associated with this object. * * @return the number of parameters. */ virtual int getNumParameters(); /** * Gets the number of animations associated with this object. * * @return the number of animations. */ virtual int getNumAnimations(); /* * Note that the default implementations for all the parameter * and temporal animation functions do nothing. */ /** * Sets a parameter for this primative. * * @param inParameterIndex the index of the parameter to set. * If an index for a non-existing parameter is specified, * this call has no effect. * @param inValue the value to set the parameter to, in [-1, 1]. * The default value for all parameters is 0. */ virtual void setParameter( int inParameterIndex, double inValue ); /** * Gets a parameter for this primative. * * @param inParameterIndex the index of the parameter to get. * If an index for a non-existing parameter is specified, * 0 is returned. * * @return the value of the parameter, in [-1, 1]. * The default value for all parameters is 0. */ virtual double getParameter( int inParameterIndex ); /** * Steps this primitive forward in time. * * @param inStepSize the size of the timestep to take. */ virtual void step( double inStepSize ); /** * Starts a temporal animation of this primitive. * The animation progresses as step() is called repeatedly. * If called for a non-existent animation or for one that is * already running, this function has no effect. */ virtual void startAnimation( int inAnimationIndex ); /** * Stops a temporal animation of this primitive. If called * for a non-existent animation or for one that is not currently * running, this function has no effect. */ virtual void stopAnimation( int inAnimationIndex ); long mHigh, mWide; long mNumVertices; Vector3D **mVertices; Vector3D **mNormals; long mNumTextures; // Note that no RGBAImage in this array should have a // selection in it. RGBAImage **mTexture; double **mAnchorX; double **mAnchorY; // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); protected: /** * Generates standard normals from the vertices. * * If subclass is not generating normals, this * must be called before primitive is drawn, but after * the vertices have been initialized. */ void generateNormals(); char mTransparent; char mBackVisible; }; inline Primitive3D::Primitive3D( long inWide, long inHigh, Vector3D **inVertices, long inNumTextures, RGBAImage **inTexture, double **inAnchorX, double **inAnchorY ) : mHigh( inHigh ), mWide( inWide ), mNumVertices( inHigh * inWide ), mNumTextures( inNumTextures ), mVertices( inVertices ), mTexture( inTexture ), mAnchorX( inAnchorX ), mAnchorY( inAnchorY ), mTransparent( false ), mBackVisible( false ), mMembersAllocated( true ) { generateNormals(); } inline Primitive3D::Primitive3D() : mTransparent( false ), mBackVisible( false ), mMembersAllocated( false ) { } inline Primitive3D::~Primitive3D() { if( mMembersAllocated ) { int i; for( i=0; isubtract( mVertices[ index ] ); } } // now cross and add into sum for( e=0; e<4; e++ ) { if( edges[e] != NULL && edges[ (e+1) % 4 ] != NULL ) { // not that this order of crossing works // because our cross product is right handed Vector3D *normal = edges[e]->cross( edges[ (e+1) % 4 ] ); normal->normalize(); // add this normal to our sum normalSum->add( normal ); delete normal; } } // now delete edges for( e=0; e<4; e++ ) { if( edges[e] != NULL ) { delete edges[e]; } } delete [] edges; // save summed normal as normal for this point normalSum->normalize(); mNormals[index] = normalSum; } } } inline void Primitive3D::setTransparent( char inTransparent ) { mTransparent = inTransparent; } inline char Primitive3D::isTransparent() { return mTransparent; } inline void Primitive3D::setBackVisible( char inIsVisible ) { mBackVisible = inIsVisible; } inline char Primitive3D::isBackVisible() { return mBackVisible; } inline Primitive3D *Primitive3D::getNewInstance() { return new Primitive3D(); } inline Primitive3D *Primitive3D::copy() { // get an instance of the derived class, if they've // overridden getNewInstance() Primitive3D *primCopy = getNewInstance(); primCopy->mHigh = mHigh; primCopy->mWide = mWide; primCopy->mNumVertices = mNumVertices; primCopy->mVertices = new Vector3D*[mNumVertices]; primCopy->mNormals = new Vector3D*[mNumVertices]; int i; for( i=0; imVertices[i] = new Vector3D( mVertices[i] ); primCopy->mNormals[i] = new Vector3D( mNormals[i] ); } primCopy->mNumTextures = mNumTextures; primCopy->mTexture = new RGBAImage*[mNumTextures]; primCopy->mAnchorX = new double*[mNumTextures]; primCopy->mAnchorY = new double*[mNumTextures]; for( i=0; imTexture[i] = mTexture[i]->copy(); primCopy->mAnchorX[i] = new double[mNumVertices]; primCopy->mAnchorY[i] = new double[mNumVertices]; memcpy( primCopy->mAnchorX[i], mAnchorX[i], sizeof( double ) * mNumVertices ); memcpy( primCopy->mAnchorY[i], mAnchorY[i], sizeof( double ) * mNumVertices ); } primCopy->mMembersAllocated = true; primCopy->setBackVisible( isBackVisible() ); primCopy->setTransparent( isTransparent() ); return primCopy; } inline int Primitive3D::getNumParameters() { return 0; } inline int Primitive3D::getNumAnimations() { return 0; } // the base class versions of these functions do nothing inline void Primitive3D::setParameter( int inParameterIndex, double inValue ) { } inline double Primitive3D::getParameter( int inParameterIndex ) { return 0; } inline void Primitive3D::step( double inStepSize ) { } inline void Primitive3D::startAnimation( int inAnimationIndex ) { } inline void Primitive3D::stopAnimation( int inAnimationIndex ) { } inline int Primitive3D::serialize( OutputStream *inOutputStream ) { int numBytes = 0; numBytes += inOutputStream->writeLong( mWide ); numBytes += inOutputStream->writeLong( mHigh ); int i; // output vertices and normals for( i=0; iserialize( inOutputStream ); } for( i=0; iserialize( inOutputStream ); } numBytes += inOutputStream->writeLong( mNumTextures ); // output textures for( i=0; iserialize( inOutputStream ); } // output anchor arrays for( i=0; iwriteDouble( mAnchorX[i][p] ); } } for( i=0; iwriteDouble( mAnchorY[i][p] ); } } numBytes += inOutputStream->write( (unsigned char *)&mTransparent, 1 ); numBytes += inOutputStream->write( (unsigned char *)&mBackVisible, 1 ); return numBytes; } inline int Primitive3D::deserialize( InputStream *inInputStream ) { int numBytes = 0; int i; if( mMembersAllocated ) { // delete the old vertices, normals, and anchors for( i=0; ireadLong( &mWide ); numBytes += inInputStream->readLong( &mHigh ); mNumVertices = mHigh * mWide; mVertices = new Vector3D*[mNumVertices]; mNormals = new Vector3D*[mNumVertices]; // input vertices and normals for( i=0; ideserialize( inInputStream ); } for( i=0; ideserialize( inInputStream ); } // input number of textures numBytes += inInputStream->readLong( &mNumTextures ); mAnchorX = new double*[mNumTextures]; mAnchorY = new double*[mNumTextures]; mTexture = new RGBAImage*[mNumTextures]; // input textures for( i=0; ideserialize( inInputStream ); } // input anchor arrays for( i=0; ireadDouble( &( mAnchorX[i][p] ) ); } } for( i=0; ireadDouble( &( mAnchorY[i][p] ) ); } } numBytes += inInputStream->read( (unsigned char *)&mTransparent, 1 ); numBytes += inInputStream->read( (unsigned char *)&mBackVisible, 1 ); mMembersAllocated = true; return numBytesRead; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/3d/Primitive3DFactory.h0000640000175000017500000000646207261725075025354 0ustar pabspabs/* * Modification History * * 2001-March-14 Jason Rohrer * Created. * * 2001-April-1 Jason Rohrer * Added subreal entity vane class to factory. */ #ifndef PRIMITIVE_3D_FACTORY_INCLUDED #define PRIMITIVE_3D_FACTORY_INCLUDED #include "Primitive3D.h" // Run-time type identification interface (RTTI) #include // include these primitives only if we are part of subreal #ifdef SUBREAL #include "subreal/entity/EntityBodyPrimitive3D.h" #define FACTORY_ENTITY_BODY_PRIMITIVE_FLAG 1 #include "subreal/entity/EntityVanePrimitive3D.h" #define FACTORY_ENTITY_VANE_PRIMITIVE_FLAG 2 #endif // the default flag #define FACTORY_DEFAULT_PRIMITIVE_FLAG 0 /** * Class that maps Primitive3D integer subclass type flags to Primitive3D * subclass instances. * * Motivation for this class: * We want to extend Object3D to support subclass typed serialization * and deserialization without placing too much of a burden on * future Object3D subclasses. For instance, we don't want subclasses * to have to write their own de/serialization functions so that * particular Primitive3D subclasses are serialized correctly. * We can avoid this burden by writing the base Object3D serialization * code so that it uses this factory to transmit subclasses with * type informaton. * * @author Jason Rohrer */ class Primitive3DFactory { public: /** * Finds an integer subclass type flag for a primitive instance. * * @param inPrimitive the primitive to determine a flag for. Must * be destroyed by the caller. * * @return a type flag for inObject. 0 (the default Object3D * baseclass flag) will be returned if subclass determination fails. */ static int primitive3DToInt( Primitive3D *inPrimitive ); /** * Constructs a new, unitialized Primitive3D (in other words, * an Primitive3D ready for deserialization) of a subclass * type matching inTypeFlag. * * @param inTypeFlag the type flag specifying the class * of the returned primitive. * * @return an (unitialized) Primitive3D class instance with a * subclass type corresponding to inTypeFlag. If no * matching class is found, a default Primitive3D baseclass * instance is returned. Must be destroyed by the caller. */ static Primitive3D *intToPrimitive3D( int inTypeFlag ); }; inline int Primitive3DFactory::primitive3DToInt( Primitive3D *inPrimitive ) { // use RTTI to determine type of inPrimitive #ifdef SUBREAL if( typeid( *inPrimitive ) == typeid( EntityBodyPrimitive3D ) ) { return FACTORY_ENTITY_BODY_PRIMITIVE_FLAG; } else if( typeid( *inPrimitive ) == typeid( EntityVanePrimitive3D ) ) { return FACTORY_ENTITY_VANE_PRIMITIVE_FLAG; } #endif // else return the default flag return FACTORY_DEFAULT_PRIMITIVE_FLAG; } inline Primitive3D *Primitive3DFactory::intToPrimitive3D( int inTypeFlag ) { switch( inTypeFlag ) { case FACTORY_DEFAULT_PRIMITIVE_FLAG: return new Primitive3D(); break; /* these primitives are only defined if * we are part of subreal */ #ifdef SUBREAL case FACTORY_ENTITY_BODY_PRIMITIVE_FLAG: return new EntityBodyPrimitive3D(); break; case FACTORY_ENTITY_VANE_PRIMITIVE_FLAG: return new EntityVanePrimitive3D(); break; #endif default: // unknown primitive flag type return new Primitive3D(); break; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/3d/Object3DFactory.h0000640000175000017500000000645207261725007024604 0ustar pabspabs/* * Modification History * * 2001-March-14 Jason Rohrer * Created. * * 2001-March-17 Jason Rohrer * Finished implementation. * * 2001-April-1 Jason Rohrer * Fixed flag name. */ #ifndef OBJECT_3D_FACTORY_INCLUDED #define OBJECT_3D_FACTORY_INCLUDED #include "Object3D.h" // Run-time type identification interface (RTTI) #include // include these objects only if we are part of subreal #ifdef SUBREAL #include "subreal/entity/EntityObject3D.h" #define FACTORY_ENTITY_OBJECT_FLAG 1 #endif // the default flag #define FACTORY_DEFAULT_OBJECT_FLAG 0 /** * Class that maps Object3D integer subclass type flags to Object3D * subclass instances. * * Motivation for this class: * We have Object3D instances that exist simultaneously on opposite * sides of the network (at both the client and server ends). Initially, * we just let the objects get sent to the server via the base class * Object3D serialization function. At that time, subclasses of Object3D * did nothing more than init an Object3D in a particular way. This * worked fine when we didn't have animations associated with various * Object3D subclasses, but doesn't work now that we have animation * code in various subclasses. During serialization (if the object * is sent as a generic Object3D), the animation code is lost on * the client end. We need a way for the client to pick the correct * subclass to construct for deserialization. We can encode the * various subtypes by integers, and then this factory class can * be used to construct a class of appropriate subtype before * deserialization. * * @author Jason Rohrer */ class Object3DFactory { public: /** * Finds an integer subclass type flag for an object instance. * * @param inObject the object to determine a flag for. Must * be destroyed by the caller. * * @return a type flag for inObject. 0 (the defautl Object3D * baseclass flag) will be returned if subclass determination fails. */ static int object3DToInt( Object3D *inObject ); /** * Constructs a new, unitialized Object3D (in other words, * an Object3D ready for deserialization) of a subclass * type matching inTypeFlag. * * @param inTypeFlag the type flag specifying the class * of the returned object. * * @return an (unitialized) Object3D class instance with a * subclass type corresponding to inTypeFlag. If no * matching class is found, a default Object3D baseclass * instance is returned. Must be destroyed by the caller. */ static Object3D *intToObject3D( int inTypeFlag ); }; inline int Object3DFactory::object3DToInt( Object3D *inObject ) { // use RTTI to determine type of inObject #ifdef SUBREAL if( typeid( *inObject ) == typeid( EntityObject3D ) ) { return FACTORY_ENTITY_OBJECT_FLAG; } #endif // else return the default flag return FACTORY_DEFAULT_OBJECT_FLAG; } inline Object3D *Object3DFactory::intToObject3D( int inTypeFlag ) { switch( inTypeFlag ) { case FACTORY_DEFAULT_OBJECT_FLAG: return new Object3D(); break; /* these objects are only defined if * we are part of subreal */ #ifdef SUBREAL case FACTORY_ENTITY_OBJECT_FLAG: return new EntityObject3D(); break; #endif default: // unknown object flag type return new Object3D(); break; } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/3d/Object3D.h0000640000175000017500000001700607254677305023262 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. Copied from ObjectGL. * * 2001-January-10 Jason Rohrer * Made class serializable. Added a parameterless constructor * to facilitate deserialization. * * 2001-January-15 Jason Rohrer * Fixed several bugs in the deserialize() function, as well as in the * destructor. * * 2001-January-16 Jason Rohrer * Changed to use a Transform3D instead of Vectors, Angles, and scales for * each primitive in the object. * * 2001-January-24 Jason Rohrer * Added a copy() function. * Fixed a bug in deserialize(). * Made mMembersAllocated public for copy function. * * 2001-January-26 Jason Rohrer * Fixed a bug in copy(). * * 2001-January-31 Jason Rohrer * Fixed bugs in serialize() and deserialize(). * * 2001-February-3 Jason Rohrer * Updated serialization code to use new interfaces. * * 2001-March-11 Jason Rohrer * Added support for paramatization and temporal animations. * * 2001-March-13 Jason Rohrer * Added interface for getting the number of parameters and animations. * * 2001-March-14 Jason Rohrer * Added use of Primitive3DFactory for typed subclass primitive * de/serialization. */ #ifndef OBJECT_3D_INCLUDED #define OBJECT_3D_INCLUDED #include "Primitive3D.h" #include "minorGems/math/geometry/Transform3D.h" #include "minorGems/io/Serializable.h" #include "Primitive3DFactory.h" /** * 3D object. * * Comprised of a collection of primitives. * * @author Jason Rohrer */ class Object3D : public Serializable { public: /** * Constructs an Object. * * No parameters are copied, so they should not be destroyed * or re-accessed by caller. All are destroyed when the * object is destroyed. * * @param inNumPrimitives the number of primitives in this object. * @param inPrimitives the primitives comprising this object. * @param inTransform a transform for each object. */ Object3D( long inNumPrimitives, Primitive3D **inPrimitives, Transform3D **inTransform ); Object3D(); ~Object3D(); /** * Copies this object. * * @return a copy of this object, which must be destroyed * by the caller. */ Object3D *copy(); /** * Gets the number of parameters associated with this object. * * @return the number of parameters. */ virtual int getNumParameters(); /** * Gets the number of animations associated with this object. * * @return the number of animations. */ virtual int getNumAnimations(); /* * Note that the default implementations for all the parameter * and temporal animation functions do nothing. */ /** * Sets a parameter for this object. * * @param inParameterIndex the index of the parameter to set. * If an index for a non-existing parameter is specified, * this call has no effect. * @param inValue the value to set the parameter to, in [-1, 1]. * The default value for all parameters is 0. */ virtual void setParameter( int inParameterIndex, double inValue ); /** * Gets a parameter for this object. * * @param inParameterIndex the index of the parameter to get. * If an index for a non-existing parameter is specified, * 0 is returned. * * @return the value of the parameter, in [-1, 1]. * The default value for all parameters is 0. */ virtual double getParameter( int inParameterIndex ); /** * Steps this object forward in time. * * @param inStepSize the size of the timestep to take. */ virtual void step( double inStepSize ); /** * Starts a temporal animation of this object. * The animation progresses as step() is called repeatedly. * If called for a non-existent animation or for one that is * already running, this function has no effect. */ virtual void startAnimation( int inAnimationIndex ); /** * Stops a temporal animation of this object. If called * for a non-existent animation or for one that is not currently * running, this function has no effect. */ virtual void stopAnimation( int inAnimationIndex ); long mNumPrimitives; Primitive3D **mPrimitives; Transform3D **mTransform; // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); char mMembersAllocated; }; inline Object3D::Object3D( long inNumPrimitives, Primitive3D **inPrimitives, Transform3D **inTransform ) : mNumPrimitives( inNumPrimitives ), mPrimitives( inPrimitives ), mTransform( inTransform ), mMembersAllocated( true ) { } inline Object3D::Object3D() : mMembersAllocated( false ) { } inline Object3D::~Object3D() { if( mMembersAllocated ) { for( int i=0; imNumPrimitives = mNumPrimitives; objCopy->mPrimitives = new Primitive3D*[ mNumPrimitives ]; objCopy->mTransform = new Transform3D*[ mNumPrimitives ]; int i; for( i=0; imPrimitives[i] = mPrimitives[i]->copy(); objCopy->mTransform[i] = new Transform3D( mTransform[i] ); } objCopy->mMembersAllocated = true; return objCopy; } inline int Object3D::getNumParameters() { return 0; } inline int Object3D::getNumAnimations() { return 0; } // the base class versions of these functions do nothing inline void Object3D::setParameter( int inParameterIndex, double inValue ) { } inline double Object3D::getParameter( int inParameterIndex ) { return 0; } inline void Object3D::step( double inStepSize ) { } inline void Object3D::startAnimation( int inAnimationIndex ) { } inline void Object3D::stopAnimation( int inAnimationIndex ) { } inline int Object3D::serialize( OutputStream *inOutputStream ) { int i; int numBytes = 0; numBytes += inOutputStream->writeLong( mNumPrimitives ); // write each primitive for( i=0; iwriteLong( typeFlag ); // write the primitive numBytes += mPrimitives[i]->serialize( inOutputStream ); } // write each primitive's transform for( i=0; iserialize( inOutputStream ); } return numBytes; } inline int Object3D::deserialize( InputStream *inInputStream ) { if( mMembersAllocated ) { // first, delete current contents of object for( int i=0; ireadLong( &mNumPrimitives ); printf( "receiving %d primitives\n", mNumPrimitives ); mPrimitives = new Primitive3D*[mNumPrimitives]; for( i=0; ireadLong( &typeFlag ); // construct a new object based on the type flag mPrimitives[i] = Primitive3DFactory::intToPrimitive3D( typeFlag ); // deserialize it numBytes += mPrimitives[i]->deserialize( inInputStream ); } mTransform = new Transform3D*[mNumPrimitives]; for( i=0; ideserialize( inInputStream ); } mMembersAllocated = true; return numBytes; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/3d/LathePrimitive3D.h0000640000175000017500000000650607252736762025006 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. Copied from LathePrimitiveGL, which this class will replace. * * 2001-January-19 Jason Rohrer * Changed to support multi-texturing internally, though this primitive * type doesn't use it. * * 2001-January-21 Jason Rohrer * Fixed a bug in the constructor. * * 2001-January-31 Jason Rohrer * Got rid of an unused variable. * * 2001-March-11 Jason Rohrer * Fixed a bug in the texture map anchor points. */ #ifndef LATHE_PRIMITIVE_3D_INCLUDED #define LATHE_PRIMITIVE_3D_INCLUDED #include "Primitive3D.h" /** * Primitive 3D lathe object. * * Made of a curve rotated around an axis. * * @author Jason Rohrer */ class LathePrimitive3D : public Primitive3D { public: /** * Constructs a LathePrimitive. The lathe curve is rotated * about the y axis. * * No parameters are copied, so they should not be destroyed * or re-accessed by caller. All are destroyed when the * primitive is destroyed. * * @param inNumCurvePoints number of points in curve to be lathed. * @param inCurvePoints points to be lathed. All z components * are set to 0 before lathing, so only x and y values matter. * @param inNumLatheSteps the number of quad segments around * the circumference of the lathed object. For example, * setting to 4 (with a lathe angle of 2pi) * will produce an extruded square object. * @param inNetLatheAngle total angle to sweep during lathing, * in (0,2pi]. * @param inTexture the texture to map onto the lathed object. * Texture is anchored by its corners to the ends of the lath * curve at the beginning and end of the lathe sweep */ LathePrimitive3D( int inNumCurvePoints, Vector3D **inCurvePoints, int inNumLatheSteps, double inNetLatheAngle, RGBAImage *inTexture ); }; inline LathePrimitive3D::LathePrimitive3D( int inNumCurvePoints, Vector3D **inCurvePoints, int inNumLatheSteps, double inNetLatheAngle, RGBAImage *inTexture ) { // first, set Primitve3D members mHigh = inNumCurvePoints; mWide = inNumLatheSteps + 1; mNumVertices = mHigh * mWide; mNumTextures = 1; mTexture = new RGBAImage*[1]; mTexture[0] = inTexture; double stepAngle = inNetLatheAngle / inNumLatheSteps; int i; // first, set all z values for control points to 0 for( i=0; imZ = 0; } mVertices = new Vector3D*[mNumVertices]; mAnchorX = new double*[1]; mAnchorY = new double*[1]; mAnchorX[0] = new double[mNumVertices]; mAnchorY[0] = new double[mNumVertices]; // anchor at texture corners, and step linearly through texture double anchorYStep = 1.0 / ( mHigh - 1 ); double anchorXStep = 1.0 / ( mWide - 1 ); for( int y=0; yreverseRotate( latheRotation ); delete latheRotation; } // cleanup as we go along delete inCurvePoints[y]; } delete [] inCurvePoints; generateNormals(); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/loadFile.cpp0000640000175000017500000000335007205611501023404 0ustar pabspabs#include #include #include "loadFile.h" // loads any file into the dest ptr. void loadFile( const char* fileName, long sizeInBytes, unsigned char* byteDestPtr) { FILE *f; int numBytesRead; f = fopen( fileName, "rb"); // open the file // don't load if file failed to open if( f == NULL ) { return; } numBytesRead = fread( (void*)byteDestPtr, sizeof(char), sizeInBytes, f); fclose(f); } // loads a photoshop RAW image file, 32-bit // NOTE: // This function exists because photoshop swaps the red and blue channels when // it saves a file as raw. Thus, the file stream doesn't match the way a video // card deals with 32-bit color. // This function loads the file and then swaps the appropriate bytes void loadRawFile( const char* fileName, long sizeInBytes, unsigned char* byteDestPtr) { //unsigned char tempChannel; long byteCount; FILE *f; int numBytesRead; f = fopen( fileName, "rb"); // open the file // don't load if file failed to open if( f == NULL ) { return; } numBytesRead = fread( (void*)byteDestPtr, sizeof(char), sizeInBytes, f); // now that file read, swap the red and blue channels: for( byteCount = 0; byteCount< sizeInBytes; byteCount=byteCount+4) { unsigned char alpha = byteDestPtr[byteCount+3]; byteDestPtr[byteCount+3] = byteDestPtr[byteCount+2]; byteDestPtr[byteCount+2] = byteDestPtr[byteCount+1]; byteDestPtr[byteCount+1] = byteDestPtr[byteCount]; byteDestPtr[byteCount] = alpha; /* tempChannel = byteDestPtr[byteCount]; // currently in red position byteDestPtr[byteCount] = byteDestPtr[byteCount+3]; // currently set to what's in alpha position byteDestPtr[byteCount+2] = tempChannel; */ } fclose(f); }Cultivation_9+dfsg1_UnixSource/minorGems/graphics/converters/0000750000175000017500000000000011462022731023352 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/graphics/converters/bmpformat.txt0000640000175000017500000000611307244265544026122 0ustar pabspabs Size Description Header 14 bytes Windows Structure: BITMAPFILEHEADER Signature 2 bytes 'BM' FileSize 4 bytes File size in bytes reserved 4 bytes unused (=0) DataOffset 4 bytes File offset to Raster Data InfoHeader 40 bytes Windows Structure: BITMAPINFOHEADER Size 4 bytes Size of InfoHeader =40 Width 4 bytes Bitmap Width Height 4 bytes Bitmap Height Planes 2 bytes Number of Planes (=1) BitCount 2 bytes Bits per Pixel 1 = monochrome palette. NumColors = 1 4 = 4bit palletized. NumColors = 16 8 = 8bit palletized. NumColors = 256 16 = 16bit RGB. NumColors = 65536 (?) 24 = 24bit RGB. NumColors = 16M Compression 4 bytes Type of Compression 0 = BI_RGB no compression 1 = BI_RLE8 8bit RLE encoding 2 = BI_RLE4 4bit RLE encoding ImageSize 4 bytes (compressed) Size of Image It is valid to set this =0 if Compression = 0 XpixelsPerM 4 bytes horizontal resolution: Pixels/meter YpixelsPerM 4 bytes vertical resolution: Pixels/meter ColorsUsed 4 bytes Number of actually used colors ColorsImportant 4 bytes Number of important colors 0 = all ColorTable 4 * NumColors bytes present only if Info.BitsPerPixel <= 8 colors should be ordered by importance Red 1 byte Red intensity Green 1 byte Green intensity Blue 1 byte Blue intensity reserved 1 byte unused (=0) repeated NumColors times Raster Data Info.ImageSize bytes The pixel data Cultivation_9+dfsg1_UnixSource/minorGems/graphics/converters/compileTestPNG0000750000175000017500000000022011341264416026133 0ustar pabspabsg++ -g -Wall -o testPNG -I../../.. testPNG.cpp PNGImageConverter.cpp ../../io/file/linux/PathLinux.cpp ../../system/unix/TimeUnix.cpp -lz -lpng Cultivation_9+dfsg1_UnixSource/minorGems/graphics/converters/LittleEndianImageConverter.h0000640000175000017500000000521207353214427030744 0ustar pabspabs/* * Modification History * * 2001-September-22 Jason Rohrer * Created. */ #ifndef LITTLE_ENDIAN_IMAGE_CONVERTER_INCLUDED #define LITTLE_ENDIAN_IMAGE_CONVERTER_INCLUDED #include "minorGems/graphics/ImageConverter.h" /** * A base class for converters that have little endian file formats. * Basically includes little endian reading and writing functions. * * @author Jason Rohrer */ class LittleEndianImageConverter : public ImageConverter { public: // does not implement the ImageConverter interface, // which makes this class abstract. protected: /** * Writes a long value in little endian format. * * @param inLong the long value to write. * @param inStream the stream to write inLong to. */ void writeLittleEndianLong( long inLong, OutputStream *inStream ); /** * Writes a short value in little endian format. * * @param inShort the short value to write. * @param inStream the stream to write inShort to. */ void writeLittleEndianShort( short inShort, OutputStream *inStream ); /** * Reads a long value in little endian format. * * @param inStream the stream to read the long value from. * * @return the long value. */ long readLittleEndianLong( InputStream *inStream ); /** * Reads a short value in little endian format. * * @param inStream the stream to read the short value from. * * @return the short value. */ short readLittleEndianShort( InputStream *inStream ); }; inline void LittleEndianImageConverter::writeLittleEndianLong( long inLong, OutputStream *inStream ) { unsigned char buffer[4]; buffer[0] = (unsigned char)( inLong & 0xFF ); buffer[1] = (unsigned char)( ( inLong >> 8 ) & 0xFF ); buffer[2] = (unsigned char)( ( inLong >> 16 ) & 0xFF ); buffer[3] = (unsigned char)( ( inLong >> 24 ) & 0xFF ); inStream->write( buffer, 4 ); } inline long LittleEndianImageConverter::readLittleEndianLong( InputStream *inStream ) { unsigned char buffer[4]; inStream->read( buffer, 4 ); long outLong = buffer[0] | ( buffer[1] << 8 ) | ( buffer[2] << 16 ) | ( buffer[3] << 24 ); return outLong; } inline void LittleEndianImageConverter::writeLittleEndianShort( short inShort, OutputStream *inStream ) { unsigned char buffer[2]; buffer[0] = (unsigned char)( inShort & 0xFF ); buffer[1] = (unsigned char)( ( inShort >> 8 ) & 0xFF ); inStream->write( buffer, 2 ); } inline short LittleEndianImageConverter::readLittleEndianShort( InputStream *inStream ) { unsigned char buffer[2]; inStream->read( buffer, 2 ); long outShort = buffer[0] | ( buffer[1] << 8 ); return outShort; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/converters/testPNG.cpp0000640000175000017500000000200411341264416025403 0ustar pabspabs#include "PNGImageConverter.h" #include "minorGems/graphics/Image.h" #include "minorGems/io/file/FileOutputStream.h" #include "minorGems/system/Time.h" int main() { int imageSize = 640; Image testImage( imageSize, imageSize, 3, false ); // red fades toward bottom // green fades toward right double *red = testImage.getChannel( 0 ); double *green = testImage.getChannel( 1 ); for( int y=0; y #include "minorGems/graphics/Image.h" #include "BMPImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileOutputStream.h" #include "minorGems/io/file/FileInputStream.h" // test function for the BMPImageConverter class int main( char inNumArgs, char**inArgs ) { if( inNumArgs != 2 ) { printf( "must pass in a file name to write to\n" ); return 1; } int length = 0; while( inArgs[1][length] != '\0' ) { length++; } File *file = new File( NULL, inArgs[1], length ); // read image in FileInputStream *stream = new FileInputStream( file ); BMPImageConverter *converter = new BMPImageConverter(); Image *image = converter->deformatImage( stream ); if( image != NULL ) { // write image back out File *fileOut = new File( NULL, "testOut.bmp", 11 ); FileOutputStream *outStream = new FileOutputStream( fileOut ); converter->formatImage( image, outStream ); delete outStream; delete image; } delete stream; delete file; delete converter; /* FileOutputStream *stream = new FileOutputStream( file ); BMPImageConverter *converter = new BMPImageConverter(); Image *image = new Image( 256, 256, 3 ); double *red = image->getChannel( 0 ); double *green = image->getChannel( 1 ); for( int y=0; ygetHeight(); y++ ) { for( int x=0; xgetWidth(); x++ ) { long index = y * image->getWidth() + x; red[index] = (double)y / (double)( image->getHeight() ); green[index] = (double)x / (double)( image->getWidth() ); //red[index] = 1.0; } } converter->formatImage( image, stream ); delete stream; // delete file explicitly delete file; delete converter; delete image; */ return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/converters/unix/0000750000175000017500000000000011401021057024326 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/graphics/converters/unix/JPEGImageConverterUnix.cpp0000640000175000017500000004047710543537355031316 0ustar pabspabs/* * Modification History * * 2001-April-27 Jason Rohrer * Created. * * 2001-April-29 Jason Rohrer * Finished implementation. * Added an optimization to formatImage, but it did not improve * performance, so it has been commented out. */ /** * Unix-specific JPEGImageConverter implementation * * Code for compression and decompression modeled after IJG's * libjpeg example code. * * For now, it use libjpeg to write converted data out to * file, and then reads it back in. */ #include "minorGems/graphics/converters/JPEGImageConverter.h" #include "minorGems/io/file/File.h" #include #include #include // include the jpeg library as a C file. // (yuk... spent way too much time trying to figure this one out!) extern "C" { #include } /* * is used for the decompression * error recovery mechanism. */ #include void JPEGImageConverter::formatImage( Image *inImage, OutputStream *inStream ) { if( inImage->getNumChannels() != 3 ) { printf( "JPEGImageConverter only works on 3-channel images.\n" ); return; } // most of this code was copied without modification from // IJG's example.c // This struct contains the JPEG compression parameters and pointers to // working space (which is allocated as needed by the JPEG library). // It is possible to have several such structures, representing multiple // compression/decompression processes, in existence at once. We refer // to any one struct (and its associated working data) as a "JPEG object". struct jpeg_compress_struct cinfo; // This struct represents a JPEG error handler. It is declared separately // because applications often want to supply a specialized error handler // (see the second half of this file for an example). But here we just // take the easy way out and use the standard error handler, which will // print a message on stderr and call exit() if compression fails. // Note that this struct must live as long as the main JPEG parameter // struct, to avoid dangling-pointer problems. struct jpeg_error_mgr jerr; // More stuff FILE * outfile; // target file JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s] int row_stride; // physical row width in image buffer // Step 1: allocate and initialize JPEG compression object // We have to set up the error handler first, in case the initialization // step fails. (Unlikely, but it could happen if you are out of memory.) // This routine fills in the contents of struct jerr, and returns jerr's // address which we place into the link field in cinfo. cinfo.err = jpeg_std_error( &jerr ); // Now we can initialize the JPEG compression object. jpeg_create_compress( &cinfo ); // Step 2: specify data destination (eg, a file) // Note: steps 2 and 3 can be done in either order. // use a temp file with a random name to make this more // thread-safe char *fileName = new char[99]; sprintf( fileName, "temp%d.dat", rand() ); // Here we use the library-supplied code to send compressed data to a // stdio stream. You can also write your own code to do something else. // VERY IMPORTANT: use "b" option to fopen() if you are on a machine that // requires it in order to write binary files. if( ( outfile = fopen( fileName, "wb" ) ) == NULL ) { printf( "can't open jpeg conversion temp file %s\n", fileName ); return; } jpeg_stdio_dest( &cinfo, outfile ); // Step 3: set parameters for compression // First we supply a description of the input image. // Four fields of the cinfo struct must be filled in: // image width and height, in pixels cinfo.image_width = inImage->getWidth(); cinfo.image_height = inImage->getHeight(); cinfo.input_components = 3; // # of color components per pixel cinfo.in_color_space = JCS_RGB; // colorspace of input image // Now use the library's routine to set default compression parameters. // (You must set at least cinfo.in_color_space before calling this, // since the defaults depend on the source color space.) jpeg_set_defaults( &cinfo ); // Now you can set any non-default parameters you wish to. // Here we just illustrate the use of // quality (quantization table) scaling: jpeg_set_quality( &cinfo, mQuality, TRUE ); // limit to baseline-JPEG values // Step 4: Start compressor // TRUE ensures that we will write a complete interchange-JPEG file. // Pass TRUE unless you are very sure of what you're doing. jpeg_start_compress( &cinfo, TRUE ); // Step 5: while (scan lines remain to be written) // jpeg_write_scanlines(...); // Here we use the library's state variable cinfo.next_scanline as the // loop counter, so that we don't have to keep track ourselves. // To keep things simple, we pass one scanline per call; you can pass // more if you wish, though. // JSAMPLEs per row in image_buffer row_stride = cinfo.image_width * 3; // channels of inImage, which we will need to pull pixel values out of double *redChannel = inImage->getChannel(0); double *greenChannel = inImage->getChannel(1); double *blueChannel = inImage->getChannel(2); // array that we will copy inImage pixels into // one scanline at a time row_pointer[0] = new JSAMPLE[ row_stride ]; //int rowNumber = 0; while( cinfo.next_scanline < cinfo.image_height ) { // jpeg_write_scanlines expects an array of pointers to scanlines. // Here the array is only one element long, but you could pass // more than one scanline at a time if that's more convenient. // make a scanline int yOffset = cinfo.next_scanline * cinfo.image_width; // for each pixel in the row for( int p=0; pgetLength(); unsigned char *fileBuffer = new unsigned char[ fileLength ]; fread( fileBuffer, 1, fileLength, inFile ); // now write the entire buffer to our output stream inStream->write( fileBuffer, fileLength ); delete [] fileBuffer; delete file; fclose( inFile ); // delete this temporary file remove( fileName ); delete [] fileName; // And we're done! } // copied this directly from IJG's example.c //extern "C" { /* * ERROR HANDLING: * * The JPEG library's standard error handler (jerror.c) is divided into * several "methods" which you can override individually. This lets you * adjust the behavior without duplicating a lot of code, which you might * have to update with each future release. * * Our example here shows how to override the "error_exit" method so that * control is returned to the library's caller when a fatal error occurs, * rather than calling exit() as the standard error_exit method does. * * We use C's setjmp/longjmp facility to return * control. This means that the * routine which calls the JPEG library must * first execute a setjmp() call to * establish the return point. We want the replacement error_exit to do a * longjmp(). But we need to make the setjmp buffer accessible to the * error_exit routine. To do this, we make a private extension of the * standard JPEG error handler object. (If we were using C++, we'd say we * were making a subclass of the regular error handler.) * * Here's the extended error handler struct: */ struct my_error_mgr { struct jpeg_error_mgr pub; /* "public" fields */ jmp_buf setjmp_buffer; /* for return to caller */ }; typedef struct my_error_mgr * my_error_ptr; /* * Here's the routine that will replace the standard error_exit method: */ METHODDEF(void) my_error_exit( j_common_ptr cinfo ) { /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ my_error_ptr myerr = (my_error_ptr)( cinfo->err ); /* Always display the message. */ /* We could postpone this until after returning, if we chose. */ (*cinfo->err->output_message)( cinfo ); /* Return control to the setjmp point */ longjmp( myerr->setjmp_buffer, 1 ); } // } Image *JPEGImageConverter::deformatImage( InputStream *inStream ) { // use a temp file with a random name to make this more // thread-safe char *fileName = new char[99]; sprintf( fileName, "temp%d.dat", rand() ); FILE *tempFile = fopen( fileName, "wb" ); if( tempFile == NULL ) { printf( "can't open jpeg conversion temp file %s\n", fileName ); return NULL; } // buffer for dumping stream to temp file unsigned char *tempBuffer = new unsigned char[1]; unsigned char previousByte = 0; // dump the JPEG stream from the input stream into tempFile // so that we can pass this file to libjpeg /* // optimization: use a buffer to prevent too many fwrite calls int bufferLength = 5000; unsigned char *fileBuffer = new unsigned char[ bufferLength ]; int currentBufferPosition = 0; while( !( tempBuffer[0] == 0xD9 && previousByte == 0xFF ) ) { previousByte = tempBuffer[0]; inStream->read( tempBuffer, 1 ); fileBuffer[currentBufferPosition] = tempBuffer[0]; if( currentBufferPosition == bufferLength - 1 ) { // at the end of the file buffer fwrite( fileBuffer, 1, bufferLength, tempFile ); currentBufferPosition = 0; } else { // keep filling the fileBuffer currentBufferPosition++; } } // now write remaining fileBuffer data to file fwrite( fileBuffer, 1, currentBufferPosition + 1, tempFile ); delete [] fileBuffer; */ // write until EOI sequence seen (0xFFD9) while( !( tempBuffer[0] == 0xD9 && previousByte == 0xFF ) ) { previousByte = tempBuffer[0]; inStream->read( tempBuffer, 1 ); fwrite( tempBuffer, 1, 1, tempFile ); } // end of jpeg stream reached. fclose( tempFile ); delete [] tempBuffer; // the remainder of this method was mostly copied from // IJG's example.c /* This struct contains the JPEG decompression parameters and pointers to * working space (which is allocated as needed by the JPEG library). */ struct jpeg_decompress_struct cinfo; /* We use our private extension JPEG error handler. * Note that this struct must live as long as the main JPEG parameter * struct, to avoid dangling-pointer problems. */ struct my_error_mgr jerr; /* More stuff */ FILE * infile; /* source file */ JSAMPARRAY buffer; /* Output row buffer */ int row_stride; /* physical row width in output buffer */ /* In this example we want to open the input * file before doing anything else, * so that the setjmp() error recovery below can assume the file is open. * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to read binary files. */ if( ( infile = fopen( fileName, "rb" ) ) == NULL ) { printf( "can't open jpeg conversion temp file %s\n", fileName ); return NULL; } /* Step 1: allocate and initialize JPEG decompression object */ /* We set up the normal JPEG error routines, then override error_exit. */ cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; /* Establish the setjmp return context for my_error_exit to use. */ if( setjmp( jerr.setjmp_buffer ) ) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, * close the input file, and return. */ jpeg_destroy_decompress( &cinfo ); fclose( infile ); printf( "error in decompressing jpeg from stream.\n" ); return NULL; } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress( &cinfo ); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src( &cinfo, infile ); /* Step 3: read file parameters with jpeg_read_header() */ (void) jpeg_read_header( &cinfo, TRUE ); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ /* Step 5: Start decompressor */ (void) jpeg_start_decompress( &cinfo ); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ /* JSAMPLEs per row in output buffer */ int imageWidth = cinfo.output_width; int imageHeight = cinfo.output_height; // the return image with 3 channels Image *returnImage = new Image( imageWidth, imageHeight, 3, false ); // channels of returnImage, // which we will need to put pixel values into of double *redChannel = returnImage->getChannel(0); double *greenChannel = returnImage->getChannel(1); double *blueChannel = returnImage->getChannel(2); int currentIndex = 0; row_stride = cinfo.output_width * cinfo.output_components; /* Make a one-row-high sample array that * will go away when done with image */ buffer = ( *cinfo.mem->alloc_sarray ) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1 ); /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ int rowNumber = 0; double inv255 = 1.0 / 255.0; while( cinfo.output_scanline < cinfo.output_height ) { /* jpeg_read_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could ask for * more than one scanline at a time if that's more convenient. */ (void) jpeg_read_scanlines( &cinfo, buffer, 1 ); // write the scanline into returnImage int yOffset = rowNumber * cinfo.output_width; // for each pixel in the row // copy it into the return image channels for( int p=0; p> 24 ) & 0xFF ); buffer[1] = (unsigned char)( ( inLong >> 16 ) & 0xFF ); buffer[2] = (unsigned char)( ( inLong >> 8 ) & 0xFF ); buffer[3] = (unsigned char)( inLong & 0xFF ); inStream->write( buffer, 4 ); } inline long BigEndianImageConverter::readBigEndianLong( InputStream *inStream ) { unsigned char buffer[4]; inStream->read( buffer, 4 ); long outLong = ( buffer[0] << 24 ) | ( buffer[1] << 16 ) | ( buffer[2] << 8 ) | buffer[3]; return outLong; } inline void BigEndianImageConverter::writeBigEndianShort( short inShort, OutputStream *inStream ) { unsigned char buffer[2]; buffer[0] = (unsigned char)( ( inShort >> 8 ) & 0xFF ); buffer[1] = (unsigned char)( inShort & 0xFF ); inStream->write( buffer, 2 ); } inline short BigEndianImageConverter::readBigEndianShort( InputStream *inStream ) { unsigned char buffer[2]; inStream->read( buffer, 2 ); long outShort = ( buffer[0] << 8 ) | buffer[1]; return outShort; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/converters/TGAImageConverter.h0000640000175000017500000002224711144105026026775 0ustar pabspabs/* * Modification History * * 2001-September-22 Jason Rohrer * Created. * * 2001-October-13 Jason Rohrer * Added support for 4-channel images. * * 2006-November-18 Jason Rohrer * Fixed two unused variable warnings. */ #ifndef TGA_IMAGE_CONVERTER_INCLUDED #define TGA_IMAGE_CONVERTER_INCLUDED #include "LittleEndianImageConverter.h" /** * TGA (Targa) implementation of the image conversion interface. * * Note that it only supports 24- and 32-bit TGA files * (and thus only 3- and 4-channel Images). * * TGA format information taken from: * http://www.cubic.org/source/archive/fileform/graphic/tga/targa.txt * * @author Jason Rohrer */ class TGAImageConverter : public LittleEndianImageConverter { public: // implement the ImageConverter interface virtual void formatImage( Image *inImage, OutputStream *inStream ); virtual Image *deformatImage( InputStream *inStream ); }; inline void TGAImageConverter::formatImage( Image *inImage, OutputStream *inStream ) { int numChannels = inImage->getNumChannels(); // make sure the image is in the right format if( numChannels != 3 && numChannels != 4 ) { printf( "Only 3- and 4-channel images can be converted to " ); printf( "the TGA format.\n" ); return; } long width = inImage->getWidth(); long height = inImage->getHeight(); long numPixels = width * height; // a buffer for writing single bytes unsigned char *byteBuffer = new unsigned char[1]; // write the identification field size // (an empty identification field) byteBuffer[0] = 0; inStream->write( byteBuffer, 1 ); // write the color map type // (no color map) byteBuffer[0] = 0; inStream->write( byteBuffer, 1 ); // write the image type code // (type 2: unmapped RGB image) byteBuffer[0] = 2; inStream->write( byteBuffer, 1 ); // no color map spec // (set to 0, though it will be ignored) unsigned char *colorMapSpec = new unsigned char[5]; int i; for( i=0; i<5; i++ ) { colorMapSpec[i] = 0; } inStream->write( colorMapSpec, 5 ); delete [] colorMapSpec; // now for the image specification // x origin coordinate writeLittleEndianShort( 0, inStream ); // y origin coordinate writeLittleEndianShort( 0, inStream ); writeLittleEndianShort( width, inStream ); writeLittleEndianShort( height, inStream ); // number of bits in pixels if( numChannels == 3 ) { byteBuffer[0] = 24; } else { byteBuffer[0] = 32; } inStream->write( byteBuffer, 1 ); // image descriptor byte if( numChannels == 3 ) { // setting to 0 specifies: // -- no attributes per pixel (for 24-bit) // -- screen origin in lower left corner // -- non-interleaved data storage byteBuffer[0] = 0; } else { // setting to 8 specifies: // -- 8 attributes per pixel (for 32-bit) (attributes are alpha bits) // -- screen origin in lower left corner // -- non-interleaved data storage byteBuffer[0] = 8; } // set bit 5 to 1 to specify screen origin in upper left corner byteBuffer[0] = byteBuffer[0] | ( 1 << 5 ); inStream->write( byteBuffer, 1 ); // We skip the image identification field, // since we set its length to 0 above. // We also skip the color map data, // since we have none (as specified above). // now we write the pixels, in BGR(A) order unsigned char *raster = new unsigned char[ numPixels * numChannels ]; double *red = inImage->getChannel( 0 ); double *green = inImage->getChannel( 1 ); double *blue = inImage->getChannel( 2 ); long rasterIndex = 0; if( numChannels == 3 ) { for( int i=0; iwrite( raster, numPixels * 3 ); } else { // numChannels == 4 double *alpha = inImage->getChannel( 3 ); for( int i=0; iwrite( raster, numPixels * numChannels ); delete [] raster; delete [] byteBuffer; } inline Image *TGAImageConverter::deformatImage( InputStream *inStream ) { // a buffer for reading single bytes unsigned char *byteBuffer = new unsigned char[1]; // read the identification field size inStream->read( byteBuffer, 1 ); int identificationFieldSize = byteBuffer[0]; // read the color map type // (only 0, or no color map, is supported) inStream->read( byteBuffer, 1 ); if( byteBuffer[0] != 0 ) { printf( "Only TGA files without colormaps can be read.\n" ); delete [] byteBuffer; return NULL; } // read the image type code // (only type 2, unmapped RGB image, is supported) inStream->read( byteBuffer, 1 ); if( byteBuffer[0] != 2 ) { printf( "Only TGA files containing unmapped RGB images can be read.\n" ); delete [] byteBuffer; return NULL; } // ignore color map spec // (skip all 5 bytes of it) unsigned char *colorMapSpec = new unsigned char[5]; inStream->read( colorMapSpec, 5 ); delete [] colorMapSpec; // now for the image specification // don't need either of these // don't set to a variable for now to avoid unused variable warnings // x origin coordinate readLittleEndianShort( inStream ); // y origin coordinate readLittleEndianShort( inStream ); long width = readLittleEndianShort( inStream ); long height = readLittleEndianShort( inStream ); long numPixels = width * height; // number of bits in pixels // only 24 bits per pixel supported inStream->read( byteBuffer, 1 ); if( byteBuffer[0] != 24 && byteBuffer[0] != 32 ) { printf( "Only 24- and 32-bit TGA files can be read.\n" ); delete [] byteBuffer; return NULL; } int numChannels = 0; if( byteBuffer[0] == 24 ) { numChannels = 3; } else { numChannels = 4; } // image descriptor byte // setting to 0 specifies: // -- no attributes per pixel (for 24-bit) // -- screen origin in lower left corner // -- non-interleaved data storage // set bit 5 to 1 to specify screen origin in upper left corner inStream->read( byteBuffer, 1 ); char originAtTop = byteBuffer[0] & ( 1 << 5 ); if( identificationFieldSize > 0 ) { // We skip the image identification field unsigned char *identificationField = new unsigned char[ identificationFieldSize ]; inStream->read( identificationField, identificationFieldSize ); delete [] identificationField; } // We also skip the color map data, // since we have none (as specified above). // now we read the pixels, in BGR(A) order unsigned char *raster = new unsigned char[ numPixels * numChannels ]; inStream->read( raster, numPixels * numChannels ); // optimization: don't init channels to black (found with profiler) Image *image = new Image( width, height, numChannels, false ); double *red = image->getChannel( 0 ); double *green = image->getChannel( 1 ); double *blue = image->getChannel( 2 ); long rasterIndex = 0; double inv255 = 1.0 / 255.0; if( numChannels == 3 ) { if( originAtTop ) { for( int i=0; i=0; y-- ) { for( int x=0; xgetChannel( 3 ); if( originAtTop ) { for( int i=0; i=0; y-- ) { int yOffset = y * width; for( int x=0; x #include #include #include #include #include void abort_(const char * s, ...) { va_list args; va_start(args, s); vfprintf(stderr, s, args); fprintf(stderr, "\n"); va_end(args); abort(); } int main() { int w = 100; int h = 100; unsigned int *data = new unsigned int[ w * h ]; // red fades toward bottom // green fades toward right unsigned int **rows = new unsigned int *[ h ]; for( int y=0; y 100 || mQuality < 0 ) { printf( "JPEG quality must be in [0,100]\n" ); mQuality = 50; } } inline void JPEGImageConverter::setQuality( int inQuality ) { mQuality = inQuality; if( mQuality > 100 || mQuality < 0 ) { printf( "JPEG quality must be in [0,100]\n" ); mQuality = 50; } } inline int JPEGImageConverter::getQuality() { return mQuality; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/converters/jpegConverterTestCompile0000750000175000017500000000020307272310031030256 0ustar pabspabsg++ -I../../.. -ljpeg -o jpegConverterTest jpegConverterTest.cpp unix/JPEGImageConverterUnix.cpp ../../io/file/linux/PathLinux.cpp Cultivation_9+dfsg1_UnixSource/minorGems/graphics/converters/jpegConverterTest.cpp0000640000175000017500000000366307273016172027553 0ustar pabspabs/* * Modification History * * 2001-April-27 Jason Rohrer * Created. * * 2001-April-29 Jason Rohrer * Completed initial version and used to test JPEGImageConverter * successfully. */ #include #include "minorGems/graphics/Image.h" #include "JPEGImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileOutputStream.h" #include "minorGems/io/file/FileInputStream.h" // test function for the BMPImageConverter class int main( char inNumArgs, char**inArgs ) { if( inNumArgs != 2 ) { printf( "must pass in a file name to write to\n" ); return 1; } int length = 0; while( inArgs[1][length] != '\0' ) { length++; } File *file = new File( NULL, inArgs[1], length ); // read image in FileInputStream *stream = new FileInputStream( file ); JPEGImageConverter *converter = new JPEGImageConverter( 50 ); Image *image = converter->deformatImage( stream ); if( image != NULL ) { // write image back out File *fileOut = new File( NULL, "testOut.jpg", 11 ); FileOutputStream *outStream = new FileOutputStream( fileOut ); converter->formatImage( image, outStream ); delete outStream; delete fileOut; delete image; } delete stream; delete converter; delete file; /* FileOutputStream *stream = new FileOutputStream( file ); JPEGImageConverter *converter = new JPEGImageConverteonverter( 50 ); Image *image = new Image( 256, 256, 3 ); double *red = image->getChannel( 0 ); double *green = image->getChannel( 1 ); for( int y=0; ygetHeight(); y++ ) { for( int x=0; xgetWidth(); x++ ) { long index = y * image->getWidth() + x; red[index] = (double)y / (double)( image->getHeight() ); green[index] = (double)x / (double)( image->getWidth() ); //red[index] = 1.0; } } converter->formatImage( image, stream ); delete stream; // delete file explicitly delete file; delete converter; delete image; */ return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/converters/PNGImageConverter.cpp0000640000175000017500000003574611374544330027363 0ustar pabspabs/* * Modification History * * 2006-November-21 Jason Rohrer * Created. * * 2010-May-18 Jason Rohrer * String parameters as const to fix warnings. */ #include "PNGImageConverter.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/graphics/RGBAImage.h" //#include "lodepng.h" //#include #include PNGImageConverter::PNGImageConverter( int inCompressionLevel ) : mCompressionLevel( inCompressionLevel ) { // set up the CRC table // code taken from the PNG spec: // http://www.w3.org/TR/2003/REC-PNG-20031110/#D-CRCAppendix unsigned long c; int n, k; for( n=0; n<256; n++ ) { c = (unsigned long)n; for( k=0; k<8; k++ ) { if( c & 1 ) { c = 0xedb88320L ^ (c >> 1); } else { c = c >> 1; } } mCRCTable[n] = c; } } unsigned long PNGImageConverter::updateCRC( unsigned long inCRC, unsigned char *inData, int inLength ) { // code taken from the PNG spec: // http://www.w3.org/TR/2003/REC-PNG-20031110/#D-CRCAppendix unsigned long c = inCRC; int n; for( n=0; n> 8); } return c; } #define ADLER_BASE 65521 /* largest prime smaller than 65536 */ /** * Updates an adler32 checksum. * code found here http://www.ietf.org/rfc/rfc1950.txt * * New adlers should start with inAdler set to 1. * * @param inAdler the current state of the checksum. * @param inData the data to add. Destroyed by caller. * @param inLength the length of the data in bytes. * * @return the new checksum. */ unsigned long updateAdler32( unsigned long inAdler, unsigned char *inData, int inLength ) { unsigned long s1 = inAdler & 0xffff; unsigned long s2 = (inAdler >> 16) & 0xffff; int n; for (n = 0; n < inLength; n++) { s1 = (s1 + inData[n]) % ADLER_BASE; s2 = (s2 + s1) % ADLER_BASE; } return (s2 << 16) + s1; } void PNGImageConverter::writeChunk( const char inChunkType[4], unsigned char *inData, unsigned long inNumBytes, OutputStream *inStream ) { // chunk layout: // 4-byte length // 4-char type // data // 4-byte CRC (applied to type and data parts) // write the length writeBigEndianLong( inNumBytes, inStream ); inStream->write( (unsigned char *)inChunkType, 4 ); // start the crc unsigned long crc = updateCRC( mStartCRC, (unsigned char *)inChunkType, 4 ); if( inData != NULL ) { // chunk has data inStream->write( inData, inNumBytes ); crc = updateCRC( crc, inData, inNumBytes ); } // final step: invert the CRC crc = crc ^ 0xffffffffL; // now write the CRC writeBigEndianLong( crc, inStream ); } // callbacks for libpng io void libpngWriteCallback( png_structp png_ptr, png_bytep data, png_size_t length ) { // unpack our extra parameter void *write_io_ptr = png_get_io_ptr( png_ptr ); OutputStream *inStream = (OutputStream *)write_io_ptr; inStream->write( data, length ); } void libpngFlushCallback( png_structp png_ptr ) { // do nothing? } void PNGImageConverter::formatImage( Image *inImage, OutputStream *inStream ) { int numChannels = inImage->getNumChannels(); // make sure the image is in the right format if( numChannels != 3 && numChannels != 4 ) { printf( "Only 3- and 4-channel images can be converted to " ); printf( "the PNG format.\n" ); return; } int w = inImage->getWidth(); int h = inImage->getHeight(); //RGBAImage rgbaImage( inImage ); unsigned char *imageBytes = RGBAImage::getRGBABytes( inImage ); // libpng implementation // adapted from this: // http://zarb.org/~gc/html/libpng.html // get pointers to rows unsigned char **rows = new unsigned char *[h]; for( int y=0; ywrite( encodedBytes, encodedSize ); delete [] encodedBytes; if( true ) { return; } */ // same for all PNG images // used to check for basic transmission errors, such as line-end flipping unsigned char pngSignature[8] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; inStream->write( pngSignature, 8 ); // data for IHDR chunk unsigned char headerData[13]; // width headerData[0] = (w >> 24) & 0xff; headerData[1] = (w >> 16) & 0xff; headerData[2] = (w >> 8) & 0xff; headerData[3] = w & 0xff; // height headerData[4] = (h >> 24) & 0xff; headerData[5] = (h >> 16) & 0xff; headerData[6] = (h >> 8) & 0xff; headerData[7] = h & 0xff; // bit depth headerData[8] = 8; // color type // 2 = truecolor (RGB) headerData[9] = 2; // compression method // method 0 (deflate) headerData[10] = 0; // filter method // method 0 supports 5 filter types headerData[11] = 0; // no interlace headerData[12] = 0; writeChunk( "IHDR", headerData, 13, inStream ); int numRawBytes = w * h * 3; // extra byte per scanline for filter type numRawBytes += h; unsigned char *rawScanlineBytes = new unsigned char[ numRawBytes ]; // ignore alpha channel double *channels[3]; int i; for( i=0; i<3; i++ ) { channels[i] = inImage->getChannel( i ); } int pixelNumber = 0; for( int y=0; y zlibBlock; // compression method 8 (deflate) // with a LZ77 window size parameter of w=7 // LZ77 window size is then 2^( w + 8 ), or in this case 32768 zlibBlock.push_back( 0x78 ); // flags // compression level 0 (2 bytes = 00b) // no preset dictionary (1 byte = 0b) // check bits for compression method (5 bits) // Should be such that if the 8-bit compression method, followed // by the 8-bit flags field, is viewed as a 16-bit number, // it is an even multiple of 31 // For our settings, check bits of 00001 works //zlibBlock.push_back( 0x01 ); // hack: mimic zlib here zlibBlock.push_back( 0xda ); // now ready for compressed data blocks int rawDataIndex = 0; // length field is 16 bits int maxUncompressedBlockLength = 65535; while( rawDataIndex < numRawBytes ) { // push another deflate block // first bit BFINAL // only 1 for final block // next two bits BTYPE // BTYPE=00 is an uncompressed block // remaining 5 bits ignored // Thus, we put 0x00 for uncompressed blocks that are not final // and 0x80 for final uncompressed block int bytesLeft = numRawBytes - rawDataIndex; int bytesInBlock; if( bytesLeft <= maxUncompressedBlockLength ) { // final // hack: when comparing output with zlib, noticed that it doesn't // set 0x80 for the final block, instead it uses 0x01 // For some reason, this was making the PNG unreadable // zlibBlock.push_back( 0x80 ); zlibBlock.push_back( 0x01 ); bytesInBlock = bytesLeft; } else { // not final zlibBlock.push_back( 0x00 ); bytesInBlock = maxUncompressedBlockLength; } // length in least-significant-byte-first order unsigned char firstLengthByte = bytesInBlock & 0xff; unsigned char secondLengthByte = (bytesInBlock >> 8) & 0xff; zlibBlock.push_back( firstLengthByte ); zlibBlock.push_back( secondLengthByte ); // those same length bytes inverted // (called "one's compliment" in the spec zlibBlock.push_back( firstLengthByte ^ 0xff); zlibBlock.push_back( secondLengthByte ^ 0xff ); // now the uncompressed data for( int b=0; b< bytesInBlock; b++ ) { zlibBlock.push_back( rawScanlineBytes[ rawDataIndex ] ); rawDataIndex++; } } // finally, adler32 of original data unsigned long adler = updateAdler32( 1L, rawScanlineBytes, numRawBytes ); zlibBlock.push_back( (adler >> 24) & 0xff ); zlibBlock.push_back( (adler >> 16) & 0xff ); zlibBlock.push_back( (adler >> 8) & 0xff ); zlibBlock.push_back( adler & 0xff ); // the zlib block is now complete /* // check against real zlib implementation z_stream zStream; zStream.next_in = rawScanlineBytes; zStream.avail_in = numRawBytes; zStream.total_in = 0; int outSize = 2 * numRawBytes + 100; unsigned char *zlibOutBuffer = new unsigned char[ outSize ]; zStream.next_out = zlibOutBuffer; zStream.avail_out = outSize; zStream.total_out = 0; zStream.data_type = Z_BINARY; zStream.zalloc = Z_NULL; zStream.zfree = Z_NULL; // init the stream // no compression int result; //result = deflateInit( &zStream, Z_DEFAULT_COMPRESSION); result = deflateInit( &zStream, Z_NO_COMPRESSION); if( result != Z_OK ) { printf( "zlib deflateInit error: %s\n", zStream.msg ); } // deflate and flush result = deflate( &zStream, Z_FINISH ); if( result != Z_STREAM_END ) { printf( "zlib deflate error (%d): %s\n", result, zStream.msg ); } printf( "Total in = %d, total out = %d\n", zStream.total_in, zStream.total_out ); printf( "Our raw bytes (%d):\n", numRawBytes ); int b; for( b=0; b zlibBlock.size() ) { minBytes = zlibBlock.size(); } for( b=0; bgetNumChannels() != 3 ) { printf( "Only 3-channel images can be converted to the BMP format.\n" ); return; } long width = inImage->getWidth(); long height = inImage->getHeight(); long numPixels = width * height; // each line should be padded with zeros to // end on a 4-byte boundary int numZeroPaddingBytes = ( 4 - ( width * 3 ) % 4 ) % 4; short bitCount = 24; long rasterSize = numPixels * 3; // zero padding bytes for each row rasterSize += numZeroPaddingBytes * height; // offset past two headers long offsetToRaster = 14 + 40; long fileSize = offsetToRaster + rasterSize; long compressionType = 0; long pixelsPerMeter = 2834; // both are 0 since we have no color map long colorsUsed = 0; long colorsImportant = 0; // write the header unsigned char *signature = new unsigned char[2]; signature[0] = 'B'; signature[1] = 'M'; inStream->write( signature, 2 ); delete [] signature; writeLittleEndianLong( fileSize, inStream ); writeLittleEndianLong( 0, inStream ); writeLittleEndianLong( offsetToRaster, inStream ); // write the info header // header size writeLittleEndianLong( 40, inStream ); writeLittleEndianLong( width, inStream ); writeLittleEndianLong( height, inStream ); // numPlanes writeLittleEndianShort( 1, inStream ); writeLittleEndianShort( bitCount, inStream ); writeLittleEndianLong( compressionType, inStream ); writeLittleEndianLong( rasterSize, inStream ); writeLittleEndianLong( pixelsPerMeter, inStream ); writeLittleEndianLong( pixelsPerMeter, inStream ); writeLittleEndianLong( colorsUsed, inStream ); writeLittleEndianLong( colorsImportant, inStream ); // no color table... // now write the raster unsigned char *raster = new unsigned char[ rasterSize ]; double *red = inImage->getChannel( 0 ); double *green = inImage->getChannel( 1 ); double *blue = inImage->getChannel( 2 ); // pixels are stored bottom-up, left to right // (row major order) long rasterIndex = 0; for( int y=height-1; y>=0; y-- ) { for( int x=0; xwrite( raster, rasterSize ); delete [] raster; } inline Image *BMPImageConverter::deformatImage( InputStream *inStream ) { // temp buffer used to skip data in the stream unsigned char *temp = new unsigned char[ 100 ]; // skip signature inStream->read( temp, 2 ); long fileSize = readLittleEndianLong( inStream ); // skip unused inStream->read( temp, 4 ); long rasterOffset = readLittleEndianLong( inStream ); long rasterSize = fileSize - rasterOffset; // skip size of header inStream->read( temp, 4 ); long width = readLittleEndianLong( inStream ); long height = readLittleEndianLong( inStream ); // skip planes inStream->read( temp, 2 ); short bitCount = readLittleEndianShort( inStream ); char failing = false; if( bitCount != 24 ) { printf( "Only 24-bit BMP file formats supported.\n" ); failing = true; } long compression = readLittleEndianLong( inStream ); if( compression != 0 ) { printf( "Only uncompressed BMP file formats supported.\n" ); failing = true; } // skip imageSize, resolution, and color usage information inStream->read( temp, 20 ); // now we're at the raster. // each line should be padded with zeros to // end on a 4-byte boundary int numZeroPaddingBytes = ( 4 - ( width * 3 ) % 4 ) % 4; unsigned char *raster = new unsigned char[ rasterSize ]; inStream->read( raster, rasterSize ); Image *returnImage; if( failing ) { return NULL; } else { returnImage = new Image( width, height, 3 ); double *red = returnImage->getChannel( 0 ); double *green = returnImage->getChannel( 1 ); double *blue = returnImage->getChannel( 2 ); // pixels are stored bottom-up, left to right // (row major order) long rasterIndex = 0; for( int y=height-1; y>=0; y-- ) { for( int x=0; x #include class Color { public : /** * Constructs a color. * * @param red, green, blue the components of the color, each in [0,1]. * @param alpha the alpha value in [0,1]. Defaults to 1. * @param inBuildComposite set to true to build the composite * upon construction, or false to skip building composite (faster). * Defaults to false. */ Color(float red, float green, float blue, float alpha=1, char inBuildComposite=false ); /** * Constructs a all-zero (except alpha set to 1) color with no * composite built. */ Color(); /** * Constructs an rgb color from HSV components. * * @param inHue, inSaturation, inValue the HSV components of the * color, each in [0,1]. * @param alpha the alpha value in [0,1]. Defaults to 1. * @param inBuildComposite set to true to build the composite * upon construction, or false to skip building composite (faster). * Defaults to true. * * @return an RGBA color equivalent to the HSV color. * Must be destroyed by caller. */ static Color *makeColorFromHSV( float inHue, float inSaturation, float inValue, float inAlpha=1, char inBuildComposite=false ); /** * Makes HSV values (each in range 0..1) from this RGB color. * * @param outH, outS, outV pointers to where values should be returned. */ void makeHSV( float *outH, float *outS, float *outV ); float r, g, b, a; char mCompositeBuilt; unsigned long composite; // 32-bit composite color Color *copy(); // make a copy of this color /** * Sets the RGBA values of this color. * * @param red, green, blue, alpha the values to set. * Alpha defaults to 0. */ void setValues( float red, float green, float blue, float alpha=1 ); /** * Sets the RGBA values of this color using the values from * another color. * * @param inOtherColor the color to copy values from. * Must be destroyed by caller. */ void setValues( Color *inOtherColor ); /** * Tests whether this color is equal to another color. * * @param inOtherColor the other color. * Must be destroyed by caller. * * @return true if they are equal, or false otherwise. */ char equals( Color *inOtherColor ); void print(); /** * Computes the linear weighted sum of two colors. * * @param inFirst the first color. * @param inSecond the second color. * @param inFirstWeight the weight given to the first color in the * sum. The second color is weighted (1-inFirstWeight). * * @return the sum color. Must be destroyed by caller. */ static Color *linearSum( Color *inFirst, Color *inSecond, float inFirstWeight ); // after adjusting the r, g, b, a values exterally // call this to remake the composite unsigned long unsigned long rebuildComposite(); // get largest component of R,G,B float getMax(); // alter color data by multiplying by weight void weightColor( float weight ); /** * Alters color data by multiplying by a weight color. * * @param inWeightColor the color to multiply this color by. * Must be destroyed by caller. */ void weightColor( Color *inWeightColor ); /** * Inverts this color. * * Ignores alpha channel. */ void invert(); /** * Saturates this color, ensuring that at most 2 components are * non-zero. * * Ignores alpha channel. */ void saturate(); // get component by component weighted 32-bit composite // (returns alpha unweighted) unsigned long getWeightedComposite( float weight ); // from this color unsigned long getWeightedComposite(unsigned long c1, float weight ); // from composite unsigned long sumComposite(unsigned long c1, unsigned long c2); // access this color as a three vector float &operator[](int rgbIndex); }; inline Color::Color() { r = 0; g = 0; b = 0; a = 1; mCompositeBuilt = false; composite = 0; } inline Color::Color(float red, float green, float blue, float alpha, char inBuildComposite ) { r = red; g = green; b = blue; a = alpha; if( inBuildComposite ) { rebuildComposite(); } else { composite = 0; mCompositeBuilt = false; } } inline Color *Color::makeColorFromHSV( float inHue, float inSaturation, float inValue, float inAlpha, char inBuildComposite ) { // based on pseudocode from http://www.easyrgb.com/math.php float r, g, b; if ( inSaturation == 0 ) { r = inValue; g = inValue; b = inValue; } else { float var_h = inHue * 6; // H must be < 1 if( var_h == 6 ) { var_h = 0; } float var_i = int( var_h ); // Or var_i = floor( var_h ) float var_1 = inValue * ( 1 - inSaturation ); float var_2 = inValue * ( 1 - inSaturation * ( var_h - var_i ) ); float var_3 = inValue * ( 1 - inSaturation * ( 1 - ( var_h - var_i ) ) ); if( var_i == 0 ) { r = inValue; g = var_3; b = var_1; } else if( var_i == 1 ) { r = var_2; g = inValue; b = var_1; } else if( var_i == 2 ) { r = var_1; g = inValue; b = var_3; } else if( var_i == 3 ) { r = var_1; g = var_2; b = inValue; } else if( var_i == 4 ) { r = var_3; g = var_1; b = inValue; } else { r = inValue; g = var_1; b = var_2; } } return new Color( r, g, b, inAlpha, inBuildComposite ); } inline void Color::makeHSV( float *outH, float *outS, float *outV ) { // based on pseudocode from http://www.easyrgb.com/math.php //Min. value of RGB float var_Min = r; if( g < var_Min ) { var_Min = g; } if( b < var_Min ) { var_Min = b; } //Max. value of RGB float var_Max = r; if( g > var_Max ) { var_Max = g; } if( b > var_Max ) { var_Max = b; } //Delta RGB value float del_Max = var_Max - var_Min; //HSV results from 0 to 1 float H; float S; float V = var_Max; if ( del_Max == 0 ) { //This is a gray, no chroma... H = 0; S = 0; } else { //Chromatic data... S = del_Max / var_Max; float del_R = ( ( ( var_Max - r ) / 6 ) + ( del_Max / 2 ) ) / del_Max; float del_G = ( ( ( var_Max - g ) / 6 ) + ( del_Max / 2 ) ) / del_Max; float del_B = ( ( ( var_Max - b ) / 6 ) + ( del_Max / 2 ) ) / del_Max; if( r == var_Max ) { H = del_B - del_G; } else if( g == var_Max ) { H = ( 1.0f / 3.0f ) + del_R - del_B; } else if ( b == var_Max ) { H = ( 2.0f / 3.0f ) + del_G - del_R; } if( H < 0 ) { H += 1; } if( H > 1 ) { H -= 1; } } *outH = H; *outS = S; *outV = V; } inline Color *Color::copy() { Color *copyColor = new Color(r,g,b,a, mCompositeBuilt ); return copyColor; } inline void Color::setValues( float red, float green, float blue, float alpha ) { r = red; g = green; b = blue; a = alpha; if( mCompositeBuilt ) { rebuildComposite(); } } inline void Color::setValues( Color *inOtherColor ) { setValues( inOtherColor->r, inOtherColor->g, inOtherColor->b, inOtherColor->a ); } inline char Color::equals( Color *inOtherColor ) { if( r == inOtherColor->r && g == inOtherColor->g && b == inOtherColor->b && a == inOtherColor->a ) { return true; } else { return false; } } inline void Color::print() { printf( "(%f, %f, %f, %f)", r, g, b, a ); } inline Color *Color::linearSum( Color *inFirst, Color *inSecond, float inFirstWeight ) { float secondWeight = 1 - inFirstWeight; float r = inFirstWeight * inFirst->r + secondWeight * inSecond->r; float g = inFirstWeight * inFirst->g + secondWeight * inSecond->g; float b = inFirstWeight * inFirst->b + secondWeight * inSecond->b; float a = inFirstWeight * inFirst->a + secondWeight * inSecond->a; return new Color( r, g, b, a, inFirst->mCompositeBuilt || inSecond->mCompositeBuilt ); } inline unsigned long Color::rebuildComposite() { composite = ((int)(b*255)) | ((int)(g*255)) << 8 | ((int)(r*255)) << 16 | ((int)(a*255)) << 24; mCompositeBuilt = true; return composite; } inline float Color::getMax() { float max = -FLT_MAX; if( r > max ) max = r; if( g > max ) max = g; if( b > max ) max = b; return max; } inline void Color::weightColor( float weight ) { r = r * weight; g = g * weight; b = b * weight; // for now, don't touch alpha if( mCompositeBuilt ) { rebuildComposite(); } } inline void Color::invert() { r = 1 - r; g = 1 - g; b = 1 - b; } inline void Color::saturate() { if( r < g && r < b ) { r = 0; } else if( g < r && g < b ) { g = 0; } else if( b < r && b < g ) { b = 0; } else if( r != 0 ) { // they are all equal, but non-zero // default to dropping red r = 0; } //else // they are all 0 // leave as black } inline void Color::weightColor( Color *inWeightColor ) { r *= inWeightColor->r; g *= inWeightColor->g; b *= inWeightColor->b; a *= inWeightColor->a; if( mCompositeBuilt ) { rebuildComposite(); } } inline float &Color::operator[](int rgbIndex) { if( rgbIndex == 0) return r; else if( rgbIndex == 1) return g; else if( rgbIndex == 2) return b; else if( rgbIndex == 3) return a; return r; // default, return r reference } inline unsigned long Color::getWeightedComposite( float weight ) { return ((int)(b*255*weight)) | ((int)(g*255*weight)) << 8 | ((int)(r*255*weight)) << 16 | ((int)(a*255)) << 24; } inline unsigned long Color::getWeightedComposite( unsigned long c1, float weight ) { int b = c1 & 0xFF; int g = (c1 >> 8) & 0xFF; int r = (c1 >> 16) & 0xFF; int a = c1 >> 24; return ((int)(b*weight)) | (((int)(g*weight)) << 8) | (((int)(r*weight)) << 16) | (((int)(a*weight)) << 24); } inline unsigned long Color::sumComposite(unsigned long c1, unsigned long c2) { int b = (c1 & 0xFF) + (c2 & 0xFF); if( b > 255) b = 255; int g = ((c1 >> 8) & 0xFF) + ((c2 >> 8) & 0xFF); if( g > 255) g = 255; int r = ((c1 >> 16) & 0xFF) + ((c2 >> 16) & 0xFF); if( r > 255) r = 255; int a = (c1 >> 24) + (c2 >> 24); if( a > 255) a = 255; return b | (g << 8) | (r << 16) | (a << 24); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/ImageConverter.h0000640000175000017500000000263310711714511024250 0ustar pabspabs/* * Modification History * * 2001-February-19 Jason Rohrer * Created. * * 2001-April-28 Jason Rohrer * Updated comments to deal with a failed deformatting operation. * * 2007-October-30 Jason Rohrer * Added virtual destructor. */ #ifndef IMAGE_CONVERTER_INCLUDED #define IMAGE_CONVERTER_INCLUDED #include "Image.h" #include "minorGems/io/InputStream.h" #include "minorGems/io/OutputStream.h" /** * Interface for a class that can convert Images to and from * various image formats. * * @author Jason Rohrer */ class ImageConverter { public: /** * Sends an image out to a stream as a particular * format. * * None of the parameters are destroyed by this call. * * @param inImage the image to convert. * @param inStream the stream to write the formatted image to. */ virtual void formatImage( Image *inImage, OutputStream *inStream ) = 0; /** * Reads an image in from a stream as a particular * format. * * None of the parameters are destroyed by this call. * * @param inStream the stream to read the formatted image. * * @return the deformatted image, or NULL if the deformatting * operation fails. Must be destroyed by caller. */ virtual Image *deformatImage( InputStream *inStream ) = 0; // to ensure proper destruction of implementing classes virtual ~ImageConverter() {}; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/getMouse.h0000640000175000017500000000034207205611501023120 0ustar pabspabs// Jason Rohrer // getMouse.h /** * * general interface for getting current mouse position * Implemented by a graphix framework on a particular platform * * * Created 3-21-2000 * Mods: * */ void getMouse( int *x, int *y );Cultivation_9+dfsg1_UnixSource/minorGems/graphics/keyCodes.h0000640000175000017500000000104407205611501023076 0ustar pabspabs// Jason Rohrer // keyCodes.h /** * * Header for defining key codes for various platforms * * * Created 12-15-99 * Mods: * Jason Rohrer 3-23-2000 Added more key codes * */ #ifdef WINDOWS_KEY_CODES #define M_KEY 0x4D #define N_KEY 0x4E #define S_KEY 0x53 #define Q_KEY 0x51 #define L_KEY 0x4C #define R_KEY 0x52 #define T_KEY 0x54 #endif #ifdef MAC_KEY_CODES #define M_KEY 0x2E #define N_KEY 0x2D #define S_KEY 0x01 #define Q_KEY 0x0C #define L_KEY 0x25 #define R_KEY 0x0F #define T_KEY 0x11 #endifCultivation_9+dfsg1_UnixSource/minorGems/graphics/linux/0000750000175000017500000000000011401021057022310 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/graphics/linux/SDLTest.cpp0000640000175000017500000001173007274131037024316 0ustar pabspabs //#include #include "minorGems/graphics/ScreenGraphics.h" #include "minorGems/graphics/GraphicBuffer.h" #include "minorGems/graphics/Color.h" #include "minorGems/ui/Mouse.h" #include #include #include int numIcons = 200; int currentStep = 0; int numSteps = 1000; void catch_int(int sig_num) { printf( "Quiting..." ); currentStep = numSteps; signal( SIGINT, catch_int); } int main() { int i, j; // let catch_int handle interrupt (^c) signal( SIGINT, catch_int); ScreenGraphics *graphics = new ScreenGraphics( 640, 480 ); Mouse *mouse = new Mouse(2); unsigned long *pixelBuff = new unsigned long[ 640 * 480 ]; GraphicBuffer *buffer = new GraphicBuffer( pixelBuff, 640, 480 ); IconMap **maps = new IconMap*[numIcons]; IconMap *mouseMap; IconMap *mouseMap1 = new IconMap( 10, 10 ); Color mouseColor1( 1.0, 0.0, 0.0, 1.0 ); for( int y=0; y<10; y++ ) { for( int x=0; x<10; x++ ) { mouseMap1->imageMap[ mouseMap1->yOffset[y] + x ] = mouseColor1.composite; } } IconMap *mouseMap2 = new IconMap( 10, 10 ); Color mouseColor2( 0.0, 1.0, 0.0, 1.0 ); for( int y=0; y<10; y++ ) { for( int x=0; x<10; x++ ) { mouseMap2->imageMap[ mouseMap2->yOffset[y] + x ] = mouseColor2.composite; } } IconMap *mouseMap3 = new IconMap( 10, 10 ); Color mouseColor3( 0.0, 0.0, 1.0, 1.0 ); for( int y=0; y<10; y++ ) { for( int x=0; x<10; x++ ) { mouseMap3->imageMap[ mouseMap3->yOffset[y] + x ] = mouseColor3.composite; } } mouseMap = mouseMap1; Color c( 0.0f, 0.0f, 0.0f, 1.0f ); buffer->fill( c ); float *xPos = new float[numIcons]; float *yPos = new float[numIcons]; float *xDelta = new float[numIcons]; float *yDelta = new float[numIcons]; for( i=0; iimageMap[ maps[i]->yOffset[y] + x ] = randColor.composite; } } } int mouseX = 0; int mouseY = 0; char buttonDown1 = false; char buttonDown2 = false; for( currentStep=0; currentSteperaseIconMap( mouseMap, mouseX, mouseY, c ); mouse->getLocation( &mouseX, &mouseY ); buffer->drawIconMap( mouseMap, mouseX, mouseY ); if( !buttonDown1 && mouse->isButtonDown(0) ) { buttonDown1 = true; mouseMap = mouseMap2; } else if( buttonDown1 && !mouse->isButtonDown(0) ) { buttonDown1 = false; mouseMap = mouseMap1; } else if( !buttonDown2 && mouse->isButtonDown(1) ) { buttonDown2 = true; mouseMap = mouseMap3; } else if( buttonDown2 && !mouse->isButtonDown(1) ) { buttonDown2 = false; mouseMap = mouseMap1; } for( i=0; ieraseIconMap( maps[i], (int)( xPos[i] - 5 ), (int)( yPos[i] - 5 ), c ); if( xPos[i] > 640 || xPos[i] < 0 ) { xDelta[i] = -xDelta[i]; } xPos[i] += xDelta[i]; if( yPos[i] > 480 || yPos[i] < 0 ) { yDelta[i] = -yDelta[i]; } yPos[i] += yDelta[i]; buffer->drawIconMap( maps[i], (int)( xPos[i] - 5 ), (int)( yPos[i] - 5 ) ); } graphics->swapBuffers( buffer ); } /* for( int i=0; i<100; i++ ) { if( i%2 == 0 ) { Color c( 1.0f, 0.0f, 0.0f, 1.0f ); buffer->fill( c ); } else { Color c( 0.0f, 1.0f, 0.0f, 1.0f ); buffer->fill( c ); } graphics->swapBuffers( buffer ); } */ printf( "Done.\n" ); return 0; /* if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { printf( "Couldn't initialize SDL: %s\n", SDL_GetError() ); exit(1); } //atexit( SDL_Quit ); SDL_Surface *screen = SDL_SetVideoMode( 640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF );//| SDL_FULLSCREEN ); if ( screen == NULL ) { printf( "Couldn't set 640x480x32 video mode: %s\n", SDL_GetError() ); exit(1); } for( int i=0; i< 100; i++ ) { if ( SDL_MUSTLOCK(screen) ) { if ( SDL_LockSurface(screen) < 0 ) { printf( "Couldn't lock screen: %s\n", SDL_GetError()); exit(1); } } Uint32 value; if( i%2 == 0 ) { value = 0x0; } else { value = 0xFFFFFFFF; } Uint32 *buffer = (Uint32 *)( screen->pixels ); for ( int y=0; yh; y++ ) { for ( int x=0; xw; x++ ) { int r = ( ( ( x * 255 ) / screen->w ) + i ) % 255; int g = ( ( ( y * 255 ) / screen->h ) + i ) % 255; int b = 0; int a = 255; //buffer[ y * screen->w + x ] = (a << 24) | (r << 16) | (g << 8) | b; buffer[ y * screen->w + x ] = value; } } if ( SDL_MUSTLOCK(screen) ) { SDL_UnlockSurface(screen); } //SDL_UpdateRect( screen, 0, 0, screen->w, screen->h ); SDL_Flip( screen ); } SDL_Quit(); SDL_Delay(2000); exit(0); */ } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/linux/ScreenGraphicsLinux.cpp0000640000175000017500000000660310447741000026750 0ustar pabspabs/* * Modification History * * 2000-November-18 Jason Rohrer * Created. * * 2000-November-19 Jason Rohrer * Change so that it doesn't use SDL's interrupt parachute, i.e., * so it will quit on ^c. * * 2001-May-1 Jason Rohrer * Changed to use more standard SDL include location. * * 2006-April-28 Jason Rohrer * Added functions for more direct access to screen pixels. * * 2006-June-26 Jason Rohrer * Added support for dirty rectangle. * Added resize support. */ #include "minorGems/graphics/ScreenGraphics.h" #include /** * Note: Linux implementation uses windowed mode. */ ScreenGraphics::ScreenGraphics( int inWidth, int inHeight ) : mWidth( inWidth ), mHeight( inHeight ) { if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 ) { printf( "Couldn't initialize SDL: %s\n", SDL_GetError() ); return; } SDL_Surface *screen = SDL_SetVideoMode( mWidth, mHeight, 32, SDL_SWSURFACE ); if ( screen == NULL ) { printf( "Couldn't set %dx%dx32 video mode: %s\n", mWidth, mHeight, SDL_GetError() ); return; } mNativeObjectPointer = (void*)screen; } ScreenGraphics::~ScreenGraphics() { SDL_Quit(); } void ScreenGraphics::resize( int inNewWidth, int inNewHeight ) { mWidth = inNewWidth; mHeight = inNewHeight; SDL_Surface *screen = SDL_SetVideoMode( mWidth, mHeight, 32, SDL_SWSURFACE ); if ( screen == NULL ) { printf( "Couldn't set %dx%dx32 video mode: %s\n", mWidth, mHeight, SDL_GetError() ); return; } mNativeObjectPointer = (void*)screen; } char ScreenGraphics::isResolutionAvailable( int inWidth, int inHeight ) { if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 ) { printf( "Couldn't initialize SDL: %s\n", SDL_GetError() ); return false; } // ask SDL if this screen size is supported char videoModeOk = (char)SDL_VideoModeOK( inWidth, inHeight, 32, SDL_SWSURFACE ); SDL_Quit(); return videoModeOk; } void ScreenGraphics::swapBuffers( GraphicBuffer *inOutBuffer ) { if( inOutBuffer->getHeight() != mHeight || inOutBuffer->getWidth() != mWidth ) { printf( "Buffer of incorrect size passed to screen.\n" ); return; } SDL_Surface *screen = (SDL_Surface*)mNativeObjectPointer; // check if we need to lock the screen if( SDL_MUSTLOCK( screen ) ) { if( SDL_LockSurface( screen ) < 0 ) { printf( "Couldn't lock screen: %s\n", SDL_GetError() ); return; } } Uint32 *buffer = (Uint32 *)( screen->pixels ); memcpy( (void*)buffer, (void*)( inOutBuffer->getBuffer() ), mWidth * mHeight * 4 ); // unlock the screen if necessary if ( SDL_MUSTLOCK(screen) ) { SDL_UnlockSurface(screen); } SDL_Flip( screen ); } GraphicBuffer *ScreenGraphics::getScreenBuffer() { SDL_Surface *screen = (SDL_Surface*)mNativeObjectPointer; Uint32 *buffer = (Uint32 *)( screen->pixels ); return new GraphicBuffer( (unsigned long *)buffer, mWidth, mHeight ); } void ScreenGraphics::flipDirtyRectangle( int inTopLeftX, int inTopLeftY, int inWidth, int inHeight ) { SDL_Surface *screen = (SDL_Surface*)mNativeObjectPointer; SDL_UpdateRect( screen, inTopLeftX, inTopLeftY, inWidth, inHeight ); } void ScreenGraphics::flipScreen() { SDL_Surface *screen = (SDL_Surface*)mNativeObjectPointer; SDL_Flip( screen ); } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/linux/graphixCommonDefs.h0000640000175000017500000000013007205611501026077 0ustar pabspabs// ALifeGUICommonDefs.h // common definitions for ALifeGui #define LINUX_KEY_CODES Cultivation_9+dfsg1_UnixSource/minorGems/graphics/Image.h0000640000175000017500000003775011070202762022367 0ustar pabspabs/* * Modification History * * 2000-December-21 Jason Rohrer * Created. * * 2001-January-6 Jason Rohrer * Fixed a bug in filter( ChannelFilter * ). * Set mSelection to NULL by default. * * 2001-January-10 Jason Rohrer * Made class serializable. * * 2001-January-15 Jason Rohrer * Made copy() not-virtual, so it can be overridden by subclasses * while allowing pointer type to determine which function * implementation is invoked. * * 2001-January-31 Jason Rohrer * Fixed a bug in copy(). * * 2001-February-3 Jason Rohrer * Updated serialization code to use new interfaces. * * 2001-February-4 Jason Rohrer * Rewrote the serialization code to send the image across as a byte * array with one byte per channel. This will reduce the transfer size by * a factor of 8. Keeping images in double format is convennient for image * creation, but the added quality never affects the end user anyway, so * there's no point in sending the extra data to a stream. * Removed an unused array allocation. * * 2005-February-21 Jason Rohrer * Made destructor virtual to avoid compiler warnings. * * 2006-August-25 Jason Rohrer * Made zero init of pixels optional (for speed). * * 2008-September-25 Jason Rohrer * Added a sub-image function and setting/getting color functions. */ #ifndef IMAGE_INCLUDED #define IMAGE_INCLUDED #include #include #include "ChannelFilter.h" #include "Color.h" #include "minorGems/io/Serializable.h" /** * A multi-channel, double-valued image. * * Is Serializable. Note that a serialized image doesn't have a selection. * * @author Jason Rohrer */ class Image : public Serializable { public: /** * Constructs an image. * * @param inWidth width of image in pixels. * @param inHeight height of image in pixels. * @param inNumChannels number of channels in image. * @param inStartPixelsAtZero true to initialize all pixels * to zero, or false to leave default memory values (garbage) * in place (pixels must be initialized by caller in this case). * Defaults to true. */ Image( int inWidth, int inHeight, int inNumChannels, char inStartPixelsAtZero = true ); virtual ~Image(); // gets the dimensions of this image. virtual long getWidth(); virtual long getHeight(); virtual long getNumChannels(); /** * Gets the values of a particular channel. * * Values are not copied. * * @param inChannel the channel to get. * * @return the values of the specified channel in row-major order. */ virtual double *getChannel( int inChannel ); /** * Gets the 3- or 4-channel color value at a given location in the * image. * * @param inIndex the image index. * * @return a color object. */ virtual Color getColor( int inIndex ); /** * Sets the 3- or 4-channel color value at a given location in the * image. * * @param inIndex the image index. * @param inColor the new color to set. */ virtual void setColor( int inIndex, Color inColor ); /** * Selects a region of the image. Default is a clear selection, * which means all regions of image are affected by an applied * filter. * * @param inSelection the image to use as the selection mask. * Values of 0 indicate pixels that are not selection, and 1 * indicate pixels that are selected, with selection amount * varying linearly between 0 and 1. * If inSelection is a single channel, then that channel is * used as a selection mask for all channels in this image. * If inSelection contains the same number of channels as this * image, then the corresponding channels of inSelection are * are used to mask each channel of this image. * If inSelection contains a number of channels different * from the number in this image, the first channel of inSelection * is used to mask each channel in this image. * * Note that inSelection is not copied or destroyed by this class. * Thus, modifying inSelection after calling setSelection will * modify the selection in this image. */ virtual void setSelection( Image *inSelection ); /** * Gets the selection for this image. * * @return the selection for this image. Returns NULL * if there is no selection. Must not be destroyed * by caller before calling clearSelection. */ virtual Image *getSelection(); /** * Clears the selection. Effectively selects the entire image. */ virtual void clearSelection(); /** * Applies a filter to the selected region of * a specified channel of this image. * * @param inFilter the filter to apply. * @param inChannel the channel to filter. */ virtual void filter( ChannelFilter *inFilter, int inChannel ); /** * Applies a filter to the selected region of * all channels of this image. * * @param inFilter the filter to apply. */ virtual void filter( ChannelFilter *inFilter ); /** * Copies the selected region of this image. Not virtual, * so can be overridden by subclasses while allowing pointer * type to determine which function implementation is invoked. * * @return a new image with the same number of channels * as this image, each containing the selected region * from each corresponding channel of this image. Unselected * regions are set to black. Returned image has no selection. */ Image *copy(); /** * Pastes the selected region from another image into * the selected region of this image. * * @param inImage the image to paste. Let c be the number * of channels in this image, and cp be the number * of channels in the image being pasted. * If ccp, then only the first cp channels * of this image are pasted into. */ virtual void paste( Image *inImage ); /** * Copies the data from the selected region of a channel. * * @param inChannel the channel to copy. * * @return a copy of the channel data. Must be destroyed * by the caller. */ virtual double *copyChannel( int inChannel ); /** * Pastes channel data into the selected region of a specified channel. * * @param inChannelData an array containing the channel * data to be pasted. Must be destroyed by caller. * @param inChannel the channel to paste into. */ virtual void pasteChannel( double *inChannelData, int inChannel ); /** * Gets the mask for a specified channel. * * @param inChannel the channel to get a mask for. * * @return the mask data for the specified channel. * If selection has the same number of channels as this image * then a different mask is returned for each channel. Otherwise, * the first channel from the selection is returned as the * mask for every channel. Returns NULL if there is no selection. */ virtual double *getChannelSelection( int inChannel ); /** * Extracts a smaller sub-image from this image. * * Ignores current selection. * * @param inStartX, inStartY, inWidth, inHeight * coordinates for the top left corner pixel of the sub-image * and the width and height of the sub-image. * * @return the sub-image as a new image. Must be destoryed by caller. */ Image *getSubImage( int inStartX, int inStartY, int inWidth, int inHeight ); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); protected: long mWide, mHigh, mNumPixels, mNumChannels; double **mChannels; // NULL if nothing selected. Image *mSelection; /** * Pastes masked channel data into the selected region of a * specified channel. * * @param inChannelData an array containing the channel * data to be pasted. Must be destroyed by caller. * @param inMask the selection mask to use for passed-in channel. * Set to NULL for no mask. * @param inChannel the channel to paste into. */ virtual void pasteChannel( double *inChannelData, double *inMask, int inChannel ); }; inline Image::Image( int inWidth, int inHeight, int inNumChannels, char inStartPixelsAtZero ) : mWide( inWidth ), mHigh( inHeight ), mNumPixels( inWidth * inHeight ), mNumChannels( inNumChannels ), mChannels( new double*[inNumChannels] ), mSelection( NULL ) { // initialize all channels for( int i=0; iapply( mChannels[ inChannel ], mWide, mHigh ); } else { // part of image selected // turn selection off and filter channel entirely Image *tempSelection = mSelection; mSelection = NULL; // filter a copy of the channel double *filteredChannel = copyChannel( inChannel ); inFilter->apply( filteredChannel, mWide, mHigh ); // now paste filtered channel back into selected region mSelection = tempSelection; pasteChannel( filteredChannel, inChannel ); } } inline void Image::filter( ChannelFilter *inFilter ) { for( int i=0; ipaste( this ); return copiedImage; } inline void Image::paste( Image *inImage ) { // copy paste in the min number of channels only int numChannelsToPaste = mNumChannels; if( numChannelsToPaste > inImage->getNumChannels() ) { numChannelsToPaste = inImage->getNumChannels(); } for( int i=0; igetChannel(i), inImage->getChannelSelection(i), i ); } } inline double *Image::copyChannel( int inChannel ) { // first, copy the channel double *copiedChannel = new double[mNumPixels]; memcpy( copiedChannel, mChannels[inChannel], sizeof( double ) * mNumPixels ); if( mSelection != NULL ) { // apply selection to copied channel double *selection = getChannelSelection( inChannel ); // scale copied channel with selection for( int i=0; igetNumChannels() == mNumChannels ) { // use separate selection for each channel return mSelection->getChannel( inChannel ); } else { // use first channel of selection for all channels return mSelection->getChannel( 0 ); } } } inline void Image::pasteChannel( double *inChannelData, int inChannel ) { pasteChannel( inChannelData, NULL, inChannel ); } // We've abstracted away the complexity in the other fuctions, // but it all seemed to filter down into this function, which // is very messy. inline void Image::pasteChannel( double *inChannelData, double *inMask, int inChannel ) { double *thisChannel = mChannels[inChannel]; if( mSelection != NULL ) { // scale incoming data with this selection double *selection = getChannelSelection(inChannel); if( inMask != NULL ) { // scale incoming data with both masks for( int i=0; igetChannel( c ); double *sourceChannel = mChannels[c]; int destY=0; for( int y=inStartY; ywriteLong( mWide ); numBytes += inOutputStream->writeLong( mHigh ); // then output number of channels numBytes += inOutputStream->writeLong( mNumChannels ); // now output each channel for( int i=0; iwriteDouble( mChannels[i][p] ); byteArray[p] = (unsigned char)( mChannels[i][p] * 255 ); } numBytes += inOutputStream->write( byteArray, mNumPixels ); delete [] byteArray; } return numBytes; } inline int Image::deserialize( InputStream *inInputStream ) { int i; // first delete old image channels for( i=0; ireadLong( &mWide ); numBytes += inInputStream->readLong( &mHigh ); mNumPixels = mWide * mHigh; // then input number of channels numBytes += inInputStream->readLong( &mNumChannels ); mChannels = new double*[mNumChannels]; // now input each channel for( i=0; iread( byteArray, mNumPixels ); // convert each byte to an 8-bit double pixel for( int p=0; preadDouble( &( mChannels[i][p] ) ); mChannels[i][p] = (double)( byteArray[p] ) / 255.0; } delete [] byteArray; } return numBytes; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/ImageColorConverter.h0000640000175000017500000003377607353715726025304 0ustar pabspabs/* * Modification History * * 2001-February-26 Jason Rohrer * Created. * Added functions for converting to and from gray byte arrays. * * 2001-September-19 Jason Rohrer * Added an RGB->HSB conversion function, which was copied * from minorGems/ai/robotics/ImageStatistics. * Added RGB<->YIQ functions. * * 2001-September-20 Jason Rohrer * Fixed a bug in the YIQ conversion. * Got rid of this bug fix, as it distorts the YIQ space, * and there is no way to prevent colors from going out of the * [0,1] range all of the time anyway. * * 2001-September-24 Jason Rohrer * Added RGB<->YCbCr functions. * Abstracted out a common coefficient multiplying function. */ #ifndef IMAGE_COLOR_CONVERTER_INCLUDED #define IMAGE_COLOR_CONVERTER_INCLUDED #include "Image.h" /** * A container class for static functions that convert * images between various color spaces. * * @author Jason Rohrer */ class ImageColorConverter { public: /** * Converts a 3-channel RGB image to a 1-channel grayscale * image using an NTSC luminosity standard. * * @param inImage the RGB image to convert. Must be destroyed * by caller. * * @return a new, grayscale version of inImage. Must be * destroyed by the caller. Returns NULL if inImage * is not a 3-channel image. */ static Image *RGBToGrayscale( Image *inImage ); /** * Converts a 1-channel grayscae image to a 3-channel RGB * image. * * @param inImage the grayscale image to convert. Must be destroyed * by caller. * * @return a new, RGB version of inImage. Must be * destroyed by the caller. Returns NULL if inImage * is not a 1-channel image. */ static Image *grayscaleToRGB( Image *inImage ); /** * Converts a 1-channel grayscae image to a 1-channel byte array. * * @param inImage the grayscale image to convert. Must be destroyed * by caller. * @param inChannelNumber the channel number to use as the * gray channel. Defaults to 0; * * @return a new byte array with one byte per image pixel, * and image [0,1] values mapped to [0,255]. */ static unsigned char *grayscaleToByteArray( Image *inImage, int inChannelNumber = 0 ); /** * Converts a byte array to a 1-channel grayscale * image. * * @param inBytes the byte array to convert. Must be destroyed * by caller. * @param inWidth the width of the image contained in the byte array. * @param inHeight the height of the image contained in the byte array. * * @return a new, grayscale image version of the byte array. Must be * destroyed by the caller. */ static Image *byteArrayToGrayscale( unsigned char *inBytes, int inWidth, int inHeight ); /** * Converts an RGB image to HSB. * * @param inRGBImage the rgb image to convert. * Must be destroyed by caller. * * @return a new image that is the HSB conversion of the * RGB image. Must be destroyed by caller. */ static Image *RGBToHSB( Image *inRGBImage ); /** * Converts an RGB image to YIQ. * * Note that color values in the resulting YIQ * image may lie outside of the range [0,1]. * * @param inRGBImage the rgb image to convert. * Must be destroyed by caller. * * @return a new image that is the YIQ conversion of the * RGB image. Must be destroyed by caller. */ static Image *RGBToYIQ( Image *inRGBImage ); /** * Converts a YIQ image to RGB. * * * Note that color values in the resulting RGB * image may lie outside of the range [0,1]. * * @param inYIQImage the rgb image to convert. * Must be destroyed by caller. * * @return a new image that is the RGB conversion of the * YIQ image. Must be destroyed by caller. */ static Image *YIQToRGB( Image *inYIQImage ); /** * Converts an RGB image to YCbCr. * * Note that in the YCbCr standard, Y is in the range * [0,1], while Cb and Cr are both in the range [-0.5, 0.5]. * This function returns Cb and Cr components shifted * into the range [0,1]. * * @param inRGBImage the rgb image to convert. * Must be destroyed by caller. * * @return a new image that is the YCbCr conversion of the * RGB image. Must be destroyed by caller. */ static Image *RGBToYCbCr( Image *inRGBImage ); /** * Converts a YCbCr image to RGB. * * * Note that in the YCbCr standard, Y is in the range * [0,1], while Cb and Cr are both in the range [-0.5, 0.5]. * This function expects input Cb and Cr components to be shifted * into the range [0,1]. * * @param inYCbCrImage the rgb image to convert. * Must be destroyed by caller. * * @return a new image that is the RGB conversion of the * YCbCr image. Must be destroyed by caller. */ static Image *YCbCrToRGB( Image *inYCbCrImage ); protected: /** * Converts an 3-channel image to another 3-channel * image using a matrix of conversion coefficients. * * The following formulae are used; * outChan0 = inC00 * inChan0 + inC01 * inChan1 + inC02 * inChan2 * outChan1 = inC10 * inChan0 + inC11 * inChan1 + inC12 * inChan2 * outChan2 = inC20 * inChan0 + inC21 * inChan1 + inC22 * inChan2 * * @param inImage the image to convert. * Must be destroyed by caller. * * @return a new image that is inImage converted, or NULL if * conversion failed (usually because inImage does not * contain 3 channels). * Must be destroyed by caller. */ static Image *coefficientConvert( Image *inImage, double inC00, double inC01, double inC02, double inC10, double inC11, double inC12, double inC20, double inC21, double inC22 ); }; inline Image *ImageColorConverter::RGBToGrayscale( Image *inImage ) { int w = inImage->getWidth(); int h = inImage->getHeight(); int numPixels = w * h; Image *grayImage = new Image( w, h, 1 ); double *red = inImage->getChannel( 0 ); double *green = inImage->getChannel( 1 ); double *blue = inImage->getChannel( 2 ); double *gray = grayImage->getChannel( 0 ); for( int i=0; igetWidth(); int h = inImage->getHeight(); int numPixels = w * h; Image *rgbImage = new Image( w, h, 3 ); double *red = rgbImage->getChannel( 0 ); double *green = rgbImage->getChannel( 1 ); double *blue = rgbImage->getChannel( 2 ); double *gray = inImage->getChannel( 0 ); for( int i=0; igetWidth(); int h = inImage->getHeight(); int numPixels = w * h; unsigned char *bytes = new unsigned char[ numPixels ]; double *gray = inImage->getChannel( inChannelNumber ); for( int i=0; igetChannel( 0 ); for( int i=0; igetNumChannels() != 3 ) { printf( "RGBtoHSB requires a 3-channel image as input.\n" ); return NULL; } int w = inRGBImage->getWidth(); int h = inRGBImage->getHeight(); int numPixels = w * h; Image *hsbImage = new Image( w, h, 3 ); double *redChannel = inRGBImage->getChannel( 0 ); double *greenChannel = inRGBImage->getChannel( 1 ); double *blueChannel = inRGBImage->getChannel( 2 ); double *hueChannel = hsbImage->getChannel( 0 ); double *satChannel = hsbImage->getChannel( 1 ); double *brightChannel = hsbImage->getChannel( 2 ); for( int i=0; i g) ? r : g; if (b > cmax) { cmax = b; } int cmin = (r < g) ? r : g; if (b < cmin) { cmin = b; } bright = ( (double)cmax ) / 255.0; if( cmax != 0 ) { sat = ( (double)( cmax - cmin ) ) / ( (double) cmax ); } else { sat = 0; } if( sat == 0 ) { hue = 0; } else { double redc = ( (double)( cmax - r ) ) / ( (double)( cmax - cmin ) ); double greenc = ( (double) ( cmax - g ) ) / ( (double)( cmax - cmin ) ); double bluec = ( (double)( cmax - b ) ) / ( (double)( cmax - cmin ) ); if( r == cmax ) { hue = bluec - greenc; } else if( g == cmax ) { hue = 2.0 + redc - bluec; } else { hue = 4.0 + greenc - redc; } hue = hue / 6.0; if( hue < 0 ) { hue = hue + 1.0; } } hueChannel[i] = hue; satChannel[i] = sat; brightChannel[i] = bright; } return hsbImage; } inline Image *ImageColorConverter::RGBToYIQ( Image *inRGBImage ) { if( inRGBImage->getNumChannels() != 3 ) { printf( "RGBtoYIQ requires a 3-channel image as input.\n" ); return NULL; } Image *yiqImage = coefficientConvert( inRGBImage, 0.299, 0.587, 0.114, 0.596, -0.274, -0.322, 0.212, -0.523, 0.311 ); return yiqImage; } inline Image *ImageColorConverter::YIQToRGB( Image *inYIQImage ) { if( inYIQImage->getNumChannels() != 3 ) { printf( "YIQtoRGB requires a 3-channel image as input.\n" ); return NULL; } Image *rgbImage = coefficientConvert( inYIQImage, 1.0, 0.956, 0.621, 1.0, -0.272, -0.647, 1.0, -1.105, 1.702 ); return rgbImage; } inline Image *ImageColorConverter::RGBToYCbCr( Image *inRGBImage ) { if( inRGBImage->getNumChannels() != 3 ) { printf( "RGBtoYCbCr requires a 3-channel image as input.\n" ); return NULL; } // coefficients taken from the color space faq /* RGB -> YCbCr (with Rec 601-1 specs) Y = 0.2989 * Red + 0.5866 * Green + 0.1145 * Blue Cb = -0.1687 * Red - 0.3312 * Green + 0.5000 * Blue Cr = 0.5000 * Red - 0.4183 * Green - 0.0816 * Blue YCbCr (with Rec 601-1 specs) -> RGB Red = Y + 0.0000 * Cb + 1.4022 * Cr Green = Y - 0.3456 * Cb - 0.7145 * Cr Blue = Y + 1.7710 * Cb + 0.0000 * Cr */ Image *ycbcrImage = coefficientConvert( inRGBImage, 0.2989, 0.5866, 0.1145, -0.1687, -0.3312, 0.5000, 0.5000, -0.4183, -0.0816 ); // adjust the Cb and Cr channels so they are in the range [0,1] int numPixels = ycbcrImage->getWidth() * ycbcrImage->getHeight(); double *cbChannel = ycbcrImage->getChannel( 1 ); double *crChannel = ycbcrImage->getChannel( 2 ); for( int i=0; igetNumChannels() != 3 ) { printf( "YCbCrtoRGB requires a 3-channel image as input.\n" ); return NULL; } // adjust the normalized Cb and Cr channels // so they are in the range [-0.5,0.5] int numPixels = inYCbCrImage->getWidth() * inYCbCrImage->getHeight(); double *cbChannel = inYCbCrImage->getChannel( 1 ); double *crChannel = inYCbCrImage->getChannel( 2 ); for( int i=0; i YCbCr (with Rec 601-1 specs) Y = 0.2989 * Red + 0.5866 * Green + 0.1145 * Blue Cb = -0.1687 * Red - 0.3312 * Green + 0.5000 * Blue Cr = 0.5000 * Red - 0.4183 * Green - 0.0816 * Blue YCbCr (with Rec 601-1 specs) -> RGB Red = Y + 0.0000 * Cb + 1.4022 * Cr Green = Y - 0.3456 * Cb - 0.7145 * Cr Blue = Y + 1.7710 * Cb + 0.0000 * Cr */ Image *rgbImage = coefficientConvert( inYCbCrImage, 1.0, 0.0000, 1.4022, 1.0, -0.3456, -0.7145, 1.0, 1.7710, 0.0000 ); // clip r, g, and b channels to the range [0,1], since // some YCbCr pixel values might map out of this range // (in other words, some YCbCr values map outside of rgb space) for( int c=0; c<3; c++ ) { double *channel = rgbImage->getChannel( c ); for( int p=0; p 1 ) { channel[p] = 1.0; } } } return rgbImage; } inline Image *ImageColorConverter::coefficientConvert( Image *inImage, double inC00, double inC01, double inC02, double inC10, double inC11, double inC12, double inC20, double inC21, double inC22 ) { if( inImage->getNumChannels() != 3 ) { return NULL; } int w = inImage->getWidth(); int h = inImage->getHeight(); int numPixels = w * h; Image *outImage = new Image( w, h, 3 ); double *outChannel0 = outImage->getChannel( 0 ); double *outChannel1 = outImage->getChannel( 1 ); double *outChannel2 = outImage->getChannel( 2 ); double *inChannel0 = inImage->getChannel( 0 ); double *inChannel1 = inImage->getChannel( 1 ); double *inChannel2 = inImage->getChannel( 2 ); for( int i=0; iScreenGraphics. * Adjusts screen resolution and fills screen with black. * * @param inWidth width of desired full screen. * @param inHeight height of desired full screen. */ ScreenGraphics( int inWidth, int inHeight ); /** * Desctructor restores screen to original state. */ ~ScreenGraphics(); /** * Resizes the screen. * * @param inNewWidth the new width. * @param inNewHeight the new height. */ void resize( int inNewWidth, int inNewHeight ); /** * Gets the pixel buffer used by the screen. * * @return a graphics buffer containing a pointer to the screen * pixels. Must be destroyed by caller. */ GraphicBuffer *getScreenBuffer(); /** * Flips a dirty rectangle onto the screen. * * @param inTopLeftX the X coordinate, in pixels, of the top left * point on the rectangle. * @param inTopLeftY the Y coordinate, in pixels, of the top left * point on the rectangle. * @param inWidth the width of the rectangle. * @param inHeight the height of the rectangle. */ void flipDirtyRectangle( int inTopLeftX, int inTopLeftY, int inWidth, int inHeight ); /** * Flips all the pixels stored in the location pointed to by * getScreenBuffer() onto the screen. */ void flipScreen(); /** * Swaps a buffer to the screen. * The next buffer to be filled is returned in * inOutBuffer. * * No guarentee is made about what will be in the next buffer returned. * (On systems that support on-card double buffering, it may even * be a buffer pointing directly into video memory.) * * @param inOutBuffer pointer to the buffer to swap to the screen, * and pointer where the next buffer will be stored. */ void swapBuffers( GraphicBuffer *inOutBuffer ); /** * Determines whether a particular screen setting is available. * * NOTE: Do not call after constructing a ScreenGraphics instance. * * @param inWidth width of desired full screen. * @param inHeight height of desired full screen. * * @return true iff screen setting is available. */ static char isResolutionAvailable( int inWidth, int inHeight ); private: int mWidth; int mHeight; void *mNativeObjectPointer; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/graphics/mac/0000750000175000017500000000000011401021057021711 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/graphics/mac/graphixCommonDefs.h0000640000175000017500000000012507205611502025505 0ustar pabspabs// ALifeGUICommonDefs.h // common definitions for ALifeGui #define MAC_KEY_CODESCultivation_9+dfsg1_UnixSource/minorGems/graphics/mac/graphixFramework.cpp0000640000175000017500000001727507205611502025761 0ustar pabspabs/** 32 bit DrawSprockets pluggable framework * * Jason Rohrer, 4-28-99 * * Mods: * Jason Rohrer 11-8-99 Changed to use GraphicBuffer object as screen buffer * Jason Rohrer 12-15-99 Added support for keyboard querying * Jason Rohrer 3-21-2000 Added support for mouse querying * */ #include #include #include // for keyboard events #include #include #include "swapBuffers.h" // interface for swapping buffers #include "getKey.h" // interface for getting key states #include "getMouse.h" // interface for getting mouse state //#include "ALifeGuiRunner.h" #include "GoGUIRunner.h" #include "GraphicBuffer.h" // Constants #define BallWidth 20 #define BallHeight 20 #define BobSize 8 /* Size of text in each ball */ //MT Tutorial #define rWindow 128 // resource ID number for window // Globals Rect windRect; WindowPtr w; DSpContextReference theContext; unsigned long *double_buffer = NULL; // the double buffer unsigned long *screen_buffer = NULL; GraphicBuffer *graphicDoubleBuffer; short bufferHigh = 480; short bufferWide = 640; // prototypes void Initialize(void); void graphixPicker(void); // our mother function, picks effect order, etc. void initVideo(DSpContextReference *theContext); void MyInitAttributes(DSpContextAttributes*); void CleanUp(DSpContextReference theContext); int main() { Initialize(); // setup a window MaxApplZone(); initVideo( &theContext ); // set up and switch video graphixPicker(); CleanUp( theContext); } void graphixPicker() { // our "mother function" // selects which effect to run next // graphix plug-in format: // myGraphix( bufferPtr, bufferHigh, bufferWide, colorBits, numFrames) // function can cll "swapBuffer32" internally whenever it needs the back buffer // sent to the screen. // jcrTorus( double_buffer, 480, 640, 32, 100); // jcrVoxels( double_buffer, 480, 640, 32, 230); // jcrTorus( double_buffer, 480, 640, 32, 50); //jcrBloom( double_buffer, 480, 640, 32, 100); // GraphicBuffer passed in contains all needed info about screen // pass in number of frames to run for //ALifeGuiRunner( *graphicDoubleBuffer, 1000 ); GoGUIRunner( *graphicDoubleBuffer, 0 ); } // Initialize the draw sprockets // set up the video, fade out display, and switch video modes void initVideo(DSpContextReference *theContext) { CGrafPtr backBuffer; PixMapHandle bufferMap; DSpContextAttributes theDesiredAttributes; OSStatus theError; MyInitAttributes(&theDesiredAttributes); theDesiredAttributes.displayWidth = 640; theDesiredAttributes.displayHeight = 480; theDesiredAttributes.colorNeeds = kDSpColorNeeds_Require; theDesiredAttributes.backBufferDepthMask = kDSpDepthMask_32; theDesiredAttributes.displayDepthMask = kDSpDepthMask_32; theDesiredAttributes.backBufferBestDepth = 32; theDesiredAttributes.displayBestDepth = 32; theError = DSpFindBestContext(&theDesiredAttributes, theContext); // add page flipping by video card to request theDesiredAttributes.contextOptions |= kDSpContextOption_PageFlip; //theDesiredAttributes.contextOptions |= kDSpContextOption_DontSyncVBL; // Reserver a display theError = DSpContext_Reserve(*theContext, &theDesiredAttributes); // fade out theError = DSpContext_FadeGammaOut(NULL, NULL); theError = DSpContext_SetState(*theContext, kDSpContextState_Active); ShowCursor(); HideCursor(); // setup global pointers to screen and back buffer theError = DSpContext_GetBackBuffer(*theContext, kDSpBufferKind_Normal, &backBuffer); bufferMap = (PixMapHandle)GetGWorldPixMap(backBuffer); double_buffer = (unsigned long*)(**bufferMap).baseAddr; // create GraphicBuffer object graphicDoubleBuffer = new GraphicBuffer( double_buffer, 640, 480 ); theError = DSpContext_GetFrontBuffer(*theContext, &backBuffer); bufferMap = (PixMapHandle)GetGWorldPixMap(backBuffer); screen_buffer = (unsigned long*)(**bufferMap).baseAddr; theError = DSpContext_FadeGammaIn(NULL, NULL); } // initialize a set of Display attributes to zero void MyInitAttributes (DSpContextAttributes *inAttributes) { if (NULL == inAttributes) DebugStr("\pStimpy! You Idiot!"); inAttributes->frequency = 0; inAttributes->displayWidth = 0; inAttributes->displayHeight = 0; inAttributes->reserved1 = 0; inAttributes->reserved2 = 0; inAttributes->colorNeeds = 0; inAttributes->colorTable = NULL; inAttributes->contextOptions = 0; inAttributes->backBufferDepthMask = 0; inAttributes->displayDepthMask = 0; inAttributes->backBufferBestDepth = 0; inAttributes->displayBestDepth = 0; inAttributes->pageCount = 0; inAttributes->gameMustConfirmSwitch = false; inAttributes->reserved3[0] = 0; inAttributes->reserved3[1] = 0; inAttributes->reserved3[2] = 0; inAttributes->reserved3[3] = 0; } void CleanUp(DSpContextReference theContext) { OSStatus theError; theError = DSpContext_FadeGammaOut(NULL, NULL); /* put the context into the inactive state */ theError = DSpContext_SetState(theContext, kDSpContextState_Inactive); /* fade back in */ theError = DSpContext_FadeGammaIn(NULL, NULL); /* release the context */ theError = DSpContext_Release(theContext); ShowCursor(); } // swap bufferB to the screen (32 bit version) void swapBuffers32( GraphicBuffer &bufferB ) { OSStatus theError; // swap buffers theError = DSpContext_SwapBuffers(theContext, NULL, 0); // get pointer to new back buffer and return it CGrafPtr backBuffer; PixMapHandle bufferMap; theError = DSpContext_GetBackBuffer(theContext, kDSpBufferKind_Normal, &backBuffer); bufferMap = (PixMapHandle)GetGWorldPixMap(backBuffer); // replace the buffer in bufferB with the new double buffer bufferB.setBuffer( (unsigned long*)(**bufferMap).baseAddr ); // memcpy(screen_buffer, bufferPtrB, 4*bufferWide*bufferHigh); } // end of swapBuffers // returns true if key represented by given key code is down char getKeyDown( int vKeyCode ) { unsigned char keyArray [16]; GetKeys( (long *)keyArray ); return ((keyArray[vKeyCode>>3] >> (vKeyCode & 7)) & 1); } // returns true if key is up char getKeyUp( int vKeyCode ) { unsigned char keyArray [16]; GetKeys( (long *)keyArray ); return !((keyArray[vKeyCode>>3] >> (vKeyCode & 7)) & 1); } void getMouse( int *x, int *y ) { Point mousePoint; DSpGetMouse( &mousePoint ); *x = mousePoint.h; *y = mousePoint.v; } void Initialize(void) { WindowPtr mainPtr; OSErr error; SysEnvRec theWorld; // // Test the computer to be sure we can do color. // If not we would crash, which would be bad. // If we can¹t run, just beep and exit. // error = SysEnvirons(1, &theWorld); if (theWorld.hasColorQD == false) { // SysBeep(50); ExitToShell(); // If no color QD, we must leave. } // Initialize all the needed managers. InitGraf(&qd.thePort); // InitFonts(); InitWindows(); InitMenus(); TEInit(); InitDialogs(nil); InitCursor(); // // To make the Random sequences truly random, we need to make the seed start // at a different number. An easy way to do this is to put the current time // and date into the seed. Since it is always incrementing the starting seed // will always be different. Don¹t for each call of Random, or the sequence // will no longer be random. Only needed once, here in the init. // GetDateTime((unsigned long*) &qd.randSeed); // // Make a new window for drawing in, and it must be a color window. // The window is full screen size, made smaller to make it more visible. // mainPtr = GetNewCWindow(rWindow, nil, (WindowPtr) -1); // MW Tutorial windRect = mainPtr->portRect; SetPort(mainPtr); // set window to current graf port TextSize(BobSize); // smaller font for drawing. } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/win32/0000750000175000017500000000000011401021060022105 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/graphics/win32/graphixCommonDefs.h0000640000175000017500000000013107205611501025703 0ustar pabspabs// ALifeGUICommonDefs.h // common definitions for ALifeGui #define WINDOWS_KEY_CODESCultivation_9+dfsg1_UnixSource/minorGems/graphics/win32/graphixFramework.cpp0000640000175000017500000002651507205611501026157 0ustar pabspabs// 32 bit DDraw pluggable framework // Jason Rohrer, 4-28-99 /** * Mods: * Jason Rohrer 11-8-99 Changed to use GraphicBuffer object as screen buffer * Jason Rohrer 3-21-2000 Added support for mouse querying */ // based on the work of: /* * water ripples effect * -------------------- * anthony greene :: emit * april 1999 */ #define WIN32_LEAN_AND_MEAN // make sure certain headers are included correctly /* includes */ #include // include the standard windows stuff //#include // include the 32 bit stuff //#include // include the multi media stuff // need winmm.lib also #include // include direct draw components #include #include "swapBuffers.h" // interface for swapping buffers #include "getKey.h" // interface for handling keyboard input #include "getMouse.h" // interface for handling mouse input #include "GraphicBuffer.h" //#include "ALifeGuiRunner.h" #include "GoGUIRunner.h" /* defines */ #define WINDOW_CLASS_NAME "WINDOW_CLASS" // this is the name of the window class #define SCREEN_WIDTH 640 // the width of the viewing surface #define SCREEN_HEIGHT 480 // the height of the viewing surface #define SCREEN_BPP 32 // the bits per pixel #define MAX_COLORS 256 // the maximum number of colors /* types */ typedef unsigned char BYTE; typedef unsigned char UCHAR; typedef unsigned short WORD; /* macros */ // these query the keyboard in real-time #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) #define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1) /* prototypes */ int DD_Init(HWND hwnd); int DD_Shutdown(void); void graphixPicker(void); // our mother function, picks effect order, etc. /* directdraw globals */ LPDIRECTDRAW lpdd = NULL; // dd object LPDIRECTDRAWSURFACE lpddsprimary = NULL; // dd primary surface LPDIRECTDRAWSURFACE lpddsback = NULL; // dd back surface LPDIRECTDRAWPALETTE lpddpal = NULL; // a pointer to the created dd palette PALETTEENTRY color_palette[256]; // holds the shadow palette entries DDSURFACEDESC ddsd; // a direct draw surface description struct DDSCAPS ddscaps; // a direct draw surface capabilities struct HRESULT ddrval; // result back from dd calls HWND main_window_handle = NULL; // used to store the window handle unsigned long *double_buffer = NULL; // the double buffer unsigned long *screen_buffer = NULL; GraphicBuffer *graphicDoubleBuffer; /* globals */ int Height[2][640*480]; int old; int nu; int running = 0; int cycle; /* direct x functions */ int DD_Init(HWND hwnd) { // this function is responsible for initializing direct draw, // it creates a primary surface int index; // looping index // now that the windows portion is complete, start up direct draw if (DirectDrawCreate(NULL,&lpdd,NULL)!=DD_OK) { // shutdown any other dd objects and kill window DD_Shutdown(); return(0); } // now set the coop level to exclusive and set for full screen and mode x if (lpdd->SetCooperativeLevel(hwnd, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX)!=DD_OK) { // shutdown any other dd objects and kill window DD_Shutdown(); return(0); } // now set the display mode if (lpdd->SetDisplayMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP)!=DD_OK) { // shutdown any other dd objects and kill window DD_Shutdown(); return(0); } // Create the primary surface memset(&ddsd,0,sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; if (lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL)!=DD_OK) { // shutdown any other dd objects and kill window DD_Shutdown(); return(0); } // end if // allocate memory for the double buffer if ((double_buffer = (unsigned long *)malloc(SCREEN_WIDTH*SCREEN_HEIGHT*4))==NULL) return(0); // create GraphicBuffer object graphicDoubleBuffer = new GraphicBuffer( double_buffer, 640, 480 ); // create the palette and attach it to the primary surface // clear all the palette entries to RGB 0,0,0 memset(color_palette,0,256*sizeof(PALETTEENTRY)); // set all of the flags to the correct value int c=0; for (index=0; index<256; index++) { // create the GRAY/RED/GREEN/BLUE palette, 64 shades of each if (index < 64) { color_palette[index].peRed = index; color_palette[index].peGreen = 0; color_palette[index].peBlue = 0; } else if (index < 128) { color_palette[index].peRed = index; color_palette[index].peGreen = 0; color_palette[index].peBlue = 0; } else if (index < 192) { color_palette[index].peRed = index; color_palette[index].peGreen = c; color_palette[index].peBlue = c; c++; } else if (index < 256) { color_palette[index].peRed = index; color_palette[index].peGreen = c; color_palette[index].peBlue = c; c++; } // set the no collapse flag color_palette[index].peFlags = PC_NOCOLLAPSE; } // now create the palette object, note that it is a member of the dd object itself if (lpdd->CreatePalette((DDPCAPS_8BIT | DDPCAPS_INITIALIZE),color_palette,&lpddpal,NULL)!=DD_OK) { // shutdown any other dd objects and kill window DD_Shutdown(); return(0); } // now attach the palette to the primary surface lpddsprimary->SetPalette(lpddpal); // return success if we got this far return(1); } int DD_Shutdown(void) { // this function tests for dd components that have been created // and releases them back to the operating system // test if the dd object exists if (lpdd) { // test if there is a primary surface if(lpddsprimary) { // release the memory and set pointer to NULL lpddsprimary->Release(); lpddsprimary = NULL; } // now release the dd object itself lpdd->Release(); lpdd = NULL; // free double buffer if (double_buffer!=NULL) free(double_buffer); // return success return(1); } else return(0); } /* windows callback fucntion */ LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // this is the main message handler of the system HDC hdc; // handle to graphics context PAINTSTRUCT ps; // used to hold the paint info // what is the message.. switch(msg) { case WM_CREATE: { // do windows inits here return(0); } break; case WM_PAINT: { // this message occurs when your window needs repainting hdc = BeginPaint(hwnd,&ps); EndPaint((struct HWND__ *)hdc,&ps); return(0); } break; case WM_DESTROY: { // this message is sent when your window is destroyed PostQuitMessage(0); return(0); } break; case WM_MOUSEMOVE: { // extract x,y /* int mouse_x = (int)LOWORD(lparam); int mouse_y = (int)HIWORD(lparam); Height[old][mouse_y*640+mouse_x] = 300; */ return(0); } break; default:break; } // let windows process any messages that we didn't take care of return (DefWindowProc(hwnd, msg, wparam, lparam)); } int WINAPI WinMain( HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { WNDCLASSEX winclass; // this holds the windows class info HWND hwnd; // this holds the handle of our new window MSG msg; // this holds a generic message // first fill in the window class stucture winclass.cbSize = sizeof(WNDCLASSEX); winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW; winclass.lpfnWndProc = WindowProc; winclass.cbClsExtra = 0; winclass.cbWndExtra = 0; winclass.hInstance = hinstance; winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); winclass.hCursor = LoadCursor(NULL, IDC_ARROW); winclass.hbrBackground = (struct HBRUSH__ *)GetStockObject(BLACK_BRUSH); winclass.lpszMenuName = NULL; winclass.lpszClassName = WINDOW_CLASS_NAME; // register the window class if (!RegisterClassEx(&winclass)) return(0); // create the window if (!(hwnd = CreateWindowEx(WS_EX_TOPMOST, WINDOW_CLASS_NAME, // class "water", // title WS_VISIBLE | WS_POPUP, 0,0, // x,y GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, // parent NULL, // menu hinstance, // instance NULL))) // creation parms return(0); // hide the mouse cursor ShowCursor(0); // save the window handle main_window_handle = hwnd; // initialize direct draw if (!DD_Init(hwnd)) { DestroyWindow(hwnd); return(0); } // peek at the message once just to make them happy PeekMessage(&msg,NULL,0,0,PM_REMOVE); // do it once, then leave graphixPicker(); // picks which effects to run. // afterwards, end!!! // shut down direct draw DD_Shutdown(); // return to Windows return(msg.wParam); } void graphixPicker() { // our "mother function" // selects which effect to run next // graphix plug-in format: // myGraphix( bufferPtr, bufferHigh, bufferWide, colorBits, numFrames) // function can cll "swapBuffer32" internally whenever it needs the back buffer // sent to the screen. // jcrTorus( double_buffer, 480, 640, 32, 100); // jcrVoxels( double_buffer, 480, 640, 32, 230); // jcrTorus( double_buffer, 480, 640, 32, 50); // jcrBloom( double_buffer, 480, 640, 32, 100); //ALifeGuiRunner( *graphicDoubleBuffer, 200 ); GoGUIRunner( *graphicDoubleBuffer, 0 ); } char getKeyDown( int vKeyCode ) { return ((GetAsyncKeyState(vKeyCode) & 0x8000) ? true : false); } char getKeyUp( int vKeyCode ) { return ((GetAsyncKeyState(vKeyCode) & 0x8000) ? false : true); } void getMouse( int *x, int *y ) { POINT p; GetCursorPos( &p ); *x = p.x; *y = p.y; } /// CODE FOR SWAPPING BUFFERES BELOW THIS POINT....... // swap bufferB to the screen (32 bit version) void swapBuffers32( GraphicBuffer &bufferB ) { unsigned long *primary_buffer = NULL, // used to draw *dest_ptr = NULL, // used in line by line copy *src_ptr = NULL; // " " short bufferHigh = 480; short bufferWide = 640; // copy the double buffer into the primary buffer memset(&ddsd,0,sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); // lock the primary surface lpddsprimary->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL); // get video pointer to primary surfce primary_buffer = (unsigned long *)ddsd.lpSurface; // test if memory is linear if (ddsd.lPitch == 640*4) { // copy memory from double buffer to primary buffer memcpy(primary_buffer, double_buffer, 640*480*4); } else { // non-linear // make copy of source and destination addresses dest_ptr = primary_buffer; src_ptr = double_buffer; // memory is non-linear, copy line by line for (int y=0; yUnlock(primary_buffer); } // end of swapBuffers Cultivation_9+dfsg1_UnixSource/minorGems/graphics/loadfile.h0000640000175000017500000000025307205611501023110 0ustar pabspabsvoid loadFile( const char* fileName, long sizeInBytes, unsigned char* byteDestPtr); void loadRawFile( const char* fileName, long sizeInBytes, unsigned char* byteDestPtr);Cultivation_9+dfsg1_UnixSource/minorGems/graphics/test/0000750000175000017500000000000011401021060022122 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/graphics/test/tgaConverter.cpp0000640000175000017500000001002007353716210025306 0ustar pabspabs/* * Modification History * * 2001-September-24 Jason Rohrer * Created. */ #include "minorGems/graphics/Image.h" #include "minorGems/graphics/ImageColorConverter.h" #include "minorGems/graphics/converters/TGAImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include "minorGems/io/file/FileOutputStream.h" #include #include #include void usage( char *inAppName ); Image *loadTGAImage( char *inFileName ); void writeTGAImage( char *inFileName, Image *inImage ); // normalizes an image into the pixel range [0,1] if some // pixels are out of range void normalizeImage( Image *inImage ); // a test program that converts a tga image between // parameterizable color formats int main( char inNumArgs, char **inArgs ) { if( inNumArgs != 5 ) { usage( inArgs[0] ); } Image *inImage = loadTGAImage( inArgs[1] ); Image *outImage; // convert inImage to outImage based on the color space strings if( !strcmp( inArgs[3], "rgb" ) ) { if( !strcmp( inArgs[4], "yiq" ) ) { outImage = ImageColorConverter::RGBToYIQ( inImage ); } if( !strcmp( inArgs[4], "ycbcr" ) ) { outImage = ImageColorConverter::RGBToYCbCr( inImage ); } } if( !strcmp( inArgs[3], "yiq" ) ) { if( !strcmp( inArgs[4], "rgb" ) ) { outImage = ImageColorConverter::YIQToRGB( inImage ); } if( !strcmp( inArgs[4], "ycbcr" ) ) { Image *tempImage = ImageColorConverter::YIQToRGB( inImage ); outImage = ImageColorConverter::RGBToYCbCr( tempImage ); delete tempImage; } } if( !strcmp( inArgs[3], "ycbcr" ) ) { if( !strcmp( inArgs[4], "rgb" ) ) { outImage = ImageColorConverter::YCbCrToRGB( inImage ); } if( !strcmp( inArgs[4], "yiq" ) ) { Image *tempImage = ImageColorConverter::YCbCrToRGB( inImage ); outImage = ImageColorConverter::RGBToYIQ( tempImage ); delete tempImage; } } writeTGAImage( inArgs[2], outImage ); delete inImage; delete outImage; return 0; } void usage( char *inAppName ) { printf( "Usage:\n" ); printf( "\t%s in_image_tga out_image_tga in_format out_format\n", inAppName ); printf( "Example:\n" ); printf( "\t%s testRGB.tga testYIQ.bmp rgb yiq\n", inAppName ); printf( "The following color formats are supported:\n" ); printf( "\trgb, yiq, ycbcr\n" ); exit( 1 ); } void normalizeImage( Image *inImage ) { // now normalize the image so that pixels are in the range [0,1] int numPixels = inImage->getWidth() * inImage->getHeight(); int numChannels = inImage->getNumChannels(); double minValue = 1000.0; double maxValue = -1000.0; int c; // search for min and max values for( c=0; cgetChannel( c ); for( int p=0; p maxValue ) { maxValue = channel[p]; } } } double diff = maxValue - minValue; if( minValue >= 0 && diff + minValue <= 1 ) { // no need to normalize, as values are already // in range return; } double scale = 1.0 / diff; // now normalize all pixels for( c=0; cgetChannel( c ); for( int p=0; pdeformatImage( imageStream ); delete imageFile; delete imageStream; delete converter; return returnImage; } void writeTGAImage( char *inFileName, Image *inImage ) { File *imageFile = new File( NULL, inFileName, strlen( inFileName ) ); FileOutputStream *imageStream = new FileOutputStream( imageFile ); TGAImageConverter *converter = new TGAImageConverter(); converter->formatImage( inImage, imageStream ); delete imageFile; delete imageStream; delete converter; } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/test/yiq2rgb.cpp0000640000175000017500000000622207353474230024236 0ustar pabspabs/* * Modification History * * 2001-September-20 Jason Rohrer * Created. * Changed so that images are normalized to [0,1] before * being output. */ #include "minorGems/graphics/Image.h" #include "minorGems/graphics/ImageColorConverter.h" #include "minorGems/graphics/converters/BMPImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include "minorGems/io/file/FileOutputStream.h" #include #include void usage( char *inAppName ); Image *loadBMPImage( char *inFileName ); void writeBMPImage( char *inFileName, Image *inImage ); // normalizes an image into the pixel range [0,1] if some // pixels are out of range void normalizeImage( Image *inImage ); // a test program that converts an rgb BMP to a yiq BMP int main( char inNumArgs, char **inArgs ) { if( inNumArgs != 3 ) { usage( inArgs[0] ); } Image *yiqImage = loadBMPImage( inArgs[1] ); if( yiqImage == NULL ) { printf( "Reading image from file %s failed\n", inArgs[1] ); usage( inArgs[0] ); } Image *rgbImage = ImageColorConverter::YIQToRGB( yiqImage ); normalizeImage( rgbImage ); writeBMPImage( inArgs[2], rgbImage ); delete rgbImage; delete yiqImage; return 0; } void usage( char *inAppName ) { printf( "Usage:\n" ); printf( "\t%s yiq_image_bmp rgb_image_bmp\n", inAppName ); printf( "Example:\n" ); printf( "\t%s testYIQ.bmp testRGB.bmp\n", inAppName ); exit( 1 ); } void normalizeImage( Image *inImage ) { // now normalize the image so that pixels are in the range [0,1] int numPixels = inImage->getWidth() * inImage->getHeight(); int numChannels = inImage->getNumChannels(); double minValue = 1000.0; double maxValue = -1000.0; int c; // search for min and max values for( c=0; cgetChannel( c ); for( int p=0; p maxValue ) { maxValue = channel[p]; } } } double diff = maxValue - minValue; if( minValue >= 0 && diff + minValue <= 1 ) { // no need to normalize, as values are already // in range return; } double scale = 1.0 / diff; // now normalize all pixels for( c=0; cgetChannel( c ); for( int p=0; pdeformatImage( imageStream ); delete imageFile; delete imageStream; delete converter; return returnImage; } void writeBMPImage( char *inFileName, Image *inImage ) { File *imageFile = new File( NULL, inFileName, strlen( inFileName ) ); FileOutputStream *imageStream = new FileOutputStream( imageFile ); BMPImageConverter *converter = new BMPImageConverter(); converter->formatImage( inImage, imageStream ); delete imageFile; delete imageStream; delete converter; } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/test/rgb2yiq.cpp0000640000175000017500000000615707353474230024245 0ustar pabspabs/* * Modification History * * 2001-September-19 Jason Rohrer * Created. * * 2001-September-20 Jason Rohrer * Finished initial implementation. * Changed so that images are normalized to [0,1] before * being output. */ #include "minorGems/graphics/Image.h" #include "minorGems/graphics/ImageColorConverter.h" #include "minorGems/graphics/converters/BMPImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include "minorGems/io/file/FileOutputStream.h" #include #include void usage( char *inAppName ); Image *loadBMPImage( char *inFileName ); void writeBMPImage( char *inFileName, Image *inImage ); // normalizes an image into the pixel range [0,1] if some // pixels are out of range void normalizeImage( Image *inImage ); // a test program that converts an rgb BMP to a yiq BMP int main( char inNumArgs, char **inArgs ) { if( inNumArgs != 3 ) { usage( inArgs[0] ); } Image *rgbImage = loadBMPImage( inArgs[1] ); Image *yiqImage = ImageColorConverter::RGBToYIQ( rgbImage ); normalizeImage( yiqImage ); writeBMPImage( inArgs[2], yiqImage ); delete rgbImage; delete yiqImage; return 0; } void usage( char *inAppName ) { printf( "Usage:\n" ); printf( "\t%s rgb_image_bmp yiq_image_bmp\n", inAppName ); printf( "Example:\n" ); printf( "\t%s testRGB.bmp testYIQ.bmp\n", inAppName ); exit( 1 ); } void normalizeImage( Image *inImage ) { // now normalize the image so that pixels are in the range [0,1] int numPixels = inImage->getWidth() * inImage->getHeight(); int numChannels = inImage->getNumChannels(); double minValue = 1000.0; double maxValue = -1000.0; int c; // search for min and max values for( c=0; cgetChannel( c ); for( int p=0; p maxValue ) { maxValue = channel[p]; } } } double diff = maxValue - minValue; if( minValue >= 0 && diff + minValue <= 1 ) { // no need to normalize, as values are already // in range return; } double scale = 1.0 / diff; // now normalize all pixels for( c=0; cgetChannel( c ); for( int p=0; pdeformatImage( imageStream ); delete imageFile; delete imageStream; delete converter; return returnImage; } void writeBMPImage( char *inFileName, Image *inImage ) { File *imageFile = new File( NULL, inFileName, strlen( inFileName ) ); FileOutputStream *imageStream = new FileOutputStream( imageFile ); BMPImageConverter *converter = new BMPImageConverter(); converter->formatImage( inImage, imageStream ); delete imageFile; delete imageStream; delete converter; } Cultivation_9+dfsg1_UnixSource/minorGems/graphics/test/tgaConverterCompile0000750000175000017500000000014107353716210026043 0ustar pabspabsg++ -g -o tgaConverter -I../../.. tgaConverter.cpp ../../../minorGems/io/file/linux/PathLinux.cppCultivation_9+dfsg1_UnixSource/minorGems/graphics/test/rgb2yiqCompile0000750000175000017500000000012407352467255024773 0ustar pabspabsg++ -o rgb2yiq -I../../.. rgb2yiq.cpp ../../../minorGems/io/file/linux/PathLinux.cppCultivation_9+dfsg1_UnixSource/minorGems/graphics/test/yiq2rgbCompile0000750000175000017500000000012707352505010024754 0ustar pabspabsg++ -g -o yiq2rgb -I../../.. yiq2rgb.cpp ../../../minorGems/io/file/linux/PathLinux.cppCultivation_9+dfsg1_UnixSource/minorGems/numtest.cpp0000640000175000017500000000135507237350067021603 0ustar pabspabs/* * Modification History * * 2001-February-3 Jason Rohrer * Created. * * 2001-February-4 Jason Rohrer * Fixed a bug that made this test not compatible with TypeIO. * Fixed comment. */ #include // function for testing how doubles are stored /* * The output from a big-endian linux is as follows: * * size of double = 8 * byte 0 = 63 * byte 1 = 255 * byte 2 = 189 * byte 3 = 137 * byte 4 = 176 * byte 5 = 126 * byte 6 = 158 * byte 7 = 168 * */ int main() { printf( "size of double = %d\n", sizeof( double ) ); double x = 1.983773889; unsigned char *doubleBuffer = (unsigned char*)( &x ); for( int i=0; i<8; i++ ) { printf( "byte %d = %d\n", i, doubleBuffer[i] ); } return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/system/0000750000175000017500000000000011401021064020673 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/system/semaphoreTest.cpp0000640000175000017500000000366407234645702024260 0ustar pabspabs/* * Modification History * * 2001-January-11 Jason Rohrer * Created. * * 2001-January-27 Jason Rohrer * Made printing in threads thread-safe. */ #include "BinarySemaphore.h" #include "Semaphore.h" #include "Thread.h" #include "ThreadSafePrinter.h" #include /** * Thread that waits on a semaphore. * * @author Jason Rohrer */ class WaitingThread : public Thread { public: WaitingThread( int inID, Semaphore *inSemaphore ); // override the run method from PThread void run(); private: Semaphore *mSemaphore; int mID; }; inline WaitingThread::WaitingThread( int inID, Semaphore *inSemaphore ) : mID( inID ), mSemaphore( inSemaphore ) { } inline void WaitingThread::run() { for( int i=0; i<10; i++ ) { ThreadSafePrinter::printf( "%d waiting for signal %d...\n", mID, i ); mSemaphore->wait(); ThreadSafePrinter::printf( "%d received signal %d.\n", mID, i ); } } /** * Thread that signals on a semaphore. * * @author Jason Rohrer */ class SignalingThread : public Thread { public: SignalingThread( Semaphore *inSemaphore ); // override the run method from PThread void run(); private: Semaphore *mSemaphore; }; inline SignalingThread::SignalingThread( Semaphore *inSemaphore ) : mSemaphore( inSemaphore ) { } inline void SignalingThread::run() { for( int i=0; i<5; i++ ) { sleep( 5000 ); ThreadSafePrinter::printf( "Signaling 20 times\n" ); for( int j=0; j<20; j++ ) { mSemaphore->signal(); } } } int main() { int i; Semaphore *semph = new Semaphore(); SignalingThread *threadS = new SignalingThread( semph ); WaitingThread **threadW = new WaitingThread*[10]; for( i=0; i<10; i++ ) { threadW[i] = new WaitingThread( i, semph ); threadW[i]->start(); } threadS->start(); for( i=0; i<10; i++ ) { threadW[i]->join(); delete threadW[i]; } threadS->join(); delete semph; delete threadS; delete [] threadW; return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/system/unix/0000750000175000017500000000000011401021064021656 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/system/unix/LauncherUnix.cpp0000640000175000017500000000077307607633527025031 0ustar pabspabs/* * Modification History * * 2003-January-10 Jason Rohrer * Created. */ #include "minorGems/system/Launcher.h" #include #include void Launcher::launchCommand( char *inCommandName, char **inArguments ) { int forkValue = fork(); if( forkValue == 0 ) { // we're in child process, so exec command execvp( inCommandName, inArguments ); // we'll never return from this call } } Cultivation_9+dfsg1_UnixSource/minorGems/system/unix/TimeUnix.cpp0000640000175000017500000000123407447774050024156 0ustar pabspabs/* * Modification History * * 2001-October-29 Jason Rohrer * Created. * * 2002-March-13 Jason Rohrer * Added include of time.h so that FreeBSD compile will work. * Changed to use newer gettimeofday that should work on all unix platforms. * Fixed a conversion bug. */ #include "minorGems/system/Time.h" #include #include #include #include void Time::getCurrentTime( unsigned long *outSeconds, unsigned long *outMilliseconds ) { struct timeval currentTime; gettimeofday( ¤tTime, NULL ); *outMilliseconds = currentTime.tv_usec / 1000; *outSeconds = currentTime.tv_sec; } Cultivation_9+dfsg1_UnixSource/minorGems/system/endian.h0000640000175000017500000000317111165410607022321 0ustar pabspabs/* * Modification History * * 2002-May-25 Jason Rohrer * Created. * * 2004-January-12 Jason Rohrer * Added support for metrowerks win32 compiler. * * 2009-April-3 Jason Rohrer * OpenBSD support. */ #include "minorGems/common.h" /** * Include this file to define __BYTE_ORDER * * After this has been included, __BYTE_ORDER will be either * __LITTLE_ENDIAN or * __BIG_ENDIAN */ #ifdef __FreeBSD__ #include #elif defined(__NetBSD__) #include #elif defined(__OpenBSD__) #include #include // default BSD case #elif defined(BSD) #include #elif defined(SOLARIS) // Code for Solaris defs adapted from: // MD5 message-digest algorithm. // by Colin Plumb in 1993, no copyright is claimed. //each solaris is different -- this won't work on 2.6 or 2.7 # include #define __LITTLE_ENDIAN 1234 #define __BIG_ENDIAN 4321 #ifdef _LITTLE_ENDIAN #define __BYTE_ORDER __LITTLE_ENDIAN #else // default to big endian #define __BYTE_ORDER __BIG_ENDIAN #endif // end solaris case #elif defined(WIN_32) || \ ( defined(__MWERKS__) && defined(__INTEL__) ) // windows case #define __LITTLE_ENDIAN 1234 #define __BYTE_ORDER __LITTLE_ENDIAN // end windows case #else // linux case #include // end linux case #endif // end of all system-specific cases // BSD calls it BYTE_ORDER, linux calls it __BYTE_ORDER #ifndef __BYTE_ORDER #define __BYTE_ORDER BYTE_ORDER #endif #ifndef __LITTLE_ENDIAN #define __LITTLE_ENDIAN LITTLE_ENDIAN #endif #ifndef __BIG_ENDIAN #define __BIG_ENDIAN BIG_ENDIAN #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/BinarySemaphore.h0000640000175000017500000000331307722702203024151 0ustar pabspabs/* * Modification History * * 2001-January-11 Jason Rohrer * Created. * * 2001-January-27 Jason Rohrer * Fixed a bug in the precompiler directives. * * 2003-August-26 Jason Rohrer * Added support for timeouts on wait. */ #include "minorGems/common.h" #ifndef BINARY_SEMAPHORE_CLASS_INCLUDED #define BINARY_SEMAPHORE_CLASS_INCLUDED #include "MutexLock.h" /** * Binary semaphore class. Semaphore starts out with a value of 0. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class BinarySemaphore { public: /** * Constructs a binary semaphore. */ BinarySemaphore(); ~BinarySemaphore(); /** * Blocks on this semaphore until signal() is called by another thread. * Note that if signal() has already been called before wait() is * called, then this call will return immediately, though the semaphore * is reset to 0 by this call. * * @param inTimeoutInMilliseconds the maximum time to wait in * milliseconds, or -1 to wait forever. Defaults to -1. * * @return 1 if the semaphore was signaled, or 0 if it timed out. */ int wait( int inTimeoutInMilliseconds = -1 ); /** * Signals the semaphore, allowing a waiting thread to return from * its call to wait(). (The semaphore is set to 1 by this call if * no thread is waiting on the semaphore currently.) */ void signal(); private: // starts at 0 int mSemaphoreValue; /** * Used by platform-specific implementations. */ void *mNativeObjectPointerA; void *mNativeObjectPointerB; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/Launcher.h0000640000175000017500000000163007607633465022641 0ustar pabspabs/* * Modification History * * 2003-January-10 Jason Rohrer * Created. */ #include "minorGems/common.h" #ifndef LAUNCHER_INCLUDED #define LAUNCHER_INCLUDED /** * Interface for launching processes. * * @author Jason Rohrer */ class Launcher { public: /** * Launches a command in a new process. * * @param inCommandName the name of the command to launch. * Must be destroyed by caller if non-const. * @param inArguments an array of argument strings for the command. * This array must be terminated by a NULL pointer. * Note that by convention, the first argument should be the * command name. * Must be destroyed by caller. */ static void launchCommand( char *inCommandName, char **inArguments ); }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/Time.h0000640000175000017500000000444310202731646021764 0ustar pabspabs/* * Modification History * * 2001-October-29 Jason Rohrer * Created. * * 2004-October-14 Jason Rohrer * Fixed sign bug. * * 2005-February-10 Jason Rohrer * Added function to get time in floating point format. */ #include "minorGems/common.h" #ifndef TIME_INCLUDED #define TIME_INCLUDED /** * Interface for platform-independent, high-resolution time access. * * @author Jason Rohrer */ class Time { public: /** * Gets the current time in seconds and milliseconds. * * No guarentee about when absolute 0 of this time * scale is for particular systems. * * @param outSeconds pointer to where the time in seconds * will be returned. * @param outMilliseconds pointer to where the extra * milliseconds will be returned. Value returned is in [0,999]. */ static void getCurrentTime( unsigned long *outSeconds, unsigned long *outMilliseconds ); /** * Gets the current time in fractional (double) seconds. * * @return the current time in seconds. */ static double getCurrentTime(); /** * Gets the number of milliseconds that have passed * since a time in seconds and milliseconds. * * @param inSeconds the start time, in seconds. * @param inMilliseconds the start time's additional milliseconds. * * @return the number of milliseconds that have passed * since inSeconds:inMilliseconds. May overflow if * more than 49 days have passed (assuming 32-bit longs). */ static unsigned long getMillisecondsSince( unsigned long inSeconds, unsigned long inMilliseconds ); }; inline double Time::getCurrentTime() { unsigned long currentTimeS; unsigned long currentTimeMS; getCurrentTime( ¤tTimeS, ¤tTimeMS ); return currentTimeS + currentTimeMS / 1000.0; } inline unsigned long Time::getMillisecondsSince( unsigned long inSeconds, unsigned long inMilliseconds ) { unsigned long currentTimeS; unsigned long currentTimeMS; getCurrentTime( ¤tTimeS, ¤tTimeMS ); unsigned long deltaS = ( currentTimeS - inSeconds ); long deltaMS = ( (long)currentTimeMS - (long)inMilliseconds ); // carry, if needed if( deltaMS < 0 ) { deltaS--; deltaMS += 1000; } return 1000 * deltaS + deltaMS; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/Semaphore.h0000640000175000017500000001023307777611355023024 0ustar pabspabs/* * Modification History * * 2001-January-11 Jason Rohrer * Created. * * 2001-January-11 Jason Rohrer * Added a willBlock() function. * * 2001-February-24 Jason Rohrer * Fixed incorrect delete usage. * * 2002-February-11 Jason Rohrer * Fixed a mistake in the signal() comment. * * 2003-August-26 Jason Rohrer * Added support for timeouts on wait. * * 2003-December-28 Jason Rohrer * Fixed a bug in semaphore value when we timeout on wait. * * 2004-January-9 Jason Rohrer * Fixed a preprocessor error. */ #include "minorGems/common.h" #ifndef SEMAPHORE_CLASS_INCLUDED #define SEMAPHORE_CLASS_INCLUDED #include "MutexLock.h" #include "BinarySemaphore.h" /** * General semaphore with an unbounded value. * * This class uses BinarySemaphores to implement general semaphores, * so it relies on platform-specific BinarySemaphore implementations, * but this class itself is platform-independent. * * @author Jason Rohrer */ class Semaphore { public: /** * Constructs a semaphore. * * @param inStartingValue the starting value for this semaphore. * Defaults to 0 if unspecified. */ Semaphore( int inStartingValue = 0 ); ~Semaphore(); /** * If this semaphore's current value is 0, then this call blocks * on this semaphore until signal() is called by another thread. * If this semaphore's value is >0, then it is decremented by this * call. * * @param inTimeoutInMilliseconds the maximum time to wait in * milliseconds, or -1 to wait forever. Defaults to -1. * * @return 1 if the semaphore was signaled, or 0 if it timed out. */ int wait( int inTimeoutInMilliseconds = -1 ); /** * If a thread is waiting on this semaphore, then the thread * becomes unblocked. * If no thread is waiting, then the semaphore is incremented. */ void signal(); /** * Returns true if a call to wait would have blocked. */ char willBlock(); private: // starts at 0 int mSemaphoreValue; // mutex semaphore starts at 1 BinarySemaphore *mMutexSemaphore; // blocking semaphore starts at 0 BinarySemaphore *mBlockingSemaphore; }; inline Semaphore::Semaphore( int inStartingValue ) : mSemaphoreValue( inStartingValue ), mMutexSemaphore( new BinarySemaphore() ), mBlockingSemaphore( new BinarySemaphore() ) { // increment the mutex semaphore to 1 mMutexSemaphore->signal(); } inline Semaphore::~Semaphore() { delete mMutexSemaphore; delete mBlockingSemaphore; } inline int Semaphore::wait( int inTimeoutInMilliseconds ) { int returnValue; // this implementation copied from _Operating System Concepts_, p. 172 // lock the mutex mMutexSemaphore->wait(); // decrement the semaphore mSemaphoreValue--; if( mSemaphoreValue < 0 ) { // we should block // release the mutex mMutexSemaphore->signal(); // block returnValue = mBlockingSemaphore->wait( inTimeoutInMilliseconds ); if( returnValue != 1 ) { // timed out // increment the semaphore, since we never got signaled // lock the mutex mMutexSemaphore->wait(); mSemaphoreValue++; // we will unlock the mutex below } } else { returnValue = 1; } // release the mutex // ( if we were signaled, then the signaller left the mutex locked ) // ( if we timed out, then we re-locked the mutex above ) mMutexSemaphore->signal(); return returnValue; } inline char Semaphore::willBlock() { char returnValue = false; // lock the mutex mMutexSemaphore->wait(); // check if we will block if( mSemaphoreValue <= 0 ) { returnValue = true; } // release the mutex mMutexSemaphore->signal(); return returnValue; } inline void Semaphore::signal() { // lock the mutex mMutexSemaphore->wait(); // increment the semaphore mSemaphoreValue++; if( mSemaphoreValue <= 0 ) { // we need to wake up a waiting thread mBlockingSemaphore->signal(); // let the waiting thread unlock the mutex } else { // no threads are waiting, so we need to unlock the mutex mMutexSemaphore->signal(); } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/MutexLock.h0000640000175000017500000000207307554113530023000 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-October-18 Jason Rohrer * Moved common include out of header and into platform-specific cpp files, * since MemoryTrack uses a mutex lock. */ #ifndef MUTEX_LOCK_CLASS_INCLUDED #define MUTEX_LOCK_CLASS_INCLUDED #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Mutex lock class. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class MutexLock { public: /** * Constructs a mutex lock; */ MutexLock(); ~MutexLock(); /** * Locks the mutex. Blocks until mutex available if it's * already locked by another thread. */ void lock(); /** * Unlocks the mutex. */ void unlock(); private: /** * Used by platform-specific implementations. */ void *mNativeObjectPointer; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/FinishedSignalThread.cpp0000640000175000017500000000203410033036772025432 0ustar pabspabs/* * Modification History * * 2002-March-9 Jason Rohrer * Created. * * 2002-March-11 Jason Rohrer * Changed so that destructor joins thread. * * 2002-April-4 Jason Rohrer * Changed name of lock to avoid confusion with subclass-provided locks. * * 2002-August-5 Jason Rohrer * Fixed member initialization order to match declaration order. * * 2004-April-1 Jason Rohrer * Moved from konspire2b into minorGems. * Changed so that destructor does not join the thread. */ #include "FinishedSignalThread.h" #include FinishedSignalThread::FinishedSignalThread() : mFinishedLock( new MutexLock() ), mFinished( false ) { } FinishedSignalThread::~FinishedSignalThread() { delete mFinishedLock; } char FinishedSignalThread::isFinished() { mFinishedLock->lock(); char finished = mFinished; mFinishedLock->unlock(); return finished; } void FinishedSignalThread::setFinished() { mFinishedLock->lock(); mFinished = true; mFinishedLock->unlock(); } Cultivation_9+dfsg1_UnixSource/minorGems/system/FinishedSignalThreadManager.cpp0000640000175000017500000000461510170260026026725 0ustar pabspabs/* * Modification History * * 2004-November-9 Jason Rohrer * Created. * Modified from MUTE's ChannelReceivingThreadManager. * * 2005-January-9 Jason Rohrer * Changed to sleep on a semaphore to allow sleep to be interrupted. */ #include "minorGems/system/FinishedSignalThreadManager.h" FinishedSignalThreadManager::FinishedSignalThreadManager() : mLock( new MutexLock() ), mThreadVector( new SimpleVector() ), mStopSignal( false ), mSleepSemaphore( new BinarySemaphore() ) { this->start(); } FinishedSignalThreadManager::~FinishedSignalThreadManager() { mLock->lock(); mStopSignal = true; mLock->unlock(); // signal the sleeping semaphore to wake up the thread mSleepSemaphore->signal(); this->join(); mLock->lock(); // destroy all remaining threads int numThreads = mThreadVector->size(); for( int i=0; igetElement( i ) ); } delete mThreadVector; mLock->unlock(); delete mLock; delete mSleepSemaphore; } void FinishedSignalThreadManager::addThread( FinishedSignalThread *inThread ) { mLock->lock(); mThreadVector->push_back( inThread ); mLock->unlock(); } void FinishedSignalThreadManager::run() { char stopped; mLock->lock(); stopped = mStopSignal; mLock->unlock(); while( !stopped ) { // wait for 10 seconds int wasSignaled = mSleepSemaphore->wait( 10000 ); if( wasSignaled == 1 ) { // signaled... we should stop return; } char foundFinished = true; while( foundFinished ) { foundFinished = false; mLock->lock(); int numThreads = mThreadVector->size(); for( int i=0; igetElement( i ) ); if( currentThread->isFinished() ) { delete currentThread; mThreadVector->deleteElement( i ); foundFinished = true; } } mLock->unlock(); } mLock->lock(); stopped = mStopSignal; mLock->unlock(); } } Cultivation_9+dfsg1_UnixSource/minorGems/system/TestThread.h0000640000175000017500000000252107565075331023142 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2001-January-27 Jason Rohrer * Switched to a ThreadSafePrinter in attempt to get it to work on Win32. * Changed print call to printf. * * 2002-November-14 Jason Rohrer * Added missing destructor. */ #include "minorGems/common.h" #ifndef TEST_THREAD_CLASS_INCLUDED #define TEST_THREAD_CLASS_INCLUDED #include "Thread.h" #include "ThreadSafePrinter.h" #include /** * Test subclass of Thread class. Useful for testing if platform-specific * thread implmentations are working. * * @author Jason Rohrer */ class TestThread : public Thread { public: /** * Constructs a test thread and tells it how high to count to. * * @param inID id number thread will print along with count. * @param inNumToCount thread will count from 0 to this number. */ TestThread( int inID, int inNumToCount ); ~TestThread(); // override the run method from PThread void run(); private: int mID; int mNumToCount; }; inline TestThread::TestThread( int inID, int inNumToCount ) : mID( inID ), mNumToCount( inNumToCount ) { } inline TestThread::~TestThread() { } inline void TestThread::run() { for( int i=0; i<=mNumToCount; i++ ) { ThreadSafePrinter::printf( "Thread %d counting %d.\n", mID, i ); } } #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/StopSignalThread.cpp0000640000175000017500000000231510170267060024626 0ustar pabspabs/* * Modification History * * 2002-April-4 Jason Rohrer * Created. * Changed to reflect the fact that the base class * destructor is called *after* the derived class destructor. * * 2002-August-5 Jason Rohrer * Fixed member initialization order to match declaration order. * * 2003-September-5 Jason Rohrer * Moved into minorGems. * * 2005-January-9 Jason Rohrer * Changed to sleep on a semaphore to make sleep interruptable by stop. */ #include "StopSignalThread.h" StopSignalThread::StopSignalThread() : mStopLock( new MutexLock() ), mStopped( false ), mSleepSemaphore( new BinarySemaphore() ) { } StopSignalThread::~StopSignalThread() { delete mStopLock; delete mSleepSemaphore; } void StopSignalThread::sleep( unsigned long inTimeInMilliseconds ) { mSleepSemaphore->wait( inTimeInMilliseconds ); } char StopSignalThread::isStopped() { mStopLock->lock(); char stoped = mStopped; mStopLock->unlock(); return stoped; } void StopSignalThread::stop() { mStopLock->lock(); mStopped = true; mStopLock->unlock(); // signal the semaphore to wake up the thread, if it is sleeping mSleepSemaphore->signal(); } Cultivation_9+dfsg1_UnixSource/minorGems/system/linux/0000750000175000017500000000000011401021064022032 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/system/linux/ThreadLinux.cpp0000640000175000017500000001335410174541132025005 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2001-January-11 Jason Rohrer * Added missing sleep() implementation. * * 2002-March-27 Jason Rohrer * Added support for gprof-friendly thread wrappers. * Fixed a compile bug when gprof threads disabled. * * 2002-August-5 Jason Rohrer * Removed an unused variable. * * 2003-February-3 Jason Rohrer * Fixed sleep to be thread safe (signals were interrupting thread sleeps). * * 2004-March-31 Jason Rohrer * Added support for detatched mode. * * 2005-January-22 Jason Rohrer * Added a static sleep function. */ #include #include #include #include #include /** * Linux-specific implementation of the Thread class member functions. * * May also be compatible with other POSIX-like systems. * * To compile: * g++ -lpthread * If thread profiling is desired for gprof on linux, compile * with -DUSE_GPROF_THREADS (otherwise, only main thread is profiled). */ #ifdef USE_GPROF_THREADS // prototype int gprof_pthread_create( pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg ); #endif // prototype /** * A wrapper for the run method, since pthreads won't take * a class's member function. Takes a pointer to the Thread to run, * cast as a void*; */ void *linuxThreadFunction( void * ); Thread::Thread() { // allocate a pthread structure on the heap mNativeObjectPointer = (void *)( new pthread_t[1] ); } Thread::~Thread() { // de-allocate the pthread structure from the heap pthread_t *threadPointer = (pthread_t *)mNativeObjectPointer; delete [] threadPointer; } void Thread::start( char inDetach ) { mIsDetached = inDetach; // get a pointer to the pthread pthread_t *threadPointer = (pthread_t *)mNativeObjectPointer; // create the pthread, which also sets it running #ifdef USE_GPROF_THREADS gprof_pthread_create( &( threadPointer[0] ), NULL, linuxThreadFunction, (void*)this ); #else pthread_create( &( threadPointer[0] ), NULL, linuxThreadFunction, (void*)this ); #endif if( mIsDetached ) { pthread_detach( threadPointer[0] ); } } void Thread::join() { void *joinStat; pthread_t *threadPointer = (pthread_t *)mNativeObjectPointer; pthread_join( threadPointer[0], &joinStat ); } void Thread::staticSleep( unsigned long inTimeInMilliseconds ) { unsigned long seconds = inTimeInMilliseconds / 1000; unsigned long milliseconds = inTimeInMilliseconds % 1000; struct timespec remainingSleepTimeStruct; remainingSleepTimeStruct.tv_sec = seconds; remainingSleepTimeStruct.tv_nsec = milliseconds * 1000000; struct timespec timeToSleepStruct; // sleep repeatedly, ignoring signals, untill we use up all of the time int sleepReturn = -1; while( sleepReturn == -1 ) { timeToSleepStruct.tv_sec = remainingSleepTimeStruct.tv_sec; timeToSleepStruct.tv_nsec = remainingSleepTimeStruct.tv_nsec; sleepReturn = nanosleep( &timeToSleepStruct, &remainingSleepTimeStruct ); } } // takes a pointer to a Thread object as the data value void *linuxThreadFunction( void *inPtrToThread ) { Thread *threadToRun = (Thread *)inPtrToThread; threadToRun->run(); if( threadToRun->isDetatched() ) { // thread detached, so we must destroy it delete threadToRun; } return inPtrToThread; } #ifdef USE_GPROF_THREADS // found at http://sam.zoy.org/doc/programming/gprof.html #include /* * pthread_create wrapper for gprof compatibility * * needed headers: * */ typedef struct wrapper_s { void * (*start_routine)(void *); void * arg; pthread_mutex_t lock; pthread_cond_t wait; struct itimerval itimer; } wrapper_t; static void * wrapper_routine(void *); /** * Same prototype as pthread_create; use some #define magic to * transparently replace it in other files */ int gprof_pthread_create( pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg ) { wrapper_t wrapper_data; int i_return; /* Initialize the wrapper structure */ wrapper_data.start_routine = start_routine; wrapper_data.arg = arg; getitimer(ITIMER_PROF, &wrapper_data.itimer); pthread_cond_init(&wrapper_data.wait, NULL); pthread_mutex_init(&wrapper_data.lock, NULL); pthread_mutex_lock(&wrapper_data.lock); /* The real pthread_create call */ i_return = pthread_create(thread, attr, &wrapper_routine, &wrapper_data); /* If the thread was successfully spawned, wait for the data * to be released */ if( i_return == 0 ) { pthread_cond_wait(&wrapper_data.wait, &wrapper_data.lock); } pthread_mutex_unlock(&wrapper_data.lock); pthread_mutex_destroy(&wrapper_data.lock); pthread_cond_destroy(&wrapper_data.wait); return i_return; } /** * The wrapper function in charge for setting the itimer value */ static void * wrapper_routine( void * data ) { /* Put user data in thread-local variables */ void * (*start_routine)(void *) = ((wrapper_t*)data)->start_routine; void * arg = ((wrapper_t*)data)->arg; /* Set the profile timer value */ setitimer(ITIMER_PROF, &((wrapper_t*)data)->itimer, NULL); /* Tell the calling thread that we don't need its data anymore */ pthread_mutex_lock(&((wrapper_t*)data)->lock); pthread_cond_signal(&((wrapper_t*)data)->wait); pthread_mutex_unlock(&((wrapper_t*)data)->lock); /* Call the real function */ return start_routine(arg); } #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/linux/MutexLockLinux.cpp0000640000175000017500000000311307554122211025502 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2002-October-18 Jason Rohrer * Moved common include out of header and into platform-specific cpp files, * since MemoryTrack uses a mutex lock. * Changed to use malloc instead of new internally to work with debugMemory. * Made use of mNativeObjectPointer a bit cleaner. */ #include "minorGems/common.h" #include #include #include /** * Linux-specific implementation of the MutexLock class member functions. * * May also be compatible with other POSIX-like systems. * * To compile: * g++ -lpthread */ MutexLock::MutexLock() { // allocate a mutex structure on the heap mNativeObjectPointer = malloc( sizeof( pthread_mutex_t ) ); // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointer; // init the mutex pthread_mutex_init( mutexPointer, NULL ); } MutexLock::~MutexLock() { // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointer; // destroy the mutex pthread_mutex_destroy( mutexPointer ); // de-allocate the mutex structure from the heap free( mNativeObjectPointer ); } void MutexLock::lock() { // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointer; pthread_mutex_lock( mutexPointer ); } void MutexLock::unlock() { // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointer; pthread_mutex_unlock( mutexPointer ); } Cultivation_9+dfsg1_UnixSource/minorGems/system/linux/BinarySemaphoreLinux.cpp0000640000175000017500000001103210401110426026644 0ustar pabspabs/* * Modification History * * 2001-January-11 Jason Rohrer * Created. * * 2003-August-26 Jason Rohrer * Added support for timeouts on wait. * * 2006-February-28 Jason Rohrer * Fixed bug in sub-second timeout computation. */ #include "minorGems/system/BinarySemaphore.h" #include "minorGems/system/Time.h" #include /** * Linux-specific implementation of the BinarySemaphore class member functions. * * May also be compatible with other POSIX-like systems. * * To compile: * g++ -lpthread */ /** * Native object pointer A is the condition variable. * Pointer B is the mutex that must go along with it. */ BinarySemaphore::BinarySemaphore() : mSemaphoreValue( 0 ) { // allocate a condition variable structure on the heap mNativeObjectPointerA = (void *)( new pthread_cond_t[1] ); // get a pointer to the cond pthread_cond_t *condPointer = (pthread_cond_t *)mNativeObjectPointerA; // init the cond pthread_cond_init( &( condPointer[0] ), NULL ); // allocate a mutex structure on the heap mNativeObjectPointerB = (void *)( new pthread_mutex_t[1] ); // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointerB; // init the mutex pthread_mutex_init( &( mutexPointer[0] ), NULL ); } BinarySemaphore::~BinarySemaphore() { // get a pointer to the cond pthread_cond_t *condPointer = (pthread_cond_t *)mNativeObjectPointerA; // destroy the cond pthread_cond_destroy( &( condPointer[0] ) ); // de-allocate the cond structure from the heap delete [] condPointer; // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointerB; // destroy the mutex pthread_mutex_destroy( &( mutexPointer[0] ) ); // de-allocate the mutex structure from the heap delete [] mutexPointer; } int BinarySemaphore::wait( int inTimeoutInMilliseconds ) { int returnValue = 1; // get a pointer to the cond pthread_cond_t *condPointer = (pthread_cond_t *)mNativeObjectPointerA; // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointerB; // lock the mutex pthread_mutex_lock( &( mutexPointer[0] ) ); if( mSemaphoreValue == 0 ) { // wait on condition variable, which automatically unlocks // the passed-in mutex if( inTimeoutInMilliseconds == -1 ) { // no timeout pthread_cond_wait( &( condPointer[0] ), &( mutexPointer[0] ) ); } else { // use timeout version unsigned long nsecPerSecond = 1000000000; unsigned long nsecPerMillisecond = 1000000; unsigned long currentSec; unsigned long currentMS; Time::getCurrentTime( ¤tSec, ¤tMS ); unsigned long currentNS = currentMS * nsecPerMillisecond; long timeoutSec = inTimeoutInMilliseconds / 1000; long extraMS = inTimeoutInMilliseconds % 1000; long extraNS = extraMS * nsecPerMillisecond; unsigned long absTimeoutSec = currentSec + timeoutSec; unsigned long absTimeoutNsec = currentNS + extraNS; // check for nsec overflow if( absTimeoutNsec > nsecPerSecond ) { absTimeoutSec += 1; absTimeoutNsec -= nsecPerSecond; } struct timespec abstime; abstime.tv_sec = absTimeoutSec; abstime.tv_nsec = absTimeoutNsec; int result = pthread_cond_timedwait( &( condPointer[0] ), &( mutexPointer[0] ), &abstime ); if( result != 0 ) { // timed out returnValue = 0; } } // mutex is apparently re-locked when we return from cond_wait } // decrement the semaphore value mSemaphoreValue = 0; // unlock the mutex again pthread_mutex_unlock( &( mutexPointer[0] ) ); return returnValue; } void BinarySemaphore::signal() { // get a pointer to the cond pthread_cond_t *condPointer = (pthread_cond_t *)mNativeObjectPointerA; // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointerB; // lock the mutex pthread_mutex_lock( &( mutexPointer[0] ) ); // increment the semaphore value mSemaphoreValue = 1; pthread_cond_signal( &( condPointer[0] ) ); // unlock the mutex pthread_mutex_unlock( &( mutexPointer[0] ) ); } Cultivation_9+dfsg1_UnixSource/minorGems/system/FinishedSignalThread.h0000640000175000017500000000326510147345462025113 0ustar pabspabs/* * Modification History * * 2002-March-9 Jason Rohrer * Created. * * 2002-March-10 Jason Rohrer * Made destructor public. * * 2002-March-11 Jason Rohrer * Changed so that destructor joins thread. * * 2002-April-4 Jason Rohrer * Changed name of lock to avoid confusion with subclass-provided locks. * * 2004-April-1 Jason Rohrer * Moved from konspire2b into minorGems. * Changed so that destructor does not join the thread. * * 2004-November-19 Jason Rohrer * Changed to virtual inheritance from Thread class. */ #ifndef FINISHED_SIGNAL_THREAD_INCLUDED #define FINISHED_SIGNAL_THREAD_INCLUDED #include "minorGems/system/Thread.h" #include "minorGems/system/MutexLock.h" /** * Abstract subclass if thread that has a * synchronized finished signal. * * @author Jason Rohrer */ class FinishedSignalThread : public virtual Thread { public: /** * Only destroys this thread. * Does not join. */ virtual ~FinishedSignalThread(); /** * Gets whether this thread is finished and * ready to be destroyed. * * @return true iff this thread is finished. */ char isFinished(); protected: FinishedSignalThread(); /** * Sets that this thread is finished and * ready to be destroyed. * * For this class to work properly, the subclass * MUST call this function at the end of its run method. */ void setFinished(); private: MutexLock *mFinishedLock; char mFinished; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/FinishedSignalThreadManager.h0000640000175000017500000000273610170257606026406 0ustar pabspabs/* * Modification History * * 2004-November-9 Jason Rohrer * Created. * Modified from MUTE's ChannelReceivingThreadManager. * * 2005-January-9 Jason Rohrer * Changed to sleep on a semaphore to allow sleep to be interrupted. */ #ifndef FINISHED_SIGNAL_THREAD_MANAGER_INCLUDED #define FINISHED_SIGNAL_THREAD_MANAGER_INCLUDED #include "minorGems/system/FinishedSignalThread.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/system/Thread.h" #include "minorGems/system/MutexLock.h" #include "minorGems/system/BinarySemaphore.h" /** * A thread that manages the destruction of FinishedSignalThreads. * * @author Jason Rohrer. */ class FinishedSignalThreadManager : public Thread { public: /** * Constructs and starts this manager. */ FinishedSignalThreadManager(); /** * Stops and destroys this manager. */ ~FinishedSignalThreadManager(); /** * Adds a thread to this manager. * * @param inThread the thread to add. * Will be destroyed by this class. */ void addThread( FinishedSignalThread *inThread ); // implements the Thread interface void run(); protected: MutexLock *mLock; SimpleVector *mThreadVector; char mStopSignal; BinarySemaphore *mSleepSemaphore; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/ThreadSafePrinter.h0000640000175000017500000000262710032526077024443 0ustar pabspabs/* * Modification History * * 2000-October-14 Jason Rohrer * Created. * * 2001-January-27 Jason Rohrer * Converted to use MutexLock and added to minorGems source tree. * Changed tprintf to be static (the mutexes don't work otherwise). * Now we're closing the argument list. * Fixed so that it works with any number of arguments. * Changed name of print function to printf. * * 2004-March-31 Jason Rohrer * Fixed static memory leak. */ #include "minorGems/common.h" #ifndef THREAD_SAFE_PRINTER_INCLUDED #define THREAD_SAFE_PRINTER_INCLUDED #include "MutexLock.h" #include // for variable argument lists #include /** * Thread safe printf function. Note that printf is actually thread safe * anyway, so this is just to demonstrate and test locks. It seems as * though printf _isn't_ thread safe on certain platforms, so this class * may be useful. * * @author Jason Rohrer */ class ThreadSafePrinter { public: static int printf( const char* inFormatString, ... ); private: static MutexLock sLock; }; // initialize static members MutexLock ThreadSafePrinter::sLock; inline int ThreadSafePrinter::printf( const char*inFormatString, ... ) { va_list argList; va_start( argList, inFormatString ); sLock.lock(); int returnVal = vprintf( inFormatString, argList ); fflush( stdout ); sLock.unlock(); va_end( argList ); return returnVal; } #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/Thread.h0000640000175000017500000000560710174541121022273 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2001-March-4 Jason Rohrer * Made sleep() static so it can be called by non-Thread classes. * * 2001-May-12 Jason Rohrer * Added comments about joining before destroying. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-August-5 Jason Rohrer * Made destructor virtual. * * 2004-March-31 Jason Rohrer * Added support for detatched mode. * * 2005-January-9 Jason Rohrer * Made sleep function virtual to allow overrides. * * 2005-January-22 Jason Rohrer * Added a static sleep function. */ #include "minorGems/common.h" #ifndef THREAD_CLASS_INCLUDED #define THREAD_CLASS_INCLUDED #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Base class to be subclassed by all threads. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class Thread { public: Thread(); virtual ~Thread(); /** * Starts this Thread. * * Note that after starting a non-detached thread, it _must_ be * joined before being destroyed to avoid memory leaks. * * Threads running in detatched mode handle their own destruction * as they terminate and do not need to be joined at all. * * @param inDetach true if this thread should run in detatched mode, * or false to run in non-detached mode. Defaults to false. */ void start( char inDetach = false ); /** * To be overriden by subclasses. * This method will be run by the Thread after start() has been called. */ virtual void run() = 0; /** * Blocks until this thread finishes executing its run() method. * * Must be called before destroying this thread, if this thread * has been started. */ void join(); /** * Puts the current thread to sleep for a specified amount of time. * * Note that given a thread instance threadA, calling threadA.sleep() * will put the calling thread to sleep. * * @param inTimeInMilliseconds the number of milliseconds to sleep. */ virtual void sleep( unsigned long inTimeInMilliseconds ) { staticSleep( inTimeInMilliseconds ); } /** * Same as sleep, but can be called without constructing a thread. */ static void staticSleep( unsigned long inTimeInMilliseconds ); /** * Gets whether this thread is detached. * * @return true if this thread is detached. */ char isDetatched() { return mIsDetached; } private: /** * Used by platform-specific implementations. */ void *mNativeObjectPointer; char mIsDetached; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/TestThread.cpp0000640000175000017500000000231710032526430023460 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2002-November-14 Jason Rohrer * Added more verbose printouts. * * 2004-March-31 Jason Rohrer * Added test of detached threads. */ #include "TestThread.h" int numToCount = 1000; /** * Main method that spawns two TestThreads. * * @author Jason Rohrer */ int main() { TestThread *thread1 = new TestThread( 1, numToCount ); TestThread *thread2 = new TestThread( 2, numToCount ); TestThread *thread3 = new TestThread( 3, numToCount ); ThreadSafePrinter::printf( "Starting thread 1\n" ); thread1->start(); ThreadSafePrinter::printf( "Starting thread 2\n" ); thread2->start(); ThreadSafePrinter::printf( "Starting thread 3 in detached mode\n" ); thread3->start( true ); Thread::sleep( 5000 ); ThreadSafePrinter::printf( "Joining thread 1\n" ); thread1->join(); ThreadSafePrinter::printf( "Joining thread 2\n" ); thread2->join(); ThreadSafePrinter::printf( "Destroying thread 1\n" ); delete thread1; ThreadSafePrinter::printf( "Destroying thread 2\n" ); delete thread2; ThreadSafePrinter::printf( "Thread 3 should handle its own destruction.\n" ); return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/system/StopSignalThread.h0000640000175000017500000000361610170267060024300 0ustar pabspabs/* * Modification History * * 2002-April-4 Jason Rohrer * Created. * Changed to reflect the fact that the base class * destructor is called *after* the derived class destructor. * * 2003-September-5 Jason Rohrer * Moved into minorGems. * * 2004-November-19 Jason Rohrer * Changed to virtual inheritance from Thread class. * * 2005-January-9 Jason Rohrer * Changed to sleep on a semaphore to make sleep interruptable by stop. */ #ifndef STOP_SIGNAL_THREAD_INCLUDED #define STOP_SIGNAL_THREAD_INCLUDED #include "minorGems/system/Thread.h" #include "minorGems/system/MutexLock.h" #include "minorGems/system/BinarySemaphore.h" /** * Abstract subclass of thread that has a stop signal. * * Note that subclasses MUST check the isStopped() function * periodically in their run() function for this class to work * properly. * * @author Jason Rohrer */ class StopSignalThread : public virtual Thread { public: /** * Only destroys this thread. * Does not stop or join. */ virtual ~StopSignalThread(); protected: StopSignalThread(); // overrides Thread::sleep to make it interruptable by our stop call virtual void sleep( unsigned long inTimeInMilliseconds ); /** * Signals this thread to stop, interrupting it if it is sleeping. * * Thread safe. * * Thread must be joined after this call returns. */ void stop(); /** * Gets whether this thread has been signaled to stop. * * Thread safe. * * @return true if this thread should stop. */ char isStopped(); private: MutexLock *mStopLock; char mStopped; BinarySemaphore *mSleepSemaphore; }; #endif Cultivation_9+dfsg1_UnixSource/minorGems/system/win32/0000750000175000017500000000000011401021064021635 5ustar pabspabsCultivation_9+dfsg1_UnixSource/minorGems/system/win32/ThreadWin32.cpp0000640000175000017500000000456010174541132024412 0ustar pabspabs/* * Modification History * * 2001-January-27 Jason Rohrer * Created. * * 2001-March-4 Jason Rohrer * Replaced include of and with * to fix compile bugs encountered with newer windows compilers. * * 2004-March-31 Jason Rohrer * Added missing call to CloseHandle in destructor. * Added support for detatched mode. * * 2004-April-1 Jason Rohrer * Fixed a bug in CloseHandle call pointed out by Mycroftxxx. * * 2005-January-22 Jason Rohrer * Added a static sleep function. */ #include "minorGems/system/Thread.h" #include /** * Win32-specific implementation of the Thread class member functions. * */ // prototype /** * A wrapper for the run method, since windows thread (perhaps) won't take * a class's member function. Takes a pointer to the Thread to run, * cast as a void*; */ DWORD WINAPI win32ThreadFunction( void * ); Thread::Thread() { // allocate a handle on the heap mNativeObjectPointer = (void *)( new HANDLE[1] ); } Thread::~Thread() { // get a pointer to the allocated handle HANDLE *threadPointer = (HANDLE *)mNativeObjectPointer; // close the handle to ensure that the thread resources are freed CloseHandle( threadPointer[0] ); // de-allocate the thread handle from the heap delete [] threadPointer; } void Thread::start( char inDetach ) { mIsDetached = inDetach; // get a pointer to the allocated handle HANDLE *threadPointer = (HANDLE *)mNativeObjectPointer; DWORD threadID; threadPointer[0] = CreateThread( (LPSECURITY_ATTRIBUTES)NULL, // no attributes (DWORD)0, // default stack size win32ThreadFunction, // function (LPVOID)this, // function arg (DWORD)0, // no creation flags (start thread immediately) &threadID ); } void Thread::join() { HANDLE *threadPointer = (HANDLE *)mNativeObjectPointer; WaitForSingleObject( threadPointer[0], INFINITE ); } void Thread::staticSleep( unsigned long inTimeInMilliseconds ) { Sleep( inTimeInMilliseconds ); } // takes a pointer to a Thread object as the data value DWORD WINAPI win32ThreadFunction( void *inPtrToThread ) { Thread *threadToRun = (Thread *)inPtrToThread; threadToRun->run(); if( threadToRun->isDetatched() ) { // thread detached, so we must destroy it delete threadToRun; } return 0; } Cultivation_9+dfsg1_UnixSource/minorGems/system/win32/BinarySemaphoreWin32.cpp0000640000175000017500000000401707722702212026273 0ustar pabspabs/* * Modification History * * 2001-January-27 Jason Rohrer * Created. * * 2001-March-4 Jason Rohrer * Replaced include of and with * to fix compile bugs encountered with newer windows compilers. * * 2003-August-26 Jason Rohrer * Added support for timeouts on wait. */ #include "minorGems/system/BinarySemaphore.h" #include /** * Win32-specific implementation of the BinarySemaphore class member functions. */ /** * Native object pointer A is the semaphore handle. * Pointer B is not used. */ BinarySemaphore::BinarySemaphore() : mSemaphoreValue( 0 ) { // allocate a handle on the heap mNativeObjectPointerA = (void *)( new HANDLE[1] ); // retrieve handle from the heap HANDLE *semaphorePointer = (HANDLE *)mNativeObjectPointerA; semaphorePointer[0] = CreateSemaphore( (LPSECURITY_ATTRIBUTES) NULL, // no attributes 0, // initial count 1, // maximum count (LPCTSTR) NULL ); // no name } BinarySemaphore::~BinarySemaphore() { // retrieve handle from the heap HANDLE *semaphorePointer = (HANDLE *)mNativeObjectPointerA; // destroy the semaphore CloseHandle( semaphorePointer[0] ); // de-allocate the handle from the heap delete [] semaphorePointer; } int BinarySemaphore::wait( int inTimeoutInMilliseconds ) { // retrieve handle from the heap HANDLE *semaphorePointer = (HANDLE *)mNativeObjectPointerA; if( inTimeoutInMilliseconds == -1 ) { WaitForSingleObject( semaphorePointer[0], INFINITE ); return 1; } else { // timeout int result = WaitForSingleObject( semaphorePointer[0], inTimeoutInMilliseconds ); if( result == WAIT_TIMEOUT ) { return 0; } else { return 1; } } } void BinarySemaphore::signal() { // retrieve handle from the heap HANDLE *semaphorePointer = (HANDLE *)mNativeObjectPointerA; ReleaseSemaphore( semaphorePointer[0], 1, (LPLONG) NULL ); } Cultivation_9+dfsg1_UnixSource/minorGems/system/win32/LauncherWin32.cpp0000640000175000017500000000065007637725606024764 0ustar pabspabs/* * Modification History * * 2003-January-10 Jason Rohrer * Created. * * 2003-March-24 Jason Rohrer * Fixed a syntax typo. */ #include "minorGems/system/Launcher.h" #include #include void Launcher::launchCommand( char *inCommandName, char **inArguments ) { _spawnvp( _P_NOWAIT, inCommandName, inArguments ); } Cultivation_9+dfsg1_UnixSource/minorGems/system/win32/MutexLockWin32.cpp0000640000175000017500000000341107554370760025127 0ustar pabspabs/* * Modification History * * 2001-January-27 Jason Rohrer * Created. * * 2001-March-4 Jason Rohrer * Replaced include of and with * to fix compile bugs encountered with newer windows compilers. * * 2002-October-18 Jason Rohrer * Moved common include out of header and into platform-specific cpp files, * since MemoryTrack uses a mutex lock. * * 2002-October-19 Jason Rohrer * Changed to use malloc instead of new internally to work with debugMemory. * Made use of mNativeObjectPointer a bit cleaner. * Fixed a few bugs with new use of mNativeObjectPointer. */ #include "minorGems/common.h" #include "minorGems/system/MutexLock.h" #include #include /** * Win32-specific implementation of the MutexLock class member functions. */ MutexLock::MutexLock() { // allocate a handle on the heap mNativeObjectPointer = malloc( sizeof( HANDLE ) ); // retrieve handle from the heap HANDLE *mutexPointer = (HANDLE *)mNativeObjectPointer; // create the mutex *mutexPointer = CreateMutex( (LPSECURITY_ATTRIBUTES) NULL, // no attributes (BOOL) false, // not initially locked (LPCTSTR) NULL ); // no name } MutexLock::~MutexLock() { // retrieve handle from the heap HANDLE *mutexPointer = (HANDLE *)mNativeObjectPointer; // destroy the mutex CloseHandle( *mutexPointer ); // de-allocate the mutex structure from the heap free( mutexPointer ); } void MutexLock::lock() { // retrieve handle from the heap HANDLE *mutexPointer = (HANDLE *)mNativeObjectPointer; WaitForSingleObject( *mutexPointer, INFINITE ); } void MutexLock::unlock() { // retrieve handle from the heap HANDLE *mutexPointer = (HANDLE *)mNativeObjectPointer; ReleaseMutex( *mutexPointer ); } Cultivation_9+dfsg1_UnixSource/minorGems/system/win32/TimeWin32.cpp0000640000175000017500000000447010133647061024104 0ustar pabspabs/* * Modification History * * 2001-November-7 Jason Rohrer * Created. * * 2002-April-11 Jason Rohrer * Added missing include, and fixed a bug. * * 2004-January-29 Jason Rohrer * Fixed so that 0-point of time is the same as on other platforms. * * 2004-October-14 Jason Rohrer * Fixed bug in second/millisecond callibration. * Fixed bug in win32 time to ANSI time translation. * Fixed daylight savings time bug. */ #include "minorGems/system/Time.h" #include #include #include #include /** * Windows implementation of Time.h. * * The 0-point should match the ANSI standard. */ void Time::getCurrentTime( unsigned long *outSeconds, unsigned long *outMilliseconds ) { // convert from win32 broken-down time (which has msec resolution) // to an ANSI time struct and then convert to an absolute time in // seconds // This procedure ensures that the 0-point matches the ANSI standard. // note: // we cannot simply call ANSI time() to get the seconds and then rely // on GetLocalTime to get the milliseconds, since the seconds value // used by GetLocalTime is (strangely enough) not calibrated to the seconds // value of time(). // In other words, it is possible for the time() seconds to advance // at a different clock cycle than the GetLocalTime seconds. // get time using a win32 call SYSTEMTIME win32TimeStruct; GetLocalTime( &win32TimeStruct ); // convert this win32 structure to the ANSI standard structure struct tm ansiTimeStruct; ansiTimeStruct.tm_sec = win32TimeStruct.wSecond; ansiTimeStruct.tm_min = win32TimeStruct.wMinute; ansiTimeStruct.tm_hour = win32TimeStruct.wHour; ansiTimeStruct.tm_mday = win32TimeStruct.wDay; // ANSI time struct has month in range [0..11] ansiTimeStruct.tm_mon = win32TimeStruct.wMonth - 1; // ANSI time struct has year that is an offset from 1900 ansiTimeStruct.tm_year = win32TimeStruct.wYear - 1900; // unknown daylight savings time (dst) status // if we fail to init this value, we can get inconsistent results ansiTimeStruct.tm_isdst = -1; unsigned long secondsSinceEpoch = mktime( &ansiTimeStruct ); *outSeconds = secondsSinceEpoch; *outMilliseconds = (unsigned long)( win32TimeStruct.wMilliseconds ); } Cultivation_9+dfsg1_UnixSource/runToBuild0000750000175000017500000000125311401021045017424 0ustar pabspabs#!/bin/bash # # Modification History # # 2006-July-13 Jason Rohrer # Copied from Transcend. # # 2006-November-26 Jason Rohrer # Added tool-tip stuff. # cd game2 chmod u+x ./configure ./configure echo "Building portaudio..." cd ../minorGems/sound/portaudio chmod u+x ./configure ./configure make cd ../../../game2 echo "Building Cultivation..." cd gameSource make cd .. cd .. cp game2/gameSource/Cultivation ./Cultivation cp game2/documentation/how_to_*.txt . cp game2/gameSource/features.txt . cp game2/gameSource/language.txt . cp game2/gameSource/font.tga . mkdir ./languages cp game2/gameSource/languages/*.txt ./languages echo "Run Cultivation to play."