D:/simple_rts/src/Unit.cpp

Go to the documentation of this file.
00001 #include "Unit.h"
00002 
00003 #include "Game.h"
00004 #include "Timer.h"
00005 #include "Projectile.h"
00006 
00007 #include "OgreMath.h"
00008 #include "OgreStringConverter.h"
00009 #include "OgreException.h"
00010 #include "OgreAnimationState.h"
00011 #include "OgreAnimation.h"
00012 #include "OgreMaterialManager.h"
00013 #include "OgreMaterial.h"
00014 #include "OgreSubEntity.h"
00015 #include "OgreMovableObject.h"
00016 
00017 using Ogre::MovableObject;
00018 using Ogre::AnimationState;
00019 using Ogre::AnimationStateSet;
00020 using Ogre::AnimationStateIterator;
00021 using Ogre::MaterialManager;
00022 using Ogre::Material;
00023 using Ogre::SubEntity;
00024 using Ogre::MaterialPtr;
00025 using Ogre::ColourValue;
00026 using Ogre::Exception;
00027 using Ogre::StringConverter;
00028 using Ogre::Degree;
00029 using Ogre::Math;
00030 using Ogre::Radian;
00031 
00032 
00033 namespace ASR
00034 {
00035         int Unit::mNumUnits = 0;
00036         const float Unit::mSearchUpdateTime = 1.0f;
00037         String Unit::TYPE_NAME = "Unit";
00038 
00039         // ----------------------------------------------------------------------------
00040         Unit::Unit( SceneNode* parentNode, String meshName )
00041                 : mMeshName ( meshName )
00042         {
00043                 mNumUnits++;
00044 
00045                 SceneManager* sceneMgr = Game::getSingletonPtr ()->getSceneManager ();
00046 
00047                 mUnitName = "unit_" + StringConverter::toString ( mNumUnits );
00048                 mEntity = sceneMgr->createEntity ( mUnitName, mMeshName);
00049                 mEntity->setUserObject( this );
00050                 mEntity->setCastShadows ( true );
00051 
00052                 mUnitNode = parentNode->createChildSceneNode( mUnitName + "_node" );
00053                 mUnitNode->attachObject( mEntity );
00054                 mUnitNode->setFixedYawAxis ( true );
00055 
00056                 mNavDirty = false;
00057 
00058                 mAnimationState = getEntity()->getAnimationState ( "Idle" );
00059                 //getEntity ()->getAnimationState ("Die")->setLoop ( false );
00060                 mStatus = US_DEAD;
00061 
00062                 assignTarget ( NULL );
00063                 setStatus ( US_IDLE );
00064 
00065                 mSearch = NULL;
00066                 mSearchElapsedTime = 0.0f;
00067 
00068                 // HACKS
00069                 mFireRate = 1.0f;
00070                 mRange = 80.0f;
00071                 mSightRadius = 200.0f;
00072                 mMaxHitPoints = 100;
00073                 mCurHitPoints = mMaxHitPoints;
00074 
00075 
00076                 // HACKS
00077                 String shieldName = mUnitName + "_shield";
00078                 mShield = sceneMgr->createEntity( shieldName, "lizardmanShield1.mesh" );
00079                 SceneNode *shield_Node = sceneMgr->getRootSceneNode()->createChildSceneNode( shieldName + "_Node" );
00080                 shield_Node->yaw(Radian(Degree(90)));
00081                 shield_Node->pitch(Radian(Degree(-90)));
00082 
00083                 String shoulderName = mUnitName + "_shoulder";
00084                 mShoulder = sceneMgr->createEntity( shoulderName, "lizardmanShoulder1.mesh" );
00085                 SceneNode *shoulder_Node = sceneMgr->getRootSceneNode()->createChildSceneNode( shoulderName + "_Node" );
00086                 shoulder_Node->roll(Radian(Degree(10)));
00087 
00088                 String swordName = mUnitName + "_sword";
00089                 mWeapon = sceneMgr->createEntity( swordName, "lizardmanWeapon1.mesh" );
00090                 SceneNode *sword_Node = sceneMgr->getRootSceneNode()->createChildSceneNode( swordName + "_Node" );
00091                 sword_Node->pitch(Radian(Degree(90)));
00092                 sword_Node->roll(Radian(Degree(95)));
00093 
00094                 mEntity->attachObjectToBone ("LeftShoulder", mShoulder, shoulder_Node->getOrientation(), Vector3(4.7, 0.5, 0.5));
00095                 mEntity->attachObjectToBone ("LeftForearm", mShield, shield_Node->getOrientation(), Vector3(3.5, 3.0, 0.2));
00096                 mEntity->attachObjectToBone ("RightHand", mWeapon, sword_Node->getOrientation(), Vector3(2.0, 0.0, 0.9));
00097         }
00098 
00099 
00100         // ----------------------------------------------------------------------------
00101         Unit::~Unit(void)
00102         {
00103                 _destroyAttacks ();
00104                 _destroySearch ();
00105         }
00106 
00107 
00108         // ----------------------------------------------------------------------------
00109         float Unit::getSightRadius () const
00110         {
00111                 return mSightRadius;
00112         }
00113 
00114 
00115         // ----------------------------------------------------------------------------
00116         void Unit::clearWaypoints()
00117         {
00118                 mNavDirty = true;
00119                 mWaypoints.clear ();
00120         }
00121 
00122 
00123         // ----------------------------------------------------------------------------
00124         void Unit::addWaypoint( const Vector3& waypoint )
00125         {
00126                 setStatus ( US_MOVING );
00127                 assignTarget ( NULL );
00128 
00129                 mNavDirty = true;
00130                 mWaypoints.push_back ( waypoint );
00131 
00132                 if ( mWaypoints.size() == 1 )
00133                         _alignToWaypoint ();
00134         }
00135 
00136 
00137         // ----------------------------------------------------------------------------
00138         void Unit::removeDestination()
00139         {
00140                 assert ( !mWaypoints.empty() );
00141                 mWaypoints.pop_front ();
00142 
00143                 // Align ourselves to the next destination, if we have one
00144                 if ( hasDestination () )
00145                 {
00146                         _alignToWaypoint ();
00147                 }
00148                 else
00149                 {
00150                         stopMovement ();
00151                 }
00152         }
00153 
00154 
00155         // ----------------------------------------------------------------------------
00156         Vector3 Unit::getDestination()
00157         {
00158                 assert ( !mWaypoints.empty() );
00159                 return mWaypoints.front ();
00160         }
00161 
00162 
00163         // ----------------------------------------------------------------------------
00164         bool Unit::hasDestination()
00165         {
00166                 return !mWaypoints.empty ();
00167         }
00168 
00169 
00170         // ----------------------------------------------------------------------------
00171         Entity* Unit::getEntity ()
00172         {
00173                 return mEntity;
00174         }
00175 
00176 
00177         // ----------------------------------------------------------------------------
00178         const String& Unit::getTypeName () const
00179         {
00180                 return Unit::TYPE_NAME;
00181         }
00182 
00183 
00184         // ----------------------------------------------------------------------------
00185         void Unit::scale ( Real x, Real y, Real z )
00186         {
00187                 mUnitNode->scale ( x, y, z );
00188         }
00189 
00190 
00191         // ----------------------------------------------------------------------------
00192         void Unit::translate ( const Vector3& vec, Node::TransformSpace space )
00193         {
00194                 mUnitNode->translate ( vec, space );
00195         }
00196 
00197 
00198         // ----------------------------------------------------------------------------
00199         Vector3 Unit::getPosition () const
00200         {
00201                 return mUnitNode->getWorldPosition ();
00202         }
00203 
00204 
00205         // ----------------------------------------------------------------------------
00206         void Unit::setPosition ( const Vector3& pos )
00207         {
00208                 mUnitNode->setPosition ( pos );
00209         }
00210 
00211 
00212         // ----------------------------------------------------------------------------
00213         void Unit::setDirection ( const Vector3& dir )
00214         {
00215                 mUnitNode->setDirection ( dir );
00216         }
00217 
00218 
00219         // ----------------------------------------------------------------------------
00220         void Unit::setDirection ( float x, float y, float z )
00221         {
00222                 mUnitNode->setDirection( x, y, z );
00223         }
00224 
00225 
00226         // ----------------------------------------------------------------------------
00227         void Unit::_alignToWaypoint ()
00228         {
00229                 // Align to the next waypoint
00230                 Vector3 dest = getDestination();
00231                 dest.y = getPosition ().y;
00232 
00233                 Vector3 direction = dest - getPosition ();
00234 
00235                 // If we are close enough to our destination, move on
00236                 if ( direction.isZeroLength () )
00237                 {
00238                         return;
00239                 }
00240 
00241                 getEntity()->getParentSceneNode ()->lookAt ( dest, SceneNode::TS_WORLD, Vector3::UNIT_Z );
00242         }
00243 
00244 
00245         // ----------------------------------------------------------------------------
00246         Unit::WaypointIterator Unit::getWaypointIterator ()
00247         {
00248                 return WaypointIterator ( mWaypoints.begin(), mWaypoints.end() );
00249         }
00250 
00251 
00252         // ----------------------------------------------------------------------------
00253         void Unit::assignTarget ( Unit* target )
00254         {
00255                 // Can't target a dead unit
00256                 if ( target != NULL && target->isDead() )
00257                         throw Exception ( Exception::ERR_INTERNAL_ERROR, "Targetting a dead unit", "Unit::assignTarget" );
00258 
00259                 // We can only assign a target when we stop moving
00260                 if ( mStatus == US_MOVING )
00261                         return;
00262 
00263                 clearWaypoints ();
00264 
00265                 mTarget = target;
00266                 mAttackTime = 0.0f;
00267 
00268                 if ( target == NULL )
00269                 {
00270                         setStatus ( US_IDLE );
00271                 }
00272                 else
00273                 {
00274                         // Determine if we need to chase or if we can engage from our current position
00275                         Vector3 distanceToTarget = target->getPosition() - getPosition();
00276 
00277                         // TODO
00278                         //              Use squared length here once we change how mRange is stored
00279                         if ( distanceToTarget.length () > getRange() )
00280                         {
00281                                 _chaseTarget ();
00282                         }
00283                         else
00284                         {
00285                                 setStatus ( US_ATTACKING );
00286                                 _alignToTarget ();
00287                         }
00288                 }
00289         }
00290 
00291 
00292         // ----------------------------------------------------------------------------
00293         bool Unit::isEngaged () const
00294         {
00295                 return (mStatus == US_ATTACKING || mStatus == US_CHASING);
00296         }
00297 
00298 
00299         // ----------------------------------------------------------------------------
00300         Unit* Unit::getTarget () const
00301         {
00302                 return mTarget;
00303         }
00304 
00305 
00306         // ----------------------------------------------------------------------------
00307         void Unit::doAttack ()
00308         {
00309                 _updateProjectiles ();
00310 
00311                 if ( isDead () )
00312                 {
00313                         clearWaypoints ();
00314                         setStatus ( US_DYING );
00315 
00316                         // HACK
00317                         hide ();
00318                         return;
00319                 }
00320 
00321                 if ( mTarget == NULL || !isEngaged() )
00322                         return;
00323 
00324                 float targetDist = _getDistanceToTarget ();
00325                 
00326                 //  The target moved away from us
00327                 if ( targetDist > getSightRadius() )
00328                 {
00329                         assignTarget ( NULL );
00330                         _destroyAttacks ();
00331                         return;
00332                 }
00333 
00334 
00335                 if ( mStatus == US_CHASING )
00336                 {
00337                         if (  targetDist <= getRange() )
00338                         {
00339                                 // Change into the attacking phase of combat
00340                                 clearWaypoints ();
00341                                 setStatus ( US_ATTACKING );
00342                         }
00343                         else
00344                         {
00345                                 mSearchElapsedTime += Timer::getSingleton ().getTimeSinceLastFrame ();
00346                                 if ( mSearchElapsedTime >= mSearchUpdateTime )
00347                                 {
00348                                         mSearchElapsedTime -= mSearchUpdateTime;
00349                                         // TODO
00350                                         //              Cache positions since we may not have to re-path
00351                                         
00352                                         // Re-path to the target
00353                                         setDestination ( mTarget->getPosition() );
00354                                         setStatus ( US_CHASING );
00355                                 }
00356                         }
00357                 }
00358                 else if ( mStatus == US_ATTACKING )
00359                 {
00360                         // We need to chase the target (we allow some lee-way)
00361                         if ( targetDist > (getRange () + 10.0f) )
00362                         {
00363                                 _chaseTarget ();
00364                                 return;
00365                         }
00366 
00367 
00368                         // Make sure we are always facing the target
00369                         _alignToTarget ();
00370 
00371                         mAttackTime += Timer::getSingleton ().getTimeSinceLastFrame();
00372 
00373                         if ( mAttackTime > _getTimeBetweenAttacks() )
00374                         {
00375                                 _spawnAttack ();
00376 
00377                                 // Check if the target is dead here so that come next frame, we
00378                                 // will acquire a new target to fight and we won't be idle for 
00379                                 // any frames
00380                                 if ( mTarget->isDead() )
00381                                 {
00382                                         assignTarget ( NULL );
00383                                 }
00384                         }
00385                 }
00386         }
00387 
00388 
00389         // ----------------------------------------------------------------------------
00390         float Unit::_getTimeBetweenAttacks ()
00391         {
00392                 return mFireRate;
00393         }
00394 
00395 
00396         // ----------------------------------------------------------------------------
00397         void Unit::_spawnAttack ()
00398         {
00399                 // HACK
00400                 float projectileSpeed = 150.0f;
00401 
00402                 Projectile* newBullet = new Projectile ( this, mTarget );
00403                 newBullet->setDamage ( _calcDamage(mTarget) );
00404                 newBullet->setSpeed ( projectileSpeed );
00405 
00406                 mProjectiles.push_back ( newBullet );
00407 
00408                 mAttackTime = 0.0f;
00409         }
00410 
00411 
00412         // ----------------------------------------------------------------------------
00413         void Unit::_updateProjectiles ()
00414         {
00415                 vector<Projectile*>::iterator iter = mProjectiles.begin();
00416                 vector<Projectile*> toRemove;
00417 
00418                 while ( iter != mProjectiles.end() )
00419                 {
00420                         if ( (*iter)->update () )
00421                                 toRemove.push_back ( (*iter) );
00422 
00423                         iter++;
00424                 }
00425 
00426                 iter = toRemove.begin();
00427                 while ( iter != toRemove.end() )
00428                 {
00429                         vector<Projectile*>::iterator projIter = find ( mProjectiles.begin(), mProjectiles.end(), (*iter) );
00430                         mProjectiles.erase ( projIter );
00431 
00432                         delete (*iter);
00433                         iter++;
00434                 }
00435         }
00436 
00437 
00438         // ----------------------------------------------------------------------------
00439         float Unit::_getDistanceToTarget ()
00440         {
00441                 OgreAssert ( mTarget != NULL, "No target to get distance to" );
00442                 Vector3 targPos = mTarget->getPosition();
00443                 targPos.y = 0.0f;
00444 
00445                 Vector3 ourPos = getPosition();
00446                 ourPos.y = 0.0f;
00447 
00448                 return ( targPos - ourPos ).length();
00449         }
00450 
00451 
00452         // ----------------------------------------------------------------------------
00453         void Unit::_destroyAttacks ()
00454         {
00455                 BulletList::iterator iter = mProjectiles.begin();
00456                 while ( iter != mProjectiles.end() )
00457                 {
00458                         delete (*iter);
00459                         iter++;
00460                 }
00461 
00462                 mProjectiles.clear ();
00463         }
00464 
00465 
00466         // ----------------------------------------------------------------------------
00467         void Unit::stopMovement ()
00468         {
00469                 setStatus ( US_IDLE );
00470                 mWaypoints.clear ();
00471 
00472                 assignTarget ( NULL );
00473         }
00474 
00475 
00476         // ----------------------------------------------------------------------------
00477         void Unit::setStatus ( UnitStatus status )
00478         {
00479                 if ( mStatus == status )
00480                         return;
00481 
00482                 mStatus = status;
00483 
00484                 mAnimationState->setEnabled ( false );
00485 
00486                 switch ( mStatus )
00487                 {
00488                 case US_MOVING:
00489                 case US_CHASING:
00490                         mAnimationState = getEntity()->getAnimationState( "Run" );
00491                         break;
00492 
00493                 case US_IDLE:
00494                 case US_ATTACKING:
00495                 case US_DEAD:
00496                 case US_DYING:
00497                 default:
00498                         mAnimationState = getEntity()->getAnimationState( "Idle" );
00499                         break;
00500                 }
00501 
00502                 mAnimationState->setEnabled( true );
00503                 mAnimationState->setTimePosition ( 0.0 );
00504         }
00505 
00506 
00507         // ----------------------------------------------------------------------------
00508         void Unit::updateAnimation (float deltaTime )
00509         {
00510                 mAnimationState->addTime ( deltaTime );
00511         }
00512 
00513 
00514         // ----------------------------------------------------------------------------
00515         void Unit::_alignToTarget ()
00516         {
00517                 Vector3 direction = mTarget->getPosition () - getPosition();
00518                 if ( direction.isZeroLength () )
00519                         return;
00520 
00521                 direction.normalise ();
00522 
00523                 Vector3 target = mTarget->getPosition ();
00524                 target.y = getPosition().y;
00525 
00526                 mUnitNode->lookAt ( target, SceneNode::TS_WORLD, Vector3::UNIT_Z );
00527         }
00528 
00529 
00530         // ----------------------------------------------------------------------------
00531         void Unit::assignDamage ( int damage )
00532         {
00533                 mCurHitPoints -= damage;
00534         }
00535 
00536 
00537         // ----------------------------------------------------------------------------
00538         bool Unit::isDead () const
00539         {
00540                 return mCurHitPoints <= 0;
00541         }
00542 
00543 
00544         // ----------------------------------------------------------------------------
00545         float Unit::_calcDamage ( const Unit* target ) const
00546         {
00547                 // HACK
00548                 return 5;
00549         }
00550 
00551 
00552         // ----------------------------------------------------------------------------
00553         String Unit::getUnitName() const
00554         {
00555                 return mUnitName;
00556         }
00557 
00558 
00559         // ----------------------------------------------------------------------------
00560         bool Unit::setDestination ( const Vector3& destination )
00561         {
00562                 AStarSearch<Level::LevelNode>* search = NULL;
00563                 Level* lev = Level::getSingletonPtr ();
00564 
00565                 _destroySearch ();
00566 
00567                 // Create the leader path
00568                 Level::LevelNode* startNode = lev->getNode ( getPosition () );
00569                 Level::LevelNode* endNode = lev->getNode ( destination );
00570 
00571                 // Create a new path for the unit
00572                 mSearch = Level::getSingleton().getPathManager()->createSearch ( getEntity ()->getName(), startNode, endNode );
00573                 while ( mSearch->isRunnable() )
00574                 {
00575                         mSearch->advanceSearch();
00576                 }
00577 
00578                 if ( mSearch->isSolved() )
00579                 {
00580                         _assignWaypoints ();
00581                         return true;
00582                 }
00583 
00584                 return false;
00585         }
00586 
00587 
00588         // ----------------------------------------------------------------------------
00589         void Unit::_assignWaypoints ()
00590         {
00591                 OgreAssert ( mSearch, "There is no search defined" );
00592                 if ( !mSearch->isSolved() )
00593                         throw Ogre::Exception ( Exception::ERR_INTERNAL_ERROR, "Copying from an unsolved search", "Unit::_assignWaypoints" );
00594 
00595                 clearWaypoints();
00596 
00597                 const WaypointList<Level::LevelNode>* waypoints = mSearch->getSolutionPath();
00598 
00599                 // Build a waypoint list starting at the second waypoint instead of moving
00600                 // to the center of the node first
00601                 if ( waypoints->getNumWaypoints () > 1 )
00602                 {
00603                         // Go up to but not including the last waypoint
00604                         for ( unsigned int i = 1; i < waypoints->getNumWaypoints() - 1; i++ )
00605                         {
00606                                 addWaypoint( waypoints->getWaypoint(i)->getWorldPosition() );
00607                         }
00608                         // Add the mouse click position to the list
00609                 }
00610                 // Otherwise, just move to the waypoint inside of the same cell
00611                 // TODO
00612                 //              Make sure that the waypoint becomes the point of clicking and not
00613                 //              the center of the cell
00614                 else if ( waypoints->getNumWaypoints () == 1 )
00615                 {
00616                         addWaypoint ( waypoints->getWaypoint (0)->getWorldPosition () );
00617                 }
00618         }
00619 
00620 
00621         // ----------------------------------------------------------------------------
00622         void Unit::_destroySearch ()
00623         {
00624                 if ( mSearch )
00625                 {
00626                         Level::getSingleton().getPathManager()->destroySearch( getEntity ()->getName() );
00627                         mSearch = NULL;
00628                 }
00629         }
00630 
00631 
00632         // ----------------------------------------------------------------------------
00633         float Unit::getRange() const
00634         {
00635                 return mRange;
00636         }
00637 
00638 
00639         // ----------------------------------------------------------------------------
00640         void Unit::_chaseTarget ()
00641         {
00642                 OgreAssert ( mTarget, "Unit has no target to chase" );
00643 
00644                 mSearchElapsedTime = 0.0f;
00645                 setDestination ( mTarget->getPosition() );
00646                 setStatus ( US_CHASING );
00647         }
00648 
00649 
00650         // ----------------------------------------------------------------------------
00651         void Unit::hide ()
00652         {
00653                 getEntity ()->getParentSceneNode ()->setVisible ( false );
00654                 Entity::ChildObjectListIterator iter = getEntity ()->getAttachedObjectIterator ();
00655                 while ( iter.hasMoreElements () )
00656                 {
00657                         MovableObject* obj = iter.getNext ();
00658                         obj->setVisible ( false );
00659                 }
00660         }
00661 
00662 
00663         // ----------------------------------------------------------------------------
00664         void Unit::show ()
00665         {
00666                 getEntity ()->getParentSceneNode ()->setVisible ( true );
00667                 Entity::ChildObjectListIterator iter = getEntity ()->getAttachedObjectIterator ();
00668                 while ( iter.hasMoreElements () )
00669                 {
00670                         MovableObject* obj = iter.getNext ();
00671                         obj->setVisible ( true );
00672                 }
00673         }
00674 }

Generated on Sun Jun 25 19:23:43 2006 for Valors End by  doxygen 1.4.7