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