1 /* bzflag
2  * Copyright (c) 1993-2021 Tim Riker
3  *
4  * This package is free software;  you can redistribute it and/or
5  * modify it under the terms of the license found in the file
6  * named COPYING that should have accompanied this file.
7  *
8  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11  */
12 
13 /* interface header */
14 #include "World.h"
15 
16 /* system implementation headers */
17 #include <fstream>
18 #include <time.h>
19 #include <assert.h>
20 
21 /* common implementation headers */
22 #include "BZDBCache.h"
23 #include "TextureManager.h"
24 #include "FileManager.h"
25 #include "CollisionManager.h"
26 #include "DynamicColor.h"
27 #include "TextureMatrix.h"
28 #include "PhysicsDriver.h"
29 #include "FlagSceneNode.h"
30 #include "ObstacleMgr.h"
31 #include "MeshDrawInfo.h"
32 #include "MeshDrawMgr.h"
33 #include "DirectoryNames.h"
34 #include "GameTime.h"
35 #include "WallObstacle.h"
36 #include "MeshObstacle.h"
37 
38 //
39 // World
40 //
41 
42 World*          World::playingField = NULL;
43 BundleMgr*      World::bundleMgr;
44 std::string     World::locale("");
45 int         World::flagTexture(-1);
46 
47 
World()48 World::World() :
49     gameType(TeamFFA),
50     gameOptions(0),
51     linearAcceleration(0.0f),
52     angularAcceleration(0.0f),
53     maxPlayers(0),
54     curMaxPlayers(0),
55     maxShots(1),
56     maxFlags(0),
57     shakeTimeout(),
58     shakeWins(),
59     players(NULL),
60     flags(NULL),
61     flagNodes(NULL),
62     flagWarpNodes(NULL),
63     wind(),
64     oldFogEffect(),
65     oldUseDrawInfo()
66 {
67     worldWeapons = new WorldPlayer();
68     waterLevel = -1.0f;
69     waterMaterial = NULL;
70     linkMaterial = NULL;
71     drawInfoCount = 0;
72     drawInfoArray = NULL;
73 }
74 
75 
~World()76 World::~World()
77 {
78     int i;
79     freeFlags();
80     freeInsideNodes();
81     freeMeshDrawMgrs();
82     for (i = 0; i < curMaxPlayers; i++)
83         delete players[i];
84     delete[] players;
85     delete worldWeapons;
86     for (i = 0; i < NumTeams; i++)
87         bases[i].clear();
88 
89     // clear the managers
90     links.clear();
91     DYNCOLORMGR.clear();
92     TEXMATRIXMGR.clear();
93     MATERIALMGR.clear();
94     PHYDRVMGR.clear();
95     TRANSFORMMGR.clear();
96     OBSTACLEMGR.clear();
97     COLLISIONMGR.clear();
98 
99     return;
100 }
101 
102 
init()103 void            World::init()
104 {
105     TextureManager &tm = TextureManager::instance();
106     flagTexture = tm.getTextureID( "flag" );
107 }
108 
done()109 void            World::done()
110 {
111     flagTexture = 0;
112 }
113 
loadCollisionManager()114 void            World::loadCollisionManager()
115 {
116     COLLISIONMGR.load();
117     return;
118 }
119 
checkCollisionManager()120 void            World::checkCollisionManager()
121 {
122     if (COLLISIONMGR.needReload())
123     {
124         // reload the collision grid
125         COLLISIONMGR.load();
126     }
127     return;
128 }
129 
setFlagTexture(FlagSceneNode * flag)130 void            World::setFlagTexture(FlagSceneNode* flag)
131 {
132     flag->setTexture(flagTexture);
133 }
134 
setWorld(World * _playingField)135 void            World::setWorld(World* _playingField)
136 {
137     playingField = _playingField;
138 }
139 
140 
getTeleportTarget(int source) const141 int World::getTeleportTarget(int source) const
142 {
143     return links.getTeleportTarget(source);
144 }
145 
146 
getTeleportTarget(int source,unsigned int seed) const147 int World::getTeleportTarget(int source, unsigned int seed) const
148 {
149     return links.getTeleportTarget(source, seed);
150 }
151 
152 
getTeleporter(const Teleporter * teleporter,int face) const153 int World::getTeleporter(const Teleporter* teleporter, int face) const
154 {
155     // search for teleporter
156     const ObstacleList& teleporters = OBSTACLEMGR.getTeles();
157     const int count = teleporters.size();
158     for (int i = 0; i < count; i++)
159     {
160         if (teleporter == (const Teleporter*)teleporters[i])
161             return ((2 * i) + face);
162     }
163     printf ("World::getTeleporter() error\n");
164     fflush(stdout);
165     exit(1);
166 }
167 
168 
getTeleporter(int source,int & face) const169 const Teleporter* World::getTeleporter(int source, int& face) const
170 {
171     const ObstacleList& teleporters = OBSTACLEMGR.getTeles();
172     if (source >= 0 && source < (int)(2 * teleporters.size()))
173     {
174         face = (source & 1);
175         return ((const Teleporter*) teleporters[source / 2]);
176     }
177     return NULL;
178 }
179 
180 
whoseBase(const float * pos) const181 TeamColor       World::whoseBase(const float* pos) const
182 {
183     if (gameType != ClassicCTF)
184         return NoTeam;
185 
186     for (int i = 1; i < NumTeams; i++)
187     {
188         for (TeamBases::const_iterator it = bases[i].begin(); it != bases[i].end(); ++it)
189         {
190             float nx = pos[0] - it->p[0];
191             float ny = pos[1] - it->p[1];
192             float rx = (float) (cosf(atanf(ny / nx) - it->p[3]) * sqrt((ny * ny) + (nx * nx)));
193             float ry = (float) (sinf(atanf(ny / nx) - it->p[3]) * sqrt((ny * ny) + (nx * nx)));
194             if (fabsf(rx) < it->p[4] &&
195                     fabsf(ry) < it->p[5])
196             {
197                 float nz = it->p[2] + it->p[6];
198                 float rz = pos[2] - nz;
199                 if (fabsf(rz) < 0.1)   // epsilon kludge
200                     return TeamColor(i);
201             }
202         }
203     }
204 
205     return NoTeam;
206 }
207 
inBuilding(const float * pos,float radius,float height) const208 const Obstacle*     World::inBuilding(const float* pos,
209                                       float radius, float height) const
210 {
211     // check everything but walls
212     const ObsList* olist = COLLISIONMGR.cylinderTest (pos, radius, height);
213     for (int i = 0; i < olist->count; i++)
214     {
215         const Obstacle* obs = olist->list[i];
216         if (obs->inCylinder(pos, radius, height))
217             return obs;
218     }
219 
220     return NULL;
221 }
222 
inBuilding(const float * pos,float angle,float dx,float dy,float dz) const223 const Obstacle*     World::inBuilding(const float* pos, float angle,
224                                       float dx, float dy, float dz) const
225 {
226     // check everything but the walls
227     const ObsList* olist = COLLISIONMGR.boxTest (pos, angle, dx, dy, dz);
228 
229     for (int i = 0; i < olist->count; i++)
230     {
231         const Obstacle* obs = olist->list[i];
232         if (obs->inBox(pos, angle, dx, dy, dz))
233             return obs;
234     }
235 
236     return NULL;
237 }
238 
239 
hitBuilding(const float * pos,float angle,float dx,float dy,float dz) const240 const Obstacle*     World::hitBuilding(const float* pos, float angle,
241                                        float dx, float dy, float dz) const
242 {
243     // check walls
244     const ObstacleList& walls = OBSTACLEMGR.getWalls();
245     for (unsigned int w = 0; w < walls.size(); w++)
246     {
247         const WallObstacle* wall = (const WallObstacle*) walls[w];
248         if (wall->inBox(pos, angle, dx, dy, dz))
249             return wall;
250     }
251 
252     // check everything else
253     const ObsList* olist = COLLISIONMGR.boxTest (pos, angle, dx, dy, dz);
254 
255     for (int i = 0; i < olist->count; i++)
256     {
257         const Obstacle* obs = olist->list[i];
258         if (!obs->isDriveThrough() && obs->inBox(pos, angle, dx, dy, dz))
259             return obs;
260     }
261 
262     return NULL;
263 }
264 
265 
compareHeights(const Obstacle * & obsA,const Obstacle * obsB)266 static inline int compareHeights(const Obstacle*& obsA, const Obstacle* obsB)
267 {
268     const Extents& eA = obsA->getExtents();
269     const Extents& eB = obsB->getExtents();
270     if (eA.maxs[2] > eB.maxs[2])
271         return -1;
272     else
273         return +1;
274 }
275 
compareObstacles(const void * a,const void * b)276 static int compareObstacles(const void* a, const void* b)
277 {
278     // - normal object come first (from lowest to highest)
279     // - then come the mesh face (highest to lowest)
280     // - and finally, the mesh objects (checkpoints really)
281     const Obstacle* obsA = *((const Obstacle* const *)a);
282     const Obstacle* obsB = *((const Obstacle* const *)b);
283     const char* typeA = obsA->getType();
284     const char* typeB = obsB->getType();
285 
286     bool isMeshA = (typeA == MeshObstacle::getClassName());
287     bool isMeshB = (typeB == MeshObstacle::getClassName());
288 
289     if (isMeshA)
290     {
291         if (!isMeshB)
292             return +1;
293         else
294             return compareHeights(obsA, obsB);
295     }
296 
297     if (isMeshB)
298     {
299         if (!isMeshA)
300             return -1;
301         else
302             return compareHeights(obsA, obsB);
303     }
304 
305     bool isFaceA = (typeA == MeshFace::getClassName());
306     bool isFaceB = (typeB == MeshFace::getClassName());
307 
308     if (isFaceA)
309     {
310         if (!isFaceB)
311             return +1;
312         else
313             return compareHeights(obsA, obsB);
314     }
315 
316     if (isFaceB)
317     {
318         if (!isFaceA)
319             return -1;
320         else
321             return compareHeights(obsA, obsB);
322     }
323 
324     return compareHeights(obsB, obsA); // reversed
325 }
326 
compareHitNormal(const void * a,const void * b)327 static int compareHitNormal (const void* a, const void* b)
328 {
329     const MeshFace* faceA = *((const MeshFace* const *) a);
330     const MeshFace* faceB = *((const MeshFace* const *) b);
331 
332     // Up Planes come first
333     if (faceA->isUpPlane() && !faceB->isUpPlane())
334         return -1;
335     if (faceB->isUpPlane() && !faceA->isUpPlane())
336         return +1;
337 
338     // highest Up Plane comes first
339     if (faceA->isUpPlane() && faceB->isUpPlane())
340     {
341         if (faceA->getPosition()[2] > faceB->getPosition()[2])
342             return -1;
343         else
344             return +1;
345     }
346 
347     // compare the dot products
348     if (faceA->scratchPad < faceB->scratchPad)
349         return -1;
350     else
351         return +1;
352 }
353 
hitBuilding(const float * oldPos,float oldAngle,const float * pos,float angle,float dx,float dy,float dz,bool directional) const354 const Obstacle* World::hitBuilding(const float* oldPos, float oldAngle,
355                                    const float* pos, float angle,
356                                    float dx, float dy, float dz,
357                                    bool directional) const
358 {
359     // check walls
360     const ObstacleList& walls = OBSTACLEMGR.getWalls();
361     for (unsigned int w = 0; w < walls.size(); w++)
362     {
363         const WallObstacle* wall = (const WallObstacle*) walls[w];
364         if (wall->inMovingBox(oldPos, oldAngle, pos, angle, dx, dy, dz))
365             return wall;
366     }
367 
368     // get the list of potential hits from the collision manager
369     const ObsList* olist =
370         COLLISIONMGR.movingBoxTest (oldPos, oldAngle, pos, angle, dx, dy, dz);
371 
372     // sort the list by type and height
373     qsort (olist->list, olist->count, sizeof(Obstacle*), compareObstacles);
374 
375 
376     int i;
377 
378     // check non-mesh obstacles
379     for (i = 0; i < olist->count; i++)
380     {
381         const Obstacle* obs = olist->list[i];
382         const char* type = obs->getType();
383         if ((type == MeshFace::getClassName()) ||
384                 (type == MeshObstacle::getClassName()))
385             break;
386         if (!obs->isDriveThrough() &&
387                 obs->inMovingBox(oldPos, oldAngle, pos, angle, dx, dy, dz))
388             return obs;
389     }
390     if (i == olist->count)
391     {
392         return NULL; // no more obstacles, we are done
393     }
394 
395     // do some prep work for mesh faces
396     int hitCount = 0;
397     float vel[3];
398     vel[0] = pos[0] - oldPos[0];
399     vel[1] = pos[1] - oldPos[1];
400     vel[2] = pos[2] - oldPos[2];
401     bool goingDown = (vel[2] <= 0.0f);
402 
403     // check mesh faces
404     for (/* do nothing */; i < olist->count; i++)
405     {
406         Obstacle* obs = olist->list[i];
407         const char* type = obs->getType();
408         if (type == MeshObstacle::getClassName())
409             break;
410         if (!obs->isDriveThrough() &&
411                 obs->inMovingBox(oldPos, oldAngle, pos, angle, dx, dy, dz))
412         {
413             const MeshFace* face = (const MeshFace*) obs;
414             const float facePos2 = face->getPosition()[2];
415             if (face->isUpPlane() &&
416                     (!goingDown || (oldPos[2] < (facePos2 - 1.0e-3f))))
417                 continue;
418             else if (face->isDownPlane() && ((oldPos[2] >= facePos2) || goingDown))
419                 continue;
420             else
421             {
422                 // add the face to the hitlist
423                 olist->list[hitCount] = obs;
424                 hitCount++;
425                 // compute its dot product and stick it in the scratchPad
426                 const float* p = face->getPlane();
427                 const float dot = (vel[0] * p[0]) + (vel[1] * p[1]) + (vel[2] * p[2]);
428                 face->scratchPad = dot;
429             }
430         }
431     }
432     // sort the list by dot product (this sort will be replaced with a running tab
433     qsort (olist->list, hitCount, sizeof(Obstacle*), compareHitNormal);
434 
435     // see if there as a valid meshface hit
436     if (hitCount > 0)
437     {
438         const MeshFace* face = (const MeshFace*) olist->list[0];
439         if (face->isUpPlane() || (face->scratchPad < 0.0f) || !directional)
440             return face;
441     }
442     if (i == olist->count)
443     {
444         return NULL; // no more obstacles, we are done
445     }
446 
447     // check mesh obstacles
448     for (/* do nothing */; i < olist->count; i++)
449     {
450         const Obstacle* obs = olist->list[i];
451         if (!obs->isDriveThrough() &&
452                 obs->inMovingBox(oldPos, oldAngle, pos, angle, dx, dy, dz))
453             return obs;
454     }
455 
456     return NULL; // no more obstacles, we are done
457 }
458 
459 
crossingTeleporter(const float * pos,float angle,float dx,float dy,float dz,float * plane) const460 bool            World::crossingTeleporter(const float* pos,
461         float angle, float dx, float dy, float dz,
462         float* plane) const
463 {
464     const ObstacleList& teleporters = OBSTACLEMGR.getTeles();
465     for (unsigned int i = 0; i < teleporters.size(); i++)
466     {
467         const Teleporter* teleporter = (const Teleporter*) teleporters[i];
468         if (teleporter->isCrossing(pos, angle, dx, dy, dz, plane))
469             return true;
470     }
471     return false;
472 }
473 
crossesTeleporter(const float * oldPos,const float * newPos,int & face) const474 const Teleporter*   World::crossesTeleporter(const float* oldPos,
475         const float* newPos,
476         int& face) const
477 {
478     // check teleporters
479     const ObstacleList& teleporters = OBSTACLEMGR.getTeles();
480     for (unsigned int i = 0; i < teleporters.size(); i++)
481     {
482         const Teleporter* teleporter = (const Teleporter*) teleporters[i];
483         if (teleporter->hasCrossed(oldPos, newPos, face))
484             return teleporter;
485     }
486 
487     // didn't cross
488     return NULL;
489 }
490 
crossesTeleporter(const Ray & r,int & face) const491 const Teleporter*   World::crossesTeleporter(const Ray& r, int& face) const
492 {
493     // check teleporters
494     const ObstacleList& teleporters = OBSTACLEMGR.getTeles();
495     for (unsigned int i = 0; i < teleporters.size(); i++)
496     {
497         const Teleporter* teleporter = (const Teleporter*) teleporters[i];
498         if (teleporter->isTeleported(r, face) > Epsilon)
499             return teleporter;
500     }
501 
502     // didn't cross
503     return NULL;
504 }
505 
getProximity(const float * p,float r) const506 float           World::getProximity(const float* p, float r) const
507 {
508     // get maximum over all teleporters
509     float bestProximity = 0.0;
510     const ObstacleList& teleporters = OBSTACLEMGR.getTeles();
511     for (unsigned int i = 0; i < teleporters.size(); i++)
512     {
513         const Teleporter* teleporter = (const Teleporter*) teleporters[i];
514         const float proximity = teleporter->getProximity(p, r);
515         if (proximity > bestProximity)
516             bestProximity = proximity;
517     }
518     return bestProximity;
519 }
520 
freeFlags()521 void            World::freeFlags()
522 {
523     int i;
524     if (flagNodes)
525         for (i = 0; i < maxFlags; i++)
526             delete flagNodes[i];
527     if (flagWarpNodes)
528         for (i = 0; i < maxFlags; i++)
529             delete flagWarpNodes[i];
530     delete[] flags;
531     delete[] flagNodes;
532     delete[] flagWarpNodes;
533     flags = NULL;
534     flagNodes = NULL;
535     flagWarpNodes = NULL;
536 }
537 
makeMeshDrawMgrs()538 void            World::makeMeshDrawMgrs()
539 {
540     // make the display list managers for source meshes
541     std::vector<MeshObstacle*> sourceMeshes;
542     OBSTACLEMGR.getSourceMeshes(sourceMeshes);
543     unsigned int count = sourceMeshes.size();
544     drawInfoArray = new MeshDrawInfo*[count];
545     drawInfoCount = 0;
546     for (unsigned int i = 0; i < count; i++)
547     {
548         MeshDrawInfo* di = sourceMeshes[i]->getDrawInfo();
549         if ((di != NULL) && !di->isCopy())
550         {
551             MeshDrawMgr* dm = new MeshDrawMgr(di);
552             di->setDrawMgr(dm);
553             drawInfoArray[drawInfoCount] = di;
554             drawInfoCount++;
555         }
556     }
557     return;
558 }
559 
560 
freeMeshDrawMgrs()561 void            World::freeMeshDrawMgrs()
562 {
563     for (int i = 0; i < drawInfoCount; i++)
564     {
565         MeshDrawInfo* di = drawInfoArray[i];
566         MeshDrawMgr* dm = di->getDrawMgr();
567         delete dm;
568         di->setDrawMgr(NULL);
569     }
570     drawInfoCount = 0;
571     delete[] drawInfoArray;
572     drawInfoArray = NULL;
573     return;
574 }
575 
576 
updateAnimations(float UNUSED (dt))577 void            World::updateAnimations(float UNUSED(dt))
578 {
579     const double gameTime = GameTime::getStepTime();
580     for (int i = 0; i < drawInfoCount; i++)
581     {
582         MeshDrawInfo* di = drawInfoArray[i];
583         di->updateAnimation(gameTime);
584     }
585     return;
586 }
587 
588 
freeInsideNodes() const589 void            World::freeInsideNodes() const
590 {
591     unsigned int i;
592     int j;
593     const ObstacleList& boxes = OBSTACLEMGR.getBoxes();
594     for (i = 0; i < boxes.size(); i++)
595     {
596         Obstacle* obs = boxes[i];
597         for (j = 0; j < obs->getInsideSceneNodeCount(); j++)
598             delete obs->getInsideSceneNodeList()[j];
599         obs->freeInsideSceneNodeList();
600     }
601     const ObstacleList& pyramids = OBSTACLEMGR.getPyrs();
602     for (i = 0; i < pyramids.size(); i++)
603     {
604         Obstacle* obs = pyramids[i];
605         for (j = 0; j < obs->getInsideSceneNodeCount(); j++)
606             delete obs->getInsideSceneNodeList()[j];
607         obs->freeInsideSceneNodeList();
608     }
609     const ObstacleList& basesR = OBSTACLEMGR.getBases();
610     for (i = 0; i < basesR.size(); i++)
611     {
612         Obstacle* obs = basesR[i];
613         for (j = 0; j < obs->getInsideSceneNodeCount(); j++)
614             delete obs->getInsideSceneNodeList()[j];
615         obs->freeInsideSceneNodeList();
616     }
617     const ObstacleList& meshes = OBSTACLEMGR.getMeshes();
618     for (i = 0; i < meshes.size(); i++)
619     {
620         Obstacle* obs = meshes[i];
621         for (j = 0; j < obs->getInsideSceneNodeCount(); j++)
622             delete obs->getInsideSceneNodeList()[j];
623         obs->freeInsideSceneNodeList();
624     }
625     return;
626 }
627 
628 
makeLinkMaterial()629 void        World::makeLinkMaterial()
630 {
631     const std::string name = "LinkMaterial";
632 
633     linkMaterial = MATERIALMGR.findMaterial(name);
634     if (linkMaterial != NULL)
635         return;
636 
637     int dyncolId = DYNCOLORMGR.findColor(name);
638     if (dyncolId < 0)
639     {
640         DynamicColor* dyncol = new DynamicColor;
641         dyncol->setLimits(0, 0.0f, 0.25f); // red
642         dyncol->setLimits(1, 0.0f, 0.25f); // green
643         dyncol->setLimits(2, 0.0f, 0.25f); // blue
644         dyncol->setLimits(3, 0.75f, 0.75f); // alpha
645         // period, offset, weight
646         float params[3] = {2.0f, 0.0f, 1.0f};
647         params[1] = 0.0f * (params[0] / 3.0f); // red
648         dyncol->addSinusoid(0, params);
649         params[1] = 1.0f * (params[0] / 3.0f); // green
650         dyncol->addSinusoid(1, params);
651         params[1] = 2.0f * (params[0] / 3.0f); // blue
652         dyncol->addSinusoid(2, params);
653         dyncol->setName(name);
654         dyncol->finalize();
655         dyncolId = DYNCOLORMGR.addColor (dyncol);
656     }
657 
658     int texmatId = TEXMATRIXMGR.findMatrix(name);
659     if (texmatId < 0)
660     {
661         TextureMatrix* texmat = new TextureMatrix;
662         texmat->setDynamicShift(0.0f, -0.05f);
663         texmat->setName(name);
664         texmat->finalize();
665         texmatId = TEXMATRIXMGR.addMatrix (texmat);
666     }
667 
668     BzMaterial mat;
669     const float color[4] = {0.0f, 0.0f, 0.0f, 0.5f};
670     mat.setDiffuse(color);
671     mat.setDynamicColor(dyncolId);
672     mat.setTexture("telelink");
673     mat.setTextureMatrix(texmatId);
674     mat.setNoLighting(true);
675     mat.setName(name);
676     linkMaterial = MATERIALMGR.addMaterial(&mat);
677 
678     return;
679 }
680 
681 
initFlag(int index)682 void            World::initFlag(int index)
683 {
684     // make sure the server has not sent us a bogus value.
685     if (index >= maxFlags || index < 0)
686         return;
687     // set color of flag (opaque)
688     const float* color = flags[index].type->getColor();
689     flagNodes[index]->setColor(color[0], color[1], color[2]);
690 
691     // if coming or going then position warp
692     Flag& flag = flags[index];
693     if (flag.status == FlagComing || flag.status == FlagGoing)
694     {
695         GLfloat pos[3];
696         pos[0] = flag.position[0];
697         pos[1] = flag.position[1];
698         pos[2] = 0.5f * flag.flightEnd * (flag.initialVelocity +
699                                           0.25f * BZDBCache::gravity * flag.flightEnd) + flag.position[2];
700         flagWarpNodes[index]->move(pos);
701         flagWarpNodes[index]->setSizeFraction(0.0f);
702     }
703 }
704 
updateWind(float UNUSED (dt))705 void            World::updateWind(float UNUSED(dt))
706 {
707     const float minWindSpeed = 0.0f; // FIXME - BZDB
708     const float maxWindSpeed = 10.0f; // FIXME - BZDB
709 
710     // pretty cheezy, should be fields and such
711     const double gt = GameTime::getStepTime();
712 
713     const double oneMinuteFactor = (1.0 / (60.0 * (M_PI * 2.0)));
714     const float wsf = (float)(0.5 + (0.5 * cos(gt * 15.0f * oneMinuteFactor)));
715     const float windSpeed = ((1.0f - wsf) * minWindSpeed) +
716                             (wsf * maxWindSpeed);
717 
718     const float windAngle = (float)((M_PI * 2.0) *
719                                     (cos(gt * 3.0f * oneMinuteFactor) +
720                                      cos(gt * 10.0f * oneMinuteFactor)));
721 
722     wind[0] = windSpeed * cosf(windAngle);
723     wind[1] = windSpeed * sinf(windAngle);
724     wind[2] = 0.0f;
725 }
726 
updateFlag(int index,float dt)727 void            World::updateFlag(int index, float dt)
728 {
729     if (!flagNodes) return;
730     const GLfloat* color = flagNodes[index]->getColor();
731     GLfloat alpha = color[3];
732     Flag& flag = flags[index];
733 
734     switch (flag.status)
735     {
736     default:
737         // do nothing (don't move cos either it's not moving or we
738         // don't know the position to move it to)
739         break;
740 
741     case FlagInAir:
742         flag.flightTime += dt;
743         if (flag.flightTime >= flag.flightEnd)
744         {
745             // touchdown
746             flag.status = FlagOnGround;
747             flag.position[0] = flag.landingPosition[0];
748             flag.position[1] = flag.landingPosition[1];
749             flag.position[2] = flag.landingPosition[2];
750         }
751         else
752         {
753             // still flying
754             float t = flag.flightTime / flag.flightEnd;
755             flag.position[0] = (1.0f - t) * flag.launchPosition[0] +
756                                t * flag.landingPosition[0];
757             flag.position[1] = (1.0f - t) * flag.launchPosition[1] +
758                                t * flag.landingPosition[1];
759             flag.position[2] = (1.0f - t) * flag.launchPosition[2] +
760                                t * flag.landingPosition[2] +
761                                flag.flightTime * (flag.initialVelocity +
762                                                   0.5f * BZDBCache::gravity * flag.flightTime);
763         }
764         break;
765 
766     case FlagComing:
767         flag.flightTime += dt;
768         if (flag.flightTime >= flag.flightEnd)
769         {
770             // touchdown
771             flag.status = FlagOnGround;
772             flag.position[2] = 0.0f;
773             alpha = 1.0f;
774         }
775         else if (flag.flightTime >= 0.5f * flag.flightEnd)
776         {
777             // falling
778             flag.position[2] = flag.flightTime * (flag.initialVelocity +
779                                                   0.5f * BZDBCache::gravity * flag.flightTime) + flag.landingPosition[2];
780             alpha = 1.0f;
781         }
782         else
783         {
784             // hovering
785             flag.position[2] = 0.5f * flag.flightEnd * (flag.initialVelocity +
786                                0.25f * BZDBCache::gravity * flag.flightEnd) + flag.landingPosition[2];
787 
788             // flag is fades in during first half of hovering period
789             // and is opaque during the second half.  flag warp grows
790             // to full size during first half, and shrinks to nothing
791             // during second half.
792             if (flag.flightTime >= 0.25f * flag.flightEnd)
793             {
794                 // second half
795                 float t = (flag.flightTime - 0.25f * flag.flightEnd) /
796                           (0.25f * flag.flightEnd);
797                 alpha = 1.0f;
798                 flagWarpNodes[index]->setSizeFraction(1.0f - t);
799             }
800             else
801             {
802                 // first half
803                 float t = flag.flightTime / (0.25f * flag.flightEnd);
804                 alpha = t;
805                 flagWarpNodes[index]->setSizeFraction(t);
806             }
807         }
808         break;
809 
810     case FlagGoing:
811         flag.flightTime += dt;
812         if (flag.flightTime >= flag.flightEnd)
813         {
814             // all gone
815             flag.status = FlagNoExist;
816         }
817         else if (flag.flightTime < 0.5f * flag.flightEnd)
818         {
819             // rising
820             flag.position[2] = flag.flightTime * (flag.initialVelocity +
821                                                   0.5f * BZDBCache::gravity * flag.flightTime) + flag.landingPosition[2];
822             alpha = 1.0f;
823         }
824         else
825         {
826             // hovering
827             flag.position[2] = 0.5f * flag.flightEnd * (flag.initialVelocity +
828                                0.25f * BZDBCache::gravity * flag.flightEnd) + flag.landingPosition[2];
829 
830             // flag is opaque during first half of hovering period
831             // and fades out during the second half.  flag warp grows
832             // to full size during first half, and shrinks to nothing
833             // during second half.
834             if (flag.flightTime < 0.75f * flag.flightEnd)
835             {
836                 // first half
837                 float t = (0.75f * flag.flightEnd - flag.flightTime) /
838                           (0.25f * flag.flightEnd);
839                 alpha = 1.0f;
840                 flagWarpNodes[index]->setSizeFraction(1.0f - t);
841             }
842             else
843             {
844                 // second half
845                 float t = (flag.flightEnd - flag.flightTime) /
846                           (0.25f * flag.flightEnd);
847                 alpha = t;
848                 flagWarpNodes[index]->setSizeFraction(t);
849             }
850         }
851         break;
852     }
853 
854     // update alpha if changed
855     if (alpha != color[3])
856         flagNodes[index]->setColor(color[0], color[1], color[2], alpha);
857 
858     // move flag scene node
859     flagNodes[index]->move(flags[index].position);
860 
861     // setup the flag angle
862     if (flag.status != FlagOnTank)
863     {
864         flagNodes[index]->setWind(wind, dt);
865         flagNodes[index]->setBillboard(true);
866     }
867     else
868     {
869         const Player* flagPlayer = NULL;
870         for (int i = 0; i < curMaxPlayers; i++)
871         {
872             const Player* p = players[i];
873             if (p && p->getId() == flag.owner)
874             {
875                 flagPlayer = p;
876                 break;
877             }
878         }
879         if (flagPlayer != NULL)
880         {
881             if (flag.type == Flags::Narrow)
882             {
883                 flagNodes[index]->setAngle(flagPlayer->getAngle());
884                 flagNodes[index]->setBillboard(false);
885             }
886             else
887             {
888                 float myWind[3];
889                 getWind(myWind, flagPlayer->getPosition());
890                 const float* vel = flagPlayer->getVelocity();
891                 myWind[0] -= vel[0];
892                 myWind[1] -= vel[1];
893                 if (flagPlayer->isFalling())
894                     myWind[2] -= vel[2];
895                 flagNodes[index]->setWind(myWind, dt);
896                 flagNodes[index]->setBillboard(true);
897             }
898         }
899         else
900         {
901             flagNodes[index]->setWind(wind, dt); // assumes homogeneous wind
902             flagNodes[index]->setBillboard(true);
903         }
904     }
905 }
906 
addFlags(SceneDatabase * scene,bool seerView)907 void            World::addFlags(SceneDatabase* scene, bool seerView)
908 {
909     if (!flagNodes) return;
910     for (int i = 0; i < maxFlags; i++)
911     {
912         // if not showing flags, only allow FlagOnTank through
913         if (flags[i].status != FlagOnTank && !BZDBCache::displayMainFlags)
914             continue;
915 
916         if (flags[i].status == FlagNoExist) continue;
917         // skip flag on a tank that isn't alive. also skip
918         // Cloaking flag on a tank if we don't have a Seer flag.
919         if (flags[i].status == FlagOnTank)
920         {
921             if ((flags[i].type == Flags::Cloaking) && !seerView)
922                 continue;
923             int j;
924             for (j = 0; j < curMaxPlayers; j++)
925                 if (players[j] && players[j]->getId() == flags[i].owner)
926                     break;
927 
928             if (j < curMaxPlayers && !(players[j]->getStatus() & PlayerState::Alive))
929                 continue;
930         }
931 
932         scene->addDynamicNode(flagNodes[i]);
933 
934         // add warp if coming/going and hovering
935         if ((flags[i].status == FlagComing &&
936                 flags[i].flightTime < 0.5 * flags[i].flightEnd) ||
937                 (flags[i].status == FlagGoing &&
938                  flags[i].flightTime >= 0.5 * flags[i].flightEnd))
939             scene->addDynamicNode(flagWarpNodes[i]);
940     }
941 }
942 
943 
944 static std::string indent = "";
945 
946 
writeDefaultOBJMaterials(std::ostream & out)947 static void writeDefaultOBJMaterials(std::ostream& out)
948 {
949     typedef struct
950     {
951         const char* name;
952         const char* texture;
953         float color[4];
954     } MatProps;
955     const MatProps defaultMats[] =
956     {
957         {"std_ground",  "std_ground.png",   {0.5f, 0.5f, 0.5f, 1.0f}},
958         {"boxtop",      "roof.png",     {1.0f, 1.0f, 0.9f, 1.0f}},
959         {"boxwall",     "boxwall.png",      {1.0f, 0.9f, 0.8f, 1.0f}},
960         {"pyrwall",     "pyrwall.png",      {0.8f, 0.8f, 1.0f, 1.0f}},
961         {"telefront",   "telelink.png",     {1.0f, 0.0f, 0.0f, 0.5f}},
962         {"teleback",    "telelink.png",     {0.0f, 1.0f, 0.0f, 0.5f}},
963         {"telerim",     "caution.png",      {1.0f, 1.0f, 0.0f, 0.5f}},
964         {"basetop_team1",   "red_basetop.png",  {1.0f, 0.8f, 0.8f, 1.0f}},
965         {"basewall_team1",  "red_basewall.png", {1.0f, 0.8f, 0.8f, 1.0f}},
966         {"basetop_team2",   "green_basetop.png",    {0.8f, 1.0f, 0.8f, 1.0f}},
967         {"basewall_team2",  "green_basewall.png",   {0.8f, 1.0f, 0.8f, 1.0f}},
968         {"basetop_team3",   "blue_basetop.png", {0.8f, 0.8f, 1.0f, 1.0f}},
969         {"basewall_team3",  "blue_basewall.png",    {0.8f, 0.8f, 1.0f, 1.0f}},
970         {"basetop_team4",   "purple_basetop.png",   {1.0f, 0.8f, 1.0f, 1.0f}},
971         {"basewall_team4",  "purple_basewall.png",  {1.0f, 0.8f, 1.0f, 1.0f}}
972     };
973     const int count = sizeof(defaultMats) / sizeof(defaultMats[0]);
974     BzMaterial mat;
975     for (int i = 0; i < count; i++)
976     {
977         const MatProps& mp = defaultMats[i];
978         mat.setName(mp.name);
979         mat.setTexture(mp.texture);
980         mat.setDiffuse(mp.color);
981         mat.printMTL(out, "");
982     }
983     return;
984 }
985 
986 
writeOBJGround(std::ostream & out)987 static void writeOBJGround(std::ostream& out)
988 {
989     const float ws = BZDBCache::worldSize / 2.0f;
990     const float ts = BZDBCache::worldSize / 100.0f;
991     out << "o bzground" << std::endl;
992     out << "v " << -ws << " " << -ws << " 0" << std::endl;
993     out << "v " << +ws << " " << -ws << " 0" << std::endl;
994     out << "v " << +ws << " " << +ws << " 0" << std::endl;
995     out << "v " << -ws << " " << +ws << " 0" << std::endl;
996     out << "vt " << -ts << " " << -ts << std::endl;
997     out << "vt " << +ts << " " << -ts << std::endl;
998     out << "vt " << +ts << " " << +ts << std::endl;
999     out << "vt " << -ts << " " << +ts << std::endl;
1000     out << "vn 0 0 1" << std::endl;
1001     out << "usemtl std_ground" << std::endl;
1002     out << "f -4/-4/-1 -3/-3/-1 -2/-2/-1 -1/-1/-1" << std::endl;
1003     out << std::endl;
1004     return;
1005 }
1006 
1007 
writeBZDBvar(const std::string & name,void * userData)1008 static void writeBZDBvar (const std::string& name, void *userData)
1009 {
1010     std::ofstream& out = *((std::ofstream*)userData);
1011     if ((BZDB.getPermission(name) == StateDatabase::Server)
1012             && (BZDB.get(name) != BZDB.getDefault(name))
1013             && (name != "poll"))
1014     {
1015         std::string qmark = "";
1016         if (BZDB.get(name).find(' ') != std::string::npos)
1017             qmark = '"';
1018         out << indent << "  -set " << name << " "
1019             << qmark << BZDB.get(name) << qmark << std::endl;
1020     }
1021     return;
1022 }
1023 
1024 
writeWorld(const std::string & filename,std::string & fullname)1025 bool World::writeWorld(const std::string& filename, std::string& fullname)
1026 {
1027     const bool saveAsOBJ = BZDB.isTrue("saveAsOBJ");
1028     if (saveAsOBJ)
1029         indent = "# ";
1030     else
1031         indent = "";
1032 
1033     fullname = getWorldDirName();
1034     fullname += filename;
1035     if (saveAsOBJ)
1036     {
1037         if (strstr(fullname.c_str(), ".obj") == NULL)
1038             fullname += ".obj";
1039     }
1040     else
1041     {
1042         if (strstr(fullname.c_str(), ".bzw") == NULL)
1043             fullname += ".bzw";
1044     }
1045 
1046     std::ostream *stream = FILEMGR.createDataOutStream(fullname.c_str());
1047     if (stream == NULL)
1048         return false;
1049 
1050     // for notational convenience
1051     std::ostream& out = *stream;
1052 
1053     time_t nowTime = time (NULL);
1054     out << "# BZFlag client: saved world on " << ctime(&nowTime) << std::endl;
1055 
1056     // Write the Server Options
1057     {
1058         out << indent << "options" << std::endl;
1059 
1060         // FIXME - would be nice to get a few other thing
1061         //   -fb, -sb, rabbit style, a real -mp, etc... (also, flags?)
1062 
1063         if (allowTeamFlags())
1064         {
1065             out << indent << "  -c" << std::endl;
1066             out << indent << "  -mp 2,";
1067             for (int t = RedTeam; t <= PurpleTeam; t++)
1068             {
1069                 if (getBase(t, 0) != NULL)
1070                     out << "2,";
1071                 else
1072                     out << "0,";
1073             }
1074             out << "2" << std::endl;
1075         }
1076         if (allowRabbit())
1077             out << indent << "  -rabbit" << std::endl;
1078         if (allowJumping())
1079             out << indent << "  -j" << std::endl;
1080         if (allShotsRicochet())
1081             out << indent << "  +r" << std::endl;
1082         if (allowHandicap())
1083             out << indent << "  -handicap" << std::endl;
1084         if (allowAntidote())
1085         {
1086             out << indent << "  -sa" << std::endl;
1087             out << indent << "  -st " << getFlagShakeTimeout() << std::endl;
1088             out << indent << "  -sw " << getFlagShakeWins() << std::endl;
1089         }
1090         if ((getLinearAcceleration() != 0.0f) ||
1091                 (getAngularAcceleration() != 0.0f))
1092         {
1093             out << indent << "  -a " << getLinearAcceleration() << " "
1094                 << getAngularAcceleration() << std::endl;
1095         }
1096 
1097         out << indent << "  -ms " << getMaxShots() << std::endl;
1098 
1099         // Write BZDB server variables that aren't defaults
1100         BZDB.iterate (writeBZDBvar, &out);
1101 
1102         out << indent << "end" << std::endl << std::endl;
1103     }
1104 
1105     // Write World object
1106     {
1107         float worldSize = BZDBCache::worldSize;
1108         float flagHeight = BZDB.eval(StateDatabase::BZDB_FLAGHEIGHT);
1109         if ((worldSize != atof(BZDB.getDefault(StateDatabase::BZDB_WORLDSIZE).c_str()))
1110                 ||  (flagHeight != atof(BZDB.getDefault(StateDatabase::BZDB_FLAGHEIGHT).c_str())))
1111         {
1112             out << indent << "world" << std::endl;
1113             if (worldSize != atof(BZDB.getDefault(StateDatabase::BZDB_WORLDSIZE).c_str()))
1114                 out << indent << "  size " << worldSize / 2.0f << std::endl;
1115             if (flagHeight != atof(BZDB.getDefault(StateDatabase::BZDB_FLAGHEIGHT).c_str()))
1116                 out << indent << "  flagHeight " << flagHeight << std::endl;
1117             out << indent << "end" << std::endl << std::endl;
1118         }
1119     }
1120 
1121     // Write dynamic colors
1122     DYNCOLORMGR.print(out, indent);
1123 
1124     // Write texture matrices
1125     TEXMATRIXMGR.print(out, indent);
1126 
1127     // Write materials
1128     if (!saveAsOBJ)
1129         MATERIALMGR.print(out, indent);
1130     else
1131     {
1132         const std::string mtlname = filename + ".mtl";
1133         const std::string mtlfile = getWorldDirName() + mtlname;
1134         std::ostream* mtlStream = FILEMGR.createDataOutStream(mtlfile.c_str());
1135         if (mtlStream != NULL)
1136         {
1137             out << "mtllib " << mtlname << std::endl; // index the mtl file
1138             out << std::endl;
1139             *mtlStream << "# BZFlag client: saved world on " << ctime(&nowTime);
1140             *mtlStream << std::endl;
1141             writeDefaultOBJMaterials(*mtlStream);
1142             MATERIALMGR.printMTL(*mtlStream, "");
1143             delete mtlStream;
1144         }
1145     }
1146 
1147     // Write physics drivers
1148     PHYDRVMGR.print(out, indent);
1149 
1150     // Write obstacle transforms
1151     TRANSFORMMGR.print(out, indent);
1152 
1153     // Write water level
1154     {
1155         if (waterLevel >= 0.0f)
1156         {
1157             out << indent << "waterLevel" << std::endl;
1158             out << indent << "  height " << waterLevel << std::endl;
1159             out << indent << "  matref ";
1160             MATERIALMGR.printReference(out, waterMaterial);
1161             out << std::endl;
1162             out << indent << "end" << std::endl << std::endl;
1163         }
1164     }
1165 
1166     // Write the world obstacles
1167     {
1168         if (saveAsOBJ)
1169             writeOBJGround(out);
1170         OBSTACLEMGR.print(out, indent);
1171     }
1172 
1173     // Write links
1174     {
1175         links.print(out, indent);
1176     }
1177 
1178     // Write weapons
1179     {
1180         for (std::vector<Weapon>::iterator it = weapons.begin();
1181                 it != weapons.end(); ++it)
1182         {
1183             Weapon weapon = *it;
1184             out << indent << "weapon" << std::endl;
1185             if (weapon.type != Flags::Null)
1186                 out << indent << "  type " << weapon.type->flagAbbv << std::endl;
1187             out << indent << "  position " << weapon.pos[0] << " " << weapon.pos[1] << " "
1188                 << weapon.pos[2] << std::endl;
1189             out << indent << "  rotation " << ((weapon.dir * 180.0) / M_PI) << std::endl;
1190             out << indent << "  initdelay " << weapon.initDelay << std::endl;
1191             if (!weapon.delay.empty())
1192             {
1193                 out << indent << "  delay";
1194                 for (std::vector<float>::iterator dit = weapon.delay.begin();
1195                         dit != weapon.delay.end(); ++dit)
1196                     out << " " << (float)*dit;
1197                 out << std::endl;
1198             }
1199             out << indent << "end" << std::endl << std::endl;
1200         }
1201     }
1202 
1203     // Write entry zones
1204     {
1205         for (std::vector<EntryZone>::iterator it = entryZones.begin();
1206                 it != entryZones.end(); ++it)
1207         {
1208             EntryZone zone = *it;
1209             out << indent << "zone" << std::endl;
1210             out << indent << "  position " << zone.pos[0] << " " << zone.pos[1] << " "
1211                 << zone.pos[2] << std::endl;
1212             out << indent << "  size " << zone.size[0] << " " << zone.size[1] << " "
1213                 << zone.size[2] << std::endl;
1214             out << indent << "  rotation " << ((zone.rot * 180.0) / M_PI) << std::endl;
1215             if (!zone.flags.empty())
1216             {
1217                 out << indent << "  flag";
1218                 std::vector<FlagType*>::iterator fit;
1219                 for (fit = zone.flags.begin(); fit != zone.flags.end(); ++fit)
1220                     out << " " << (*fit)->flagAbbv;
1221                 out << std::endl;
1222             }
1223             if (!zone.teams.empty())
1224             {
1225                 out << indent << "  team";
1226                 std::vector<TeamColor>::iterator tit;
1227                 for (tit = zone.teams.begin(); tit != zone.teams.end(); ++tit)
1228                     out << " " << (*tit);
1229                 out << std::endl;
1230             }
1231             if (!zone.safety.empty())
1232             {
1233                 out << indent << "  safety";
1234                 std::vector<TeamColor>::iterator sit;
1235                 for (sit = zone.safety.begin(); sit != zone.safety.end(); ++sit)
1236                     out << " " << (*sit);
1237                 out << std::endl;
1238             }
1239             out << indent << "end" << std::endl << std::endl;
1240         }
1241     }
1242 
1243     delete stream;
1244 
1245     return true;
1246 }
1247 
drawLines(int count,float (* vertices)[3],int color)1248 static void drawLines (int count, float (*vertices)[3], int color)
1249 {
1250     const float colors[][4] =
1251     {
1252         { 0.25f, 0.25f, 0.25f, 0.8f }, // gray    (branch node)
1253         { 0.25f, 0.25f, 0.0f,  0.8f }, // yellow  (regular)
1254         { 0.0f,  0.25f, 0.25f, 0.8f }, // cyan    (meshed)
1255         { 0.25f, 0.0f,  0.25f, 0.8f }, // purple  (meshed + regular)
1256     };
1257     const int colorCount = sizeof(colors) / sizeof(colors[0]);
1258 
1259     if (color < 0)
1260         color = 0;
1261     else if (color >= colorCount)
1262         color = colorCount - 1;
1263     glColor4fv (colors[color]);
1264 
1265     glBegin (GL_LINE_STRIP);
1266     for (int i = 0; i < count; i++)
1267         glVertex3fv (vertices[i]);
1268     glEnd ();
1269 
1270     return;
1271 }
1272 
1273 
drawInsideOutsidePoints()1274 static void drawInsideOutsidePoints()
1275 {
1276     std::vector<const float*> insides;
1277     std::vector<const float*> outsides;
1278 
1279     const ObstacleList& meshes = OBSTACLEMGR.getMeshes();
1280     for (unsigned int i = 0; i < meshes.size(); i++)
1281     {
1282         const MeshObstacle* mesh = (const MeshObstacle*) meshes[i];
1283         const int    checkCount  = mesh->getCheckCount();
1284         const char*  checkTypes  = mesh->getCheckTypes();
1285         const afvec3* checkPoints = mesh->getCheckPoints();
1286         for (int c = 0; c < checkCount; c++)
1287         {
1288             switch (checkTypes[c])
1289             {
1290             case MeshObstacle::CheckInside:
1291             {
1292                 insides.push_back(checkPoints[c]);
1293                 break;
1294             }
1295             case MeshObstacle::CheckOutside:
1296             {
1297                 outsides.push_back(checkPoints[c]);
1298                 break;
1299             }
1300             default:
1301             {
1302                 break;
1303             }
1304             }
1305         }
1306     }
1307 
1308     glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_POINT_BIT | GL_LINE_BIT);
1309 
1310     glDisable(GL_DEPTH_TEST);
1311     glEnable(GL_POINT_SMOOTH);
1312     glEnable(GL_LINE_SMOOTH);
1313     glLineWidth(1.49f);
1314     glPointSize(4.49f);
1315 
1316     glBegin(GL_POINTS);
1317     {
1318         glColor4f(0.0f, 1.0f, 0.0f, 0.8f);
1319         for (size_t i = 0; i < insides.size(); i++)
1320             glVertex3fv(insides[i]);
1321         glColor4f(1.0f, 0.0f, 0.0f, 0.8f);
1322         for (size_t i = 0; i < outsides.size(); i++)
1323             glVertex3fv(outsides[i]);
1324     }
1325     glEnd();
1326 
1327     glBegin(GL_LINES);
1328     {
1329         glColor4f(0.0f, 1.0f, 0.0f, 0.2f);
1330         for (size_t i = 0; i < insides.size(); i++)
1331         {
1332             glVertex3f(insides[i][0], insides[i][1], 0.0f);
1333             glVertex3fv(insides[i]);
1334         }
1335         glColor4f(1.0f, 0.0f, 0.0f, 0.2f);
1336         for (size_t i = 0; i < outsides.size(); i++)
1337         {
1338             glVertex3f(outsides[i][0], outsides[i][1], 0.0f);
1339             glVertex3fv(outsides[i]);
1340         }
1341     }
1342     glEnd();
1343 
1344     glPopAttrib();
1345 }
1346 
1347 
drawCollisionGrid() const1348 void World::drawCollisionGrid() const
1349 {
1350     GLboolean usingTextures;
1351 
1352     glGetBooleanv (GL_TEXTURE_2D, &usingTextures);
1353     glDisable (GL_TEXTURE_2D);
1354 
1355     COLLISIONMGR.draw (&drawLines);
1356 
1357     drawInsideOutsidePoints();
1358 
1359     if (usingTextures)
1360         glEnable (GL_TEXTURE_2D);
1361 
1362     return;
1363 }
1364 
getCurrentRabbit() const1365 RemotePlayer* World::getCurrentRabbit() const
1366 {
1367     if (players == NULL)
1368         return NULL;
1369     for (int i = 0; i < curMaxPlayers; i++)
1370     {
1371         RemotePlayer* p = players[i];
1372         if (p && p->isAlive() && (p->getTeam() == RabbitTeam))
1373             return p;
1374     }
1375     return NULL;
1376 }
1377 
1378 
1379 // Local Variables: ***
1380 // mode: C++ ***
1381 // tab-width: 4 ***
1382 // c-basic-offset: 4 ***
1383 // indent-tabs-mode: nil ***
1384 // End: ***
1385 // ex: shiftwidth=4 tabstop=4
1386