1 /*
2 
3 *************************************************************************
4 
5 ArmageTron -- Just another Tron Lightcycle Game in 3D.
6 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
7 
8 **************************************************************************
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 ***************************************************************************
25 
26 */
27 
28 #include "eGameObject.h"
29 #include "uInputQueue.h"
30 #include "eTimer.h"
31 #include "eTess2.h"
32 #include "eWall.h"
33 #include "tConsole.h"
34 #include "rScreen.h"
35 #include "eSound.h"
36 #include "eAdvWall.h"
37 #include "eGrid.h"
38 #include "uInput.h"
39 #include "tMath.h"
40 #include "nConfig.h"
41 #include "eTeam.h"
42 
43 #include <map>
44 
45 uActionPlayer eGameObject::se_turnRight("CYCLE_TURN_RIGHT", -10);
46 
47 uActionPlayer eGameObject::se_turnLeft("CYCLE_TURN_LEFT", -10);
48 
49 
50 // entry and deletion in the list of all gameObjects
AddToList()51 void eGameObject::AddToList(){
52     eSoundLocker locker;
53 
54     if ( id < 0 )
55         AddRef();
56 
57     grid->gameObjectsInactive.Remove(this,inactiveID);
58     grid->gameObjects.Add(this,id);
59 }
RemoveFromList()60 void eGameObject::RemoveFromList(){
61     eSoundLocker locker;
62 
63     int oldID = id;
64 
65     currentFace = 0;
66 
67     grid->gameObjects.Remove(this,id);
68     grid->gameObjectsInactive.Add(this,inactiveID);
69 
70     if ( oldID >= 0 )
71         Release();
72 }
73 
RemoveFromListsAll()74 void eGameObject::RemoveFromListsAll(){
75     eSoundLocker locker;
76 
77     int oldID = id;
78 
79     currentFace = 0;
80 
81     grid->gameObjects.Remove(this,id);
82     grid->gameObjectsInactive.Remove(this,inactiveID);
83     grid->gameObjectsInteresting.Remove(this,interestingID);
84 
85     if ( oldID >= 0 )
86         Release();
87 }
88 
RemoveFromGame()89 void eGameObject::RemoveFromGame()
90 {
91     tJUST_CONTROLLED_PTR< eGameObject > keepAlive;
92     if ( id >= 0 )
93         keepAlive = this;
94 
95     OnRemoveFromGame();
96     DoRemoveFromGame();
97 }
98 
99 
100 // called on RemoveFromGame(). Call base class implementation, too, in your implementation.
OnRemoveFromGame()101 void eGameObject::OnRemoveFromGame()
102 {
103     // remove from grid
104     currentFace = 0;
105 
106     // remove from lists
107     RemoveFromListsAll();
108 }
109 
110 
111 // called on RemoveFromGame(). Do not call base class implementation of this function, don't expect to get called from subclasses.
DoRemoveFromGame()112 void eGameObject::DoRemoveFromGame()
113 {
114     // simply delete
115     delete this;
116 }
117 
118 
eGameObject(eGrid * g,const eCoord & p,const eCoord & d,eFace * currentface,bool autodel)119 eGameObject::eGameObject(eGrid *g,const eCoord &p,const eCoord &d,eFace *currentface,bool autodel)
120         :autodelete(autodel),pos(p),dir(d),z(0),grid(g){
121     tASSERT(g);
122     currentFace=currentface;
123     lastTime=se_GameTime();
124     id=-1;
125     interestingID=-1;
126     inactiveID=-1;
127     if ( lastTime < 0 )
128         lastTime=0;
129     team = 0;
130 }
131 
~eGameObject()132 eGameObject::~eGameObject(){
133     currentFace = 0;
134     RemoveFromListsAll();
135     tCHECK_DEST;
136 }
137 
138 // returns the type of this object (important for interaction of
139 // two gameObjects)
140 //gameobject_type gameobject::type(){return ArmageTron_GENERIC;}
141 
142 // makes two gameObjects interact:
InteractWith(eGameObject *,REAL,int)143 void eGameObject::InteractWith(eGameObject *,REAL,int){}
144 
145 // what happens if we pass eWall w?
PassEdge(const eWall * w,REAL,REAL,int)146 void eGameObject::PassEdge(const eWall *w,REAL,REAL,int){
147     if (w) Kill();
148 }
149 
150 static int se_moveTimeout = 100;
151 static tSettingItem<int> se_moveTimeoutC("GAMEOBJECT_MOVE_TIMEOUT", se_moveTimeout);
152 
153 // data structures for storing temp wall collisions
154 struct eTempEdgePassing
155 {
156     eWall *wall; //!< the wall the object collides with
157     REAL ratio;  //!< the location of the collision point on the wall
158 };
159 typedef std::multimap< REAL, eTempEdgePassing > eTempEdgeMap;
160 
161 
162 // moves
Move(const eCoord & dest,REAL startTime,REAL endTime,bool useTempWalls)163 void eGameObject::Move( const eCoord &dest, REAL startTime, REAL endTime, bool useTempWalls )
164 {
165 #ifdef DEBUG
166     grid->Check();
167 #endif
168     if (!finite(dest.x) || !finite(dest.y))
169     {
170         st_Breakpoint();
171         return;
172     }
173 
174     tStackObject< ePoint > start(pos),stop(dest);
175     ePoint* pstart = &start;
176     ePoint* pstop = &stop;
177 
178     // clip movement to rim walls
179     REAL clip = eWallRim::Clip(start,stop,-10);
180     endTime = startTime + ( endTime - startTime ) * clip;
181 
182     grid->Range(stop.NormSquared());
183 
184 #ifdef DEBUG
185     if (!finite(stop.x) || !finite(stop.y))
186     {
187         st_Breakpoint();
188 
189         static_cast<eCoord&>(stop) = dest;
190         eWallRim::Bound(stop,-10);
191 
192         return;
193     }
194 #endif
195 
196     //  se_GridRange(dest.Norm_squared());
197     eTempEdgeMap tempCollisions;
198 
199     tStackObject< eTempEdge >  te( pstart, pstop );
200     eHalfEdge  &e=*te.Edge(0);
201 
202     // check all the currently drawn eWalls:
203     if ( useTempWalls )
204     {
205         for(int i=grid->wallsNotYetInserted.Len()-1;i>=0;i--){
206             const eHalfEdge *other_e=grid->wallsNotYetInserted[i]->Edge();
207             if (//!sg_netPlayerWalls(i)->Preliminary() &&
208                 other_e->Point() && other_e->Other() && other_e->Other()->Point()){
209                 tJUST_CONTROLLED_PTR< ePoint > new_cross_p=e.IntersectWith(other_e);
210                 if (new_cross_p){
211                     REAL e_ratio =e.Ratio(*new_cross_p);
212                     REAL o_ratio =other_e->Ratio(*new_cross_p);
213                     if (0<=e_ratio && 1>=e_ratio &&
214                             0<=o_ratio && 1>=o_ratio)
215                     { // find the fall
216                         eWall *w = other_e->GetWall();
217                         if (!w)
218                         {
219                             w = other_e->Other()->GetWall();
220                             o_ratio = 1-o_ratio;
221                         }
222                         if (w)
223                         {
224                             // insert data into map structure for later processing
225                             eTempEdgePassing passing;
226                             passing.wall = w;
227                             passing.ratio = o_ratio;
228                             tempCollisions.insert( std::pair< REAL, eTempEdgePassing >( e_ratio, passing) );
229                         }
230                     }
231                 }
232             }
233         }
234     }
235 
236     // find a replacement face if required
237     FindCurrentFace();
238 
239     // the total distance to travel
240     REAL totalDistance = ( stop - pos ).Norm();
241 
242     if (currentFace){
243         // start iterator for collisions with temporary walls
244         eTempEdgeMap::const_iterator currentTempCollision = tempCollisions.begin();
245 
246         int timeout = se_moveTimeout;
247 
248         REAL lastDistance = 1E+30; // the distance of pos and stop in the last step
249         eHalfEdge *in    = NULL;   // incoming edge to prevent entdless loop
250 
251         while (currentFace && timeout >0 && !currentFace->IsInside(stop)){
252             // the vector to our destination:
253             eCoord vec=stop - pos;
254 
255             // count down timeout if we're moving into the wrong direction
256             REAL distance = vec.Norm();
257             if ( distance >= lastDistance )
258             {
259                 timeout--;
260             }
261             else
262             {
263                 timeout = se_moveTimeout;
264                 if ( lastDistance > 1E+29 )
265                     lastDistance = distance * 1.1;
266                 lastDistance = .1 * lastDistance + distance * (.9 - EPS);
267 
268                 // check if the target has been reached within tolerance; it can only make matters
269                 // worse then to continue, even if the current face claims we're not part of it.
270                 if ( distance <= EPS * totalDistance )
271                 {
272                     // st_Breakpoint();
273                     break;
274                 }
275             }
276 #ifdef DEBUG_X
277 rerun:
278 #endif
279 
280             eHalfEdge *run   = currentFace->Edge(); // runs through all edges of the face
281             eHalfEdge *best  = NULL;                // the best face to leave
282             eHalfEdge *end   = run;
283             REAL bestScore   = -10.0;
284             REAL bestERatio  = .5;
285             REAL bestRRatio  = .5;
286             eCoord bestCross   (0,0);
287 
288             // look for the best way out
289             do
290             {
291                 run = run->Next();
292 
293                 if (run == in) // never leave through the edge we entered
294                     continue;
295 
296                 eCoord runVec = run->Vec();
297 
298                 REAL score = runVec * vec / ( se_EstimatedRangeOfMult( runVec, vec ) + EPS );
299                 static const REAL smallBias = .01;
300 
301                 // keep a bit of the score, but not too much. We want to
302                 // sort out exactly parallel walls here.
303                 if ( score > smallBias || ( score > 0 && !run->GetWall() ) )
304                     score = smallBias;
305 
306                 eCoord cross = e.IntersectWithCareless(run);
307 
308                 // project crossing to face edge without score penalty
309                 REAL run_ratio = run->Ratio(cross);
310                 if ( !good( run_ratio ) )
311                 {
312                     // score -= 100;
313                     run_ratio = .5;
314                 }
315 
316                 if (run_ratio < 0)
317                 {
318                     // score += run_ratio;
319                     run_ratio = 0;
320                 }
321                 else if (run_ratio > 1)
322                 {
323                     // score += (1-run_ratio);
324                     run_ratio = 1;
325                 }
326                 cross = *run->Point() + run->Vec() * run_ratio;
327 
328                 // determine how far off the movement edge the modified intersection lies
329                 REAL e_side  = vec * ( cross - pos ) / distance;
330                 score -= fabs( e_side );
331                 // REAL run_side  = runVec * ( cross - *run->Point() ) / runVec.NormSquared();
332 
333                 REAL e_ratio   = e.Ratio(cross);
334 
335                 // see whether the intersection is beyond the end points of the movement vector
336                 if ( !good( e_ratio ) )
337                 {
338                     score -= 100;
339                     e_ratio = .5;
340                 }
341 
342                 if (e_ratio < 0)
343                 {
344                     score += e_ratio;
345                     e_ratio = 0;
346                 }
347                 else if (e_ratio > 1)
348                 {
349                     score += (1-e_ratio);
350                     e_ratio = 1;
351                 }
352 
353                 if (score > bestScore)
354                 {
355                     best       = run;
356                     bestScore  = score;
357                     bestERatio = e_ratio;
358                     bestRRatio = run_ratio;
359                     bestCross  = cross;
360                 }
361 
362             }
363             while (run != end);
364 
365 #ifdef DEBUG_X
366             if ( !good( bestScore ) || bestScore < -50 )
367             {
368                 st_Breakpoint();
369                 goto rerun;
370             }
371 #endif
372 
373 #define TIME( ratio ) ( startTime+(endTime-startTime)*( ratio ) )
374 
375             if (best)
376             {
377                 // handle stored temp collisions
378                 while ( currentTempCollision != tempCollisions.end() && (*currentTempCollision).first < bestERatio )
379                 {
380                     eTempEdgePassing const & passing = (*currentTempCollision).second;
381                     PassEdge( passing.wall, TIME( (*currentTempCollision).first ), passing.ratio, 0 );
382                     ++ currentTempCollision;
383                 }
384 
385                 REAL time=TIME( bestERatio );
386 
387                 // move to the collision point
388                 pos = bestCross;
389 
390                 // leave this face (through a wall)
391                 eWall*     w     = best->GetWall();
392                 if (w)
393                     PassEdge(w,time,bestRRatio,0);
394 
395                 // set next incoming edge
396                 tASSERT(best->Other());
397                 in          = best->Other();
398 
399                 // enter the next face (through a wall)
400                 if (in)
401                 {
402                     bestRRatio = 1-bestRRatio;
403                     w = in->GetWall();
404 
405                     if (w)
406                         PassEdge(w,time,bestRRatio,0);
407                 }
408 
409                 // switch to the next face
410                 if (in)
411                     currentFace=in->Face();
412                 else
413                     currentFace=NULL;
414             }
415             else
416             {
417                 timeout = 0;
418             }
419         }
420 
421         if (timeout <= 0)
422             grid->requestCleanup = true;
423         else
424             pos=stop;
425 
426         // handle stored temp collisions
427         while ( currentTempCollision != tempCollisions.end() )
428         {
429             eTempEdgePassing const & passing = (*currentTempCollision).second;
430             PassEdge( passing.wall, TIME( (*currentTempCollision).first ), passing.ratio, 0 );
431             ++ currentTempCollision;
432         }
433     }
434     else // !currentFace
435     {
436         // just move.
437         pos = dest;
438     }
439 
440     // not if the movement timed out
441     // pos=stop;
442 
443     // find a replacement face if required
444     FindCurrentFace();
445 
446     //#ifdef DEBUG
447     //se_CheckGrid();
448     //#endif
449 
450     //if (id<0)
451     //    currentFace = NULL;
452 
453     lastTime = endTime;
454 }
455 
456 // emulate old bug allowing objects to tunnel through walls
457 static short se_bugTunnel = false;
458 static nSettingItem<short> se_bugTunnelConfig("BUG_TUNNEL",
459         se_bugTunnel );
460 
461 class eFaceFindFilter: public tConsoleFilter
462 {
DoFilterLine(tString & line)463     virtual void DoFilterLine( tString& line )
464     {
465         line = tString( "FindCurrentFace() is running, so this message probably means there is a BUG: " ) + line;
466     }
467 };
468 
FindCurrentFace()469 void eGameObject::FindCurrentFace(){
470     // find a replacement for a removed face
471     if ( currentFace && !currentFace->IsInGrid() )
472     {
473         if ( !se_bugTunnel )
474         {
475             currentFace = currentFace->FindReplacement( pos, Direction(), LastDirection() );
476             if ( !currentFace && sn_GetNetState() != nCLIENT )
477             {
478                 static bool warn = true;
479                 if (warn)
480                 {
481                     tERR_WARN("Possible phase bug!\n");
482                 }
483 
484                 warn = false;
485             }
486         }
487         else
488         {
489             // allow tunneling through walls
490             currentFace = NULL;
491         }
492     }
493 
494     // don't fetch a new current face if you're out of the game
495     if ( !currentFace && GOID() < 0 )
496     {
497 #ifdef DEBUG
498         con << "Attempting to get a current face, but object is not in game.\n";
499         st_Breakpoint();
500 #endif
501         return;
502     }
503 
504     // did that do the trick? If no, use brute force.
505     if ( !currentFace )
506         currentFace = grid->FindSurroundingFace(pos);
507 
508     if ( currentFace )
509     {
510         // check if the position lies inside the current triangle
511         REAL insideness = currentFace->Insideness(pos);
512         if ( insideness < 0 )
513         {
514             eFaceFindFilter filter;
515             // if ( sn_GetNetState() != nCLIENT )
516             //    con << "insideness = " << insideness << "\n";
517 
518             // no. Find the center of the current face.
519             int i;
520             eCoord center;
521             eHalfEdge * run = currentFace->Edge();
522             for ( i = 2; i >= 0; --i )
523             {
524                 run = run->Next();
525                 center = center + ( *run->Point() - pos );
526             }
527             eCoord centerToPos = -center*(1/3.0);
528             center = pos - centerToPos;
529 
530             // find a position that lies just inside the current triange
531             REAL centerInsideness = currentFace->Insideness(center);
532             eCoord inside;
533 
534             if( centerInsideness < 0 )
535             {
536                 // this should not happen! but will, if the triangle has wrong orientation.
537                 inside = center;
538             }
539             else
540             {
541                 REAL factor = ( -insideness/( centerInsideness - insideness ) );
542                 if ( factor > 1 )
543                 {
544                     factor = 1;
545                 }
546                 inside = pos - centerToPos * factor;
547             }
548 
549             static bool recurse = true;
550             if ( recurse )
551             {
552                 class RecursionGuard
553                 {
554                 public:
555                     RecursionGuard( bool& recursion )
556                             :recursion_( recursion )
557                     {
558                         recursion = false;
559                     }
560 
561                     ~RecursionGuard()
562                     {
563                         recursion_ = true;
564                     }
565 
566                 private:
567                     bool& recursion_;
568                 };
569 
570                 RecursionGuard guard( recurse );
571 
572                 // warp to the known good position and move back to where the
573                 // object should be
574                 eCoord oldPos = pos;
575                 pos = inside;
576 #ifdef DEBUG
577                 eFace * lastFace = currentFace;
578 #endif
579                 try
580                 {
581                     Move( oldPos, lastTime, lastTime, false );
582                 }
583                 catch( eDeath & ) // ignore death exceptions and leave object where it would have died
584                 {
585 #ifdef DEBUG
586                     // 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)
587                     // besides, (now, this was changed) the start position changed.
588                     try
589                     {
590                         pos = center;
591                         currentFace = lastFace;
592                         Move( oldPos, lastTime, lastTime, false );
593                     }
594                     catch( eDeath & ){}
595 #endif
596                 }
597 
598                 recurse = true;
599             }
600             else
601             {
602                 // alternative if true movement is not possible:
603                 // project current position into triangle
604                 run = currentFace->Edge();
605                 for ( i = 2; i >= 0; --i )
606                 {
607                     run = run->Next();
608                     eCoord centerToPoint = *run->Point() - center;
609                     eCoord runVec = run->Vec();
610                     REAL prod = centerToPoint * runVec;
611                     if (prod < 0)
612                     {
613                         REAL toClamp = (centerToPos * runVec) / prod;
614                         if ( toClamp > 1 )
615                         {
616                             centerToPos = centerToPos * (1/toClamp);
617                         }
618                     }
619                 }
620                 pos = center + centerToPos;
621             }
622         }
623     }
624 }
625 
626 // simulates behaviour up to currentTime:
Timestep(REAL t)627 bool eGameObject::Timestep(REAL t){
628     lastTime = t;
629     return 0;
630 }
631 // return value: shall this object be destroyed?
632 
OnRoundBegin()633 void eGameObject::OnRoundBegin(){}
OnRoundEnd()634 void eGameObject::OnRoundEnd(){}
635 
Kill()636 void eGameObject::Kill(){}
637 
638 // draws it to the screen using OpenGL
Render(const eCamera *)639 void eGameObject::Render(const eCamera *){}
640 
641 // *******************************************************************************
642 // *
643 // *	RendersAlpha
644 // *
645 // *******************************************************************************
646 //!
647 //!		@return	True if alpha blending is used
648 //!
649 // *******************************************************************************
RendersAlpha() const650 bool eGameObject::RendersAlpha() const{return false;}
651 
652 // Cockpit
RenderCockpitFixedBefore(bool)653 bool eGameObject::RenderCockpitFixedBefore(bool){return true;}
654 // return value: draw everything else?
655 
656 // the same purpose, but called after main rendering
RenderCockpitFixedAfter(bool)657 void eGameObject::RenderCockpitFixedAfter(bool){}
658 // virtual perspective
RenderCockpitVirtual(bool)659 void eGameObject::RenderCockpitVirtual(bool){}
660 
661 
662 #ifdef POWERPAK_DEB
PPDisplay()663 void eGameObject::PPDisplay(){
664     PD_PutPixel(DoubleBuffer,
665                 se_X_ToScreen(pos.x),
666                 se_Y_ToScreen(pos.y),
667                 PD_CreateColor(DoubleBuffer,255,0,100));
668     PD_PutPixel(DoubleBuffer,
669                 se_X_ToScreen(pos.x+1),
670                 se_Y_ToScreen(pos.y),
671                 PD_CreateColor(DoubleBuffer,255,0,100));
672     PD_PutPixel(DoubleBuffer,
673                 se_X_ToScreen(pos.x-1),
674                 se_Y_ToScreen(pos.y),
675                 PD_CreateColor(DoubleBuffer,255,0,100));
676     PD_PutPixel(DoubleBuffer,
677                 se_X_ToScreen(pos.x),
678                 se_Y_ToScreen(pos.y+1),
679                 PD_CreateColor(DoubleBuffer,255,0,100));
680     PD_PutPixel(DoubleBuffer,
681                 se_X_ToScreen(pos.x),
682                 se_Y_ToScreen(pos.y-1),
683                 PD_CreateColor(DoubleBuffer,255,0,100));
684 
685 }
686 #endif
687 
688 // Receives control from player; nothing to do here
Act(uActionPlayer *,REAL)689 bool eGameObject::Act(uActionPlayer *,REAL){return false;}
690 
691 
TimestepThis(REAL currentTime,eGameObject * c)692 bool eGameObject::TimestepThis(REAL currentTime,eGameObject *c){
693 #ifdef DEBUG
694     c->grid->Check();
695 #endif
696 
697     tJUST_CONTROLLED_PTR< eGameObject > keep( c ); // keep object alive
698 
699     REAL maxstep=.2;
700 
701     // don't do a thing if the timestep is too small
702     if (fabs(currentTime - c->lastTime) < .001)
703         return false;
704 
705     // be more careful when going back
706     if (currentTime<c->lastTime)
707         maxstep=.1;
708 
709     int number_of_steps=int(fabs((currentTime-c->lastTime)/maxstep));
710     if (number_of_steps<1)
711         number_of_steps=1;
712     if ( number_of_steps > 10 )
713     {
714         number_of_steps = 10;
715     }
716 
717     REAL lastTime=c->lastTime;
718 
719     bool ret=false;
720 
721     for(int i=1;i<=number_of_steps;i++)
722     {
723         // make current face valid
724         c->FindCurrentFace();
725 
726         if (sn_GetNetState()!=nCLIENT)
727             for(int j=c->grid->gameObjectsInteresting.Len()-1;j>=0;j--)
728                 c->InteractWith(c->grid->gameObjectsInteresting(j),currentTime,0);
729 
730         REAL timeThisStep = lastTime+i*(currentTime-lastTime)/number_of_steps;
731         ret = ret || c->Timestep(timeThisStep);
732         c->FindCurrentFace();
733 
734         // see if the object refused to get simulated, if yes, give up
735         if ( 2 * c->lastTime < timeThisStep + lastTime )
736             break;
737     }
738 #ifdef DEBUG
739     c->grid->Check();
740 #endif
741 
742     return ret;
743 }
744 
745 #ifdef DEDICATED
746 static REAL se_maxSimulateAhead = .01f;
747 static tSettingItem<REAL> se_maxSimulateAheadConf( "MAX_SIMULATE_AHEAD", se_maxSimulateAhead );
748 #endif
749 
750 // what is left of this time for the gameobject to use up
751 static REAL se_maxSimulateAheadLeft = 0.0f;
MaxSimulateAhead()752 REAL eGameObject::MaxSimulateAhead()
753 {
754     return se_maxSimulateAheadLeft;
755 }
756 
757 static REAL se_lazyLag = 0;
758 //! @return the maximum extra simulation time difference, on top of regular lag, caused by lazy simulation
GetMaxLazyLag()759 REAL eGameObject::GetMaxLazyLag()
760 {
761     return se_lazyLag;
762 }
763 
764 //! @param lag the maximum extra simulation time difference, on top of regular lag, caused by lazy simulation
SetMaxLazyLag(REAL lag)765 void eGameObject::SetMaxLazyLag( REAL lag )
766 {
767     se_lazyLag = lag;
768 }
769 
TimestepThisWrapper(eGrid * grid,REAL currentTime,eGameObject * c,REAL minTimestep)770 void eGameObject::TimestepThisWrapper(eGrid * grid, REAL currentTime, eGameObject *c, REAL minTimestep )
771 {
772     su_FetchAndStoreSDLInput();
773 
774     REAL simTime=currentTime;
775     // backdate the object a bit
776 #ifndef DEDICATED
777     if (sn_GetNetState()==nCLIENT && !sr_predictObjects)
778 #endif
779         simTime -= c->Lag();
780 
781 #ifdef DEDICATED
782     REAL nextTime = c->NextInterestingTime();
783 
784     // store the time left to simulate
785     se_maxSimulateAheadLeft = simTime + se_maxSimulateAhead - nextTime;
786     if ( se_maxSimulateAheadLeft < 0 )
787         se_maxSimulateAheadLeft = 0;
788 
789     REAL lagThreshold = c->LagThreshold();
790     if ( simTime - lagThreshold < nextTime && nextTime < simTime + se_maxSimulateAhead )
791     {
792         // something interesting is going to happen, see what it is
793         simTime = nextTime;
794     }
795     else
796     {
797         // add an extra portion of lag compensation
798         simTime -= lagThreshold;
799 
800         if ( simTime < c->LastTime() + minTimestep )
801         {
802             // don't waste your time on too small timesteps
803             return;
804         }
805     }
806 #endif
807 
808     // check for teleports out of arena bounds
809     if (!eWallRim::IsBound(c->pos,-20))
810     {
811         se_maxSimulateAheadLeft = 0;
812 
813         c->Kill();
814         return;
815     }
816 
817     // only simulate forward here
818     if ( simTime > c->lastTime )
819     {
820         if (TimestepThis(simTime,c))
821         {
822             if (c->autodelete)
823                 c->RemoveFromGame();
824             else
825             {
826                 c->currentFace=NULL;
827                 c->RemoveFromList();
828             }
829         }
830     }
831 
832     se_maxSimulateAheadLeft = 0.0;
833 }
834 
835 // does a timestep and all interactions for every eGameObject
s_Timestep(eGrid * grid,REAL currentTime,REAL minTimestep)836 void eGameObject::s_Timestep(eGrid *grid, REAL currentTime, REAL minTimestep)
837 {
838 #ifdef DEBUG
839     grid->Check();
840 #endif
841 
842     // simulate game objects
843     for(int i=grid->gameObjects.Len()-1;i>=0;i--)
844     {
845         eGameObject * c = grid->gameObjects(i);
846         TimestepThisWrapper( grid, currentTime, c, minTimestep );
847     }
848 
849 #ifdef DEBUG
850     grid->Check();
851 #endif
852 }
853 
854 #ifdef DEBUG
855 eGameObject *displayed_gameobject = 0;
856 #endif
857 
RenderAll(eGrid * grid,const eCamera * cam)858 void eGameObject::RenderAll(eGrid *grid, const eCamera *cam){
859     //if (!sr_glOut)
860     //    return;
861 
862     // first, we need to render all the non-alpha blended objects.
863     // if we encounter non-alpha blended objects after alpha blended objects
864     // have already been rendered, we need to re-sort them to the back.
865     eGameObject * firstAlpha = NULL;
866     for(int i=grid->gameObjects.Len()-1;i>=0;i--){
867         su_FetchAndStoreSDLInput();
868         if (sr_glOut){
869             eGameObject * object = grid->gameObjects(i);
870 #ifdef DEBUG
871             displayed_gameobject = object;
872 #endif
873             object->Render(cam);
874 
875             bool thisAlpha = object->RendersAlpha();
876             if ( !thisAlpha && firstAlpha )
877             {
878                 // resort the object, switch places with the first alpha blended one.
879                 // This will only have an effect in the next frame,
880                 // but the small flickering error is to be tolerated, especially
881                 // since alpha blended game objects tend to gently fade in.
882                 int firstAlphaID = firstAlpha->id;
883 
884                 eSoundLocker locker;
885 
886                 grid->gameObjects.Remove(firstAlpha,firstAlpha->id);
887                 grid->gameObjects.Add(firstAlpha,firstAlpha->id);
888                 grid->gameObjects.Remove(object,object->id);
889                 grid->gameObjects.Add(object,object->id);
890 
891                 // the first alpha blended object no longer is the first. Look for
892                 // a replacement, only one object is a candidate.
893                 firstAlpha = 0;
894                 if ( firstAlphaID > 0 )
895                 {
896                     firstAlpha = grid->gameObjects(firstAlphaID - 1);
897                     tASSERT( firstAlpha->RendersAlpha() );
898                 }
899             }
900             if ( thisAlpha && !firstAlpha )
901             {
902                 // store first known alpha blending object
903                 firstAlpha = object;
904             }
905 #ifdef DEBUG
906             displayed_gameobject = 0;
907 #endif
908         }
909     }
910 }
911 
912 #ifdef POWERPAK_DEB
PPDisplayAll()913 void eGameObject::PPDisplayAll(){
914     for(int i=gameObjects.Len()-1;i>=0;i--){
915         if (pp_out) gameObjects(i)->PPDisplay();
916     }
917 }
918 #endif
919 
920 
DeleteAll(eGrid * grid)921 void eGameObject::DeleteAll(eGrid *grid){
922     int i;
923     for(i=grid->gameObjects.Len()-1;i>=0;i--)
924     {
925         eGameObject* o = grid->gameObjects(i);
926         o->RemoveFromGame();
927 #ifdef POWERPAK_DEB
928         if (pp_out) o->PPDisplay();
929 #endif
930     }
931 }
932 
eReferencableGameObject(eGrid * grid,const eCoord & p,const eCoord & d,eFace * currentface,bool autodelete)933 eReferencableGameObject::eReferencableGameObject(eGrid *grid, const eCoord &p,const eCoord &d, eFace *currentface, bool autodelete)
934 : eGameObject( grid, p, d, currentface, autodelete )
935 {
936 }
937 
938 // delegate real reference counting
AddRef()939 void eReferencableGameObject::AddRef()
940 {
941     tReferencable< eReferencableGameObject >::AddRef();
942 }
943 
Release()944 void eReferencableGameObject::Release()
945 {
946     tReferencable< eReferencableGameObject >::Release();
947 }
948 
DoRemoveFromGame()949 void eReferencableGameObject::DoRemoveFromGame()
950 {
951     // nothing needs to be done, the reference counting takes care of the destruction
952 }
953 
eStackGameObject(eGrid * grid,const eCoord & p,const eCoord & d,eFace * currentface)954 eStackGameObject::eStackGameObject(eGrid *grid, const eCoord &p,const eCoord &d, eFace *currentface)
955 : eGameObject( grid, p, d, currentface, false )
956 {
957 }
958 
AddRef()959 void eStackGameObject::AddRef()
960 {
961 }
962 
Release()963 void eStackGameObject::Release()
964 {
965 }
966 
DoRemoveFromGame()967 void eStackGameObject::DoRemoveFromGame()
968 {
969     // must not get called
970     tERR_ERROR("Stack game object removed from game.");
971 }
972 
973 
974