Example #1
0
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<Vector3D *> thisLayerLeafTerminii;
    
    
    for( int j=0; j<numFullJoints; j++ ) {

        // lower leaves are darker
        double colorScaleFactor = (double)(j+1) / (double)maxNumJoints; 
        // min scaling of 0.5
        colorScaleFactor = colorScaleFactor * 0.5 + 0.5;
        
        
        Color thisLevelColor;
        thisLevelColor.setValues( &leafColor );
        thisLevelColor.weightColor( colorScaleFactor );
        
        
        
        
        Angle3D currentAngle( &startAngle );

        double zValue = leafPosition.mZ;

        if( zValue <= inMaxZ && zValue >= inMaxZ ) {
            // draw this joint
            for( int g=0; g<numLeavesPerJoint; g++ ) {

                if( Features::drawShadows ) {
                    // draw shadow
                    glColor4f( 0, 0, 0, 0.5 );
                    mLeaf.draw( &leafPosition, &currentAngle,
                                currentScale * radius * 1.05 );
                    }
                
                // draw leaf
                setGLColor( &thisLevelColor );
                
                mLeaf.draw( &leafPosition, &currentAngle,
                            currentScale * radius,
                            &leafWalkerTerminus );


                thisLayerLeafTerminii.push_back(
                    new Vector3D( &leafWalkerTerminus ) );

                
                currentAngle.add( &angleIncrement );
                }

            // finally cap this joint
            setGLColor( &thisLevelColor );
            mJointCapTexture->enable();
            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( &currentAngle );

        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; g<numLeavesPerJoint; g++ ) {

                if( Features::drawShadows ) {
                    // draw shadow
                    glColor4f( 0, 0, 0, 0.5 );
                    mLeaf.draw( &leafPosition, &currentAngle,
                                partialJoint * currentScale * radius * 1.05 );
                    }
                
                setGLColor( &thisLevelColor );

                mLeaf.draw( &leafPosition, &currentAngle,
                            // scale down further by partial fraction
                            partialJoint * currentScale * radius );

                currentAngle.add( &angleIncrement );
                }

            // finally cap this joint
            setGLColor( &thisLevelColor );
            mJointCapTexture->enable();
            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; t<numTerminii && !collision; t++ ) {
                Vector3D *newTerminus =
                    *( thisLayerLeafTerminii.getElement( t ) );
                
                // make sure not the same as existing
                char same = false;
                for( int e=0; e<numExisting && !same; e++ ) {
                    Vector3D *existing = *( mLeafTerminii.getElement( e ) );

                    if( existing->equals( 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; f<numFlowers && found; f++ ) {
                        if( *( mFlowerTerminusIndicies.getElement( f ) )
                            ==
                            foundIndex ) {
                            // collision with existing flower location
                            found = false;
                            }
                        }
                    for( f=0; f<numFruit && found; f++ ) {
                        if( *( mFruitTerminusIndices.getElement( f ) )
                            ==
                            foundIndex ) {
                            // collision with existing fruit location
                            found = false;
                            }
                        }
                    numTries++;
                    }

                if( found ) {
                    mFlowerTerminusIndicies.push_back( foundIndex );
                    mFlowerStages.push_back( 0 );
                    mFlowerAngles.push_back(
                        new Angle3D(
                            0, 0,
                            globalRandomSource.getRandomBoundedDouble(
                                0, 2 * M_PI ) ) );
                    }
            
                mTimeSinceLastFlower = 0;
                }

        
            // recount, since we may have added some
            numFlowers = mFlowerTerminusIndicies.size();
            
            for( int f=0; f<numFlowers; f++ ) {
                int terminusIndex =
                    *( mFlowerTerminusIndicies.getElement( f ) );
                
                Vector3D *terminus =
                    *( mLeafTerminii.getElement( terminusIndex ) );
            
                double zValue = terminus->mZ;
                
                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; f<numFruit; f++ ) {
            int terminusIndex =
                *( mFruitTerminusIndices.getElement( f ) );
                
            Vector3D *terminus =
                *( mLeafTerminii.getElement( terminusIndex ) );
            
            double zValue = terminus->mZ;
            
            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<numTerminii; t++ ) {
        delete *( thisLayerLeafTerminii.getElement( t ) );
        }
    }
