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