src/engine/eGameObject.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 *************************************************************************
00004 
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
00007 
00008 **************************************************************************
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023   
00024 ***************************************************************************
00025 
00026 */
00027 
00028 #include "eGameObject.h"
00029 #include "uInputQueue.h"
00030 #include "eTimer.h"
00031 #include "eTess2.h"
00032 #include "eWall.h"
00033 #include "tConsole.h"
00034 #include "rScreen.h"
00035 #include "rGL.h"
00036 
00037 #include "eSoundMixer.h"
00038 
00039 #include "eAdvWall.h"
00040 #include "eGrid.h"
00041 #include "uInput.h"
00042 #include "tMath.h"
00043 #include "nConfig.h"
00044 #include "eTeam.h"
00045 
00046 #include <map>
00047 
00048 uActionPlayer eGameObject::se_turnRight("CYCLE_TURN_RIGHT", -10);
00049 
00050 uActionPlayer eGameObject::se_turnLeft("CYCLE_TURN_LEFT", -10);
00051 
00052 
00053 // entry and deletion in the list of all gameObjects
00054 void eGameObject::AddToList(){
00055     if ( id < 0 )
00056         AddRef();
00057 
00058     grid->gameObjectsInactive.Remove(this,inactiveID);
00059     grid->gameObjects.Add(this,id);
00060 }
00061 void eGameObject::RemoveFromList(){
00062     int oldID = id;
00063 
00064     currentFace = 0;
00065 
00066     grid->gameObjects.Remove(this,id);
00067     grid->gameObjectsInactive.Add(this,inactiveID);
00068 
00069     if ( oldID >= 0 )
00070         Release();
00071 }
00072 
00073 void eGameObject::RemoveFromListsAll(){
00074     int oldID = id;
00075 
00076     currentFace = 0;
00077 
00078     grid->gameObjects.Remove(this,id);
00079     grid->gameObjectsInactive.Remove(this,inactiveID);
00080     grid->gameObjectsInteresting.Remove(this,interestingID);
00081 
00082     if ( oldID >= 0 )
00083         Release();
00084 
00085 }
00086 
00087 void eGameObject::RemoveFromGame()
00088 {
00089     tJUST_CONTROLLED_PTR< eGameObject > keepAlive;
00090     if ( id >= 0 )
00091         keepAlive = this;
00092 
00093     OnRemoveFromGame();
00094     DoRemoveFromGame();
00095 }
00096 
00097 
00098 // called on RemoveFromGame(). Call base class implementation, too, in your implementation.
00099 void eGameObject::OnRemoveFromGame()
00100 {
00101     // remove from grid
00102     currentFace = 0;
00103 
00104     // remove from lists
00105     RemoveFromListsAll();
00106 }
00107 
00108 
00109 // called on RemoveFromGame(). Do not call base class implementation of this function, don't expect to get called from subclasses.
00110 void eGameObject::DoRemoveFromGame()
00111 {
00112     // simply delete
00113     delete this;
00114 }
00115 
00116 
00117 eGameObject::eGameObject(eGrid *g,const eCoord &p,const eCoord &d,eFace *currentface,bool autodel)
00118         :autodelete(autodel),pos(p),dir(d),z(0),grid(g){
00119     tASSERT(g);
00120     currentFace=currentface;
00121     lastTime=se_GameTime();
00122     id=-1;
00123     interestingID=-1;
00124     inactiveID=-1;
00125     if ( lastTime < 0 )
00126         lastTime=0;
00127     team = 0;
00128 }
00129 
00130 eGameObject::~eGameObject(){
00131     currentFace = 0;
00132     RemoveFromListsAll();
00133     tCHECK_DEST;
00134 }
00135 
00136 // returns the type of this object (important for interaction of
00137 // two gameObjects)
00138 //gameobject_type gameobject::type(){return ArmageTron_GENERIC;}
00139 
00140 // makes two gameObjects interact:
00141 void eGameObject::InteractWith(eGameObject *,REAL,int){}
00142 
00143 // what happens if we pass eWall w?
00144 void eGameObject::PassEdge(const eWall *w,REAL,REAL,int){
00145     if (w) Kill();
00146 }
00147 
00148 static int se_moveTimeout = 100;
00149 static tSettingItem<int> se_moveTimeoutC("GAMEOBJECT_MOVE_TIMEOUT", se_moveTimeout);
00150 
00151 // data structures for storing temp wall collisions
00152 struct eTempEdgePassing
00153 {
00154     eWall *wall; 
00155     REAL ratio;  
00156 };
00157 typedef std::multimap< REAL, eTempEdgePassing > eTempEdgeMap;
00158 
00159 
00160 // moves
00161 void eGameObject::Move( const eCoord &dest, REAL startTime, REAL endTime, bool useTempWalls )
00162 {
00163 #ifdef DEBUG
00164     grid->Check();
00165 #endif
00166     if (!finite(dest.x) || !finite(dest.y))
00167     {
00168         st_Breakpoint();
00169         return;
00170     }
00171 
00172     tStackObject< ePoint > start(pos),stop(dest);
00173     ePoint* pstart = &start;
00174     ePoint* pstop = &stop;
00175 
00176     // clip movement to rim walls
00177     REAL clip = eWallRim::Clip(start,stop,-10);
00178     endTime = startTime + ( endTime - startTime ) * clip;
00179 
00180     grid->Range(stop.NormSquared());
00181 
00182 #ifdef DEBUG
00183     if (!finite(stop.x) || !finite(stop.y))
00184     {
00185         st_Breakpoint();
00186 
00187         static_cast<eCoord&>(stop) = dest;
00188         eWallRim::Bound(stop,-10);
00189 
00190         return;
00191     }
00192 #endif
00193 
00194     //  se_GridRange(dest.Norm_squared());
00195     eTempEdgeMap tempCollisions;
00196 
00197     tStackObject< eTempEdge >  te( pstart, pstop );
00198     eHalfEdge  &e=*te.Edge(0);
00199 
00200     // check all the currently drawn eWalls:
00201     if ( useTempWalls )
00202     {
00203         for(int i=grid->wallsNotYetInserted.Len()-1;i>=0;i--){
00204             const eHalfEdge *other_e=grid->wallsNotYetInserted[i]->Edge();
00205             if (
00206                 other_e->Point() && other_e->Other() && other_e->Other()->Point()){
00207                 tJUST_CONTROLLED_PTR< ePoint > new_cross_p=e.IntersectWith(other_e);
00208                 if (new_cross_p){
00209                     REAL e_ratio =e.Ratio(*new_cross_p);
00210                     REAL o_ratio =other_e->Ratio(*new_cross_p);
00211                     if (0<=e_ratio && 1>=e_ratio &&
00212                             0<=o_ratio && 1>=o_ratio)
00213                     { // find the fall
00214                         eWall *w = other_e->GetWall();
00215                         if (!w)
00216                         {
00217                             w = other_e->Other()->GetWall();
00218                             o_ratio = 1-o_ratio;
00219                         }
00220                         if (w)
00221                         {
00222                             // insert data into map structure for later processing
00223                             eTempEdgePassing passing;
00224                             passing.wall = w;
00225                             passing.ratio = o_ratio;
00226                             tempCollisions.insert( std::pair< REAL, eTempEdgePassing >( e_ratio, passing) );
00227                         }
00228                     }
00229                 }
00230             }
00231         }
00232     }
00233 
00234     // find a replacement face if required
00235     FindCurrentFace();
00236 
00237     // the total distance to travel
00238     REAL totalDistance = ( stop - pos ).Norm();
00239 
00240     if (currentFace){
00241         // start iterator for collisions with temporary walls
00242         eTempEdgeMap::const_iterator currentTempCollision = tempCollisions.begin();
00243 
00244         // we modify our position while we go; we need to compensate
00245         // all time calculations for that. This variable stores how much
00246         // of the way to the target position we're already gone.
00247         REAL goneRatio = 0;
00248 
00249         int timeout = se_moveTimeout;
00250 
00251         REAL lastDistance = 1E+30; // the distance of pos and stop in the last step
00252         eHalfEdge *in    = NULL;   // incoming edge to prevent entdless loop
00253 
00254         while (currentFace && timeout >0 && !currentFace->IsInside(stop)){
00255             // the vector to our destination:
00256             eCoord vec=stop - pos;
00257 
00258             // count down timeout if we're moving into the wrong direction
00259             REAL distance = vec.Norm();
00260             if ( distance >= lastDistance )
00261             {
00262                 timeout--;
00263             }
00264             else
00265             {
00266                 timeout = se_moveTimeout;
00267                 if ( lastDistance > 1E+29 )
00268                     lastDistance = distance * 1.1;
00269                 lastDistance = .1 * lastDistance + distance * (.9 - EPS);
00270 
00271                 // check if the target has been reached within tolerance; it can only make matters
00272                 // worse then to continue, even if the current face claims we're not part of it.
00273                 if ( distance <= EPS * totalDistance )
00274                 {
00275                     // st_Breakpoint();
00276                     break;
00277                 }
00278             }
00279 #ifdef DEBUG_X
00280 rerun:
00281 #endif
00282 
00283             eHalfEdge *run   = currentFace->Edge(); // runs through all edges of the face
00284             eHalfEdge *best  = NULL;                // the best face to leave
00285             eHalfEdge *end   = run;
00286             REAL bestScore   = -1000;
00287             REAL bestERatio  = .5;
00288             REAL bestRRatio  = .5;
00289             eCoord bestCross   (0,0);
00290 
00291             // look for the best way out
00292             do
00293             {
00294                 run = run->Next();
00295 
00296                 if (run == in) // never leave through the edge we entered
00297                     continue;
00298 
00299                 eCoord runVec = run->Vec();
00300 
00301                 REAL score = runVec * vec / ( se_EstimatedRangeOfMult( runVec, vec ) + EPS );
00302                 static const REAL smallBias = .01;
00303 
00304                 // keep a bit of the score, but not too much. We want to
00305                 // sort out exactly parallel walls here.
00306                 if ( score > smallBias || ( score > 0 && !run->GetWall() ) )
00307                     score = smallBias;
00308 
00309                 eCoord cross = e.IntersectWithCareless(run);
00310 
00311                 // project crossing to face edge without score penalty
00312                 REAL run_ratio = run->Ratio(cross);
00313                 if ( !good( run_ratio ) )
00314                 {
00315                     // score -= 100;
00316                     run_ratio = .5;
00317                 }
00318 
00319                 if (run_ratio < 0)
00320                 {
00321                     // score += run_ratio;
00322                     run_ratio = 0;
00323                 }
00324                 else if (run_ratio > 1)
00325                 {
00326                     // score += (1-run_ratio);
00327                     run_ratio = 1;
00328                 }
00329                 cross = *run->Point() + run->Vec() * run_ratio;
00330 
00331                 // determine how far off the movement edge the modified intersection lies
00332                 REAL e_side  = vec * ( cross - pos ) / distance;
00333                 score -= fabs( e_side );
00334                 // REAL run_side  = runVec * ( cross - *run->Point() ) / runVec.NormSquared();
00335 
00336                 REAL e_ratio   = e.Ratio(cross);
00337 
00338                 // see whether the intersection is beyond the end points of the movement vector
00339                 if ( !good( e_ratio ) )
00340                 {
00341                     score -= 100;
00342                     e_ratio = .5;
00343                 }
00344 
00345                 if (e_ratio < 0)
00346                 {
00347                     score += e_ratio;
00348                     e_ratio = 0;
00349                 }
00350                 else if (e_ratio > 1)
00351                 {
00352                     score += (1-e_ratio);
00353                     e_ratio = 1;
00354                 }
00355 
00356                 if (!best || score > bestScore)
00357                 {
00358                     best       = run;
00359                     bestScore  = score;
00360                     bestERatio = e_ratio;
00361                     bestRRatio = run_ratio;
00362                     bestCross  = cross;
00363                 }
00364 
00365             }
00366             while (run != end);
00367 
00368 #ifdef DEBUG_X
00369             if ( !good( bestScore ) || bestScore < -50 )
00370             {
00371                 st_Breakpoint();
00372                 goto rerun;
00373             }
00374 #endif
00375 
00376 #define TIME( ratio ) ( startTime+(endTime-startTime)*( ratio ) )
00377 
00378             if (best)
00379             {
00380                 // update the fraction of the full way we've gone so far
00381                 goneRatio = goneRatio + ( 1 - goneRatio ) * bestERatio;
00382 
00383                 // handle stored temp collisions
00384                 while ( currentTempCollision != tempCollisions.end() && (*currentTempCollision).first < goneRatio )
00385                 {
00386                     eTempEdgePassing const & passing = (*currentTempCollision).second;
00387                     PassEdge( passing.wall, TIME( (*currentTempCollision).first ), passing.ratio, 0 );
00388                     ++ currentTempCollision;
00389                 }
00390 
00391                 REAL time=TIME( bestERatio );
00392 
00393                 // move to the collision point
00394                 pos = bestCross;
00395 
00396                 // leave this face (through a wall)
00397                 eWall*     w     = best->GetWall();
00398                 if (w)
00399                     PassEdge(w,time,bestRRatio,0);
00400 
00401                 // set next incoming edge
00402                 tASSERT(best->Other());
00403                 in          = best->Other();
00404 
00405                 // enter the next face (through a wall)
00406                 if (in)
00407                 {
00408                     bestRRatio = 1-bestRRatio;
00409                     w = in->GetWall();
00410 
00411                     if (w)
00412                         PassEdge(w,time,bestRRatio,0);
00413                 }
00414 
00415                 // switch to the next face
00416                 if (in)
00417                     currentFace=in->Face();
00418                 else
00419                     currentFace=NULL;
00420             }
00421             else
00422             {
00423                 timeout = 0;
00424                 st_Breakpoint();
00425             }
00426         }
00427 
00428         if (timeout <= 0)
00429             grid->requestCleanup = true;
00430         else
00431             pos=stop;
00432 
00433         // handle stored temp collisions
00434         while ( currentTempCollision != tempCollisions.end() )
00435         {
00436             eTempEdgePassing const & passing = (*currentTempCollision).second;
00437             PassEdge( passing.wall, TIME( (*currentTempCollision).first ), passing.ratio, 0 );
00438             ++ currentTempCollision;
00439         }
00440     }
00441     else // !currentFace
00442     {
00443         // just move.
00444         pos = dest;
00445     }
00446 
00447     // not if the movement timed out
00448     // pos=stop;
00449 
00450     // find a replacement face if required
00451     FindCurrentFace();
00452 
00453     //#ifdef DEBUG
00454     //se_CheckGrid();
00455     //#endif
00456 
00457     //if (id<0)
00458     //    currentFace = NULL;
00459 
00460     lastTime = endTime;
00461 }
00462 
00463 // emulate old bug allowing objects to tunnel through walls
00464 static short se_bugTunnel = false;
00465 static nSettingItem<short> se_bugTunnelConfig("BUG_TUNNEL",
00466         se_bugTunnel );
00467 
00468 class eFaceFindFilter: public tConsoleFilter
00469 {
00470     virtual void DoFilterLine( tString& line )
00471     {
00472         line = tString( "FindCurrentFace() is running, so this message probably means there is a BUG: " ) + line;
00473     }
00474 };
00475 
00476 void eGameObject::FindCurrentFace(){
00477     // find a replacement for a removed face
00478     if ( currentFace && !currentFace->IsInGrid() )
00479     {
00480         if ( !se_bugTunnel )
00481         {
00482             currentFace = currentFace->FindReplacement( pos, Direction(), LastDirection() );
00483             if ( !currentFace && sn_GetNetState() != nCLIENT )
00484             {
00485                 static bool warn = true;
00486                 if (warn)
00487                 {
00488                     tERR_WARN("Possible phase bug!\n");
00489                 }
00490 
00491                 warn = false;
00492             }
00493         }
00494         else
00495         {
00496             // allow tunneling through walls
00497             currentFace = NULL;
00498         }
00499     }
00500 
00501     // don't fetch a new current face if you're out of the game
00502     if ( !currentFace && GOID() < 0 )
00503     {
00504 #ifdef DEBUG
00505         con << "Attempting to get a current face, but object is not in game.\n";
00506         st_Breakpoint();
00507         return;
00508 #endif        
00509     }
00510 
00511     // did that do the trick? If no, use brute force.
00512     if ( !currentFace )
00513         currentFace = grid->FindSurroundingFace(pos);
00514 
00515     if ( currentFace )
00516     {
00517         // check if the position lies inside the current triangle
00518         REAL insideness = currentFace->Insideness(pos);
00519         if ( insideness < 0 )
00520         {
00521             eFaceFindFilter filter;
00522             // if ( sn_GetNetState() != nCLIENT )
00523             //    con << "insideness = " << insideness << "\n";
00524 
00525             // no. Find the center of the current face.
00526             int i;
00527             eCoord center;
00528             eHalfEdge * run = currentFace->Edge();
00529             for ( i = 2; i >= 0; --i )
00530             {
00531                 run = run->Next();
00532                 center = center + ( *run->Point() - pos );
00533             }
00534             eCoord centerToPos = -center*(1/3.0);
00535             center = pos - centerToPos;
00536 
00537             static bool recurse = true;
00538             if ( recurse )
00539             {
00540                 class RecursionGuard
00541                 {
00542                 public:
00543                     RecursionGuard( bool& recursion )
00544                             :recursion_( recursion )
00545                     {
00546                         recursion = false;
00547                     }
00548 
00549                     ~RecursionGuard()
00550                     {
00551                         recursion_ = true;
00552                     }
00553 
00554                 private:
00555                     bool& recursion_;
00556                 };
00557 
00558                 RecursionGuard guard( recurse );
00559 
00560                 // warp to the known good position and move back to where the
00561                 // object should be
00562                 eCoord oldPos = pos;
00563                 pos = center;
00564 #ifdef DEBUG
00565                 eFace * lastFace = currentFace;
00566 #endif
00567                 try
00568                 {
00569                     Move( oldPos, lastTime, lastTime, false );
00570                 }
00571                 catch( eDeath & ) // ignore death exceptions and leave object where it would have died
00572                 {
00573 #ifdef DEBUG
00574                     // try again (yeah, this looks like a WTF, but it really helps in some cases because the situation has changed since the last try. /me blames floating points)
00575                     try
00576                     {
00577                         pos = center;
00578                         currentFace = lastFace;
00579                         Move( oldPos, lastTime, lastTime, false );
00580                     }
00581                     catch( eDeath & ){}
00582 #endif
00583                 }
00584 
00585                 recurse = true;
00586             }
00587             else
00588             {
00589                 // alternative if true movement is not possible:
00590                 // project current position into triangle
00591                 run = currentFace->Edge();
00592                 for ( i = 2; i >= 0; --i )
00593                 {
00594                     run = run->Next();
00595                     eCoord centerToPoint = *run->Point() - center;
00596                     eCoord runVec = run->Vec();
00597                     REAL prod = centerToPoint * runVec;
00598                     if (prod < 0)
00599                     {
00600                         REAL toClamp = (centerToPos * runVec) / prod;
00601                         if ( toClamp > 1 )
00602                         {
00603                             centerToPos = centerToPos * (1/toClamp);
00604                         }
00605                     }
00606                 }
00607                 pos = center + centerToPos;
00608             }
00609         }
00610     }
00611 }
00612 
00613 // simulates behaviour up to currentTime:
00614 bool eGameObject::Timestep(REAL t){
00615     lastTime = t;
00616     return 0;
00617 }
00618 // return value: shall this object be destroyed?
00619 
00620 void eGameObject::OnRoundBegin(){}
00621 void eGameObject::OnRoundEnd(){}
00622 
00623 void eGameObject::Kill(){}
00624 
00625 // draws it to the screen using OpenGL
00626 void eGameObject::Render(const eCamera *){}
00627 void eGameObject::Render2D(tCoord scale) const {}
00628 
00629 // *******************************************************************************
00630 // *
00631 // *    RendersAlpha
00632 // *
00633 // *******************************************************************************
00637 // *******************************************************************************
00638 bool eGameObject::RendersAlpha() const{return false;}
00639 
00640 // Cockpit
00641 bool eGameObject::RenderCockpitFixedBefore(bool){return true;}
00642 // return value: draw everything else?
00643 
00644 // the same purpose, but called after main rendering
00645 void eGameObject::RenderCockpitFixedAfter(bool){}
00646 // virtual perspective
00647 void eGameObject::RenderCockpitVirtual(bool){}
00648 
00649 
00650 #ifdef POWERPAK_DEB
00651 void eGameObject::PPDisplay(){
00652     PD_PutPixel(DoubleBuffer,
00653                 se_X_ToScreen(pos.x),
00654                 se_Y_ToScreen(pos.y),
00655                 PD_CreateColor(DoubleBuffer,255,0,100));
00656     PD_PutPixel(DoubleBuffer,
00657                 se_X_ToScreen(pos.x+1),
00658                 se_Y_ToScreen(pos.y),
00659                 PD_CreateColor(DoubleBuffer,255,0,100));
00660     PD_PutPixel(DoubleBuffer,
00661                 se_X_ToScreen(pos.x-1),
00662                 se_Y_ToScreen(pos.y),
00663                 PD_CreateColor(DoubleBuffer,255,0,100));
00664     PD_PutPixel(DoubleBuffer,
00665                 se_X_ToScreen(pos.x),
00666                 se_Y_ToScreen(pos.y+1),
00667                 PD_CreateColor(DoubleBuffer,255,0,100));
00668     PD_PutPixel(DoubleBuffer,
00669                 se_X_ToScreen(pos.x),
00670                 se_Y_ToScreen(pos.y-1),
00671                 PD_CreateColor(DoubleBuffer,255,0,100));
00672 
00673 }
00674 #endif
00675 
00676 // Receives control from player; nothing to do here
00677 bool eGameObject::Act(uActionPlayer *,REAL){return false;}
00678 
00679 
00680 bool eGameObject::TimestepThis(REAL currentTime,eGameObject *c){
00681 #ifdef DEBUG
00682     c->grid->Check();
00683 #endif
00684 
00685     tJUST_CONTROLLED_PTR< eGameObject > keep( c ); // keep object alive
00686 
00687     REAL maxstep=.2;
00688 
00689     // don't do a thing if the timestep is too small
00690     if (fabs(currentTime - c->lastTime) < .001)
00691         return false;
00692 
00693     // be more careful when going back
00694     if (currentTime<c->lastTime)
00695         maxstep=.1;
00696 
00697     int number_of_steps=int(fabs((currentTime-c->lastTime)/maxstep));
00698     if (number_of_steps<1)
00699         number_of_steps=1;
00700     if ( number_of_steps > 10 )
00701     {
00702         number_of_steps = 10;
00703     }
00704 
00705     REAL lastTime=c->lastTime;
00706 
00707     bool ret=false;
00708 
00709     for(int i=1;i<=number_of_steps;i++)
00710     {
00711         // make current face valid
00712         c->FindCurrentFace();
00713 
00714         if (sn_GetNetState()!=nCLIENT)
00715             for(int j=c->grid->gameObjectsInteresting.Len()-1;j>=0;j--)
00716                 c->InteractWith(c->grid->gameObjectsInteresting(j),currentTime,0);
00717 
00718         REAL timeThisStep = lastTime+i*(currentTime-lastTime)/number_of_steps;
00719         ret = ret || c->Timestep(timeThisStep);
00720         c->FindCurrentFace();
00721 
00722         // see if the object refused to get simulated, if yes, give up
00723         if ( 2 * c->lastTime < timeThisStep + lastTime )
00724             break;
00725     }
00726 #ifdef DEBUG
00727     c->grid->Check();
00728 #endif
00729 
00730     return ret;
00731 }
00732 
00733 #ifdef DEDICATED
00734 static REAL se_maxSimulateAhead = .01f;
00735 static tSettingItem<REAL> se_maxSimulateAheadConf( "MAX_SIMULATE_AHEAD", se_maxSimulateAhead );
00736 #endif
00737 
00738 // what is left of this time for the gameobject to use up
00739 static REAL se_maxSimulateAheadLeft = 0.0f;
00740 REAL eGameObject::MaxSimulateAhead()
00741 {
00742     return se_maxSimulateAheadLeft;
00743 }
00744 
00745 static REAL se_lazyLag = 0;
00747 REAL eGameObject::GetMaxLazyLag()
00748 {
00749     return se_lazyLag;
00750 }
00751 
00753 void eGameObject::SetMaxLazyLag( REAL lag )
00754 {
00755     se_lazyLag = lag;
00756 }
00757 
00758 void eGameObject::TimestepThisWrapper(eGrid * grid, REAL currentTime, eGameObject *c, REAL minTimestep )
00759 {
00760     su_FetchAndStoreSDLInput();
00761 
00762     REAL simTime=currentTime;
00763     // backdate the object a bit
00764 #ifndef DEDICATED
00765     if (sn_GetNetState()==nCLIENT && !sr_predictObjects)
00766 #endif
00767         simTime -= c->Lag();
00768 
00769 #ifdef DEDICATED
00770     REAL nextTime = c->NextInterestingTime();
00771 
00772     // store the time left to simulate
00773     se_maxSimulateAheadLeft = simTime + se_maxSimulateAhead - nextTime;
00774     if ( se_maxSimulateAheadLeft < 0 )
00775         se_maxSimulateAheadLeft = 0;
00776 
00777     REAL lagThreshold = c->LagThreshold();
00778     if ( simTime - lagThreshold < nextTime && nextTime < simTime + se_maxSimulateAhead )
00779     {
00780         // something interesting is going to happen, see what it is
00781         simTime = nextTime;
00782     }
00783     else
00784     {
00785         // add an extra portion of lag compensation
00786         simTime -= lagThreshold;
00787 
00788         if ( simTime < c->LastTime() + minTimestep )
00789         {
00790             // don't waste your time on too small timesteps
00791             return;
00792         }
00793     }
00794 #endif
00795 
00796     // check for teleports out of arena bounds
00797     if (!eWallRim::IsBound(c->pos,-20))
00798     {
00799         se_maxSimulateAheadLeft = 0;
00800 
00801         c->Kill();
00802         return;
00803     }
00804 
00805     // only simulate forward here
00806     if ( simTime > c->lastTime )
00807     {
00808         if (TimestepThis(simTime,c))
00809         {
00810             if (c->autodelete)
00811                 c->RemoveFromGame();
00812             else
00813             {
00814                 c->currentFace=NULL;
00815                 c->RemoveFromList();
00816             }
00817         }
00818     }
00819 
00820     se_maxSimulateAheadLeft = 0.0;
00821 }
00822 
00823 // does a timestep and all interactions for every eGameObject
00824 void eGameObject::s_Timestep(eGrid *grid, REAL currentTime, REAL minTimestep)
00825 {
00826 #ifdef DEBUG
00827     grid->Check();
00828 #endif
00829 
00830     // simulate game objects
00831     for(int i=grid->gameObjects.Len()-1;i>=0;i--)
00832     {
00833         eGameObject * c = grid->gameObjects(i);
00834         TimestepThisWrapper( grid, currentTime, c, minTimestep );
00835     }
00836 
00837 #ifdef DEBUG
00838     grid->Check();
00839 #endif
00840 }
00841 
00842 #ifdef DEBUG
00843 eGameObject *displayed_gameobject = 0;
00844 #endif
00845 
00846 void eGameObject::RenderAll(eGrid *grid, const eCamera *cam){
00847     //if (!sr_glOut)
00848     //    return;
00849     
00850     // first, we need to render all the non-alpha blended objects.
00851     // if we encounter non-alpha blended objects after alpha blended objects
00852     // have already been rendered, we need to re-sort them to the back.
00853     eGameObject * firstAlpha = NULL;
00854     for(int i=grid->gameObjects.Len()-1;i>=0;i--){
00855         su_FetchAndStoreSDLInput();
00856         if (sr_glOut){
00857             eGameObject * object = grid->gameObjects(i);
00858 #ifdef DEBUG
00859             displayed_gameobject = object;
00860 #endif
00861 
00862             sr_CheckGLError();
00863 
00864             object->Render(cam);
00865 
00866             bool thisAlpha = object->RendersAlpha();
00867             if ( !thisAlpha && firstAlpha )
00868             {
00869                 // resort the object, switch places with the first alpha blended one.
00870                 // This will only have an effect in the next frame,
00871                 // but the small flickering error is to be tolerated, especially
00872                 // since alpha blended game objects tend to gently fade in.
00873                 int firstAlphaID = firstAlpha->id;
00874                 grid->gameObjects.Remove(firstAlpha,firstAlpha->id);
00875                 grid->gameObjects.Add(firstAlpha,firstAlpha->id);
00876                 grid->gameObjects.Remove(object,object->id);
00877                 grid->gameObjects.Add(object,object->id);
00878                 
00879                 // the first alpha blended object no longer is the first. Look for
00880                 // a replacement, only one object is a candidate.
00881                 firstAlpha = 0;
00882                 if ( firstAlphaID > 0 )
00883                 {   
00884                     firstAlpha = grid->gameObjects(firstAlphaID - 1);
00885                     tASSERT( firstAlpha->RendersAlpha() );
00886                 }
00887             }
00888             if ( thisAlpha && !firstAlpha )
00889             {
00890                 // store first known alpha blending object
00891                 firstAlpha = object;
00892             }
00893             sr_CheckGLError();
00894 
00895 #ifdef DEBUG
00896             displayed_gameobject = 0;
00897 #endif
00898         }
00899     }
00900 }
00901 
00902 #ifdef POWERPAK_DEB
00903 void eGameObject::PPDisplayAll(){
00904     for(int i=gameObjects.Len()-1;i>=0;i--){
00905         if (pp_out) gameObjects(i)->PPDisplay();
00906     }
00907 }
00908 #endif
00909 
00910 
00911 void eGameObject::DeleteAll(eGrid *grid){
00912     int i;
00913     for(i=grid->gameObjects.Len()-1;i>=0;i--)
00914     {
00915         eGameObject* o = grid->gameObjects(i);
00916         o->RemoveFromGame();
00917 #ifdef POWERPAK_DEB
00918         if (pp_out) o->PPDisplay();
00919 #endif
00920     }
00921 }
00922 
00923 eReferencableGameObject::eReferencableGameObject(eGrid *grid, const eCoord &p,const eCoord &d, eFace *currentface, bool autodelete)
00924 : eGameObject( grid, p, d, currentface, autodelete )
00925 {
00926 }
00927 
00928 // delegate real reference counting
00929 void eReferencableGameObject::AddRef()
00930 {
00931     tReferencable< eReferencableGameObject >::AddRef();
00932 }
00933 
00934 void eReferencableGameObject::Release()
00935 {
00936     tReferencable< eReferencableGameObject >::Release();
00937 }
00938 
00939 void eReferencableGameObject::DoRemoveFromGame()
00940 {
00941     // nothing needs to be done, the reference counting takes care of the destruction
00942 }
00943 
00944 eStackGameObject::eStackGameObject(eGrid *grid, const eCoord &p,const eCoord &d, eFace *currentface)
00945 : eGameObject( grid, p, d, currentface, false )
00946 {
00947 }
00948 
00949 void eStackGameObject::AddRef()
00950 {
00951 }
00952 
00953 void eStackGameObject::Release()
00954 {
00955 }
00956 
00957 void eStackGameObject::DoRemoveFromGame()
00958 {
00959     // must not get called
00960     tERR_ERROR("Stack game object removed from game.");
00961 }
00962 
00963 

Generated on Sat Mar 15 22:55:44 2008 for Armagetron Advanced by  doxygen 1.5.4