Example #2
0
/**
 * Zeichnet einen Vektor als Pfeil.
 * @param pos Startpunkt des Pfeils.
 * @param dir Richtung und Länge des Pfeils.
 */
void drawVector(Vector3D* pos, Vector3D* dir) {

	//spitze des Vektors
	Vector3D tip = Vector3D(pos);
	//normalisierte Richtung des Vektors
	Vector3D ndir = Vector3D(dir);
	ndir.normalize();

	//Referenzvektor, darf dem Vektor nicht entsprechen
	Vector3D refdir = Vector3D(1, 0, 0);
	if (refdir.equals(&ndir)) {
		refdir.getXYZ()[0] = 0;
		refdir.getXYZ()[1] = 1;
	}

	//Radius der Vektorspitze
	float radius = .012 * dir->getLength();
	//Länge der Vektorspitze
	float length = .024 * dir->getLength();

	//Zwei Referenzvektoren, die zum Ausgansvektor und zueinander einen Winkel von 90° einschließen
	Vector3D* refvec = ndir.crossProduct(&refdir);
	Vector3D* refvec2 = ndir.crossProduct(refvec);
	refvec->normalize();
	refvec2->normalize();

	//zeichnen der Linie von Ausganspunkt bis Spitze des Pfeils
	tip.add(dir);
	Vector3D behind = Vector3D(pos);
	glLineWidth(2.0);
	glBegin(GL_LINES);
	glVertex3dv(behind.getXYZ());
	glVertex3dv(tip.getXYZ());
	glEnd();

	//Hilfsvektoren zum zusammentragen der Spitze des Vektors
	Vector3D v1 = Vector3D(refvec);
	Vector3D v2 = Vector3D(refvec2);
	Vector3D v3 = Vector3D(v1);
	v3.mult(-1);
	Vector3D v4 = Vector3D(refvec2);
	v4.mult(-1);

	//zeichen der Pfeilspitze
	v1.mult(radius);
	v2.mult(radius);
	v3.mult(radius);
	v4.mult(radius);
	ndir.mult(dir->getLength() - length);
	v1.add(&ndir);
	v2.add(&ndir);
	v3.add(&ndir);
	v4.add(&ndir);
	v1.add(pos);
	v2.add(pos);
	v3.add(pos);
	v4.add(pos);
	glBegin(GL_TRIANGLES);
	glVertex3dv(v1.getXYZ());
	glVertex3dv(v2.getXYZ());
	glVertex3dv(tip.getXYZ());

	glVertex3dv(v2.getXYZ());
	glVertex3dv(v3.getXYZ());
	glVertex3dv(tip.getXYZ());

	glVertex3dv(v3.getXYZ());
	glVertex3dv(v4.getXYZ());
	glVertex3dv(tip.getXYZ());

	glVertex3dv(v4.getXYZ());
	glVertex3dv(v1.getXYZ());
	glVertex3dv(tip.getXYZ());
	glEnd();
	glLineWidth(1.0);
	delete refvec;
	delete refvec2;
}
Example #3
0
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<Plant*> *plants = mWorld->getPlotPlants( mGardener );

    int numPlants = plants->size();

    SimpleVector<Seeds*> *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<Seeds*> *seedsVector =
                        mGardener->getAllSeeds();

                    // find best for this soil
                    Seeds *best = NULL;
                    double minSoilDistance = 2;
                    
                    for( int i=0; i<seedsVector->size(); 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; i<numPlants && ripePlant == NULL; i++ ) {
        Plant *thisPlant = *( plants->getElement( 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; i<numPlants; i++ ) {
        Plant *thisPlant = *( plants->getElement( 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;
    
    }