Angel 3.2
A 2D Game Prototyping Engine
World.cpp
1 
2 // Copyright (C) 2008-2013, Shane Liesegang
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of the copyright holder nor the names of any
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "stdafx.h"
31 #include "../Infrastructure/World.h"
32 
33 #include "../Infrastructure/Camera.h"
34 #include "../Infrastructure/Log.h"
35 #include "../AI/SpatialGraph.h"
36 #if !ANGEL_MOBILE
37  #include "../Infrastructure/Console.h"
38  #include "../Input/Input.h"
39  #include "../Input/MouseInput.h"
40  #include "../Input/Controller.h"
41  #include "../Input/InputManager.h"
42 #else
43  #include "../Infrastructure/TextRendering.h"
44 #endif
45 #include "../Infrastructure/Textures.h"
46 #include "../Actors/PhysicsActor.h"
47 #include "../Messaging/Switchboard.h"
48 #include "../Scripting/LuaModule.h"
49 #include "../Infrastructure/Preferences.h"
50 #include "../Infrastructure/SoundDevice.h"
51 #include "../UI/UserInterface.h"
52 #include "../Util/DrawUtil.h"
53 
54 #if defined(WIN32) && defined(ANGEL_RELEASE)
55  #include <comdef.h>
56 #endif
57 #include <algorithm>
58 
59 World* World::s_World = NULL;
60 
61 World::World()
62 {
63  _simulateOn = true;
64  _initialized = false;
65  _started = false;
66  _physicsRemainderDT = 0.0f;
67  _physicsSetUp = false;
68  _physicsRunning = false;
69  _running = false;
70 
71  _blockersOn = false;
72  _blockerRestitution = 0.0f;
73  _blockers[0] = _blockers[1] = _blockers[2] = _blockers[3] = NULL;
74  _gameManager = NULL;
75  _elementsLocked = false;
76  _highResScreen = false;
77 
78  _processingDeferredAdds = false;
79 }
80 
82 {
83  if (s_World == NULL)
84  {
85  s_World = new World();
86  }
87  return *s_World;
88 }
89 
90 void ReloadLevel( const String& levelName )
91 {
92  theWorld.UnloadAll();
93  theWorld.LoadLevel( levelName );
94 }
95 
96 
97 void UnloadAllStatic( const String& /*input*/ )
98 {
99  theWorld.UnloadAll();
100 }
101 
102 #if !ANGEL_MOBILE
103  void windowClosed(GLFWwindow* window)
104  {
105  theWorld.StopGame();
106  }
107 #endif
108 
109 bool World::Initialize(unsigned int windowWidth, unsigned int windowHeight, String windowName, bool antiAliasing, bool fullScreen, bool resizable)
110 {
111  if (_initialized)
112  {
113  return false;
114  }
115 
116  _running = true;
117 
118  // Windows DLL locations
119  #if defined(WIN32) && defined(ANGEL_RELEASE)
120  String bitsPath = "bits";
121  char currentDir[MAX_PATH];
122  _getcwd(currentDir, MAX_PATH);
123  String currDir(currentDir);
124 
125  StringList libs;
126  #if !ANGEL_DISABLE_DEVIL
127  libs.push_back("DevIL.dll");
128  libs.push_back("ILU.dll");
129  libs.push_back("ILUT.dll");
130  #endif
131  #if ANGEL_DISABLE_FMOD
132  libs.push_back("OpenAL32.dll");
133  #else
134  libs.push_back("fmodex.dll");
135  #endif
136 
137  for (int i=0; i < libs.size(); i++)
138  {
139  String libstring = currDir + "\\" + bitsPath + "\\" + libs[i];
140  HMODULE work = LoadLibrary(libstring.c_str());
141  if (work == 0)
142  {
143  DWORD err = GetLastError();
144  _com_error error(err);
145  LPCSTR errorText = error.ErrorMessage();
146  sysLog.Printf("ERROR: Couldn't load DLL (%s); %s", libs[i].c_str(), errorText);
147  }
148  }
149 
150  // does bits directory exist?
151  DWORD dwAttrib = GetFileAttributes(bitsPath.c_str());
152  if (dwAttrib != INVALID_FILE_ATTRIBUTES && dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
153  {
154  _chdir(bitsPath.c_str());
155  }
156  #endif
157 
158  // General windowing initialization
159  #if !ANGEL_MOBILE
160  glfwInit();
161  #endif
162 
163  #if defined(__APPLE__)
164  // Set up paths correctly in the .app bundle
165  #if !ANGEL_MOBILE
166  CFBundleRef mainBundle = CFBundleGetMainBundle();
167  CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
168  char path[PATH_MAX];
169  if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
170  {
171  sysLog.Log("ERROR: Problem setting up working directory! Probably nothing will work!");
172  }
173  CFRelease(resourcesURL);
174  chdir(path);
175  chdir("..");
176  #if defined(ANGEL_DEBUG)
177  // set paths to the local resources rather than the copied ones
178  String fileName = __FILE__;
179  String dirPath = fileName.substr(0, fileName.size() - String("Angel/Infrastructure/World.cpp").size());
180  CFURLRef exeURL = CFBundleCopyExecutableURL(mainBundle);
181  char exePath[PATH_MAX];
182  if (!CFURLGetFileSystemRepresentation(exeURL, TRUE, (UInt8 *)exePath, PATH_MAX))
183  {
184  sysLog.Log("ERROR: Problem setting up working directory! Probably nothing will work!");
185  }
186  CFRelease(exeURL);
187  chdir(dirPath.c_str());
188  StringList pathElements = SplitString(exePath, "/");
189  String exeName = pathElements[pathElements.size()-1];
190  chdir(exeName.c_str());
191  #endif
192  #else
193  CFBundleRef mainBundle = CFBundleGetMainBundle();
194  CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
195  char path[PATH_MAX];
196  if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
197  {
198  std::cout << "Problem setting up working directory! Probably nothing will work!" << std::endl;
199  }
200  CFRelease(resourcesURL);
201  chdir(path);
202  chdir("Angel"); // the iPhone doesn't like having a "Resources" directory in the root of the .app bundle
203  #endif
204  #endif
205 
206  //Start scripting
208 
209  //Reset values based on preferences
210  antiAliasing = thePrefs.OverrideInt("WindowSettings", "antiAliasing", antiAliasing);
211  fullScreen = thePrefs.OverrideInt("WindowSettings", "fullScreen", fullScreen);
212  resizable = thePrefs.OverrideInt("WindowSettings", "resizable", resizable);
213  windowHeight = thePrefs.OverrideInt("WindowSettings", "height", windowHeight);
214  windowWidth = thePrefs.OverrideInt("WindowSettings", "width", windowWidth);
215  windowName = thePrefs.OverrideString("WindowSettings", "name", windowName);
216 
217  //Windowing system setup
218  #if !ANGEL_MOBILE
219  if (antiAliasing)
220  {
221  glfwWindowHint(GLFW_SAMPLES, 4); //4x looks pretty good
222  _antiAliased = true;
223  }
224  else
225  {
226  _antiAliased = false;
227  }
228 
229  GLFWmonitor* openOn = NULL; // windowed
230  if (fullScreen)
231  {
232  openOn = glfwGetPrimaryMonitor();
233  }
234  if (resizable)
235  {
236  glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);
237  }
238  else
239  {
240  glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
241  }
242 
243  _mainWindow = glfwCreateWindow(windowWidth, windowHeight, windowName.c_str(), openOn, NULL);
244  glfwMakeContextCurrent(_mainWindow);
245 
246  int fbw, fbh;
247  glfwGetFramebufferSize(_mainWindow, &fbw, &fbh);
248  if (fbw == windowWidth * 2)
249  {
251  }
252 
253  #if defined(WIN32)
254  glfwSwapInterval(0); // because double-buffering and Windows don't get along apparently
255  #else
256  glfwSwapInterval(1);
257  #endif
258  glfwSetWindowSizeCallback(_mainWindow, Camera::ResizeCallback);
259  glfwSetKeyCallback(_mainWindow, keyboardInput);
260  glfwSetCharCallback(_mainWindow, charInput);
261  glfwSetCursorPosCallback(_mainWindow, MouseMotion);
262  glfwSetMouseButtonCallback(_mainWindow, MouseButton);
263  glfwSetScrollCallback(_mainWindow, MouseWheel);
264  glfwSetWindowCloseCallback(_mainWindow, windowClosed);
265  _prevTime = glfwGetTime();
266 
267  Camera::ResizeCallback(_mainWindow, fbw, fbh);
268  #else
269  struct timeval tv;
270  gettimeofday(&tv, NULL);
271  _currTime = _startTime = tv.tv_sec + (double) tv.tv_usec / 1000000.0;
272  #endif
273 
274  //OpenGL state setup
275  #if !ANGEL_MOBILE
276  glClearDepth(1.0f);
277  glPolygonMode(GL_FRONT, GL_FILL);
278  #else
279  glEnable(GL_POLYGON_OFFSET_FILL);
280  glPolygonOffset(1.0f, -1.0f);
281  #endif
282  glShadeModel(GL_FLAT);
283  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
284  glEnable(GL_CULL_FACE);
285  glFrontFace(GL_CCW);
286  glCullFace(GL_BACK);
287  glDepthFunc(GL_LEQUAL);
288  glEnable(GL_DEPTH_TEST);
289  glEnable(GL_BLEND);
290  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
291  glClearStencil(0);
292  glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
293 
294  //Get textures going
295  InitializeTextureLoading();
296 
297  //Subscribe to camera changes
298  theSwitchboard.SubscribeTo(this, "CameraChange");
299 
300  //initialize singletons
301  #if !ANGEL_MOBILE
302  theInput;
303  theControllerManager.Setup();
304  #endif
305  theSound;
306  theSpatialGraph;
307 
308  #if !ANGEL_MOBILE
310  #else
311  // register fonts, since we don't have the console doing it for us on the phone
312  RegisterFont("Resources/Fonts/Inconsolata.otf", 24, "Console");
313  RegisterFont("Resources/Fonts/Inconsolata.otf", 18, "ConsoleSmall");
314  #endif
315 
317 
318  return _initialized = true;
319 }
320 
321 #if !ANGEL_MOBILE
322  GLFWwindow* World::GetMainWindow()
323  {
324  return _mainWindow;
325  }
326 #endif
327 
328 std::vector<Vec3ui> World::GetVideoModes()
329 {
330  std::vector<Vec3ui> forReturn;
331  #if !ANGEL_MOBILE
332  int numModes = 0;
333  const GLFWvidmode* vidModes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &numModes);
334 
335  for (int i=0; i < numModes; i++)
336  {
337  Vec3ui avm;
338  avm.X = vidModes[i].width;
339  avm.Y = vidModes[i].height;
340  avm.Z = vidModes[i].redBits + vidModes[i].greenBits + vidModes[i].blueBits;
341  forReturn.push_back(avm);
342  }
343  #else
344  //TODO: return the device's native resolution?
345  #endif
346 
347  return forReturn;
348 }
349 
350 void World::AdjustWindow(int windowWidth, int windowHeight, const String& windowName)
351 {
352  #if !ANGEL_MOBILE
353  glfwSetWindowTitle(_mainWindow, windowName.c_str());
354 
355  int width, height;
356  glfwGetWindowSize(_mainWindow, &width, &height);
357  if ( (width != windowWidth) || (height != windowHeight) )
358  {
359  glfwSetWindowSize(_mainWindow, windowWidth, windowHeight);
360  }
361  #endif
362 }
363 
364 void World::MoveWindow(int xPosition, int yPosition)
365 {
366  #if !ANGEL_MOBILE
367  glfwSetWindowPos(_mainWindow, xPosition, yPosition);
368  #endif
369 }
370 
371 
372 bool World::SetupPhysics(const Vector2& gravity, const Vector2& maxVertex, const Vector2& minVertex)
373 {
374  if (_physicsSetUp)
375  {
376  return false;
377  }
378 
379  //setup physics
380  b2AABB worldAABB;
381  worldAABB.lowerBound.Set(minVertex.X, minVertex.Y);
382  worldAABB.upperBound.Set(maxVertex.X, maxVertex.Y);
383  b2Vec2 gravityVector(gravity.X, gravity.Y);
384  _physicsWorld = new b2World(gravityVector);
385 
386  _physicsWorld->SetContactListener(this);
387 
388  return _physicsSetUp = _physicsRunning = true;
389 }
390 
391 
393 {
394  #if !ANGEL_MOBILE
395  theInput.Destroy();
396  #endif
397  theSound.Shutdown();
398 
399  FinalizeTextureLoading();
401 
402  theUI.Shutdown();
403 
404  if (_gameManager != NULL)
405  {
406  delete _gameManager;
407  }
408 }
409 
411 {
412  if (_started)
413  {
414  return;
415  }
416  _started = true;
417 
418  theSwitchboard.Broadcast(new Message("GameStart"));
419 
420  //enter main loop
421  while(_running)
422  {
423  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
424  glMatrixMode(GL_MODELVIEW);
425  glPushMatrix();
426 
427  TickAndRender();
428 
429  glMatrixMode(GL_MODELVIEW);
430  glPopMatrix();
431  #if !ANGEL_MOBILE
432  glfwSwapBuffers(_mainWindow);
433  glfwPollEvents();
434  #endif
435  }
436 
437  #if !ANGEL_MOBILE
438  glfwDestroyWindow(_mainWindow);
439  glfwTerminate();
440  #endif
441 }
442 
444 {
445  _running = false;
446 }
447 
448 void World::ScriptExec(const String& code)
449 {
451 }
452 
453 void World::LoadLevel(const String& levelName)
454 {
455  lua_State* L = LuaScriptingModule::GetLuaState();
456  lua_getglobal(L, "LoadLevel");
457  lua_pushstring(L, levelName.c_str());
458  if (lua_pcall(L, 1, 0, 0 ))
459  {
460  const char* errs = lua_tostring(L, -1);
461  sysLog.Printf("ERROR: %s\n", errs);
462  // error, will be in the stack trace
463  lua_gc(L, LUA_GCCOLLECT, 0); // garbage collect on error
464  }
465 }
466 
467 float World::CalculateNewDT()
468 {
469  #if ANGEL_MOBILE
470  // SJML - We're now using the iOS GLKit to handle the timing of the update
471  // functions, so there's no need to compare against time of day anymore.
472  // This code is being left here (commented) in case we decide to someday
473  // port to other mobile platforms, stop using GLKit, etc.
474  // struct timeval tv;
475  // gettimeofday(&tv, NULL);
476  // _currTime = tv.tv_sec + (double) tv.tv_usec / 1000000.0 - _startTime;
477  return _systemEstimatedDT;
478  #else
479  _currTime = glfwGetTime();
480  #endif
481  _dt = MathUtil::Clamp((_currTime - _prevTime), 0.0f, MAX_TIMESTEP);
482  _prevTime = _currTime;
483  return _dt;
484 }
485 
486 void World::Simulate(bool simRunning)
487 {
488  float frame_dt = CalculateNewDT();
489 
490  //system updates
491  #if !ANGEL_MOBILE
492  theControllerManager.UpdateState();
493  _console->Update( (float)frame_dt );
494  #endif
495 
496  // Must be called once per frame.
497  theSound.Update();
498 
499  //make sure the game manager gets updates first, if we have one
500  if (_gameManager)
501  {
502  _gameManager->Update(frame_dt);
503  }
504 
505  if (simRunning)
506  {
507  // Deliver any messages that have been queued from the last frame.
508  theSwitchboard.SendAllMessages();
509 
510  RunPhysics(frame_dt);
511 
512  //Flag that the _elements array is locked so we don't try to add any
513  // new actors during the update.
514  _elementsLocked = true;
515  UpdateRenderables(frame_dt);
516  CleanupRenderables();
517  _elementsLocked = false;
518 
519  // Now that we're done updating the list, allow any deferred Adds to be processed.
520  ProcessDeferredAdds();
521  ProcessDeferredLayerChanges();
522  ProcessDeferredRemoves();
523 
524  theSwitchboard.Update(frame_dt);
525 
526  UpdateDebugItems(frame_dt);
527  //if there are any system updates that still need to be run, put them here
528  }
529 
530  //making this the last update so we can accurately lock on position for rendering
531  theCamera.Update(frame_dt);
532 }
533 
534 void World::RunPhysics(float frame_dt)
535 {
536  if (!_physicsSetUp || !_physicsRunning)
537  return;
538 
539  _currentTouches.clear();
540 
541  // fixed time step
542  const float physicsDT = 1.0f/60.f;
543 
544  float total_step = _physicsRemainderDT + frame_dt;
545  while (total_step >= physicsDT)
546  {
547  // more iterations -> more stability, more cpu
548  // tune to your liking...
549  GetPhysicsWorld().Step(physicsDT, /*velocity iterations*/ 10, /* position iterations */ 10);
550  total_step -= physicsDT;
551  }
552  _physicsRemainderDT = total_step;
553 
554  // update PhysicsActors
555  for (b2Body* b = GetPhysicsWorld().GetBodyList(); b; b = b->GetNext())
556  {
557  b2Vec2 vec = b->GetPosition();
558  PhysicsActor *physActor = reinterpret_cast<PhysicsActor*>(b->GetUserData());
559  if (physActor != NULL)
560  {
561  physActor->_syncPosRot(vec.x, vec.y, MathUtil::ToDegrees(b->GetAngle()));
562  }
563  }
564 }
565 
566 void World::SendCollisionNotifications(b2Contact* contact, bool beginning)
567 {
568  String messageStart;
569  if (beginning)
570  {
571  messageStart = "CollisionStartWith";
572  }
573  else
574  {
575  messageStart = "CollisionEndWith";
576  }
577 
578  PhysicsActor* pa1 = (PhysicsActor*)contact->GetFixtureA()->GetBody()->GetUserData();
579  PhysicsActor* pa2 = (PhysicsActor*)contact->GetFixtureB()->GetBody()->GetUserData();
580 
581  if (pa1 != NULL)
582  {
583  String pa1Message = messageStart + pa1->GetName();
584  if (theSwitchboard.GetSubscribersTo(pa1Message).size() > 0)
585  {
586  if (_currentTouches[pa1].find(pa2) == _currentTouches[pa1].end())
587  {
588  TypedMessage<b2Contact*>* coll = new TypedMessage<b2Contact*>(pa1Message, contact, pa2);
589  theSwitchboard.Broadcast(coll);
590  }
591  _currentTouches[pa1].insert(pa2);
592  }
593  }
594 
595  if (pa2 != NULL)
596  {
597  String pa2Message = messageStart + pa2->GetName();
598  if (theSwitchboard.GetSubscribersTo(pa2Message).size() > 0)
599  {
600  if (_currentTouches[pa2].find(pa1) == _currentTouches[pa2].end())
601  {
602  TypedMessage<b2Contact*>* coll = new TypedMessage<b2Contact*>(pa2Message, contact, pa1);
603  theSwitchboard.Broadcast(coll);
604  }
605  _currentTouches[pa2].insert(pa1);
606  }
607  }
608 }
609 
610 void World::BeginContact(b2Contact* contact)
611 {
612  SendCollisionNotifications(contact, true);
613 }
614 
615 void World::EndContact(b2Contact* contact)
616 {
617  SendCollisionNotifications(contact, false);
618 }
619 
621 {
622  Tick();
623  Render();
624 }
625 
627 {
628  Simulate(_simulateOn);
629 }
630 
632 {
633  // Setup the camera matrix.
634  theCamera.Render();
635 
636  DrawRenderables();
637 
638  // Give the GameManager a chance to draw something.
639  if (_gameManager)
640  _gameManager->Render();
641 
642  //Render debug information
643  theSpatialGraph.Render();
644 
645  theUI.Render();
646 
647  DrawDebugItems();
648 
649  #if !ANGEL_MOBILE
650  //Draw developer console
651  _console->Render();
652  #endif
653 
654  HandleGLErrors();
655 }
656 
657 void World::CleanupRenderables()
658 {
659  RenderableIterator it = theWorld.GetFirstRenderable();
660  while (it != theWorld.GetLastRenderable())
661  {
662  if ((*it)->IsDestroyed())
663  {
664  Renderable* deadManWalking = *it;
665  it = it.erase(it);
666  delete deadManWalking;
667  }
668  else
669  {
670  ++it;
671  }
672  }
673 }
674 
675 void World::UpdateRenderables(float frame_dt)
676 {
677  RenderableIterator it = theWorld.GetFirstRenderable();
678  while (it != theWorld.GetLastRenderable())
679  {
680  (*it)->Update(frame_dt);
681  ++it;
682  }
683 }
684 
685 void World::DrawRenderables()
686 {
687  RenderableIterator it = theWorld.GetFirstRenderable();
688  while (it != theWorld.GetLastRenderable())
689  {
690  (*it)->Render();
691  ++it;
692  }
693 }
694 
695 const float World::GetDT()
696 {
697  return _dt;
698 }
699 
701 {
702  return _simulateOn = true;
703 }
704 
706 {
707  _simulateOn = false;
708  return true;
709 }
710 
712 {
713  return _simulateOn;
714 }
715 
717 {
718  if (!_physicsSetUp)
719  {
720  sysLog.Log("WARNING: Attempted to pause physics, but physics haven't been set up yet.");
721  return false;
722  }
723  _physicsRunning = false;
724  return true;
725 }
726 
728 {
729  if (!_physicsSetUp)
730  {
731  sysLog.Log("WARNING: Attempted to resume physics, but physics haven't been set up yet.");
732  return false;
733  }
734  _physicsRunning = true;
735  return false;
736 }
737 
739 {
740  theCamera.Reset();
741  UnloadAll();
742 }
743 
744 void World::SetBackgroundColor(const Color& bgColor)
745 {
746  glClearColor(bgColor.R, bgColor.G, bgColor.B, 1.0f);
747 }
748 
749 void World::Add(Renderable *newElement, int layer)
750 {
751  if (newElement == NULL)
752  {
753  sysLog.Log("WARNING: Can't add a null element to the World.");
754  return;
755  }
756 
757  //Check to see if it's an Actor; give it a name if it doesn't have one
758  Actor *a = dynamic_cast<Actor*> (newElement);
759  if (a != NULL && !_processingDeferredAdds)
760  {
761  // Ensures that the actor has a unique, non-empty name.
762  a->SetName(a->GetName());
763  }
764 
765  // If we're not locked, add directly to _elements.
766  if (!_elementsLocked)
767  {
768  newElement->_layer = layer;
769  _layers[layer].push_back(newElement);
770  }
771  // If we're locked, add to _deferredAdds and we'll add the new
772  // Renderable after we're done updating all the _elements.
773  else
774  {
775  RenderableLayerPair addMe;
776  addMe._layer = layer;
777  addMe._renderable = newElement;
778  _deferredAdds.push_back( addMe );
779  }
780 }
781 
782 void World::Add(Renderable *newElement, const String& layer)
783 {
784  Add(newElement, GetLayerByName(layer));
785 }
786 
787 void World::Remove(Renderable *oldElement)
788 {
789  if (oldElement == NULL)
790  {
791  return;
792  }
793 
794  if (_elementsLocked)
795  {
796  _deferredRemoves.push_back(oldElement);
797  return;
798  }
799 
800  // First, make sure that it isn't deferred in the _deferredAdds list.
801  std::vector<RenderableLayerPair>::iterator it = _deferredAdds.begin();
802  while (it != _deferredAdds.end())
803  {
804  if ((*it)._renderable == oldElement)
805  {
806  _deferredAdds.erase(it);
807  return;
808  }
809  ++it;
810  }
811 
812  // If we didn't find it in the deferred list, find/remove it from the layers.
813  bool found = false;
814  // Find the layer that matches the elements layer.
815  RenderLayers::iterator layer = _layers.find(oldElement->_layer);
816  // Found the layer (list of renderables).
817  if ( layer != _layers.end() )
818  {
819  // Now that we have the list of elements for this layer, let's remove the requested element.
820  //rb - TODO - Cache off vector.
821  std::vector<Renderable*>::iterator element = (*layer).second.begin();
822  for ( ; element != (*layer).second.end(); ++element )
823  {
824  // Found it.
825  if ( (*element) == oldElement )
826  {
827  // Remove the element.
828  element = (*layer).second.erase(element);
829  found = true;
830  // Nothing else to do.
831  break;
832  }
833  }
834  if (!found)
835  {
836  //log or error handle
837  }
838  }
839 }
840 
841 void World::UpdateLayer(Renderable* element, int newLayer)
842 {
843  if (element->_layer == newLayer)
844  {
845  return;
846  }
847 
848  RenderableLayerPair layerChange;
849  layerChange._layer = newLayer;
850  layerChange._renderable = element;
851  _deferredLayerChanges.push_back( layerChange );
852 }
853 
854 void World::UpdateLayer(Renderable* element, const String& newLayerName)
855 {
856  UpdateLayer(element, GetLayerByName(newLayerName));
857 }
858 
859 void World::NameLayer(const String& name, int number)
860 {
861  _layerNames[name] = number;
862 }
863 
864 const int World::GetLayerByName(const String& name)
865 {
866  std::map<String,int>::iterator it = _layerNames.find(name);
867  if (it != _layerNames.end())
868  {
869  return it->second;
870  }
871  else
872  {
873  return 0;
874  }
875 }
876 
877 void World::DrawDebugLine(const Vector2& a, const Vector2& b, float time, const Color& color)
878 {
879  DebugLine* line = new DebugLine();
880  line->_points[0] = a.X;
881  line->_points[1] = a.Y;
882  line->_points[2] = b.X;
883  line->_points[3] = a.Y;
884  line->_color = color;
885  line->_timeRemaining = time;
886  line->_bPermanent = (time < 0);
887  _debugDrawItems.push_back(line);
888 }
889 
891 {
892  for (b2Body* b = GetPhysicsWorld().GetBodyList(); b; b = b->GetNext())
893  {
894  b->SetAwake(true);
895  }
896 }
897 
899 {
900  return *_physicsWorld;
901 }
902 
903 void World::SetSideBlockers(bool turnOn, float restitution)
904 {
905  if (!MathUtil::FuzzyEquals(restitution, -1.0f))
906  {
907  _blockerRestitution = restitution;
908  }
909 
910  if (_blockers[0] != NULL)
911  {
912  theWorld.Remove(_blockers[0]);
913  theWorld.Remove(_blockers[1]);
914  theWorld.Remove(_blockers[2]);
915  theWorld.Remove(_blockers[3]);
916  delete _blockers[0];
917  delete _blockers[1];
918  delete _blockers[2];
919  delete _blockers[3];
920  _blockers[0] = NULL;
921  _blockers[1] = NULL;
922  _blockers[2] = NULL;
923  _blockers[3] = NULL;
924  }
925 
926  if (!turnOn)
927  {
928  _blockersOn = false;
929  return;
930  }
931 
932  _blockersOn = true;
933 
934  Vector2 botLeft = theCamera.GetWorldMinVertex();
935  Vector2 topRight = theCamera.GetWorldMaxVertex();
936  float worldHeight = topRight.Y - botLeft.Y;
937  float worldWidth = topRight.X - botLeft.X;
938 
939  float thickness = 5.0f; //just so it's thick enough to avoid tunnelling
940  Vector2 screenOrigin(((topRight.X - botLeft.X) * 0.5f) + botLeft.X,
941  ((topRight.Y - botLeft.Y) * 0.5f) + botLeft.Y);
942 
943  //right blocker
944  _blockers[0] = new PhysicsActor();
945  _blockers[0]->SetPosition(topRight.X + (thickness * 0.5f), screenOrigin.Y);
946  _blockers[0]->SetColor(1, .5, .5);
947  _blockers[0]->SetSize(thickness, worldHeight + 1.0f);
948  _blockers[0]->SetDensity(0.0f);
949  _blockers[0]->SetFriction(0.1f);
950  _blockers[0]->SetRestitution(_blockerRestitution);
951  _blockers[0]->InitPhysics();
952 
953  //left blocker
954  _blockers[1] = new PhysicsActor();
955  _blockers[1]->SetPosition(botLeft.X - (thickness * 0.5f), screenOrigin.Y);
956  _blockers[1]->SetColor(1, .5, .5);
957  _blockers[1]->SetSize(thickness, worldHeight + 1.0f);
958  _blockers[1]->SetDensity(0.0f);
959  _blockers[1]->SetFriction(0.1f);
960  _blockers[1]->SetRestitution(_blockerRestitution);
961  _blockers[1]->InitPhysics();
962 
963  //top blocker
964  _blockers[2] = new PhysicsActor();
965  _blockers[2]->SetPosition(screenOrigin.X, topRight.Y + (thickness * 0.5f));
966  _blockers[2]->SetColor(1.0f, .5f, .5f);
967  _blockers[2]->SetSize(worldWidth + 1.0f, thickness);
968  _blockers[2]->SetDensity(0.0f);
969  _blockers[2]->SetFriction(0.1f);
970  _blockers[2]->SetRestitution(_blockerRestitution);
971  _blockers[2]->InitPhysics();
972 
973  //bottom blocker
974  _blockers[3] = new PhysicsActor();
975  _blockers[3]->SetPosition(screenOrigin.X, botLeft.Y - (thickness * 0.5f));
976  _blockers[3]->SetColor(1.0f, .5f, .5f);
977  _blockers[3]->SetSize(worldWidth + 1.0f, thickness);
978  _blockers[3]->SetDensity(0.0f);
979  _blockers[3]->SetFriction(0.1f);
980  _blockers[3]->SetRestitution(_blockerRestitution);
981  _blockers[3]->InitPhysics();
982 
983  // We don't want these removed when we call ReloadLevel.
984  for (int i=0; i<4; ++i)
985  {
986  _blockers[i]->Tag("NoDelete");
987  }
988 
989  theWorld.Add(_blockers[0]);
990  theWorld.Add(_blockers[1]);
991  theWorld.Add(_blockers[2]);
992  theWorld.Add(_blockers[3]);
993 }
994 
996 {
997  if (m->GetMessageName() == "CameraChange")
998  {
999  if (_blockersOn)
1000  {
1001  SetSideBlockers(true);
1002  }
1003  }
1004 }
1005 
1006 void World::ProcessDeferredAdds()
1007 {
1008  _processingDeferredAdds = true;
1009  std::vector<RenderableLayerPair>::iterator it = _deferredAdds.begin();
1010  while(it != _deferredAdds.end())
1011  {
1012  Add( (*it)._renderable, (*it)._layer );
1013  ++it;
1014  }
1015 
1016  _deferredAdds.clear();
1017  _processingDeferredAdds = false;
1018 }
1019 
1020 void World::ProcessDeferredLayerChanges()
1021 {
1022  //TODO: use appropriate layer
1023  std::vector<RenderableLayerPair>::iterator it = _deferredLayerChanges.begin();
1024  while(it != _deferredLayerChanges.end())
1025  {
1026  Remove((*it)._renderable);
1027  Add( (*it)._renderable, (*it)._layer );
1028  ++it;
1029  }
1030  _deferredLayerChanges.clear();
1031 }
1032 
1033 void World::ProcessDeferredRemoves()
1034 {
1035  RenderList::iterator it = _deferredRemoves.begin();
1036  while(it != _deferredRemoves.end())
1037  {
1038  Remove(*it);
1039  ++it;
1040  }
1041  _deferredRemoves.clear();
1042 }
1043 
1044 void World::UpdateDebugItems(float frame_dt)
1045 {
1046  DebugDrawIterator itdd = _debugDrawItems.begin();
1047  while (itdd != _debugDrawItems.end())
1048  {
1049  if ( !(*itdd)->_bPermanent )
1050  {
1051  (*itdd)->_timeRemaining -= _dt;
1052  if ((*itdd)->_timeRemaining <= 0.f)
1053  {
1054  DebugDrawBase* pDD = (*itdd);
1055  _debugDrawItems.erase(itdd);
1056  delete pDD;
1057  }
1058  else
1059  {
1060  itdd++;
1061  }
1062  }
1063  }
1064 }
1065 
1067 {
1068  DebugDrawIterator itdd = _debugDrawItems.begin();
1069  while (itdd != _debugDrawItems.end())
1070  {
1071  DebugDrawBase* pDD = (*itdd);
1072  _debugDrawItems.erase(itdd);
1073  delete pDD;
1074  }
1075 }
1076 
1077 void World::DrawDebugItems()
1078 {
1079  DebugDrawIterator itdd = _debugDrawItems.begin();
1080  while (itdd != _debugDrawItems.end())
1081  {
1082  (*itdd)->SetupDraw();
1083  (*itdd)->Draw();
1084  itdd++;
1085  }
1086 }
1087 
1088 // This should only be done once. (at least for now)
1090 {
1091  if ( (_gameManager != NULL) || (gameManager == NULL) )
1092  {
1093  sysLog.Log("ERROR: Can only have one game manager!");
1094  return;
1095  }
1096 
1097  _gameManager = gameManager;
1098 }
1099 
1101 {
1102  RenderableIterator it = theWorld.GetFirstRenderable();
1103  while (it != GetLastRenderable())
1104  {
1105  Renderable* renderable = (*it);
1106  if( _gameManager != NULL && _gameManager->IsProtectedFromUnloadAll(renderable))
1107  {
1108  // Let the persistent actors know that we're unloading the level.
1109  Actor* actor = dynamic_cast<Actor*>(renderable);
1110  if (actor)
1111  actor->LevelUnloaded();
1112  ++it;
1113  continue;
1114  }
1115 
1116  it = it.erase( it );
1117  renderable->Destroy();
1118  delete renderable;
1119  }
1120 
1122 }
1123 
1125 {
1126  _console = console;
1127 }
1128 
1130 {
1131  return _console;
1132 }