OSDN Git Service

625bd8c6ca2017a79a0225749070bebcc48dd3ac
[skyscrapersim/skyscraper.git] / src / sbs / sbs.cpp
1 /* $Id$ */
2
3 /*
4         Scalable Building Simulator - Core
5         The Skyscraper Project - Version 1.11 Alpha
6         Copyright (C)2004-2016 Ryan Thoryk
7         http://www.skyscrapersim.com
8         http://sourceforge.net/projects/skyscraper
9         Contact - ryan@skyscrapersim.com
10
11         This program is free software; you can redistribute it and/or
12         modify it under the terms of the GNU General Public License
13         as published by the Free Software Foundation; either version 2
14         of the License, or (at your option) any later version.
15
16         This program is distributed in the hope that it will be useful,
17         but WITHOUT ANY WARRANTY; without even the implied warranty of
18         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19         GNU General Public License for more details.
20
21         You should have received a copy of the GNU General Public License
22         along with this program; if not, write to the Free Software
23         Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24 */
25
26 #include <OgreRoot.h>
27 #include <OgreSceneManager.h>
28 #include <OgreFileSystem.h>
29 #include <OgreConfigFile.h>
30 #include <OgreTimer.h>
31 #include "OgreStringVector.h"
32 #include <fmod.hpp>
33 #include <OgreBulletDynamicsRigidBody.h>
34 #include <OgreBulletCollisionsRay.h>
35 #include "globals.h"
36 #include "sbs.h"
37 #include "manager.h"
38 #include "camera.h"
39 #include "dynamicmesh.h"
40 #include "mesh.h"
41 #include "floor.h"
42 #include "elevator.h"
43 #include "elevatorcar.h"
44 #include "elevatordoor.h"
45 #include "shaft.h"
46 #include "stairs.h"
47 #include "action.h"
48 #include "person.h"
49 #include "texture.h"
50 #include "escalator.h"
51 #include "polygon.h"
52 #include "light.h"
53 #include "wall.h"
54 #include "callbutton.h"
55 #include "control.h"
56 #include "trigger.h"
57 #include "soundsystem.h"
58 #include "sound.h"
59 #include "door.h"
60 #include "model.h"
61 #include "timer.h"
62 #include "profiler.h"
63 #include "revsbs.h"
64
65 namespace SBS {
66
67 SBS::SBS(Ogre::SceneManager* mSceneManager, FMOD::System *fmodsystem, int instance_number, const Ogre::Vector3 &position, float rotation, const Ogre::Vector3 &area_min, const Ogre::Vector3 &area_max) : Object(0)
68 {
69         sbs = this;
70         this->mSceneManager = mSceneManager;
71
72         version = "0.11.0." + std::string(SVN_REVSTR);
73         version_state = "Alpha";
74
75         //root object needs to self-register
76         ObjectCount = 0;
77         RegisterObject(this);
78         InstanceNumber = instance_number;
79
80         //set up SBS object
81         SetValues("SBS", "SBS", true);
82
83         mRoot = Ogre::Root::getSingletonPtr();
84
85         //load config file
86         configfile = new Ogre::ConfigFile();
87         configfile->load("skyscraper.ini");
88
89         //initialize variables
90         BuildingName = "";
91         BuildingDesigner = "";
92         BuildingLocation = "";
93         BuildingDescription = "";
94         BuildingVersion = "";
95         IsRunning = false;
96         Floors = 0;
97         Basements = 0;
98         IsFalling = false;
99         InStairwell = false;
100         InElevator = false;
101         IsBuildingsEnabled = false;
102         IsExternalEnabled = false;
103         IsLandscapeEnabled = false;
104         IsSkyboxEnabled = false;
105         fps_frame_count = 0;
106         fps_tottime = 0;
107         FPS = 0;
108         FrameRate = 30;
109         FrameLimiter = false;
110         AutoShafts = GetConfigBool("Skyscraper.SBS.AutoShafts", true);
111         AutoStairs = GetConfigBool("Skyscraper.SBS.AutoStairs", true);
112         ElevatorSync = false;
113         ElevatorNumber = 1;
114         CarNumber = 1;
115         wall_orientation = 1;
116         floor_orientation = 2;
117         DrawMainN = true;
118         DrawMainP = true;
119         DrawSideN = false;
120         DrawSideP = false;
121         DrawTop = false;
122         DrawBottom = false;
123         DrawMainNOld = true;
124         DrawMainPOld = true;
125         DrawSideNOld = false;
126         DrawSidePOld = false;
127         DrawTopOld = false;
128         DrawBottomOld = false;
129         delta = 0.01f;
130         wall1a = false;
131         wall1b = false;
132         wall2a = false;
133         wall2b = false;
134         ProcessElevators = GetConfigBool("Skyscraper.SBS.ProcessElevators", true);
135         remaining_delta = 0;
136         start_time = 0;
137         running_time = 0;
138         InShaft = false;
139         DeleteColliders = false;
140         soundcount = 0;
141         UnitScale = GetConfigFloat("Skyscraper.SBS.UnitScale", 4);
142         Verbose = GetConfigBool("Skyscraper.SBS.Verbose", false);
143         InterfloorOnTop = false;
144         FastDelete = false;
145         WallCount = 0;
146         PolygonCount = 0;
147         SkyBox = 0;
148         Landscape = 0;
149         External = 0;
150         Buildings = 0;
151         current_time = 0;
152         current_virtual_time = 0;
153         elapsed_time = 0;
154         average_time = 0;
155         timer = new Ogre::Timer();
156         AmbientR = 1;
157         AmbientG = 1;
158         AmbientB = 1;
159         OldAmbientR = 1;
160         OldAmbientG = 1;
161         OldAmbientB = 1;
162         TexelOverride = false;
163         enable_profiling = false;
164         enable_advanced_profiling = false;
165         SkyName = GetConfigString("Skyscraper.SBS.SkyName", "noon");
166         ShaftDisplayRange = GetConfigInt("Skyscraper.SBS.ShaftDisplayRange", 3);
167         StairsDisplayRange = GetConfigInt("Skyscraper.SBS.StairsDisplayRange", 5);
168         ShaftOutsideDisplayRange = GetConfigInt("Skyscraper.SBS.ShaftOutsideDisplayRange", 3);
169         StairsOutsideDisplayRange = GetConfigInt("Skyscraper.SBS.StairsOutsideDisplayRange", 3);
170         FloorDisplayRange = GetConfigInt("Skyscraper.SBS.FloorDisplayRange", 3);
171         SmoothFrames = GetConfigInt("Skyscraper.SBS.SmoothFrames", 200);
172         RenderOnStartup = GetConfigBool("Skyscraper.SBS.RenderOnStartup", false);
173         EscalatorCount = 0;
174         RandomActivity = GetConfigBool("Skyscraper.SBS.RandomActivity", false);
175
176         camera = 0;
177         Buildings = 0;
178         External = 0;
179         Landscape = 0;
180         mWorld = 0;
181         soundsystem = 0;
182         area_trigger = 0;
183         texturemanager = 0;
184
185         if (UnitScale <= 0)
186                 UnitScale = 1;
187
188         //Print SBS banner
189         PrintBanner();
190
191         //add instance number to reports
192         InstancePrompt = ToString(InstanceNumber) + "> ";
193
194         //move to specified position
195         Move(position);
196
197         //rotate engine
198         Rotate(0.0f, rotation, 0.0f);
199
200         //create main engine area trigger
201         SetBounds(area_min, area_max);
202
203         //create sound system object if sound is enabled
204         if (fmodsystem)
205                 soundsystem = new SoundSystem(this, fmodsystem);
206 }
207
208 void SBS::Initialize()
209 {
210         //create texture manager
211         texturemanager = new TextureManager(this);
212
213         //set up physics
214         Ogre::AxisAlignedBox box (Ogre::Vector3::ZERO, Ogre::Vector3::ZERO);
215         mWorld = new OgreBulletDynamics::DynamicsWorld(mSceneManager, box, Ogre::Vector3::ZERO, true);
216         mWorld->setAllowedCcdPenetration(0);
217
218         /*debugDrawer = new OgreBulletCollisions::DebugDrawer();
219         debugDrawer->setDrawWireframe(true);
220         mWorld->setDebugDrawer(debugDrawer);
221         Ogre::SceneNode *node = mSceneManager->getRootSceneNode()->createChildSceneNode("debugDrawer", Ogre::Vector3::ZERO);
222         node->attachObject(static_cast<Ogre::SimpleRenderable*> (debugDrawer));
223         */
224
225         //mount sign texture packs
226         Mount("signs-sans.zip", "signs/sans");
227         Mount("signs-sans_bold.zip", "signs/sans_bold");
228         Mount("signs-sans_cond.zip", "signs/sans_cond");
229         Mount("signs-sans_cond_bold.zip", "signs/sans_cond_bold");
230
231         //create object meshes
232         Buildings = new MeshObject(this, "Buildings");
233         External = new MeshObject(this, "External");
234         Landscape = new MeshObject(this, "Landscape");
235         //Landscape->tricollider = false;
236
237         //create manager objects
238         floor_manager = new FloorManager(this);
239         elevator_manager = new ElevatorManager(this);
240         shaft_manager = new ShaftManager(this);
241         stairs_manager = new StairsManager(this);
242         door_manager = new DoorManager(this);
243         revolvingdoor_manager = new RevolvingDoorManager(this);
244
245         //create camera object
246         this->camera = new Camera(this);
247 }
248
249 SBS::~SBS()
250 {
251         //engine destructor
252
253         Report("Deleting simulator objects...");
254
255         FastDelete = true;
256
257         //delete people
258         for (size_t i = 0; i < PersonArray.size(); i++)
259         {
260                 if (PersonArray[i])
261                 {
262                         PersonArray[i]->parent_deleting = true;
263                         delete PersonArray[i];
264                 }
265                 PersonArray[i] = 0;
266         }
267
268         //delete controls
269         for (size_t i = 0; i < ControlArray.size(); i++)
270         {
271                 if (ControlArray[i])
272                 {
273                         ControlArray[i]->parent_deleting = true;
274                         delete ControlArray[i];
275                 }
276                 ControlArray[i] = 0;
277         }
278
279         //delete triggers
280         for (size_t i = 0; i < TriggerArray.size(); i++)
281         {
282                 if (TriggerArray[i])
283                 {
284                         TriggerArray[i]->parent_deleting = true;
285                         delete TriggerArray[i];
286                 }
287                 TriggerArray[i] = 0;
288         }
289
290         //delete models
291         for (size_t i = 0; i < ModelArray.size(); i++)
292         {
293                 if (ModelArray[i])
294                 {
295                         ModelArray[i]->parent_deleting = true;
296                         delete ModelArray[i];
297                 }
298                 ModelArray[i] = 0;
299         }
300
301         //delete lights
302         for (size_t i = 0; i < lights.size(); i++)
303         {
304                 if (lights[i])
305                 {
306                         lights[i]->parent_deleting = true;
307                         delete lights[i];
308                 }
309                 lights[i] = 0;
310         }
311
312         //delete camera object
313         if (camera)
314         {
315                 camera->parent_deleting = true;
316                 delete camera;
317         }
318         camera = 0;
319
320         //delete manager objects
321         if (floor_manager)
322                 delete floor_manager;
323         floor_manager = 0;
324
325         if (elevator_manager)
326                 delete elevator_manager;
327         elevator_manager = 0;
328
329         if (shaft_manager)
330                 delete shaft_manager;
331         shaft_manager = 0;
332
333         if (stairs_manager)
334                 delete stairs_manager;
335         stairs_manager = 0;
336
337         if (door_manager)
338                 delete door_manager;
339         door_manager = 0;
340
341         if (revolvingdoor_manager)
342                 delete revolvingdoor_manager;
343         revolvingdoor_manager = 0;
344
345         //delete sounds
346         for (size_t i = 0; i < sounds.size(); i++)
347         {
348                 if (sounds[i])
349                 {
350                         sounds[i]->parent_deleting = true;
351                         delete sounds[i];
352                 }
353                 sounds[i] = 0;
354         }
355
356         //delete actions
357         for (size_t i = 0; i < ActionArray.size(); i++)
358         {
359                 if (ActionArray[i])
360                         delete ActionArray[i];
361                 ActionArray[i] = 0;
362         }
363
364         //delete mesh objects
365         if (SkyBox)
366         {
367                 SkyBox->parent_deleting = true;
368                 delete SkyBox;
369         }
370         SkyBox = 0;
371
372         if (Landscape)
373         {
374                 Landscape->parent_deleting = true;
375                 delete Landscape;
376         }
377         Landscape = 0;
378
379         if (External)
380         {
381                 External->parent_deleting = true;
382                 delete External;
383         }
384         External = 0;
385
386         if (Buildings)
387         {
388                 Buildings->parent_deleting = true;
389                 delete Buildings;
390         }
391         Buildings = 0;
392
393         //delete sound system
394         if (soundsystem)
395                 delete soundsystem;
396         soundsystem = 0;
397
398         //delete texture manager
399         if (texturemanager)
400                 delete texturemanager;
401         texturemanager = 0;
402
403         //delete main area trigger
404         if (area_trigger)
405                 delete area_trigger;
406         area_trigger = 0;
407
408         //delete physics objects
409         if (mWorld)
410         {
411                 //delete mWorld->getDebugDrawer();
412                 //mWorld->setDebugDrawer(0);
413                 delete mWorld;
414         }
415         mWorld = 0;
416
417         ObjectArray.clear();
418         verify_results.clear();
419
420         if (timer)
421                 delete timer;
422         timer = 0;
423
424         if (configfile)
425                 delete configfile;
426         configfile = 0;
427
428         //clear self reference
429         sbs = 0;
430
431         Report("Exiting");
432 }
433
434 bool SBS::Start(Ogre::Camera *camera)
435 {
436         //Post-init startup code goes here, before the runloop
437
438         //prepare 3D geometry for use
439         Prepare();
440
441         //free text texture memory
442         texturemanager->FreeTextureBoxes();
443
444         //reset building state
445         ResetState();
446
447         //initialize objects (cascades down through entire object tree)
448         Init();
449
450         //play looping global sounds
451         for (size_t i = 0; i < sounds.size(); i++)
452         {
453                 if (sounds[i])
454                 {
455                         if (sounds[i]->GetLoopState() == true)
456                                 sounds[i]->Play();
457                 }
458         }
459
460         //attach camera object
461         AttachCamera(camera);
462
463         //enable elevators
464         sbs->GetElevatorManager()->EnableAll(true);
465
466         //enable random activity if specified
467         if (RandomActivity == true)
468                 EnableRandomActivity(true);
469
470         IsRunning = true;
471
472         return true;
473 }
474
475 void SBS::PrintBanner()
476 {
477         Report("");
478         Report(" Scalable Building Simulator " + version + " " + version_state);
479         Report(" Copyright (C)2004-2016 Ryan Thoryk");
480         Report(" This software comes with ABSOLUTELY NO WARRANTY. This is free");
481         Report(" software, and you are welcome to redistribute it under certain");
482         Report(" conditions. For details, see the file gpl.txt\n");
483 }
484
485 void SBS::Loop()
486 {
487         //Main simulator loop
488         SBS_PROFILE("SBS::Loop");
489
490         //This makes sure all timer steps are the same size, in order to prevent the physics from changing
491         //depending on frame rate
492
493         unsigned long timing;
494
495         if (SmoothFrames > 0)
496                 timing = GetAverageTime();
497         else
498                 timing = GetElapsedTime();
499
500         float elapsed = float(timing) / 1000.0f;
501
502         //calculate start and running time
503         if (start_time == 0)
504                 start_time = GetRunTime() / 1000.0f;
505         running_time = (GetRunTime() / 1000.0f) - start_time;
506
507         //move camera or update character movement
508         camera->MoveCharacter();
509
510         //update physics
511         if (camera->EnableBullet == true)
512         {
513                 if (enable_advanced_profiling == false)
514                         ProfileManager::Start_Profile("Collisions/Physics");
515                 else
516                         ProfileManager::Start_Profile("Bullet");
517                 mWorld->stepSimulation(elapsed, 0);
518                 ProfileManager::Stop_Profile();
519         }
520
521         //sync camera to physics
522         camera->Sync();
523
524         //update sound
525         if (soundsystem)
526                 soundsystem->Loop();
527
528         elapsed += remaining_delta;
529
530         //limit the elapsed value to prevent major slowdowns during debugging
531         if (elapsed > .5f)
532                 elapsed = .5f;
533
534         ProfileManager::Start_Profile("Simulator Loop");
535         while (elapsed >= delta)
536         {
537                 //Determine floor that the camera is on
538                 camera->UpdateCameraFloor();
539
540                 //process child object dynamic runloops
541                 LoopChildren();
542
543                 camera->CheckObjects();
544
545                 //process auto areas
546                 CheckAutoAreas();
547
548                 elapsed -= delta;
549         }
550         remaining_delta = elapsed;
551
552         //process timers
553         ProcessTimers();
554
555         //process engine boundary trigger
556         if (area_trigger)
557                 area_trigger->Loop();
558
559         ProfileManager::Stop_Profile();
560
561         //process camera loop
562         camera->Loop();
563 }
564
565 void SBS::CalculateFrameRate()
566 {
567         //calculate frame rate
568         fps_tottime += elapsed_time;
569         fps_frame_count++;
570         if (fps_tottime > 500)
571         {
572                 FPS = (float (fps_frame_count) * 1000.0f) / float (fps_tottime);
573                 fps_frame_count = 0;
574                 fps_tottime = 0;
575         }
576 }
577
578 bool SBS::AddWallMain(Object *parent, MeshObject* mesh, const std::string &name, const std::string &texture, float thickness, float x1, float z1, float x2, float z2, float height_in1, float height_in2, float altitude1, float altitude2, float tw, float th, bool autosize)
579 {
580         Wall *object = new Wall(mesh, parent, true);
581         bool result = AddWallMain(object, name, texture, thickness, x1, z1, x2, z2, height_in1, height_in2, altitude1, altitude2, tw, th, autosize);
582         delete object;
583         return result;
584 }
585
586 bool SBS::AddWallMain(Wall* wallobject, const std::string &name, const std::string &texture, float thickness, float x1, float z1, float x2, float z2, float height_in1, float height_in2, float altitude1, float altitude2, float tw, float th, bool autosize)
587 {
588         //Adds a wall with the specified dimensions
589
590         //exit if coordinates are invalid
591         if (x1 == x2 && z1 == z2)
592                 return ReportError("Invalid coordinates for wall '" + name + "'");
593
594         if (height_in1 == 0.0f && height_in2 == 0.0f)
595                 return ReportError("No wall height specified for wall '" + name + "'");
596
597         //determine axis of wall
598         int axis = 0;
599         if (std::abs(x1 - x2) > (std::abs(z1 - z2) + 0.00001))
600                 //x axis
601                 axis = 1;
602         else
603                 //z axis
604                 axis = 2;
605
606         //convert to clockwise coordinates if on x-axis, or counterclockwise if on z-axis
607         if ((x1 > x2 && axis == 1) || (z1 < z2 && axis == 2))
608         {
609                 //reverse coordinates
610                 Swap(x1, x2);
611                 Swap(z1, z2);
612                 Swap(altitude1, altitude2);
613                 Swap(height_in1, height_in2);
614         }
615
616         //map coordinates
617         Ogre::Vector3 v1 (x1, altitude1 + height_in1, z1); //left top
618         Ogre::Vector3 v2 (x2, altitude2 + height_in2, z2); //right top
619         Ogre::Vector3 v3 (x2, altitude2, z2); //right base
620         Ogre::Vector3 v4 (x1, altitude1, z1); //left base
621
622         Ogre::Vector3 v5 = v1;
623         Ogre::Vector3 v6 = v2;
624         Ogre::Vector3 v7 = v3;
625         Ogre::Vector3 v8 = v4;
626
627         //exit if outside of the engine boundaries
628         if (area_trigger)
629         {
630                 Ogre::Vector3 v1x = wallobject->GetMesh()->GetPosition() + v1;
631                 Ogre::Vector3 v2x = wallobject->GetMesh()->GetPosition() + v3;
632                 if (area_trigger->IsOutside(v1x, v2x) == true)
633                         return false;
634         }
635
636         //expand to specified thickness
637         if (axis == 1)
638         {
639                 //x axis
640                 if (wall_orientation == 0)
641                 {
642                         //left
643                         v5.z += thickness;
644                         v6.z += thickness;
645                         v7.z += thickness;
646                         v8.z += thickness;
647                 }
648                 if (wall_orientation == 1)
649                 {
650                         //center
651                         float half = thickness / 2;
652                         v1.z -= half;
653                         v2.z -= half;
654                         v3.z -= half;
655                         v4.z -= half;
656                         v5.z += half;
657                         v6.z += half;
658                         v7.z += half;
659                         v8.z += half;
660                 }
661                 if (wall_orientation == 2)
662                 {
663                         //right
664                         v1.z -= thickness;
665                         v2.z -= thickness;
666                         v3.z -= thickness;
667                         v4.z -= thickness;
668                 }
669         }
670         else
671         {
672                 //z axis
673                 if (wall_orientation == 0)
674                 {
675                         //left
676                         v5.x += thickness;
677                         v6.x += thickness;
678                         v7.x += thickness;
679                         v8.x += thickness;
680                 }
681                 if (wall_orientation == 1)
682                 {
683                         //center
684                         float half = thickness / 2;
685                         v1.x -= half;
686                         v2.x -= half;
687                         v3.x -= half;
688                         v4.x -= half;
689                         v5.x += half;
690                         v6.x += half;
691                         v7.x += half;
692                         v8.x += half;
693                 }
694                 if (wall_orientation == 2)
695                 {
696                         //right
697                         v1.x -= thickness;
698                         v2.x -= thickness;
699                         v3.x -= thickness;
700                         v4.x -= thickness;
701                 }
702         }
703
704         //create polygons and set names
705         std::string NewName, texture2 = texture;
706         float tw2 = tw, th2 = th;
707
708         bool FlipTexture = texturemanager->FlipTexture;
709         bool TextureOverride = texturemanager->TextureOverride;
710
711         if (FlipTexture == true)
712                 texturemanager->ProcessTextureFlip(tw, th);
713
714         if (DrawMainN == true)
715         {
716                 if (FlipTexture == true)
717                 {
718                         tw2 = texturemanager->widthscale[0];
719                         th2 = texturemanager->heightscale[0];
720                 }
721                 if (TextureOverride == true)
722                         texture2 = texturemanager->mainnegtex;
723
724                 NewName = name;
725                 if (GetDrawWallsCount() > 1)
726                         NewName.append(":front");
727                 wallobject->AddQuad(NewName, texture2, v1, v2, v3, v4, tw2, th2, autosize); //front wall
728         }
729
730         if (DrawMainP == true)
731         {
732                 if (FlipTexture == true)
733                 {
734                         tw2 = texturemanager->widthscale[1];
735                         th2 = texturemanager->heightscale[1];
736                 }
737                 if (TextureOverride == true)
738                         texture2 = texturemanager->mainpostex;
739
740                 NewName = name;
741                 NewName.append(":back");
742                 wallobject->AddQuad(NewName, texture2, v6, v5, v8, v7, tw2, th2, autosize); //back wall
743         }
744
745         if (thickness != 0.0f)
746         {
747                 if (DrawSideN == true)
748                 {
749                         if (FlipTexture == true)
750                         {
751                                 tw2 = texturemanager->widthscale[2];
752                                 th2 = texturemanager->heightscale[2];
753                         }
754                         if (TextureOverride == true)
755                                 texture2 = texturemanager->sidenegtex;
756
757                         NewName = name;
758                         NewName.append(":left");
759                         if (axis == 1)
760                                 wallobject->AddQuad(NewName, texture2, v5, v1, v4, v8, tw2, th2, autosize); //left wall
761                         else
762                                 wallobject->AddQuad(NewName, texture2, v2, v6, v7, v3, tw2, th2, autosize); //left wall
763                 }
764
765                 if (DrawSideP == true)
766                 {
767                         if (FlipTexture == true)
768                         {
769                                 tw2 = texturemanager->widthscale[3];
770                                 th2 = texturemanager->heightscale[3];
771                         }
772                         if (TextureOverride == true)
773                                 texture2 = texturemanager->sidepostex;
774
775                         NewName = name;
776                         NewName.append(":right");
777                         if (axis == 1)
778                                 wallobject->AddQuad(NewName, texture2, v2, v6, v7, v3, tw2, th2, autosize); //right wall
779                         else
780                                 wallobject->AddQuad(NewName, texture2, v5, v1, v4, v8, tw2, th2, autosize); //right wall
781                 }
782
783                 if (DrawTop == true)
784                 {
785                         if (FlipTexture == true)
786                         {
787                                 tw2 = texturemanager->widthscale[4];
788                                 th2 = texturemanager->heightscale[4];
789                         }
790                         if (TextureOverride == true)
791                                 texture2 = texturemanager->toptex;
792
793                         NewName = name;
794                         NewName.append(":top");
795                         wallobject->AddQuad(NewName, texture2, v5, v6, v2, v1, tw2, th2, autosize); //top wall
796                 }
797
798                 if (DrawBottom == true)
799                 {
800                         if (FlipTexture == true)
801                         {
802                                 tw2 = texturemanager->widthscale[5];
803                                 th2 = texturemanager->heightscale[5];
804                         }
805                         if (TextureOverride == true)
806                                 texture2 = texturemanager->bottomtex;
807
808                         NewName = name;
809                         NewName.append(":bottom");
810                         wallobject->AddQuad(NewName, texture2, v4, v3, v7, v8, tw2, th2, autosize); //bottom wall
811                 }
812         }
813
814         return true;
815 }
816
817 bool SBS::AddFloorMain(Object *parent, MeshObject* mesh, const std::string &name, const std::string &texture, float thickness, float x1, float z1, float x2, float z2, float altitude1, float altitude2, bool reverse_axis, bool texture_direction, float tw, float th, bool autosize, bool legacy_behavior)
818 {
819         Wall *object = new Wall(mesh, parent, true);
820         bool result = AddFloorMain(object, name, texture, thickness, x1, z1, x2, z2, altitude1, altitude2, reverse_axis, texture_direction, tw, th, autosize, legacy_behavior);
821         delete object;
822         return result;
823 }
824
825 bool SBS::AddFloorMain(Wall* wallobject, const std::string &name, const std::string &texture, float thickness, float x1, float z1, float x2, float z2, float altitude1, float altitude2, bool reverse_axis, bool texture_direction, float tw, float th, bool autosize, bool legacy_behavior)
826 {
827         //Adds a floor with the specified dimensions and vertical offset
828
829         //direction determines the direction of slope (for different altitude values):
830         //false - left/right from altitude1 to altitude2, or legacy (broken) "ReverseAxis = false" behavior if legacy_behavior is true
831         //true - back/forwards from altitude1 to altitude2, or legacy (broken) "ReverseAxis = true" behavior if legacy_behavior is true
832
833         //exit if coordinates are invalid
834         if (x1 == x2 || z1 == z2)
835                 return ReportError("Invalid coordinates for floor '" + name + "'");
836
837         //convert to clockwise coordinates
838
839         //determine axis of floor
840         int axis = 0;
841         if (std::abs(x1 - x2) > (std::abs(z1 - z2) + 0.00001))
842                 //x axis
843                 axis = 1;
844         else
845                 //z axis
846                 axis = 2;
847
848         if (legacy_behavior == false)
849         {
850                 //current behavior
851
852                 if (x1 > x2)
853                 {
854                         Swap(x1, x2);
855
856                         if (reverse_axis == true)
857                                 Swap(altitude1, altitude2);
858                 }
859                 if (z1 > z2)
860                 {
861                         Swap(z1, z2);
862
863                         if (reverse_axis == false)
864                                 Swap(altitude1, altitude2);
865                 }
866         }
867         else
868         {
869                 //legacy (broken) behavior, for compatibility with previous versions
870
871                 if ((x1 > x2 && axis == 1) || (z1 > z2 && axis == 2))
872                 {
873                         //reverse coordinates if the difference between x or z coordinates is greater
874                         Swap(x1, x2);
875                         Swap(z1, z2);
876                         Swap(altitude1, altitude2);
877                 }
878         }
879
880         //map coordinates
881         Ogre::Vector3 v1, v2, v3, v4;
882
883         if (reverse_axis == false)
884         {
885                 v1 = Ogre::Vector3(x1, altitude1, z1); //bottom left
886                 v2 = Ogre::Vector3(x2, altitude1, z1); //bottom right
887                 v3 = Ogre::Vector3(x2, altitude2, z2); //top right
888                 v4 = Ogre::Vector3(x1, altitude2, z2); //top left
889         }
890         else
891         {
892                 if (legacy_behavior == true)
893                 {
894                         v1 = Ogre::Vector3(x1, altitude1, z1); //bottom left
895                         v2 = Ogre::Vector3(x1, altitude1, z2); //top left
896                         v3 = Ogre::Vector3(x2, altitude2, z2); //top right
897                         v4 = Ogre::Vector3(x2, altitude2, z1); //bottom right
898                 }
899                 else
900                 {
901                         v1 = Ogre::Vector3(x2, altitude2, z1); //bottom right
902                         v2 = Ogre::Vector3(x2, altitude2, z2); //top right
903                         v3 = Ogre::Vector3(x1, altitude1, z2); //top left
904                         v4 = Ogre::Vector3(x1, altitude1, z1); //bottom left
905                 }
906         }
907
908         Ogre::Vector3 v5 = v1;
909         Ogre::Vector3 v6 = v2;
910         Ogre::Vector3 v7 = v3;
911         Ogre::Vector3 v8 = v4;
912
913         //exit if outside of the engine boundaries
914         if (area_trigger)
915         {
916                 Ogre::Vector3 v1x = wallobject->GetMesh()->GetPosition() + v1;
917                 Ogre::Vector3 v2x = wallobject->GetMesh()->GetPosition() + v3;
918                 if (area_trigger->IsOutside(v1x, v2x) == true)
919                         return false;
920         }
921
922         //expand to specified thickness
923         if (floor_orientation == 0)
924         {
925                 //bottom
926                 v5.y += thickness;
927                 v6.y += thickness;
928                 v7.y += thickness;
929                 v8.y += thickness;
930         }
931         if (floor_orientation == 1)
932         {
933                 //center
934                 float half = thickness / 2;
935                 v1.y -= half;
936                 v2.y -= half;
937                 v3.y -= half;
938                 v4.y -= half;
939                 v5.y += half;
940                 v6.y += half;
941                 v7.y += half;
942                 v8.y += half;
943         }
944         if (floor_orientation == 2)
945         {
946                 //top
947                 v1.y -= thickness;
948                 v2.y -= thickness;
949                 v3.y -= thickness;
950                 v4.y -= thickness;
951         }
952
953         //create polygons and set names
954         std::string NewName, texture2 = texture;
955         float tw2 = tw, th2 = th;
956
957         bool FlipTexture = texturemanager->FlipTexture;
958         bool TextureOverride = texturemanager->TextureOverride;
959
960         if (FlipTexture == true)
961                 texturemanager->ProcessTextureFlip(tw, th);
962
963         //turn on rotation if set
964         bool old_planarrotate = texturemanager->GetPlanarRotate();
965         texturemanager->SetPlanarRotate(texture_direction);
966
967         if (DrawMainN == true)
968         {
969                 if (FlipTexture == true)
970                 {
971                         tw2 = texturemanager->widthscale[0];
972                         th2 = texturemanager->heightscale[0];
973                 }
974                 if (TextureOverride == true)
975                         texture2 = texturemanager->mainnegtex;
976
977                 NewName = name;
978                 if (GetDrawWallsCount() > 1)
979                         NewName.append(":front");
980                 wallobject->AddQuad(NewName, texture2, v1, v2, v3, v4, tw2, th2, autosize); //bottom wall
981         }
982
983         if (DrawMainP == true)
984         {
985                 if (FlipTexture == true)
986                 {
987                         tw2 = texturemanager->widthscale[1];
988                         th2 = texturemanager->heightscale[1];
989                 }
990                 if (TextureOverride == true)
991                         texture2 = texturemanager->mainpostex;
992
993                 NewName = name;
994                 NewName.append(":back");
995                 wallobject->AddQuad(NewName, texture2, v8, v7, v6, v5, tw2, th2, autosize); //top wall
996         }
997
998         if (thickness != 0.0f)
999         {
1000                 if (DrawSideN == true)
1001                 {
1002                         if (FlipTexture == true)
1003                         {
1004                                 tw2 = texturemanager->widthscale[2];
1005                                 th2 = texturemanager->heightscale[2];
1006                         }
1007                         if (TextureOverride == true)
1008                                 texture2 = texturemanager->sidenegtex;
1009
1010                         NewName = name;
1011                         NewName.append(":left");
1012                         wallobject->AddQuad(NewName, texture2, v8, v5, v1, v4, tw2, th2, autosize); //left wall
1013                 }
1014
1015                 if (DrawSideP == true)
1016                 {
1017                         if (FlipTexture == true)
1018                         {
1019                                 tw2 = texturemanager->widthscale[3];
1020                                 th2 = texturemanager->heightscale[3];
1021                         }
1022                         if (TextureOverride == true)
1023                                 texture2 = texturemanager->sidepostex;
1024
1025                         NewName = name;
1026                         NewName.append(":right");
1027                         wallobject->AddQuad(NewName, texture2, v6, v7, v3, v2, tw2, th2, autosize); //right wall
1028                 }
1029
1030                 if (DrawTop == true)
1031                 {
1032                         if (FlipTexture == true)
1033                         {
1034                                 tw2 = texturemanager->widthscale[4];
1035                                 th2 = texturemanager->heightscale[4];
1036                         }
1037                         if (TextureOverride == true)
1038                                 texture2 = texturemanager->toptex;
1039
1040                         NewName = name;
1041                         NewName.append(":top");
1042                         wallobject->AddQuad(NewName, texture2, v5, v6, v2, v1, tw2, th2, autosize); //front wall
1043                 }
1044
1045                 if (DrawBottom == true)
1046                 {
1047                         if (FlipTexture == true)
1048                         {
1049                                 tw2 = texturemanager->widthscale[5];
1050                                 th2 = texturemanager->heightscale[5];
1051                         }
1052                         if (TextureOverride == true)
1053                                 texture2 = texturemanager->bottomtex;
1054
1055                         NewName = name;
1056                         NewName.append(":bottom");
1057                         wallobject->AddQuad(NewName, texture2, v7, v8, v4, v3, tw2, th2, autosize); //back wall
1058                 }
1059         }
1060
1061         texturemanager->SetPlanarRotate(old_planarrotate);
1062
1063         return true;
1064 }
1065
1066 void SBS::Report(const std::string &message)
1067 {
1068         Ogre::LogManager::getSingleton().logMessage(InstancePrompt + message);
1069         LastNotification = message;
1070 }
1071
1072 bool SBS::ReportError(const std::string &message)
1073 {
1074         Ogre::LogManager::getSingleton().logMessage(InstancePrompt + message, Ogre::LML_CRITICAL);
1075         LastError = message;
1076         return false;
1077 }
1078
1079 Wall* SBS::CreateWallBox(MeshObject* mesh, const std::string &name, const std::string &texture, float x1, float x2, float z1, float z2, float height_in, float voffset, float tw, float th, bool inside, bool outside, bool top, bool bottom, bool autosize)
1080 {
1081         //create 4 walls
1082
1083         if (!mesh)
1084                 return 0;
1085
1086         //exit if coordinates are invalid
1087         if (x1 == x2 && z1 == z2)
1088         {
1089                 ReportError("Invalid coordinates for wall '" + name + "'");
1090                 return 0;
1091         }
1092
1093         //create wall object
1094         Wall *wall = mesh->CreateWallObject(name);
1095
1096         bool x_thickness = false, z_thickness = false;
1097         std::string NewName, texture2 = texture;
1098
1099         if (x1 != x2)
1100                 x_thickness = true;
1101         if (z1 != z2)
1102                 z_thickness = true;
1103
1104         //swap values if the first is greater than the second
1105         if (x1 > x2)
1106                 Swap(x1, x2);
1107
1108         if (z1 > z2)
1109                 Swap(z1, z2);
1110
1111         bool TextureOverride = texturemanager->TextureOverride;
1112
1113         if (inside == true)
1114         {
1115                 //generate a box visible from the inside
1116
1117                 NewName = name;
1118                 NewName.append(":inside");
1119
1120                 if (x_thickness == true)
1121                 {
1122                         if (TextureOverride == true)
1123                                 texture2 = texturemanager->mainnegtex;
1124
1125                         wall->AddQuad( //front
1126                                         NewName,
1127                                         texture2,
1128                                         Ogre::Vector3(x1, voffset, z1),
1129                                         Ogre::Vector3(x2, voffset, z1),
1130                                         Ogre::Vector3(x2, voffset + height_in, z1),
1131                                         Ogre::Vector3(x1, voffset + height_in, z1), tw, th, autosize);
1132
1133                         if (TextureOverride == true)
1134                                 texture2 = texturemanager->mainpostex;
1135
1136                         wall->AddQuad( //back
1137                                         NewName,
1138                                         texture2,
1139                                         Ogre::Vector3(x2, voffset, z2),
1140                                         Ogre::Vector3(x1, voffset, z2),
1141                                         Ogre::Vector3(x1, voffset + height_in, z2),
1142                                         Ogre::Vector3(x2, voffset + height_in, z2), tw, th, autosize);
1143                 }
1144                 if (z_thickness == true)
1145                 {
1146                         if (TextureOverride == true)
1147                                 texture2 = texturemanager->sidepostex;
1148
1149                         wall->AddQuad( //right
1150                                         NewName,
1151                                         texture2,
1152                                         Ogre::Vector3(x2, voffset, z1),
1153                                         Ogre::Vector3(x2, voffset, z2),
1154                                         Ogre::Vector3(x2, voffset + height_in, z2),
1155                                         Ogre::Vector3(x2, voffset + height_in, z1), tw, th, autosize);
1156
1157                         if (TextureOverride == true)
1158                                 texture2 = texturemanager->sidenegtex;
1159
1160                         wall->AddQuad( //left
1161                                         NewName,
1162                                         texture2,
1163                                         Ogre::Vector3(x1, voffset, z2),
1164                                         Ogre::Vector3(x1, voffset, z1),
1165                                         Ogre::Vector3(x1, voffset + height_in, z1),
1166                                         Ogre::Vector3(x1, voffset + height_in, z2), tw, th, autosize);
1167                 }
1168                 if (x_thickness == true && z_thickness == true)
1169                 {
1170                         if (bottom == true)
1171                         {
1172                                 if (TextureOverride == true)
1173                                         texture2 = texturemanager->bottomtex;
1174
1175                                 wall->AddQuad( //bottom
1176                                                 NewName,
1177                                                 texture2,
1178                                                 Ogre::Vector3(x1, voffset, z2),
1179                                                 Ogre::Vector3(x2, voffset, z2),
1180                                                 Ogre::Vector3(x2, voffset, z1),
1181                                                 Ogre::Vector3(x1, voffset, z1), tw, th, autosize);
1182                         }
1183
1184                         if (top == true)
1185                         {
1186                                 if (TextureOverride == true)
1187                                         texture2 = texturemanager->toptex;
1188
1189                                 wall->AddQuad( //top
1190                                                 NewName,
1191                                                 texture2,
1192                                                 Ogre::Vector3(x1, voffset + height_in, z1),
1193                                                 Ogre::Vector3(x2, voffset + height_in, z1),
1194                                                 Ogre::Vector3(x2, voffset + height_in, z2),
1195                                                 Ogre::Vector3(x1, voffset + height_in, z2), tw, th, autosize);
1196                         }
1197                 }
1198         }
1199
1200         if (outside == true)
1201         {
1202                 NewName = name;
1203                 NewName.append(":outside");
1204
1205                 if (x_thickness == true)
1206                 {
1207                         if (TextureOverride == true)
1208                                 texture2 = texturemanager->mainnegtex;
1209
1210                         wall->AddQuad( //front
1211                                         NewName,
1212                                         texture2,
1213                                         Ogre::Vector3(x1, voffset + height_in, z1),
1214                                         Ogre::Vector3(x2, voffset + height_in, z1),
1215                                         Ogre::Vector3(x2, voffset, z1),
1216                                         Ogre::Vector3(x1, voffset, z1), tw, th, autosize);
1217
1218                         if (TextureOverride == true)
1219                                 texture2 = texturemanager->mainpostex;
1220
1221                         wall->AddQuad( //back
1222                                         NewName,
1223                                         texture2,
1224                                         Ogre::Vector3(x2, voffset + height_in, z2),
1225                                         Ogre::Vector3(x1, voffset + height_in, z2),
1226                                         Ogre::Vector3(x1, voffset, z2),
1227                                         Ogre::Vector3(x2, voffset, z2), tw, th, autosize);
1228                 }
1229                 if (z_thickness == true)
1230                 {
1231                         if (TextureOverride == true)
1232                                 texture2 = texturemanager->sidepostex;
1233
1234                         wall->AddQuad( //right
1235                                         NewName,
1236                                         texture2,
1237                                         Ogre::Vector3(x2, voffset + height_in, z1),
1238                                         Ogre::Vector3(x2, voffset + height_in, z2),
1239                                         Ogre::Vector3(x2, voffset, z2),
1240                                         Ogre::Vector3(x2, voffset, z1), tw, th, autosize);
1241
1242                         if (TextureOverride == true)
1243                                 texture2 = texturemanager->sidenegtex;
1244
1245                         wall->AddQuad( //left
1246                                         NewName,
1247                                         texture2,
1248                                         Ogre::Vector3(x1, voffset + height_in, z2),
1249                                         Ogre::Vector3(x1, voffset + height_in, z1),
1250                                         Ogre::Vector3(x1, voffset, z1),
1251                                         Ogre::Vector3(x1, voffset, z2), tw, th, autosize);
1252                 }
1253                 if (x_thickness == true && z_thickness == true)
1254                 {
1255                         if (bottom == true)
1256                         {
1257                                 if (TextureOverride == true)
1258                                         texture2 = texturemanager->bottomtex;
1259
1260                                 wall->AddQuad( //bottom
1261                                                 NewName,
1262                                                 texture2,
1263                                                 Ogre::Vector3(x1, voffset, z1),
1264                                                 Ogre::Vector3(x2, voffset, z1),
1265                                                 Ogre::Vector3(x2, voffset, z2),
1266                                                 Ogre::Vector3(x1, voffset, z2), tw, th, autosize);
1267                         }
1268                         if (top == true)
1269                         {
1270                                 if (TextureOverride == true)
1271                                         texture2 = texturemanager->toptex;
1272
1273                                 wall->AddQuad( //top
1274                                                 NewName,
1275                                                 texture2,
1276                                                 Ogre::Vector3(x1, voffset + height_in, z2),
1277                                                 Ogre::Vector3(x2, voffset + height_in, z2),
1278                                                 Ogre::Vector3(x2, voffset + height_in, z1),
1279                                                 Ogre::Vector3(x1, voffset + height_in, z1), tw, th, autosize);
1280                         }
1281                 }
1282         }
1283         return wall;
1284 }
1285
1286 Wall* SBS::CreateWallBox2(MeshObject* mesh, const std::string &name, const std::string &texture, float CenterX, float CenterZ, float WidthX, float LengthZ, float height_in, float voffset, float tw, float th, bool inside, bool outside, bool top, bool bottom, bool autosize)
1287 {
1288         //create 4 walls from a central point
1289
1290         float x1 = CenterX - (WidthX / 2);
1291         float x2 = CenterX + (WidthX / 2);
1292         float z1 = CenterZ - (LengthZ / 2);
1293         float z2 = CenterZ + (LengthZ / 2);
1294
1295         return CreateWallBox(mesh, name, texture, x1, x2, z1, z2, height_in, voffset, tw, th, inside, outside, top, bottom, autosize);
1296 }
1297
1298 void SBS::AddPolygon(Wall* wallobject, const std::string &texture, std::vector<Ogre::Vector3> &varray, float tw, float th)
1299 {
1300         //creates a polygon in the specified wall object
1301
1302         if (!wallobject)
1303                 return;
1304
1305         std::vector<Ogre::Vector3> varray1 = varray;
1306         std::vector<Ogre::Vector3> varray2;
1307
1308         //get number of stored vertices
1309         size_t num = varray.size();
1310
1311         //create a second array with reversed vertices
1312         varray2.reserve(num);
1313         for (size_t i = num - 1; i < num; --i)
1314                 varray2.push_back(varray1[i]);
1315
1316         //create 2 polygons (front and back) from the vertex array
1317
1318         //get polygon native direction
1319         Ogre::Vector3 direction = GetPolygonDirection(varray1);
1320
1321         //if the polygon is facing right, down or to the back, reverse faces
1322         //to keep the vertices clockwise
1323         std::vector<Ogre::Vector3> tmppoly;
1324         if (direction.x == 1 || direction.y == -1 || direction.z == 1)
1325         {
1326                 tmppoly = varray1;
1327                 varray1 = varray2;
1328                 varray2 = tmppoly;
1329         }
1330
1331         std::string name = wallobject->GetName();
1332
1333         //add the polygons
1334         if (DrawMainN == true)
1335         {
1336                 std::string NewName = name;
1337                 if (DrawMainP == true)
1338                         NewName.append(":0");
1339                 wallobject->AddPolygon(NewName, texture, varray1, tw, th, true);
1340         }
1341         if (DrawMainP == true)
1342         {
1343                 std::string NewName = name;
1344                 if (DrawMainN == true)
1345                         NewName.append(":1");
1346                 wallobject->AddPolygon(NewName, texture, varray2, tw, th, true);
1347         }
1348 }
1349
1350 Wall* SBS::AddCustomWall(MeshObject* mesh, const std::string &name, const std::string &texture, std::vector<Ogre::Vector3> &varray, float tw, float th)
1351 {
1352         //Adds a wall from a specified array of 3D vectors
1353
1354         if (!mesh)
1355                 return 0;
1356
1357         //create wall object
1358         Wall *wall = mesh->CreateWallObject(name);
1359
1360         //create polygon in wall object
1361         AddPolygon(wall, texture, varray, tw, th);
1362
1363         return wall;
1364 }
1365
1366 Wall* SBS::AddCustomFloor(MeshObject* mesh, const std::string &name, const std::string &texture, std::vector<Ogre::Vector2> &varray, float altitude, float tw, float th)
1367 {
1368         //Same as AddCustomWall, with only one altitude value value
1369         std::vector<Ogre::Vector3> varray3;
1370
1371         //set up 3D vertex array
1372         varray3.reserve(varray.size());
1373         for (size_t i = 0; i < varray.size(); i++)
1374         {
1375                 varray3.push_back(Ogre::Vector3(varray[i].x, altitude, varray[i].y));
1376         }
1377
1378         //pass data on to AddCustomWall function
1379         return AddCustomWall(mesh, name, texture, varray3, tw, th);
1380 }
1381
1382 Wall* SBS::AddTriangleWall(MeshObject* mesh, const std::string &name, const std::string &texture, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float tw, float th)
1383 {
1384         //Adds a triangular wall with the specified dimensions
1385         std::vector<Ogre::Vector3> varray;
1386
1387         //set up temporary vertex array
1388         varray.reserve(3);
1389         varray.push_back(Ogre::Vector3(x1, y1, z1));
1390         varray.push_back(Ogre::Vector3(x2, y2, z2));
1391         varray.push_back(Ogre::Vector3(x3, y3, z3));
1392
1393         //pass data on to AddCustomWall function
1394         return AddCustomWall(mesh, name, texture, varray, tw, th);
1395 }
1396
1397 void SBS::EnableBuildings(bool value)
1398 {
1399         //turns buildings on/off
1400         Buildings->Enabled(value);
1401         IsBuildingsEnabled = value;
1402 }
1403
1404 void SBS::EnableLandscape(bool value)
1405 {
1406         //turns landscape on/off
1407         Landscape->Enabled(value);
1408         IsLandscapeEnabled = value;
1409 }
1410
1411 void SBS::EnableExternal(bool value)
1412 {
1413         //turns external on/off
1414         External->Enabled(value);
1415         IsExternalEnabled = value;
1416 }
1417
1418 void SBS::EnableSkybox(bool value)
1419 {
1420         //turns skybox on/off
1421         if (SkyBox)
1422         {
1423                 SkyBox->Enabled(value);
1424                 IsSkyboxEnabled = value;
1425         }
1426         else
1427                 IsSkyboxEnabled = true;
1428 }
1429
1430 void SBS::CreateSky()
1431 {
1432         //create skybox
1433
1434         //only create skybox if first engine instance
1435         if (InstanceNumber > 0)
1436                 return;
1437
1438         Mount("sky-" + SkyName + ".zip", "sky");
1439
1440         //load textures
1441         SetLighting();
1442         texturemanager->LoadTexture("sky/up.jpg", "SkyTop", 1, 1, false, false, false, 0);
1443         texturemanager->LoadTexture("sky/down.jpg", "SkyBottom", 1, 1, false, false, false, 0);
1444         texturemanager->LoadTexture("sky/left.jpg", "SkyLeft", 1, 1, false, false, false, 0);
1445         texturemanager->LoadTexture("sky/right.jpg", "SkyRight", 1, 1, false, false, false, 0);
1446         texturemanager->LoadTexture("sky/front.jpg", "SkyFront", 1, 1, false, false, false, 0);
1447         texturemanager->LoadTexture("sky/back.jpg", "SkyBack", 1, 1, false, false, false, 0);
1448         ResetLighting();
1449
1450         SkyBox = new MeshObject(this, "SkyBox");
1451         SkyBox->no_collider = true;
1452
1453         //create a skybox that extends by default 30 miles (30 * 5280 ft) in each direction
1454         float skysize = GetConfigInt("Skyscraper.SBS.HorizonDistance", 30) * 5280.0f;
1455         texturemanager->ResetTextureMapping(true);
1456         Wall *wall = new Wall(SkyBox, SkyBox, true);
1457
1458         wall->AddQuad( //front
1459                 "SkyFront",
1460                 "SkyFront",
1461                 Ogre::Vector3(-skysize, -skysize, -skysize),
1462                 Ogre::Vector3(skysize, -skysize, -skysize),
1463                 Ogre::Vector3(skysize, skysize, -skysize),
1464                 Ogre::Vector3(-skysize, skysize, -skysize), 1, 1, false);
1465         wall->AddQuad( //right
1466                 "SkyRight",
1467                 "SkyRight",
1468                 Ogre::Vector3(skysize, -skysize, -skysize),
1469                 Ogre::Vector3(skysize, -skysize, skysize),
1470                 Ogre::Vector3(skysize, skysize, skysize),
1471                 Ogre::Vector3(skysize, skysize, -skysize), 1, 1, false);
1472         wall->AddQuad( //back
1473                 "SkyBack",
1474                 "SkyBack",
1475                 Ogre::Vector3(skysize, -skysize, skysize),
1476                 Ogre::Vector3(-skysize, -skysize, skysize),
1477                 Ogre::Vector3(-skysize, skysize, skysize),
1478                 Ogre::Vector3(skysize, skysize, skysize), 1, 1, false);
1479         wall->AddQuad( //left
1480                 "SkyLeft",
1481                 "SkyLeft",
1482                 Ogre::Vector3(-skysize, -skysize, skysize),
1483                 Ogre::Vector3(-skysize, -skysize, -skysize),
1484                 Ogre::Vector3(-skysize, skysize, -skysize),
1485                 Ogre::Vector3(-skysize, skysize, skysize), 1, 1, false);
1486         wall->AddQuad( //bottom
1487                 "SkyBottom",
1488                 "SkyBottom",
1489                 Ogre::Vector3(-skysize, -skysize, skysize),
1490                 Ogre::Vector3(skysize, -skysize, skysize),
1491                 Ogre::Vector3(skysize, -skysize, -skysize),
1492                 Ogre::Vector3(-skysize, -skysize, -skysize), 1, -1, false);
1493         wall->AddQuad( //top
1494                 "SkyTop",
1495                 "SkyTop",
1496                 Ogre::Vector3(-skysize, skysize, -skysize),
1497                 Ogre::Vector3(skysize, skysize, -skysize),
1498                 Ogre::Vector3(skysize, skysize, skysize),
1499                 Ogre::Vector3(-skysize, skysize, skysize), -1, -1, false);
1500
1501         texturemanager->ResetTextureMapping();
1502         delete wall;
1503 }
1504
1505 int SBS::GetFloorNumber(float altitude, int lastfloor, bool checklastfloor)
1506 {
1507         //Returns floor number located at a specified altitude
1508
1509         if (GetTotalFloors() == 0)
1510                 return 0;
1511
1512         //check to see if altitude is below bottom floor
1513         if (altitude < GetFloor(-Basements)->Altitude)
1514                 return -Basements;
1515
1516         //if checklastfloor is specified, compare altitude with lastfloor
1517         if (checklastfloor == true && GetFloor(lastfloor))
1518         {
1519                 float lastfloor_altitude = GetFloor(lastfloor)->Altitude;
1520                 float upperfloor_altitude;
1521                 if (lastfloor < Floors - 1)
1522                         upperfloor_altitude = GetFloor(lastfloor + 1)->Altitude;
1523                 else
1524                         upperfloor_altitude = GetFloor(lastfloor)->Altitude + GetFloor(lastfloor)->FullHeight();
1525
1526                 if (upperfloor_altitude > altitude && lastfloor_altitude <= altitude)
1527                         return lastfloor;
1528                 else
1529                 {
1530                         //if altitude is below lastfloor, search downwards; otherwise search upwards
1531                         if (altitude < lastfloor_altitude)
1532                         {
1533                                 for (int i = lastfloor - 1; i >= -Basements; i--)
1534                                 {
1535                                         if (GetFloor(i + 1)->Altitude > altitude && GetFloor(i)->Altitude <= altitude)
1536                                                 return i;
1537                                 }
1538                         }
1539                         else if (altitude >= upperfloor_altitude)
1540                         {
1541                                 for (int i = lastfloor + 1; i < Floors; i++)
1542                                 {
1543                                         if (GetFloor(i - 1)->Altitude <= altitude && GetFloor(i)->Altitude > altitude)
1544                                                 return i - 1;
1545                                         if (i == Floors - 1 && GetFloor(i)->Altitude <= altitude)
1546                                                 return i; //return top floor if on top
1547                                 }
1548                         }
1549                 }
1550         }
1551
1552         //otherwise do a slow linear search through floors
1553         for (int i = -Basements + 1; i < Floors; i++)
1554         {
1555                 //check to see if altitude is within a floor (between the current floor's base and
1556                 //the lower floor's base)
1557                 if ((GetFloor(i)->Altitude > altitude) && (GetFloor(i - 1)->Altitude <= altitude))
1558                         return i - 1;
1559                 //check to see if altitude is above top floor's altitude
1560                 if ((i == Floors - 1) && (altitude > GetFloor(i)->Altitude))
1561                         return i;
1562         }
1563         return 0;
1564 }
1565
1566 float SBS::GetDistance(float x1, float x2, float z1, float z2)
1567 {
1568         //returns the distance between 2 2D vectors
1569
1570         if (z1 == z2)
1571                 return std::abs(x1 - x2);
1572         if (x1 == x2)
1573                 return std::abs(z1 - z2);
1574         if ((x1 != x2) && (z2 != x2))
1575                 return sqrtf(powf(std::abs(x1 - x2), 2) + powf(std::abs(z1 - z2), 2)); //calculate diagonals
1576         return 0;
1577 }
1578
1579 Shaft* SBS::CreateShaft(int number, float CenterX, float CenterZ, int startfloor, int endfloor)
1580 {
1581         //create a shaft object
1582
1583         return shaft_manager->Create(number, CenterX, CenterZ, startfloor, endfloor);
1584 }
1585
1586 Stairs* SBS::CreateStairwell(int number, float CenterX, float CenterZ, int startfloor, int endfloor)
1587 {
1588         //create a stairwell object
1589
1590         return stairs_manager->Create(number, CenterX, CenterZ, startfloor, endfloor);
1591 }
1592
1593 Elevator* SBS::NewElevator(int number)
1594 {
1595         //create a new elevator object
1596
1597         return elevator_manager->Create(number);
1598 }
1599
1600 Floor* SBS::NewFloor(int number)
1601 {
1602         //create a new floor object
1603
1604         return floor_manager->Create(number);
1605 }
1606
1607 int SBS::GetElevatorCount()
1608 {
1609         //return the number of elevators
1610         return elevator_manager->GetCount();
1611 }
1612
1613 int SBS::GetTotalFloors()
1614 {
1615         //return the number of floors, including basements
1616         return floor_manager->GetCount();
1617 }
1618
1619 int SBS::GetShaftCount()
1620 {
1621         //return the number of shafts
1622         return shaft_manager->GetCount();
1623 }
1624
1625 int SBS::GetStairsCount()
1626 {
1627         //return the number of stairs
1628         return stairs_manager->GetCount();
1629 }
1630
1631 Floor* SBS::GetFloor(int number)
1632 {
1633         //return pointer to floor object
1634
1635         return floor_manager->Get(number);
1636 }
1637
1638 Elevator* SBS::GetElevator(int number)
1639 {
1640         //return pointer to elevator object
1641
1642         return elevator_manager->Get(number);
1643 }
1644
1645 Shaft* SBS::GetShaft(int number)
1646 {
1647         //return pointer to shaft object
1648
1649         return shaft_manager->Get(number);
1650 }
1651
1652 Stairs* SBS::GetStairs(int number)
1653 {
1654         //return pointer to stairs object
1655
1656         return stairs_manager->Get(number);
1657 }
1658
1659 bool SBS::SetWallOrientation(std::string direction)
1660 {
1661         //changes internal wall orientation parameter.
1662         //direction can either be "left" (negative), "center" (0), or "right" (positive).
1663         //default on startup is 1, or center.
1664         //the parameter is used to determine the location of the wall's
1665         //x1/x2 or z1/z2 coordinates in relation to the thickness extents
1666
1667         SetCase(direction, false);
1668
1669         if (direction == "left")
1670                 wall_orientation = 0;
1671         else if (direction == "center")
1672                 wall_orientation = 1;
1673         else if (direction == "right")
1674                 wall_orientation = 2;
1675         else
1676                 return ReportError("SetWallOrientation: Invalid wall orientation");
1677         return true;
1678 }
1679
1680 int SBS::GetWallOrientation()
1681 {
1682         return wall_orientation;
1683 }
1684
1685 bool SBS::SetFloorOrientation(std::string direction)
1686 {
1687         //changes internal floor orientation parameter.
1688         //direction can either be "bottom" (negative), "center" (0), or "top" (positive).
1689         //default on startup is 2, or top.
1690         //the parameter is used to determine the location of the floor's
1691         //x1/x2 or z1/z2 coordinates in relation to the thickness extents
1692
1693         SetCase(direction, false);
1694
1695         if (direction == "bottom")
1696                 floor_orientation = 0;
1697         else if (direction == "center")
1698                 floor_orientation = 1;
1699         else if (direction == "top")
1700                 floor_orientation = 2;
1701         else
1702                 return ReportError("SetFloorOrientation: Invalid floor orientation");
1703         return true;
1704 }
1705
1706 int SBS::GetFloorOrientation()
1707 {
1708         return floor_orientation;
1709 }
1710
1711 void SBS::DrawWalls(bool MainN, bool MainP, bool SideN, bool SideP, bool Top, bool Bottom)
1712 {
1713         //sets which walls should be drawn
1714
1715         //first backup old parameters
1716         DrawMainNOld = DrawMainN;
1717         DrawMainPOld = DrawMainP;
1718         DrawSideNOld = DrawSideN;
1719         DrawSidePOld = DrawSideP;
1720         DrawTopOld = DrawTop;
1721         DrawBottomOld = DrawBottom;
1722
1723         //now set new parameters
1724         DrawMainN = MainN;
1725         DrawMainP = MainP;
1726         DrawSideN = SideN;
1727         DrawSideP = SideP;
1728         DrawTop = Top;
1729         DrawBottom = Bottom;
1730 }
1731
1732 void SBS::ResetWalls(bool ToDefaults)
1733 {
1734         //if ToDefaults is true, this resets the DrawWalls data to the defaults.
1735         //if ToDefaults is false, this reverts the DrawWalls data to the previous settings.
1736
1737         if (ToDefaults == true)
1738                 DrawWalls(true, true, false, false, false, false);
1739         else
1740                 DrawWalls(DrawMainNOld, DrawMainPOld, DrawSideNOld, DrawSidePOld, DrawTopOld, DrawBottomOld);
1741 }
1742
1743 int SBS::GetDrawWallsCount()
1744 {
1745         //gets the number of wall polygons enabled
1746
1747         int sides = 0;
1748
1749         if (DrawMainN == true)
1750                 sides++;
1751         if (DrawMainP == true)
1752                 sides++;
1753         if (DrawSideN == true)
1754                 sides++;
1755         if (DrawSideP == true)
1756                 sides++;
1757         if (DrawTop == true)
1758                 sides++;
1759         if (DrawBottom == true)
1760                 sides++;
1761
1762         return sides;
1763 }
1764
1765 float SBS::MetersToFeet(float meters)
1766 {
1767         //converts meters to feet
1768         return meters * 3.2808399f;
1769 }
1770
1771 float SBS::FeetToMeters(float feet)
1772 {
1773         //converts feet to meters
1774         return feet / 3.2808399f;
1775 }
1776
1777 Wall* SBS::AddDoorwayWalls(MeshObject* mesh, const std::string &wallname, const std::string &texture, float tw, float th)
1778 {
1779         //add joining doorway polygons if needed
1780
1781         if (!mesh)
1782                 return 0;
1783
1784         if (wall1a == true && wall2a == true)
1785         {
1786                 Wall *wall = mesh->CreateWallObject(wallname);
1787
1788                 //convert extents to relative positioning
1789                 Ogre::Vector2 extents_x = wall_extents_x - wall->GetMesh()->GetPosition().x;
1790                 Ogre::Vector2 extents_y = wall_extents_y - wall->GetMesh()->GetPosition().y;
1791                 Ogre::Vector2 extents_z = wall_extents_z - wall->GetMesh()->GetPosition().z;
1792
1793                 //true if doorway is facing forward/backward
1794                 //false if doorway is facing left/right
1795                 bool direction = std::abs(extents_x.x - extents_x.y) > std::abs(extents_z.x - extents_z.y);
1796
1797                 DrawWalls(false, true, false, false, false, false);
1798                 if (direction == true)
1799                         AddWallMain(wall, "DoorwayLeft", texture, 0, extents_x.x, extents_z.x, extents_x.x, extents_z.y, extents_y.y - extents_y.x, extents_y.y - extents_y.x, extents_y.x, extents_y.x, tw, th, true);
1800                 else
1801                         AddWallMain(wall, "DoorwayLeft", texture, 0, extents_x.x, extents_z.x, extents_x.y, extents_z.x, extents_y.y - extents_y.x, extents_y.y - extents_y.x, extents_y.x, extents_y.x, tw, th, true);
1802                 ResetWalls();
1803
1804                 DrawWalls(true, false, false, false, false, false);
1805                 if (direction == true)
1806                         AddWallMain(wall, "DoorwayRight", texture, 0, extents_x.y, extents_z.x, extents_x.y, extents_z.y, extents_y.y - extents_y.x, extents_y.y - extents_y.x, extents_y.x, extents_y.x, tw, th, true);
1807                 else
1808                         AddWallMain(wall, "DoorwayRight", texture, 0, extents_x.x, extents_z.y, extents_x.y, extents_z.y, extents_y.y - extents_y.x, extents_y.y - extents_y.x, extents_y.x, extents_y.x, tw, th, true);
1809
1810                 AddFloorMain(wall, "DoorwayTop", texture, 0, extents_x.x, extents_z.x, extents_x.y, extents_z.y, extents_y.y, extents_y.y, false, false, tw, th, true);
1811                 ResetWalls();
1812
1813                 ResetDoorwayWalls();
1814
1815                 return wall;
1816         }
1817
1818         return 0;
1819 }
1820
1821 void SBS::ResetDoorwayWalls()
1822 {
1823         wall1a = false;
1824         wall1b = false;
1825         wall2a = false;
1826         wall2b = false;
1827         wall_extents_x = 0;
1828         wall_extents_y = 0;
1829         wall_extents_z = 0;
1830 }
1831
1832 Wall* SBS::AddWall(MeshObject* mesh, const std::string &name, const std::string &texture, float thickness, float x1, float z1, float x2, float z2, float height_in1, float height_in2, float altitude1, float altitude2, float tw, float th)
1833 {
1834         //Adds a wall with the specified dimensions, to the specified mesh object
1835
1836         if (!mesh)
1837                 return 0;
1838
1839         Wall *wall = mesh->CreateWallObject(name);
1840
1841         AddWallMain(wall, name, texture, thickness, x1, z1, x2, z2, height_in1, height_in2, altitude1, altitude2, tw, th, true);
1842         return wall;
1843 }
1844
1845 Wall* SBS::AddFloor(MeshObject* mesh, const std::string &name, const std::string &texture, float thickness, float x1, float z1, float x2, float z2, float altitude1, float altitude2, bool reverse_axis, bool texture_direction, float tw, float th, bool legacy_behavior)
1846 {
1847         //Adds a floor with the specified dimensions and vertical offset, to the specified mesh object
1848
1849         if (!mesh)
1850                 return 0;
1851
1852         Wall *wall = mesh->CreateWallObject(name);
1853
1854         AddFloorMain(wall, name, texture, thickness, x1, z1, x2, z2, altitude1, altitude2, reverse_axis, texture_direction, tw, th, true, legacy_behavior);
1855         return wall;
1856 }
1857
1858 Wall* SBS::AddGround(const std::string &name, const std::string &texture, float x1, float z1, float x2, float z2, float altitude, int tile_x, int tile_z)
1859 {
1860         //Adds ground based on a tiled-floor layout, with the specified dimensions and vertical offset
1861         //this does not support thickness
1862
1863         Ogre::Vector3 v1, v2, v3, v4;
1864
1865         float minx, minz, maxx, maxz;
1866
1867         //get min and max values
1868         if (x1 < x2)
1869         {
1870                 minx = x1;
1871                 maxx = x2;
1872         }
1873         else
1874         {
1875                 minx = x2;
1876                 maxx = x1;
1877         }
1878         if (z1 < z2)
1879         {
1880                 minz = z1;
1881                 maxz = z2;
1882         }
1883         else
1884         {
1885                 minz = z2;
1886                 maxz = z1;
1887         }
1888
1889         Wall *wall = Landscape->CreateWallObject(name);
1890
1891         Report("Creating ground...");
1892
1893         //create polygon tiles
1894         for (float i = minx; i < maxx; i += tile_x)
1895         {
1896                 float sizex, sizez;
1897
1898                 if (i + tile_x > maxx)
1899                         sizex = maxx - i;
1900                 else
1901                         sizex = (float)tile_x;
1902
1903                 for (float j = minz; j < maxz; j += tile_z)
1904                 {
1905                         if (j + tile_z > maxz)
1906                                 sizez = maxz - i;
1907                         else
1908                                 sizez = (float)tile_z;
1909
1910                         DrawWalls(true, true, false, false, false, false);
1911                         AddFloorMain(wall, name, texture, 0, i, j, i + sizex, j + sizez, altitude, altitude, false, false, 1, 1, false);
1912                         ResetWalls(false);
1913                 }
1914         }
1915         Report("Finished ground");
1916         return wall;
1917 }
1918
1919 void SBS::EnableFloorRange(int floor, int range, bool value, bool enablegroups, int shaftnumber, int stairsnumber)
1920 {
1921         //turn on/off a range of floors
1922         //if range is 3, show shaft on current floor (floor), and 1 floor below and above (3 total floors)
1923         //if range is 1, show only the current floor (floor)
1924
1925         SBS_PROFILE("SBS::EnableFloorRange");
1926
1927         //range must be greater than 0
1928         if (range < 1)
1929                 range = 1;
1930
1931         //range must be an odd number; if it's even, then add 1
1932         if (IsEven(range) == true)
1933                 range++;
1934
1935         //floor must be valid
1936         if (!IsValidFloor(floor))
1937                 return;
1938
1939         int additionalfloors;
1940         if (range > 1)
1941                 additionalfloors = (range - 1) / 2;
1942         else
1943                 additionalfloors = 0;
1944
1945         Shaft *shaft = 0;
1946         Stairs *stairs = 0;
1947
1948         if (shaftnumber > 0)
1949                 shaft = GetShaft(shaftnumber);
1950         if (stairsnumber > 0)
1951                 stairs = GetStairs(stairsnumber);
1952
1953         //disable floors 1 floor outside of range, unless part of group
1954         if (value == true)
1955         {
1956                 int floorval = floor - additionalfloors - 1;
1957                 if (IsValidFloor(floorval) && GetFloor(floor)->IsInGroup(floorval) == false)
1958                         GetFloor(floorval)->Enabled(false);
1959
1960                 floorval = floor + additionalfloors + 1;
1961                 if (IsValidFloor(floorval) && GetFloor(floor)->IsInGroup(floorval) == false)
1962                         GetFloor(floorval)->Enabled(false);
1963         }
1964
1965         //enable floors within range
1966         for (int i = floor - additionalfloors; i <= floor + additionalfloors; i++)
1967         {
1968                 Floor *floorobj = GetFloor(i);
1969
1970                 if (floorobj)
1971                 {
1972                         if (shaft)
1973                         {
1974                                 //if a shaft is specified, only show the floor if it is in the related shaft's ShowFloorsList array
1975                                 if (shaft->ShowFloors > 0)
1976                                 {
1977                                         bool showfloor = shaft->IsShowFloor(i);
1978
1979                                         if (showfloor == true && value == true)
1980                                         {
1981                                                 if (floorobj->IsEnabled == false)
1982                                                 {
1983                                                         floorobj->Enabled(true);
1984                                                         if (enablegroups == true)
1985                                                                 floorobj->EnableGroup(true);
1986                                                 }
1987                                         }
1988                                         else
1989                                         {
1990                                                 //only disable floor if it hasn't been enabled separately by a related group
1991                                                 if (floorobj->EnabledGroup == true)
1992                                                 {
1993                                                         //for now check to see if the group floor is a ShowFloor
1994                                                         if (shaft->IsShowFloor(floorobj->EnabledGroup_Floor) == true)
1995                                                                 return;
1996                                                 }
1997
1998                                                 if (floorobj->IsEnabled == true)
1999                                                 {
2000                                                         floorobj->Enabled(false);
2001                                                         if (enablegroups == true)
2002                                                                 floorobj->EnableGroup(false);
2003                                                 }
2004                                         }
2005                                 }
2006                         }
2007                         else if (stairs)
2008                         {
2009                                 //if a stairwell is specified, only show the floor if it is in the related stairwell's ShowFloorsList array
2010                                 if (stairs->ShowFloors == true)
2011                                 {
2012                                         bool showfloor = stairs->IsShowFloor(i);
2013
2014                                         if (showfloor == true && value == true)
2015                                         {
2016                                                 if (floorobj->IsEnabled == false)
2017                                                 {
2018                                                         floorobj->Enabled(true);
2019                                                         if (enablegroups == true)
2020                                                                 floorobj->EnableGroup(true);
2021                                                 }
2022                                         }
2023                                         else
2024                                         {
2025                                                 //only disable floor if it hasn't been enabled separately by a related group
2026                                                 if (floorobj->EnabledGroup == true)
2027                                                 {
2028                                                         //for now check to see if the group floor is a ShowFloor
2029                                                         if (stairs->IsShowFloor(floorobj->EnabledGroup_Floor) == true)
2030                                                                 return;
2031                                                 }
2032
2033                                                 if (floorobj->IsEnabled == true)
2034                                                 {
2035                                                         floorobj->Enabled(false);
2036                                                         if (enablegroups == true)
2037                                                                 floorobj->EnableGroup(false);
2038                                                 }
2039                                         }
2040                                 }
2041                         }
2042                         else
2043                         {
2044                                 floorobj->Enabled(value);
2045                                 if (enablegroups == true)
2046                                         floorobj->EnableGroup(value);
2047                         }
2048                 }
2049         }
2050 }
2051
2052 bool SBS::RegisterTimerCallback(TimerObject *timer)
2053 {
2054         //register a timer object for callbacks
2055
2056         if (!timer)
2057                 return false;
2058
2059         for (size_t i = 0; i < timercallbacks.size(); i++)
2060         {
2061                 if (timercallbacks[i] == timer)
2062                         return false;
2063         }
2064
2065         //if timer isn't already in the array, add it
2066         timercallbacks.push_back(timer);
2067
2068         return true;
2069 }
2070
2071 bool SBS::UnregisterTimerCallback(TimerObject *timer)
2072 {
2073         if (!timer)
2074                 return false;
2075
2076         for (size_t i = 0; i < timercallbacks.size(); i++)
2077         {
2078                 //unregister existing call button callback
2079                 if (timercallbacks[i] == timer)
2080                 {
2081                         timercallbacks.erase(timercallbacks.begin() + i);
2082                         return true;
2083                 }
2084         }
2085
2086         return false;
2087 }
2088
2089 void SBS::ProcessTimers()
2090 {
2091         SBS_PROFILE("SBS::ProcessTimers");
2092
2093         //process all registered timers
2094         for (size_t i = 0; i < timercallbacks.size(); i++)
2095         {
2096                 if (timercallbacks[i])
2097                         timercallbacks[i]->Loop();
2098         }
2099 }
2100
2101 int SBS::GetTimerCallbackCount()
2102 {
2103         //return the number of registered call button callbacks
2104         return (int)timercallbacks.size();
2105 }
2106
2107 bool SBS::Mount(const std::string &filename, const std::string &path)
2108 {
2109         //mounts a zip file into the virtual filesystem
2110
2111         std::string newfile = "data/" + filename;
2112         std::string file = VerifyFile(newfile);
2113
2114         Report("Mounting " + file + " as path " + path);
2115         try
2116         {
2117                 Ogre::ResourceGroupManager::getSingleton().addResourceLocation(file, "Zip", path, true);
2118         }
2119         catch (Ogre::Exception &e)
2120         {
2121                 return ReportError("Error mounting file " + file + "\n" + e.getDescription());
2122         }
2123         return true;
2124 }
2125
2126 void SBS::AddFloorAutoArea(Ogre::Vector3 start, Ogre::Vector3 end)
2127 {
2128         //adds an auto area that enables/disables floors
2129
2130         AutoArea newarea;
2131         newarea.start = start;
2132         newarea.end = end;
2133         newarea.inside = false;
2134         newarea.camerafloor = 0;
2135         FloorAutoArea.push_back(newarea);
2136 }
2137
2138 void SBS::CheckAutoAreas()
2139 {
2140         //check all automatic areas
2141
2142         SBS_PROFILE("SBS::CheckAutoAreas");
2143
2144         Ogre::Vector3 position = camera->GetPosition();
2145         int floor = camera->CurrentFloor;
2146
2147         for (size_t i = 0; i < FloorAutoArea.size(); i++)
2148         {
2149                 //reset inside value if floor changed
2150                 if (FloorAutoArea[i].camerafloor != floor)
2151                         FloorAutoArea[i].inside = false;
2152
2153                 if (InBox(FloorAutoArea[i].start, FloorAutoArea[i].end, position) == true && FloorAutoArea[i].inside == false)
2154                 {
2155                         //user moved into box; enable floors
2156                         FloorAutoArea[i].inside = true;
2157                         FloorAutoArea[i].camerafloor = floor;
2158                         if (floor > -Basements)
2159                         {
2160                                 GetFloor(floor - 1)->Enabled(true);
2161                                 GetFloor(floor - 1)->EnableGroup(true);
2162                         }
2163                         GetFloor(floor)->Enabled(true);
2164                         GetFloor(floor)->EnableGroup(true);
2165                         if (floor < Floors - 1)
2166                         {
2167                                 GetFloor(floor + 1)->Enabled(true);
2168                                 GetFloor(floor + 1)->EnableGroup(true);
2169                         }
2170                 }
2171                 if (InBox(FloorAutoArea[i].start, FloorAutoArea[i].end, position) == false && FloorAutoArea[i].inside == true)
2172                 {
2173                         //user moved out of box; disable floors except current
2174                         FloorAutoArea[i].inside = false;
2175                         FloorAutoArea[i].camerafloor = 0;
2176                         if (floor > -Basements)
2177                         {
2178                                 GetFloor(floor - 1)->Enabled(false);
2179                                 GetFloor(floor - 1)->EnableGroup(false);
2180                         }
2181                         if (floor < Floors - 1)
2182                         {
2183                                 GetFloor(floor + 1)->Enabled(false);
2184                                 GetFloor(floor + 1)->EnableGroup(false);
2185                         }
2186                         GetFloor(floor)->Enabled(true);
2187                         GetFloor(floor)->EnableGroup(true);
2188                 }
2189         }
2190 }
2191
2192 int SBS::GetMeshCount()
2193 {
2194         //return total number of mesh objects
2195         return (int)meshes.size();
2196 }
2197
2198 Sound* SBS::AddSound(const std::string &name, const std::string &filename, const Ogre::Vector3 &position, bool loop, float volume, int speed, float min_distance, float max_distance, float doppler_level, float cone_inside_angle, float cone_outside_angle, float cone_outside_volume, const Ogre::Vector3 &direction)
2199 {
2200         //create a looping sound object
2201         Sound *sound = new Sound(this, name, false);
2202         sounds.push_back(sound);
2203
2204         //set parameters and play sound
2205         sound->SetPosition(position);
2206         sound->SetDirection(direction);
2207         sound->SetVolume(volume);
2208         sound->SetSpeed(speed);
2209         sound->SetDistances(min_distance, max_distance);
2210         sound->SetDirection(direction);
2211         sound->SetDopplerLevel(doppler_level);
2212         sound->SetConeSettings(cone_inside_angle, cone_outside_angle, cone_outside_volume);
2213         sound->Load(filename);
2214         sound->SetLoopState(loop);
2215         if (loop && IsRunning == true)
2216                 sound->Play();
2217
2218         return sound;
2219 }
2220
2221 std::vector<Sound*> SBS::GetSound(const std::string &name)
2222 {
2223         //get sound by name
2224
2225         std::string findname = name;
2226         SetCase(findname, false);
2227         std::vector<Sound*> soundlist;
2228         for (size_t i = 0; i < sounds.size(); i++)
2229         {
2230                 if (sounds[i])
2231                 {
2232                         std::string name2 = sounds[i]->GetName();
2233                         SetCase(name2, false);
2234                         if (findname == name2)
2235                                 soundlist.push_back(sounds[i]);
2236                 }
2237         }
2238         return soundlist;
2239 }
2240
2241 int SBS::GetSoundCount()
2242 {
2243         //return total number of allocated sounds
2244         return soundcount;
2245 }
2246
2247 void SBS::IncrementSoundCount()
2248 {
2249         soundcount++;
2250 }
2251
2252 void SBS::DecrementSoundCount()
2253 {
2254         soundcount--;
2255 }
2256
2257 float SBS::ToLocal(float remote_value)
2258 {
2259         //convert remote (OGRE) vertex positions to local (SBS) positions
2260
2261         //note - OGRE uses a right-hand coordinate system, while SBS uses left-hand.
2262         //this means that all Z values that use this function must be inverted.
2263
2264         return remote_value * UnitScale;
2265 }
2266
2267 Ogre::Vector2 SBS::ToLocal(const Ogre::Vector2& remote_value)
2268 {
2269         //convert remote (OGRE) vertex positions to local (SBS) positions
2270
2271         //note - OGRE uses a right-hand coordinate system, while SBS uses left-hand.
2272         //this means that all Z values that use this function must be inverted.
2273
2274         return remote_value * UnitScale;
2275 }
2276
2277 Ogre::Vector3 SBS::ToLocal(const Ogre::Vector3& remote_value, bool rescale, bool flip_z)
2278 {
2279         //convert remote (OGRE) vertex positions to local (SBS) positions
2280         //also convert Z value to OGRE's right-hand coordinate system
2281
2282         Ogre::Vector3 newvalue;
2283         newvalue.x = remote_value.x;
2284         newvalue.y = remote_value.y;
2285
2286         if (flip_z == true)
2287                 newvalue.z = -remote_value.z; //flip z value for OGRE's right-hand coordinate system
2288         else
2289                 newvalue.z = remote_value.z;
2290
2291         if (rescale == true)
2292                 return newvalue * UnitScale;
2293         else
2294                 return newvalue;
2295 }
2296
2297 float SBS::ToRemote(float local_value)
2298 {
2299         //convert local (SBS) vertex positions to remote (OGRE) positions
2300
2301         //note - OGRE uses a right-hand coordinate system, while SBS uses left-hand.
2302         //this means that all Z values that use this function must be inverted.
2303
2304         return local_value / UnitScale;
2305 }
2306
2307 Ogre::Vector2 SBS::ToRemote(const Ogre::Vector2& local_value)
2308 {
2309         //convert local (SBS) vertex positions to remote (OGRE) positions
2310
2311         //note - OGRE uses a right-hand coordinate system, while SBS uses left-hand.
2312         //this means that all Z values that use this function must be inverted.
2313
2314         return local_value / UnitScale;
2315 }
2316
2317 Ogre::Vector3 SBS::ToRemote(const Ogre::Vector3& local_value, bool rescale, bool flip_z)
2318 {
2319         //convert local (SBS) vertex positions to remote (OGRE) positions
2320
2321         Ogre::Vector3 newvalue;
2322         newvalue.x = local_value.x;
2323         newvalue.y = local_value.y;
2324
2325         if (flip_z == true)
2326                 newvalue.z = -local_value.z; //flip z value for OGRE's right-hand coordinate system
2327         else
2328                 newvalue.z = local_value.z;
2329
2330         if (rescale == true)
2331                 return (newvalue / UnitScale);
2332         else
2333                 return newvalue;
2334 }
2335
2336 int SBS::GetObjectCount()
2337 {
2338         //return number of registered SBS objects
2339         return ObjectCount;
2340 }
2341
2342 Object* SBS::GetObject(int number)
2343 {
2344         //return object pointer from global array
2345         if (number >= 0 && number < (int)ObjectArray.size())
2346                 return ObjectArray[number];
2347         else
2348                 return 0;
2349 }
2350
2351 int SBS::RegisterObject(Object *object)
2352 {
2353         //add object to global array
2354         ObjectCount++;
2355         ObjectArray.push_back(object);
2356         return (int)ObjectArray.size() - 1;
2357 }
2358
2359 bool SBS::UnregisterObject(int number)
2360 {
2361         //remove object
2362         //note - this doesn't delete the objects
2363
2364         if (number < (int)ObjectArray.size())
2365         {
2366                 if (ObjectArray[number])
2367                 {
2368                         if (ObjectArray[number]->GetNumber() == number)
2369                         {
2370                                 std::vector<Object*> objects;
2371                                 objects.push_back(ObjectArray[number]);
2372                                 RemoveActionParent(objects);
2373                                 ObjectArray[number] = 0;
2374                                 ObjectCount--;
2375                                 return true;
2376                         }
2377                 }
2378         }
2379         return false;
2380 }
2381
2382 bool SBS::IsValidFloor(int floor)
2383 {
2384         //determine if a floor is valid
2385
2386         if (GetFloor(floor))
2387                 return true;
2388         return false;
2389 }
2390
2391 std::string SBS::DumpState()
2392 {
2393         //dump basic simulator state to a string
2394
2395         std::string output = "SBS version: " + version + "\n";
2396         output.append("Instance number: " + ToString(InstanceNumber) + "\n");
2397         output.append("Building Name: " + BuildingName + "\n");
2398         output.append("Building Filename: " + BuildingFilename + "\n");
2399         output.append("Building Version: " + BuildingVersion + "\n");
2400         output.append("InStairwell: ");
2401         output.append(BoolToString(InStairwell));
2402         output.append("\n");
2403         output.append("InElevator: ");
2404         output.append(BoolToString(InElevator));
2405         output.append("\n");
2406         output.append("InShaft: ");
2407         output.append(BoolToString(InShaft));
2408         output.append("\n");
2409         output.append("CameraFloor: ");
2410         if (camera)
2411                 output.append(ToString(camera->CurrentFloor));
2412         output.append("\n");
2413         output.append("ElevatorNumber: ");
2414         output.append(ToString(ElevatorNumber));
2415         output.append("\n");
2416         output.append("CarNumber: ");
2417         output.append(ToString(CarNumber));
2418         output.append("\n");
2419         output.append("ElevatorSync: ");
2420         output.append(BoolToString(ElevatorSync));
2421         output.append("\n");
2422         output.append("Running Time: ");
2423         output.append(TruncateNumber(running_time, 2));
2424         output.append("\n");
2425         output.append("BuildingsEnabled: ");
2426         output.append(BoolToString(IsBuildingsEnabled));
2427         output.append("\n");
2428         output.append("ExternalEnabled: ");
2429         output.append(BoolToString(IsExternalEnabled));
2430         output.append("\n");
2431         output.append("LandscapeEnabled: ");
2432         output.append(BoolToString(IsLandscapeEnabled));
2433         output.append("\n");
2434         output.append("SkyboxEnabled: ");
2435         output.append(BoolToString(IsSkyboxEnabled));
2436         output.append("\n");
2437         output.append("Verbose: ");
2438         output.append(BoolToString(Verbose));
2439         output.append("\n");
2440         output.append("InterfloorOnTop: ");
2441         output.append(BoolToString(InterfloorOnTop));
2442         output.append("\n");
2443         output.append("Object Count: ");
2444         output.append(ToString(ObjectCount));
2445         output.append("\n");
2446         if (camera)
2447         {
2448                 output.append("Camera Floor: ");
2449                 output.append(ToString(camera->CurrentFloor));
2450                 output.append("\n");
2451                 output.append("Camera Position: " + TruncateNumber(camera->GetPosition().x, 2) + ", " + TruncateNumber(camera->GetPosition().y, 2) + ", " + TruncateNumber(camera->GetPosition().z, 2) + "\n");
2452         }
2453
2454         return output;
2455 }
2456
2457 bool SBS::DeleteObject(Object *object)
2458 {
2459         //object deletion routine
2460         //this should be called to delete a simulator object during runtime
2461
2462         if (!object)
2463                 return ReportError("Invalid object");
2464
2465         std::string number = ToString(object->GetNumber());
2466         bool deleted = false;
2467
2468         //don't delete permanent objects
2469         if (object->IsPermanent() == true)
2470                 return ReportError("Cannot delete permanent object " + number);
2471
2472         std::string type = object->GetType();
2473
2474         //perform standard delete based on object type
2475         if (type == "Floor")
2476         {
2477                 Floor *floor = static_cast<Floor*>(object);
2478
2479                 //make sure no shaft is dependent on this floor
2480                 for (int i = 0; i < shaft_manager->GetCount(); i++)
2481                 {
2482                         Shaft *shaft = shaft_manager->GetIndex(i);
2483                         if (shaft)
2484                         {
2485                                 if (shaft->IsValidFloor(floor->Number) == true)
2486                                         return ReportError("Cannot delete floor " + ToString(floor->Number) + " - in use by shaft " + ToString(shaft->ShaftNumber));
2487                         }
2488                 }
2489
2490                 //make sure no stairwell is dependent on this floor
2491                 for (int i = 0; i < stairs_manager->GetCount(); i++)
2492                 {
2493                         Stairs *stairs = stairs_manager->GetIndex(i);
2494                         if (stairs)
2495                         {
2496                                 if (stairs->IsValidFloor(floor->Number) == true)
2497                                         return ReportError("Cannot delete floor " + ToString(floor->Number) + " - in use by stairwell " + ToString(stairs->StairsNum));
2498                         }
2499                 }
2500
2501                 //restrict deletions to only lowest/highest floors
2502                 if (floor->Number >= 0 && GetFloor(floor->Number + 1))
2503                         return ReportError("Only the highest floor can be deleted");
2504
2505                 if (floor->Number < 0 && GetFloor(floor->Number - 1))
2506                         return ReportError("Only the lowest basement can be deleted");
2507
2508                 deleted = true;
2509         }
2510         else if (type == "Elevator")
2511                 deleted = true;
2512         else if (type == "ButtonPanel")
2513         {
2514                 if (object->GetParent()->GetType() == "ElevatorCar")
2515                         deleted = true;
2516         }
2517         else if (type == "CallButton")
2518                 deleted = true;
2519         else if (type == "DirectionalIndicator")
2520                 deleted = true;
2521         else if (type == "Door")
2522                 deleted = true;
2523         else if (type == "RevolvingDoor")
2524                 deleted = true;
2525         else if (type == "ElevatorDoor")
2526                 deleted = true;
2527         else if (type == "FloorIndicator")
2528                 deleted = true;
2529         else if (type == "Shaft")
2530         {
2531                 Shaft *shaft = static_cast<Shaft*>(object);
2532
2533                 //make sure no elevator is dependent on this shaft
2534                 for (int i = 0; i < elevator_manager->GetCount(); i++)
2535                 {
2536                         Elevator *elev = elevator_manager->GetIndex(i);
2537                         if (elev)
2538                         {
2539                                 if (elev->AssignedShaft == shaft->ShaftNumber)
2540                                         return ReportError("Cannot delete shaft " + ToString(shaft->ShaftNumber) + " - in use by elevator " + ToString(elev->Number));
2541                         }
2542                 }
2543
2544                 deleted = true;
2545         }
2546         else if (type == "Sound")
2547                 deleted = true;
2548         else if (type == "Stairs")
2549                 deleted = true;
2550         else if (type == "Wall")
2551         {
2552                 Wall *obj = static_cast<Wall*>(object);
2553                 obj->DeletePolygons(true);
2554                 deleted = true;
2555         }
2556         else if (type == "Model")
2557                 deleted = true;
2558         else if (type == "Control")
2559                 deleted = true;
2560         else if (type == "Trigger")
2561                 deleted = true;
2562         else if (type == "DoorWrapper")
2563         {
2564                 ElevatorDoor::DoorWrapper* wrapper = static_cast<ElevatorDoor::DoorWrapper*>(object);
2565                 if (wrapper->IsShaftDoor == false)
2566                         return ReportError("Deleting the main elevator door wrapper is not supported yet");
2567
2568                 deleted = true;
2569         }
2570         else if (type == "Escalator")
2571                 deleted = true;
2572         else if (type == "Person")
2573                 deleted = true;
2574         else if (type == "ElevatorCar")
2575         {
2576                 ElevatorCar *car = static_cast<ElevatorCar*>(object);
2577                 if (car->Number != car->GetElevator()->GetCarCount())
2578                         return ReportError("Only the highest elevator car can be deleted");
2579                 if (car->Number == 1)
2580                         return ReportError("Cannot delete the primary elevator car");
2581
2582                 deleted = true;
2583         }
2584
2585         //delete object
2586         if (deleted == true)
2587                 delete object;
2588
2589         camera->ResetCollisions();
2590
2591         return deleted;
2592 }
2593
2594 bool SBS::DeleteObject(int object)
2595 {
2596         //delete object by numeric ID
2597         return DeleteObject(GetObject(object));
2598 }
2599
2600 bool SBS::MoveObject(Object *object, Ogre::Vector3 position, bool relative, bool X, bool Y, bool Z)
2601 {
2602         //move an object by reference
2603         //if relative is false, the X, Y and Z values determine which position axes should be set
2604
2605         if (!object)
2606                 return ReportError("Invalid object");
2607
2608         if (relative == false)
2609         {
2610                 if (X == false)
2611                         position.x = object->GetPosition().x;
2612                 if (Y == false)
2613                         position.y = object->GetPosition().y;
2614                 if (Z == false)
2615                         position.z = object->GetPosition().z;
2616
2617                 object->SetPosition(position);
2618         }
2619         else
2620                 object->Move(position);
2621
2622         return true;
2623 }
2624
2625 bool SBS::RotateObject(Object *object, Ogre::Vector3 rotation, float speed, bool relative, bool X, bool Y, bool Z)
2626 {
2627         //rotate an object by reference
2628         //if relative is false, the X, Y and Z values determine which position axes should be set
2629
2630         if (!object)
2631                 return ReportError("Invalid object");
2632
2633         if (relative == true)
2634                 object->Rotate(rotation, speed);
2635         else
2636         {
2637                 if (X == false)
2638                         rotation.x = object->GetRotation().x;
2639                 if (Y == false)
2640                         rotation.y = object->GetRotation().y;
2641                 if (Z == false)
2642                         rotation.z = object->GetRotation().z;
2643                 object->SetRotation(rotation);
2644         }
2645
2646         return true;
2647 }
2648
2649 void SBS::RemoveFloor(Floor *floor)
2650 {
2651         //remove a floor (does not delete the object)
2652
2653         floor_manager->Remove(floor);
2654 }
2655
2656 void SBS::RemoveElevator(Elevator *elevator)
2657 {
2658         //remove an elevator (does not delete the object)
2659
2660         elevator_manager->Remove(elevator);
2661 }
2662
2663 void SBS::RemoveShaft(Shaft *shaft)
2664 {
2665         //remove a shaft (does not delete the object)
2666
2667         shaft_manager->Remove(shaft);
2668 }
2669
2670 void SBS::RemoveStairs(Stairs *stairs)
2671 {
2672         //remove a stairs object (does not delete the object)
2673
2674         stairs_manager->Remove(stairs);
2675 }
2676
2677 void SBS::RemoveSound(Sound *sound)
2678 {
2679         //remove a sound from the array
2680         //this does not delete the object
2681
2682         for (size_t i = 0; i < sounds.size(); i++)
2683         {
2684                 if (sounds[i] == sound)
2685                 {
2686                         sounds.erase(sounds.begin() + i);
2687                         return;
2688                 }
2689         }
2690 }
2691
2692 void SBS::RemoveLight(Light *light)
2693 {
2694         //remove a light reference (does not delete the object itself)
2695         for (size_t i = 0; i < lights.size(); i++)
2696         {
2697                 if (lights[i] == light)
2698                 {
2699                         lights.erase(lights.begin() + i);
2700                         return;
2701                 }
2702         }
2703 }
2704
2705 void SBS::RemoveModel(Model *model)
2706 {
2707         //remove a model reference (does not delete the object itself)
2708         for (size_t i = 0; i < ModelArray.size(); i++)
2709         {
2710                 if (ModelArray[i] == model)
2711                 {
2712                         ModelArray.erase(ModelArray.begin() + i);
2713                         return;
2714                 }
2715         }
2716 }
2717
2718 void SBS::RemoveControl(Control *control)
2719 {
2720         //remove a control reference (does not delete the object itself)
2721         for (size_t i = 0; i < ControlArray.size(); i++)
2722         {
2723                 if (ControlArray[i] == control)
2724                 {
2725                         ControlArray.erase(ControlArray.begin() + i);
2726                         return;
2727                 }
2728         }
2729 }
2730
2731 void SBS::RemoveTrigger(Trigger *trigger)
2732 {
2733         //remove a trigger reference (does not delete the object itself)
2734         for (size_t i = 0; i < TriggerArray.size(); i++)
2735         {
2736                 if (TriggerArray[i] == trigger)
2737                 {
2738                         TriggerArray.erase(TriggerArray.begin() + i);
2739                         return;
2740                 }
2741         }
2742 }
2743
2744 std::string SBS::VerifyFile(const std::string &filename)
2745 {
2746         bool result = false;
2747         return VerifyFile(filename, result, false);
2748 }
2749
2750 std::string SBS::VerifyFile(std::string filename, bool &result, bool skip_cache)
2751 {
2752         //verify a filename
2753         //if it exists, return the same filename
2754         //otherwise search the related folder and find a matching filename with a different
2755         //case (fixes case-sensitivity issues mainly on Linux)
2756         //returns the original string if not found
2757         //"result" will return if the file exists or not, but only accurately if skip_cache is true
2758
2759         TrimString(filename);
2760         ReplaceAll(filename, "\\", "/");
2761         result = false;
2762
2763         //check for a cached result
2764         if (skip_cache == false)
2765         {
2766                 for (size_t i = 0; i < verify_results.size(); i++)
2767                 {
2768                         if (verify_results[i].filename == filename)
2769                                 return verify_results[i].result;
2770                 }
2771         }
2772
2773 #if OGRE_VERSION >= 0x00010900
2774         Ogre::FileSystemArchive filesystem(".", "FileSystem", false);
2775 #else
2776         Ogre::FileSystemArchive filesystem(".", "FileSystem");
2777 #endif
2778
2779         //check for a mount point
2780         Ogre::StringVectorPtr listing;
2781         std::string shortname;
2782         std::string group = GetMountPath(filename, shortname);
2783
2784         if (group == "General")
2785         {
2786                 //for the General group, check the native filesystem
2787
2788                 if (filesystem.exists(filename) == true)
2789                 {
2790                         //if exact filename exists, cache and exit
2791                         CacheFilename(filename, filename);
2792                         result = true;
2793                         return filename;
2794                 }
2795
2796                 //otherwise get listing of files to check
2797                 if (filesystem_listing.isNull())
2798                         filesystem_listing = filesystem.list();
2799                 listing = filesystem_listing;
2800         }
2801         else
2802         {
2803                 //for other groups, check resource mount points
2804
2805                 if (Ogre::ResourceGroupManager::getSingleton().resourceExists(group, shortname) == true)
2806                 {
2807                         //if exact filename exists, cache and exit
2808                         CacheFilename(filename, filename);
2809                         result = true;
2810                         return filename;
2811                 }
2812
2813                 //otherwise get listing of files to check
2814                 listing = Ogre::ResourceGroupManager::getSingleton().listResourceNames(group);
2815         }
2816
2817         //go through file listing, to find a match with a different case
2818         for (size_t i = 0; i < listing->size(); i++)
2819         {
2820                 std::string check = listing->at(i);
2821                 std::string checkoriginal = SetCaseCopy(check, false);
2822                 std::string checkfile = SetCaseCopy(filename, false);
2823                 if (checkoriginal == checkfile)
2824                 {
2825                         //if match is found, cache and exit
2826                         CacheFilename(filename, check);
2827                         result = true;
2828                         return check;
2829                 }
2830         }
2831
2832         //if no match is found, cache original name and exit
2833         CacheFilename(filename, filename);
2834         return filename;
2835 }
2836
2837 bool SBS::FileExists(const std::string &filename)
2838 {
2839         //check to see if the specified file exists
2840
2841         bool result;
2842         VerifyFile(filename, result, true);
2843
2844         return result;
2845 }
2846
2847 int SBS::GetWallCount()
2848 {
2849         //return total number of registered walls
2850         return WallCount;
2851 }
2852
2853 int SBS::GetPolygonCount()
2854 {
2855         //return total number of registered walls
2856         return PolygonCount;
2857 }
2858
2859 void SBS::Prepare(bool report)
2860 {
2861         //prepare objects for run
2862
2863         if (report == true)
2864         {
2865                 Report("Preparing objects...");
2866                 Report("Processing geometry...");
2867         }
2868
2869         //prepare mesh objects
2870         for (size_t i = 0; i < meshes.size(); i++)
2871         {
2872                 meshes[i]->Prepare();
2873         }
2874
2875         //process dynamic meshes
2876         for (size_t i = 0; i < dynamic_meshes.size(); i++)
2877         {
2878                 dynamic_meshes[i]->Prepare();
2879         }
2880
2881         if (report == true)
2882                 Report("Creating colliders...");
2883
2884         for (size_t i = 0; i < meshes.size(); i++)
2885         {
2886                 if (meshes[i]->tricollider == true)
2887                         meshes[i]->CreateCollider();
2888                 else
2889                         meshes[i]->CreateBoxCollider();
2890         }
2891
2892         if (report == true)
2893                 Report("Finished prepare");
2894 }
2895
2896 Light* SBS::AddLight(const std::string &name, int type, const Ogre::Vector3 &position, const Ogre::Vector3 &direction, float color_r, float color_g, float color_b, float spec_color_r, float spec_color_g, float spec_color_b, float spot_inner_angle, float spot_outer_angle, float spot_falloff, float att_range, float att_constant, float att_linear, float att_quadratic)
2897 {
2898         //add a global light
2899
2900         Light* light = new Light(this, name, type, position, direction, color_r, color_g, color_b, spec_color_r, spec_color_g, spec_color_b, spot_inner_angle, spot_outer_angle, spot_falloff, att_range, att_constant, att_linear, att_quadratic);
2901         lights.push_back(light);
2902         return light;
2903 }
2904
2905 void SBS::AddMeshHandle(MeshObject* handle)
2906 {
2907         meshes.push_back(handle);
2908 }
2909
2910 void SBS::DeleteMeshHandle(MeshObject* handle)
2911 {
2912         for (size_t i = 0; i < meshes.size(); i++)
2913         {
2914                 if (meshes[i] == handle)
2915                 {
2916                         meshes.erase(meshes.begin() + i);
2917                         return;
2918                 }
2919         }
2920 }
2921
2922 MeshObject* SBS::FindMeshObject(const std::string &name)
2923 {
2924         //find a mesh object by searching for matching wrapper
2925         for (size_t i = 0; i < meshes.size(); i++)
2926         {
2927                 if (meshes[i]->name == name)
2928                         return meshes[i];
2929         }
2930         return 0;
2931 }
2932
2933 Model* SBS::AddModel(const std::string &name, const std::string &filename, bool center, const Ogre::Vector3 &position, const Ogre::Vector3 &rotation, float max_render_distance, float scale_multiplier, bool enable_physics, float restitution, float friction, float mass)
2934 {
2935         //add a model
2936         Model* model = new Model(this, name, filename, center, position, rotation, max_render_distance, scale_multiplier, enable_physics, restitution, friction, mass);
2937         if (model->load_error == true)
2938         {
2939                 delete model;
2940                 return 0;
2941         }
2942         ModelArray.push_back(model);
2943         return model;
2944 }
2945
2946 void SBS::AddModel(Model *model)
2947 {
2948         //add a model reference
2949
2950         if (!model)
2951                 return;
2952
2953         for (size_t i = 0; i < ModelArray.size(); i++)
2954         {
2955                 if (ModelArray[i] == model)
2956                         return;
2957         }
2958
2959         ModelArray.push_back(model);
2960 }
2961
2962 int SBS::GetConfigInt(const std::string &key, int default_value)
2963 {
2964         std::string result = configfile->getSetting(key, "", ToString(default_value));
2965         return ToInt(result);
2966 }
2967
2968 std::string SBS::GetConfigString(const std::string &key, const std::string &default_value)
2969 {
2970         return configfile->getSetting(key, "", default_value);
2971 }
2972
2973 bool SBS::GetConfigBool(const std::string &key, bool default_value)
2974 {
2975         std::string result = configfile->getSetting(key, "", ToString(default_value));
2976         return ToBool(result);
2977 }
2978
2979 float SBS::GetConfigFloat(const std::string &key, float default_value)
2980 {
2981         std::string result = configfile->getSetting(key, "", ToString(default_value));
2982         return ToFloat(result);
2983 }
2984
2985 bool SBS::InBox(const Ogre::Vector3 &start, const Ogre::Vector3 &end, const Ogre::Vector3 &test)
2986 {
2987         //determine if a point (test) is inside the box defines by start and end vertices
2988
2989         if (test.x > start.x && test.y > start.y && test.z > start.z && test.x < end.x && test.y < end.y && test.z < end.z)
2990                 return true;
2991         return false;
2992 }
2993
2994 void SBS::AdvanceClock()
2995 {
2996         //advance the clock
2997
2998         unsigned long last = current_time;
2999
3000         //get current time
3001         current_time = GetCurrentTime();
3002         if (last == 0)
3003                 last = current_time;
3004
3005         if (current_time < last)
3006                 elapsed_time = current_time + ((unsigned long)-1 - last) + 1;
3007         else
3008                 elapsed_time = current_time - last;
3009         current_virtual_time += elapsed_time;
3010         frame_times.push_back(current_time);
3011         CalculateAverageTime();
3012 }
3013
3014 unsigned long SBS::GetCurrentTime()
3015 {
3016         //get current time
3017         return timer->getMilliseconds();
3018 }
3019
3020 unsigned long SBS::GetRunTime()
3021 {
3022         //returns simulator run time
3023         return current_virtual_time;
3024 }
3025
3026 unsigned long SBS::GetElapsedTime()
3027 {
3028         //returns the actual elapsed time between frames
3029         return elapsed_time;
3030 }
3031
3032 unsigned long SBS::GetAverageTime()
3033 {
3034         //returns the average elapsed time between frames
3035         return average_time;
3036 }
3037
3038 void SBS::CalculateAverageTime()
3039 {
3040         //calculates the average frame processing time for a specified number of frames
3041
3042         if (frame_times.size() <= 1)
3043                 return;
3044
3045         //SmoothFrames is the maximum number of milliseconds to hold timing info
3046
3047         //find oldest time to keep
3048         std::deque<unsigned long>::iterator it = frame_times.begin(), end = frame_times.end() - 2;
3049
3050         while (it != end)
3051         {
3052                 if (frame_times.back() - *it > SmoothFrames)
3053                         ++it;
3054                 else
3055                         break;
3056         }
3057
3058         //remove old times
3059         frame_times.erase(frame_times.begin(), it);
3060
3061         //calculate average time
3062         average_time = (frame_times.back() - frame_times.front()) / ((unsigned long)frame_times.size() - 1);
3063 }
3064
3065 std::string SBS::GetMountPath(std::string filename, std::string &newfilename)
3066 {
3067         //get mountpoint (resource group) path of given file
3068         //if not found, return "General"
3069
3070         Ogre::StringVector list = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
3071         ReplaceAll(filename, "\\", "/");
3072         newfilename = filename;
3073
3074         for (size_t i = 0; i < list.size(); i++)
3075         {
3076                 if (StartsWith(filename, list[i] + "/") == true)
3077                 {
3078                         newfilename = filename.substr(list[i].size() + 1);
3079                         return list[i];
3080                 }
3081         }
3082         return "General";
3083 }
3084
3085 void SBS::ShowColliders(bool value)
3086 {
3087         try
3088         {
3089                 if (mWorld)
3090                         mWorld->setShowDebugShapes(value);
3091                 camera->ShowDebugShape(value);
3092         }
3093         catch (Ogre::Exception &e)
3094         {
3095                 ReportError("Error enabling/disabling collider shapes\n" + e.getDescription());
3096         }
3097 }
3098
3099 void SBS::CacheFilename(const std::string &filename, const std::string &result)
3100 {
3101         //caches filename information for VerifyFile function
3102         VerifyResult verify;
3103         verify.filename = filename;
3104         verify.result = result;
3105         verify_results.push_back(verify);
3106 }
3107
3108 void SBS::SetLighting(float red, float green, float blue)
3109 {
3110         OldAmbientR = AmbientR;
3111         OldAmbientG = AmbientG;
3112         OldAmbientB = AmbientB;
3113         AmbientR = red;
3114         AmbientG = green;
3115         AmbientB = blue;
3116 }
3117
3118 void SBS::ResetLighting()
3119 {
3120         AmbientR = OldAmbientR;
3121         AmbientG = OldAmbientG;
3122         AmbientB = OldAmbientB;
3123 }
3124
3125 Control* SBS::AddControl(const std::string &name, const std::string &sound, const std::string &direction, float CenterX, float CenterZ, float width, float height, float voffset, int selection_position, std::vector<std::string> &action_names, std::vector<std::string> &textures)
3126 {
3127         //add a control
3128         std::vector<Action*> actionnull; //not used
3129         Control* control = new Control(this, name, false, sound, action_names, actionnull, textures, direction, width, height, true, selection_position);
3130         control->SetPosition(CenterX, voffset, CenterZ);
3131         ControlArray.push_back(control);
3132         return control;
3133 }
3134
3135 Trigger* SBS::AddTrigger(const std::string &name, const std::string &sound_file, const Ogre::Vector3 &area_min, const Ogre::Vector3 &area_max, std::vector<std::string> &action_names)
3136 {
3137         //add a trigger
3138         Trigger* trigger = new Trigger(this, name, false, sound_file, area_min, area_max, action_names);
3139         TriggerArray.push_back(trigger);
3140         return trigger;
3141 }
3142
3143 Action* SBS::AddAction(const std::string &name, std::vector<Object*> &action_parents, const std::string &command, const std::vector<std::string> &parameters)
3144 {
3145         //add a global action
3146
3147         Action *action = new Action(this, name, action_parents, command, parameters);
3148         ActionArray.push_back(action);
3149         return action;
3150 }
3151
3152 Action* SBS::AddAction(const std::string &name, std::vector<Object*> &action_parents, const std::string &command)
3153 {
3154         //add a global action
3155
3156         Action *action = new Action(this, name, action_parents, command);
3157         ActionArray.push_back(action);
3158         return action;
3159 }
3160
3161 std::vector<Action*> SBS::GetAction(std::string name)
3162 {
3163         //get action by name
3164         ReplaceAll(name, " ", "");
3165         std::vector<Action*> actionlist;
3166         for (size_t i = 0; i < ActionArray.size(); i++)
3167         {
3168                 std::string actionname = ActionArray[i]->GetName();
3169                 ReplaceAll(actionname, " ", "");
3170                 if (actionname == name)
3171                         actionlist.push_back(ActionArray[i]);
3172         }
3173         return actionlist;
3174 }
3175
3176 int SBS::GetActionCount()
3177 {
3178         //get number of registered actions
3179         return (int)ActionArray.size();
3180 }
3181
3182 bool SBS::AddActionParent(const std::string &name, std::vector<Object*> &parents)
3183 {
3184         //add parent to actions specified by 'name'
3185
3186         bool result = false;
3187         std::vector<Action*> actionlist = GetAction(name);
3188
3189         for (size_t i = 0; i < actionlist.size(); i++)
3190         {
3191                 Action *action = actionlist[i];
3192                 for (size_t j = 0; j < parents.size(); j++)
3193                 {
3194                         if (action->AddParent(parents[j]))
3195                                 result = true;
3196                 }
3197         }
3198         return result;
3199 }
3200
3201 bool SBS::RemoveActionParent(const std::string &name, std::vector<Object*> &parents)
3202 {
3203         //remove parent object from actions specified by 'name'
3204
3205         bool result = false;
3206         std::vector<Action*> actionlist = GetAction(name);
3207
3208         for (size_t i = 0; i < actionlist.size(); i++)
3209         {
3210                 Action *action = actionlist[i];
3211                 for (size_t j = 0; j < parents.size(); j++)
3212                 {
3213                         if (action->RemoveParent(parents[j]))
3214                                 result = true;
3215                 }
3216         }
3217         return result;
3218 }
3219
3220 bool SBS::RemoveActionParent(std::vector<Object*> &parents)
3221 {
3222         //remove parent object from all action objects
3223
3224         bool result = false;
3225         for (size_t i = 0; i < ActionArray.size(); i++)
3226         {
3227                 Action *action = ActionArray[i];
3228                 for (size_t j = 0; j < parents.size(); j++)
3229                 {
3230                         if (action->RemoveParent(parents[j]))
3231                                 result = true;
3232                 }
3233         }
3234         return result;
3235 }
3236
3237 bool SBS::RemoveAction(std::string name)
3238 {
3239         //remove action by name
3240
3241         ReplaceAll(name, " ", "");
3242         bool result = false;
3243         for (size_t i = 0; i < ActionArray.size(); i++)
3244         {
3245                 if (ActionArray[i])
3246                 {
3247                         std::string actionname = ActionArray[i]->GetName();
3248                         ReplaceAll(actionname, " ", "");
3249                         if (actionname == name)
3250                         {
3251                                 delete ActionArray[i];
3252                                 ActionArray.erase(ActionArray.begin() + i);
3253                                 i--;
3254                                 result = true;
3255                         }
3256                 }
3257         }
3258         return result;
3259 }
3260
3261 bool SBS::RemoveAction(Action *action)
3262 {
3263         //remove action
3264
3265         if (!action)
3266                 return false;
3267
3268         bool result = false;
3269         for (size_t i = 0; i < ActionArray.size(); i++)
3270         {
3271                 if (ActionArray[i] == action)
3272                 {
3273                         delete ActionArray[i];
3274                         ActionArray.erase(ActionArray.begin() + i);
3275                         i--;
3276                         result = true;
3277
3278                         //remove reference to action in all control objects
3279                         for (size_t j = 0; j < control_index.size(); j++)
3280                         {
3281                                 control_index[j]->RemoveAction(action);
3282                         }
3283                 }
3284         }
3285         return result;
3286 }
3287
3288 Object* SBS::GetObject(std::string name)
3289 {
3290         //get object by name
3291
3292         ReplaceAll(name, " ", "");
3293
3294         for (size_t i = 0; i < ObjectArray.size(); i++)
3295         {
3296                 if (ObjectArray[i])
3297                 {
3298                         std::string tmpname = ObjectArray[i]->GetName();
3299                         ReplaceAll(tmpname, " ", "");
3300                         if (tmpname == name)
3301                                 return ObjectArray[i];
3302                 }
3303         }
3304         return 0;
3305 }
3306
3307 std::vector<Object*> SBS::GetObjectRange(const std::string &expression)
3308 {
3309         //get object by name range expression (ex. "Floors 1 to 3")
3310
3311         std::vector<Object*> objects;
3312         size_t temp = expression.find("to", 0);
3313
3314         //the name 'elevator' matches the previous search - in this case, detect it and undo
3315         size_t temp2 = expression.find("tor", 0);
3316         if (temp == temp2)
3317                 temp = 0;
3318
3319         std::string type;
3320
3321         if (temp > 0 && temp != std::string::npos)
3322         {
3323                 if (expression.substr(0, 6) == "Floors")
3324                         type = "floor";
3325                 else if (expression.substr(0, 9) == "Elevators")
3326                         type = "elevator";
3327                 else if (expression.substr(0, 6) == "Shafts")
3328                         type = "shaft";
3329                 else if (expression.substr(0, 10) == "Stairwells")
3330                         type = "stairwell";
3331                 else
3332                 {
3333                         ReportError("GetObjectRange: Invalid object type");
3334                         return objects;
3335                 }
3336
3337                 std::string str1 = expression.substr(type.size() + 1, temp - (type.size() + 1));
3338                 std::string str2 = expression.substr(temp + 2, expression.length() - (temp + 2));
3339                 TrimString(str1);
3340                 TrimString(str2);
3341                 int RangeL = 0, RangeH = 0;
3342                 if (!IsNumeric(str1, RangeL) || !IsNumeric(str2, RangeH))
3343                 {
3344                         ReportError("GetObjectRange: Invalid range");
3345                         return objects;
3346                 }
3347
3348                 for (size_t i = 0; i < ObjectArray.size(); i++)
3349                 {
3350                         if (ObjectArray[i])
3351                         {
3352                                 std::string tmpname = ObjectArray[i]->GetName();
3353                                 for (int j = RangeL; j <= RangeH; j++)
3354                                 {
3355                                         std::string number = ToString(j);
3356                                         if (type == "floor")
3357                                         {
3358                                                 if (tmpname == "Floor " + number)
3359                                                         objects.push_back(ObjectArray[i]);
3360                                         }
3361                                         if (type == "elevator")
3362                                         {
3363                                                 if (tmpname == "Elevator " + number)
3364                                                         objects.push_back(ObjectArray[i]);
3365                                         }
3366                                         if (type == "shaft")
3367                                         {
3368                                                 if (tmpname == "Shaft " + number)
3369                                                         objects.push_back(ObjectArray[i]);
3370                                         }
3371                                         if (type == "stairwell")
3372                                         {
3373                                                 if (tmpname == "Stairwell " + number)
3374                                                         objects.push_back(ObjectArray[i]);
3375                                         }
3376                                 }
3377                         }
3378                 }
3379         }
3380         else
3381         {
3382                 //return single result
3383                 Object *obj = GetObject(expression);
3384                 if (obj)
3385                         objects.push_back(obj);
3386         }
3387
3388         return objects;
3389 }
3390
3391 Action* SBS::GetAction(int index)
3392 {
3393         if (index >= 0 && index < (int)ActionArray.size())
3394                 return ActionArray[index];
3395         return 0;
3396 }
3397
3398 bool SBS::RunAction(const std::string &name)
3399 {
3400         //run action by name - will run multiple actions if the name is the same
3401
3402         std::vector<Action*> actionlist = GetAction(name);
3403
3404         bool result = true;
3405         for (size_t i = 0; i < actionlist.size(); i++)
3406         {
3407                 bool result2 = false;
3408                 bool hold = false; //not used
3409
3410                 if (actionlist[i])
3411                         result2 = actionlist[i]->DoAction(this, hold);
3412
3413                 if (result2 == false)
3414                         result = false;
3415         }
3416         return result;
3417 }
3418
3419 bool SBS::RunAction(int index)
3420 {
3421         //run action by index number
3422
3423         Action *action = GetAction(index);
3424         bool hold = false; //not used
3425         if (action)
3426                 return action->DoAction(this, hold);
3427         return false;
3428 }
3429
3430 void SBS::AddKey(int keyid, const std::string &name)
3431 {
3432         //adds key 'keyid' to the user's keyring
3433         Key key;
3434         key.id = keyid;
3435         key.name = name;
3436         keys.push_back(key);
3437
3438         Report("Added key " + ToString(keyid) + " (" + name + ") to keyring");
3439 }
3440
3441 bool SBS::CheckKey(int keyid)
3442 {
3443         //checks to see if the user has the specified key
3444
3445         for (size_t i = 0; i < keys.size(); i++)
3446         {
3447                 if (keys[i].id == keyid)
3448                         return true;
3449         }
3450         return false;
3451 }
3452
3453 void SBS::ListKeys()
3454 {
3455         //list all keys
3456
3457         Report("\n--- Keys ---\n");
3458
3459         for (size_t i = 0; i < keys.size(); i++)
3460         {
3461                 std::string id = ToString(keys[i].id);
3462                 Report(id + " - " + keys[i].name);
3463         }
3464         Report("");
3465 }
3466
3467 void SBS::RegisterControl(Control *control)
3468 {
3469         //add control to index
3470         control_index.push_back(control);
3471 }
3472
3473 void SBS::UnregisterControl(Control *control)
3474 {
3475         //remove control from index
3476
3477         for (size_t i = 0; i < control_index.size(); i++)
3478         {
3479                 if (control_index[i] == control)
3480                 {
3481                         control_index.erase(control_index.begin() + i);
3482                         return;
3483                 }
3484         }
3485 }
3486
3487 void SBS::ShowFloorList()
3488 {
3489         //show floor information for all floors
3490
3491         bool header_shown = false;
3492         for (int i = -Basements; i < Floors; i++)
3493         {
3494                 Floor *floor = GetFloor(i);
3495                 if (floor)
3496                 {
3497                         if (header_shown == false)
3498                         {
3499                                 floor->ShowInfo(false, true);
3500                                 header_shown = true;
3501                         }
3502                         else
3503                                 floor->ShowInfo(false, false);
3504                 }
3505         }
3506         Report("");
3507 }
3508
3509 void SBS::ShowSceneNodes(bool value)
3510 {
3511         //show all scene nodes for debugging
3512
3513         mSceneManager->setDisplaySceneNodes(value);
3514 }
3515
3516 void SBS::ShowBoundingBoxes(bool value)
3517 {
3518         //show all mesh bounding boxes for debugging
3519
3520         mSceneManager->showBoundingBoxes(value);
3521 }
3522
3523 void SBS::ListVisibleMeshes()
3524 {
3525         //list all meshes visible by the main camera
3526
3527         Report("\n--- Visible Dynamic Meshes ---");
3528         Report("Name\t-\tSubmeshes\n");
3529         int count = 0;
3530         int submeshes = 0;
3531         int total = 0;
3532
3533         for (size_t i = 0; i < dynamic_meshes.size(); i++)
3534         {
3535                 for (size_t j = 0; j < dynamic_meshes[i]->GetMeshCount(); j++)
3536                 {
3537                         if (camera->IsDynamicMeshVisible(dynamic_meshes[i], (int)j) == true)
3538                         {
3539                                 submeshes = dynamic_meshes[i]->GetSubMeshCount((int)j);
3540                                 Report(dynamic_meshes[i]->GetMeshName((int)j) + "\t-\t" + ToString(submeshes));
3541                                 count++;
3542                                 total += submeshes;
3543                         }
3544                 }
3545         }
3546         Report("Total: " + ToString(count) + " meshes, " + ToString(total) + " submeshes");
3547         Report("");
3548 }
3549
3550 int SBS::GetEscalatorCount()
3551 {
3552         //return total number of allocated sounds
3553         return EscalatorCount;
3554 }
3555
3556 void SBS::IncrementEscalatorCount()
3557 {
3558         EscalatorCount++;
3559 }
3560
3561 void SBS::DecrementEscalatorCount()
3562 {
3563         EscalatorCount--;
3564 }
3565
3566 bool SBS::HitBeam(const Ogre::Ray &ray, float max_distance, MeshObject *&mesh, Wall *&wall, Ogre::Vector3 &hit_position)
3567 {
3568         //use a given ray and distance, and return the nearest hit mesh and if applicable, wall object
3569         //note that the ray's origin and direction need to be in engine-relative values
3570
3571         //create a new ray that has absolute positioning, for engine offsets
3572         Ogre::Ray ray2 (ToRemote(ToGlobal(ToLocal(ray.getOrigin()))), GetOrientation() * ray.getDirection());
3573
3574         //get a collision callback from Bullet
3575         OgreBulletCollisions::CollisionClosestRayResultCallback callback (ray2, mWorld, max_distance);
3576
3577         //check for collision
3578         mWorld->launchRay(callback);
3579
3580         //exit if no collision
3581         if (callback.doesCollide() == false)
3582                 return false;
3583
3584         //get collided collision object
3585         OgreBulletCollisions::Object* object = callback.getCollidedObject();
3586
3587         if (!object)
3588                 return false;
3589
3590         //get name of collision object's grandparent scenenode (which is the same name as the mesh object)
3591         std::string meshname = object->getRootNode()->getParentSceneNode()->getName();
3592
3593         //get hit/intersection position
3594         hit_position = ToLocal(callback.getCollisionPoint());
3595
3596         //get associated mesh object
3597         mesh = FindMeshObject(meshname);
3598         if (!mesh)
3599                 return false;
3600
3601         //get wall object, if any
3602         Ogre::Vector3 isect;
3603         float distance = 2000000000.;
3604         Ogre::Vector3 normal = Ogre::Vector3::ZERO;
3605         wall = mesh->FindWallIntersect(ray.getOrigin(), ray.getPoint(max_distance), isect, distance, normal);
3606
3607         return true;
3608 }
3609
3610 void SBS::EnableRandomActivity(bool value)
3611 {
3612         //enable random activity, by creating random people
3613
3614         if (value == true)
3615         {
3616                 //create regular people
3617                 for (int i = 0; i < GetTotalFloors(); i++)
3618                 {
3619                         Person *person = CreatePerson("Random " + ToString(i + 1), 0, false);
3620
3621                         //enable random activity on the person
3622                         person->EnableRandomActivity(true);
3623                 }
3624
3625                 //create a service person
3626                 int i = GetTotalFloors();
3627                 Person *person = CreatePerson("Random " + ToString(i + 1), 0, true);
3628
3629                 //enable random activity on the person
3630                 person->EnableRandomActivity(true);
3631         }
3632         else if (value == false)
3633         {
3634                 for (size_t i = 0; i < PersonArray.size(); i++)
3635                 {
3636                         if (PersonArray[i]->IsRandomActivityEnabled() == true)
3637                         {
3638                                 Person *person = PersonArray[i];
3639                                 delete person;
3640                                 i--;
3641                         }
3642                 }
3643         }
3644
3645         RandomActivity = value;
3646 }
3647
3648 bool SBS::IsObjectValid(Object *object, std::string type)
3649 {
3650         //test if an object is valid
3651
3652         if (type == "Floor")
3653         {
3654                 for (int i = 0; i < floor_manager->GetCount(); i++)
3655                 {
3656                         if (floor_manager->GetIndex(i) == static_cast<Floor*>(object))
3657                                 return true;
3658                 }
3659         }
3660         else if (type == "Elevator")
3661         {
3662                 for (int i = 0; i < elevator_manager->GetCount(); i++)
3663                 {
3664                         if (elevator_manager->GetIndex(i) == static_cast<Elevator*>(object))
3665                                 return true;
3666                 }
3667         }
3668         else if (type == "Shaft")
3669         {
3670                 for (int i = 0; i < shaft_manager->GetCount(); i++)
3671                 {
3672                         if (shaft_manager->GetIndex(i) == static_cast<Shaft*>(object))
3673                                 return true;
3674                 }
3675         }
3676         else if (type == "Stairs")
3677         {
3678                 for (int i = 0; i < stairs_manager->GetCount(); i++)
3679                 {
3680                         if (stairs_manager->GetIndex(i) == static_cast<Stairs*>(object))
3681                                 return true;
3682                 }
3683         }
3684         else if (type == "Mesh")
3685         {
3686                 for (size_t i = 0; i < meshes.size(); i++)
3687                 {
3688                         if (meshes[i] == static_cast<MeshObject*>(object))
3689                                 return true;
3690                 }
3691         }
3692         else if (type == "Control")
3693         {
3694                 for (size_t i = 0; i < control_index.size(); i++)
3695                 {
3696                         if (control_index[i] == static_cast<Control*>(object))
3697                                 return true;
3698                 }
3699         }
3700
3701         //do a slow full scan of the object array for all other objects
3702         for (size_t i = 0; i < ObjectArray.size(); i++)
3703         {
3704                 if (ObjectArray[i] == object)
3705                         return true;
3706         }
3707         return false;
3708 }
3709
3710 bool SBS::IsActionValid(Action *action)
3711 {
3712         //test if an action is valid
3713
3714         for (size_t i = 0; i < ActionArray.size(); i++)
3715         {
3716                 if (ActionArray[i] == action)
3717                         return true;
3718         }
3719         return false;
3720 }
3721
3722 Person* SBS::CreatePerson(std::string name, int floor, bool service_access)
3723 {
3724         //create a person
3725
3726         if (name == "")
3727         {
3728                 int number = GetPersonCount() + 1;
3729                 name = "Person " + ToString(number);
3730         }
3731         Person *person = new Person(this, name, floor, service_access);
3732         PersonArray.push_back(person);
3733         return person;
3734 }
3735
3736 void SBS::RemovePerson(Person *person)
3737 {
3738         //remove a person (does not delete the object)
3739
3740         for (size_t i = 0; i < PersonArray.size(); i++)
3741         {
3742                 if (PersonArray[i] == person)
3743                 {
3744                         PersonArray.erase(PersonArray.begin() + i);
3745                         return;
3746                 }
3747         }
3748 }
3749
3750 bool SBS::AttachCamera(Ogre::Camera *camera, bool init_state)
3751 {
3752         if (camera)
3753                 return this->camera->Attach(camera, init_state);
3754         return false;
3755 }
3756
3757 bool SBS::DetachCamera()
3758 {
3759         return camera->Detach();
3760 }
3761
3762 std::string SBS::ProcessFullName(std::string name, int &instance, int &object_number, bool strip_number)
3763 {
3764         //if given a full object ID name (such as "0:(4)Landscape"),
3765         //return base name and parse out instance number and object number
3766
3767         //if strip_number is false, leave object number identifier in string
3768
3769         //get and strip off engine instance number
3770         size_t index = name.find(":(");
3771         instance = ToInt(name.substr(0, index));
3772         name.erase(name.begin(), name.begin() + index + 1);
3773
3774         //get and optionally strip off object number
3775         index = name.find(")");
3776         object_number = ToInt(name.substr(1, index - 1));
3777
3778         if (strip_number == true)
3779                 name.erase(name.begin(), name.begin() + index + 1);
3780
3781         return name;
3782 }
3783
3784 Person* SBS::GetPerson(int number)
3785 {
3786         if (number < 0 || number > (int)PersonArray.size() - 1)
3787                 return 0;
3788
3789         return PersonArray[number];
3790 }
3791
3792 bool SBS::IsInside()
3793 {
3794         //return true if the user is inside the sim engine's area
3795
3796         if (area_trigger)
3797                 return area_trigger->IsInside();
3798
3799         //if no trigger is defined, user is always inside the area
3800         return true;
3801 }
3802
3803 bool SBS::IsInside(const Ogre::Vector3 &position)
3804 {
3805         //return true if the specified position is inside the sim engine's area
3806
3807         if (area_trigger)
3808                 return area_trigger->IsInside(position);
3809
3810         //if no trigger is defined, position is always inside the area
3811         return true;
3812 }
3813
3814 bool SBS::GetBounds(Ogre::Vector3 &min, Ogre::Vector3 &max)
3815 {
3816         if (area_trigger)
3817         {
3818                 min = area_trigger->GetMin();
3819                 max = area_trigger->GetMax();
3820                 return true;
3821         }
3822
3823         min = Ogre::Vector3::ZERO;
3824         max = Ogre::Vector3::ZERO;
3825         return false;
3826 }
3827
3828 void SBS::CutOutsideBoundaries(bool landscape, bool buildings, bool external, bool floors)
3829 {
3830         //cut landscape and buildings for engine bounds if needed
3831         //run this function before calling Start()
3832
3833         if (!area_trigger)
3834                 return;
3835
3836         Report("Cutting outside boundaries...");
3837         Ogre::Vector3 min = area_trigger->GetMin();
3838         Ogre::Vector3 max = area_trigger->GetMax();
3839
3840         if (landscape == true)
3841                 Landscape->CutOutsideBounds(min, max, true, true);
3842         if (buildings == true)
3843                 Buildings->CutOutsideBounds(min, max, true, true);
3844         if (external == true)
3845                 External->CutOutsideBounds(min, max, true, true);
3846
3847         if (floors == true)
3848         {
3849                 for (int i = 0; i < floor_manager->GetCount(); i++)
3850                         floor_manager->GetIndex(i)->Level->CutOutsideBounds(min, max, true, true);
3851         }
3852 }
3853
3854 void SBS::CutInsideBoundaries(const Ogre::Vector3 &min, const Ogre::Vector3 &max, bool landscape, bool buildings, bool external, bool floors)
3855 {
3856         //cut landscape and buildings for specified bounds
3857         //run this function before calling Start()
3858
3859         if (landscape == true)
3860                 Landscape->Cut(min, max, true, true);
3861         if (buildings == true)
3862                 Buildings->Cut(min, max, true, true);
3863         if (external == true)
3864                 External->Cut(min, max, true, true);
3865
3866         if (floors == true)
3867         {
3868                 for (int i = 0; i < floor_manager->GetCount(); i++)
3869                         floor_manager->GetIndex(i)->Level->Cut(min, max, true, true);
3870         }
3871 }
3872
3873 void SBS::SetBounds(const Ogre::Vector3 &area_min, const Ogre::Vector3 &area_max)
3874 {
3875         //don't set bounds if the primary engine
3876         if (InstanceNumber == 0)
3877                 return;
3878
3879         if (area_min != Ogre::Vector3::ZERO && area_max != Ogre::Vector3::ZERO && !area_trigger)
3880         {
3881                 std::vector<std::string> names;
3882                 names.push_back("Off");
3883                 area_trigger = new Trigger(this, "System Boundary", true, "", area_min, area_max, names);
3884         }
3885 }
3886
3887 void SBS::ResetState()
3888 {
3889         //reset building to original state
3890
3891         //turn on main objects
3892         EnableBuildings(true);
3893         EnableLandscape(true);
3894         EnableExternal(true);
3895         EnableSkybox(true);
3896
3897         //turn off interior objects
3898         floor_manager->EnableAll(false);
3899         shaft_manager->EnableAll(false);
3900         stairs_manager->EnableAll(false);
3901         elevator_manager->EnableAll(false);
3902
3903         //reset camera state
3904         camera->ResetState();
3905 }
3906
3907 Ogre::Vector3 SBS::ToGlobal(const Ogre::Vector3 &position)
3908 {
3909         //convert an engine-relative position to a global (scene) position
3910
3911         return (GetOrientation().Inverse() * position) + GetPosition();
3912 }
3913
3914 Ogre::Vector3 SBS::FromGlobal(const Ogre::Vector3 &position)
3915 {
3916         //convert a global (scene) position to an engine-relative position
3917
3918         return (GetOrientation() * (position - GetPosition()));
3919 }
3920
3921 Ogre::Quaternion SBS::ToGlobal(const Ogre::Quaternion &orientation)
3922 {
3923         //convert an engine-relative orientation (rotation) to a global (scene) orientation
3924
3925         return (GetOrientation() * orientation);
3926 }
3927
3928 Ogre::Quaternion SBS::FromGlobal(const Ogre::Quaternion &orientation)
3929 {
3930         //convert a global (scene) orientation (rotation) to an engine-relative orientation
3931
3932         return (GetOrientation().Inverse() * orientation);
3933 }
3934
3935 Model* SBS::GetModel(std::string name)
3936 {
3937         //get a model by name
3938
3939         SetCase(name, false);
3940
3941         for (size_t i = 0; i < ModelArray.size(); i++)
3942         {
3943                 if (SetCaseCopy(ModelArray[i]->GetName(), false) == name)
3944                         return ModelArray[i];
3945         }
3946
3947         return 0;
3948 }
3949
3950 void SBS::RegisterDynamicMesh(DynamicMesh *dynmesh)
3951 {
3952         //register a dynamic mesh with the system
3953
3954         dynamic_meshes.push_back(dynmesh);
3955 }
3956
3957 void SBS::UnregisterDynamicMesh(DynamicMesh *dynmesh)
3958 {
3959         //unregister a dynamic mesh from the system
3960
3961         for (size_t i = 0; i < dynamic_meshes.size(); i++)
3962         {
3963                 if (dynamic_meshes[i] == dynmesh)
3964                 {
3965                         dynamic_meshes.erase(dynamic_meshes.begin() + i);
3966                         return;
3967                 }
3968         }
3969 }
3970
3971 SoundSystem* SBS::GetSoundSystem()
3972 {
3973         return soundsystem;
3974 }
3975
3976 int SBS::GetPersonCount()
3977 {
3978         return (int)PersonArray.size();
3979 }
3980
3981 bool SBS::HasBounds()
3982 {
3983         return (area_trigger != 0);
3984 }
3985
3986 FloorManager* SBS::GetFloorManager()
3987 {
3988         return floor_manager;
3989 }
3990
3991 ElevatorManager* SBS::GetElevatorManager()
3992 {
3993         return elevator_manager;
3994 }
3995
3996 ShaftManager* SBS::GetShaftManager()
3997 {
3998         return shaft_manager;
3999 }
4000
4001 StairsManager* SBS::GetStairsManager()
4002 {
4003         return stairs_manager;
4004 }
4005
4006 DoorManager* SBS::GetDoorManager()
4007 {
4008         return door_manager;
4009 }
4010
4011 TextureManager* SBS::GetTextureManager()
4012 {
4013         return texturemanager;
4014 }
4015
4016 RevolvingDoorManager* SBS::GetRevolvingDoorManager()
4017 {
4018         return revolvingdoor_manager;
4019 }
4020
4021 }