OSDN Git Service

Year update
[skyscrapersim/skyscraper.git] / src / sbs / elevator.cpp
1 /* $Id$ */
2
3 /*
4         Scalable Building Simulator - Elevator Subsystem Class
5         The Skyscraper Project - Version 1.8 Alpha
6         Copyright (C)20042012 Ryan Thoryk
7         http://www.skyscrapersim.com
8         http://sourceforge.net/projects/skyscraper
9         Contact - ryan@tliquest.net
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 <OgreBulletCollisionsRay.h>
27 #include "globals.h"
28 #include "random.h"
29 #include "sbs.h"
30 #include "elevator.h"
31 #include "camera.h"
32 #include "shaft.h"
33 #include "unix.h"
34
35 #include <time.h>
36
37 extern SBS *sbs; //external pointer to the SBS engine
38
39 Elevator::Elevator(int number)
40 {
41         //set up SBS object
42         object = new Object();
43         object->SetValues(this, sbs->object, "Elevator", "", false);
44
45         std::string buffer;
46
47         //set elevator number
48         Number = number;
49
50         //init variables
51         Name = "";
52         QueuePositionDirection = 0;
53         LastQueueDirection = 0;
54         LastQueueFloor[0] = 0;
55         LastQueueFloor[1] = 0;
56         ElevatorSpeed = 0;
57         MoveElevator = false;
58         GotoFloor = 0;
59         Acceleration = 0;
60         Deceleration = 0;
61         AccelJerk = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.AccelJerk", 1);
62         DecelJerk = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.DecelJerk", 1);
63         ElevatorStart = 0;
64         ElevatorFloor = 0;
65         Direction = 0;
66         DistanceToTravel = 0;
67         Destination = 0;
68         ElevatorRate = 0;
69         StoppingDistance = 0;
70         CalculateStoppingDistance = false;
71         Brakes = false;
72         EmergencyStop = false;
73         AssignedShaft = 0;
74         IsEnabled = true;
75         Height = 0;
76         TempDeceleration = 0;
77         ErrorOffset = 0;
78         JerkRate = 0;
79         JerkPos = 0;
80         ElevatorIsRunning = false;
81         oldfloor = 0;
82         IsMoving = false;
83         lastfloor = 0;
84         lastfloorset = false;
85         CarStartSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.CarStartSound", "");
86         CarMoveSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.CarMoveSound", "");
87         CarStopSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.CarStopSound", "");
88         CarIdleSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.CarIdleSound", "elevidle.wav");
89         MotorStartSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorStartSound", "motor_start.wav");
90         MotorRunSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorRunSound", "motor_running.wav");
91         MotorStopSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorStopSound", "motor_stop.wav");
92         MotorIdleSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorIdleSound", "");
93         AlarmSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.AlarmSound", "bell1.wav");
94         AlarmSoundStop = sbs->GetConfigString("Skyscraper.SBS.Elevator.AlarmSoundStop", "bell1-stop.wav");
95         UseFloorSkipText = false;
96         ACP = sbs->GetConfigBool("Skyscraper.SBS.Elevator.ACP", false);
97         ACPFloor = sbs->GetConfigInt("Skyscraper.SBS.Elevator.ACPFloor", 0);
98         UpPeak = sbs->GetConfigBool("Skyscraper.SBS.Elevator.UpPeak", false);
99         DownPeak = sbs->GetConfigBool("Skyscraper.SBS.Elevator.DownPeak", false);
100         IndependentService = sbs->GetConfigBool("Skyscraper.SBS.Elevator.IndependentService", false);
101         InspectionService = sbs->GetConfigBool("Skyscraper.SBS.Elevator.InspectionService", false);
102         FireServicePhase1 = sbs->GetConfigInt("Skyscraper.SBS.Elevator.FireService1", 0);
103         FireServicePhase2 = sbs->GetConfigInt("Skyscraper.SBS.Elevator.FireService2", 0);
104         RecallFloor = 0;
105         RecallFloorAlternate = 0;
106         OnFloor = true;
107         RecallSet = false;
108         RecallAltSet = false;
109         ACPFloorSet = false;
110         RecallUnavailable = false;
111         ManualGo = false;
112         AlarmActive = false;
113         NumDoors = 1;
114         Created = false;
115         lastcheckresult = false;
116         checkfirstrun = true;
117         UseFloorBeeps = false;
118         UseFloorSounds = false;
119         UseMessageSounds = false;
120         MotorPosition = 0;
121         ActiveCallFloor = 0;
122         ActiveCallDirection = 0;
123         lastdoor_result = 0;
124         lastdoor_number = 0;
125         QueueResets = sbs->GetConfigBool("Skyscraper.SBS.Elevator.QueueResets", false);
126         FirstRun = true;
127         CameraOffset = 0;
128         ParkingFloor = 0;
129         ParkingDelay = 0;
130         Leveling = false;
131         LevelingSpeed = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.LevelingSpeed", 0.2);
132         LevelingOffset = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.LevelingOffset", 0.5);
133         LevelingOpen = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.LevelingOpen", 0);
134         tmpDecelJerk = 0;
135         FinishedMove = false;
136         WaitForDoors = false;
137         ActiveDirection = 0;
138         RandomActivity = sbs->GetConfigBool("Skyscraper.SBS.Elevator.RandomActivity", false);
139         RandomProbability = sbs->GetConfigInt("Skyscraper.SBS.Elevator.RandomProbability", 10);
140         RandomFrequency = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.RandomFrequency", 3);
141         RandomLobby = 0;
142         RandomLobbySet = false;
143         mainsound = 0;
144         idlesound = 0;
145         motorsound = 0;
146         alarm = 0;
147         floorbeep = 0;
148         floorsound = 0;
149         OriginFloor = 0;
150         Fan = true;
151         NotifyEarly = sbs->GetConfigInt("Skyscraper.SBS.Elevator.NotifyEarly", 0);
152         Running = sbs->GetConfigBool("Skyscraper.SBS.Elevator.Run", true);
153         Notified = false;
154         Parking = false;
155         MusicPosition = 0;
156         Music = sbs->GetConfigString("Skyscraper.SBS.Elevator.Music", "");
157         MusicOn = sbs->GetConfigBool("Skyscraper.SBS.Elevator.MusicOn", true);;
158         MusicOnMove = sbs->GetConfigBool("Skyscraper.SBS.Elevator.MusicOnMove", false);;
159         DepartureDelay = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.DepartureDelay", 0.0);
160         ArrivalDelay = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.ArrivalDelay", 0.0);
161         WaitForTimer = false;
162         SoundsQueued = false;
163         HeightSet = false;
164         messagesnd = 0;
165         musicsound = 0;
166
167         //create timers
168         timer = new Timer(this, 0);
169         random_timer = new Timer(this, 1);
170         arrival_delay = new Timer(this, 2);
171         departure_delay = new Timer(this,3);
172
173         //create object meshes
174         buffer = Ogre::StringConverter::toString(Number);
175         buffer.insert(0, "Elevator ");
176         TrimString(buffer);
177         object->SetName(buffer.c_str());
178         ElevatorMesh = new MeshObject(object, buffer.c_str(), true);
179
180         if (sbs->Verbose)
181                 Report("elevator object created");
182 }
183
184 Elevator::~Elevator()
185 {
186         //delete models
187         for (int i = 0; i < (int)ModelArray.size(); i++)
188         {
189                 if (ModelArray[i])
190                         delete ModelArray[i];
191                 ModelArray[i] = 0;
192         }
193
194         //delete lights
195         for (int i = 0; i < (int)lights.size(); i++)
196         {
197                 if (lights[i])
198                         delete lights[i];
199                 lights[i] = 0;
200         }
201
202         //delete timers
203         if (sbs->Verbose)
204                 Report("deleting timers");
205
206         if (timer)
207         {
208                 timer->Stop();
209                 delete timer;
210         }
211         timer = 0;
212
213         if (random_timer)
214         {
215                 random_timer->Stop();
216                 delete random_timer;
217         }
218         random_timer = 0;
219
220         if (arrival_delay)
221         {
222                 arrival_delay->Stop();
223                 delete arrival_delay;
224         }
225         arrival_delay = 0;
226
227         if (departure_delay)
228         {
229                 departure_delay->Stop();
230                 delete departure_delay;
231         }
232         departure_delay = 0;
233
234         //delete directional indicators
235         if (sbs->Verbose)
236                 Report("deleting interior directional indicators");
237
238         for (int i = 0; i < (int)DirIndicatorArray.size(); i++)
239         {
240                 if (DirIndicatorArray[i])
241                 {
242                         DirIndicatorArray[i]->object->parent_deleting = true;
243                         delete DirIndicatorArray[i];
244                 }
245         }
246         DirIndicatorArray.clear();
247
248         //delete doors
249         if (sbs->Verbose)
250                 Report("deleting doors");
251
252         if (DoorArray.size() > 0)
253         {
254                 for (int i = 0; i < (int)DoorArray.size(); i++)
255                 {
256                         if (DoorArray[i])
257                         {
258                                 DoorArray[i]->object->parent_deleting = true;
259                                 delete DoorArray[i];
260                         }
261                 }
262         }
263
264         //delete floor indicators
265         if (sbs->Verbose)
266                 Report("deleting floor indicators");
267
268         for (int i = 0; i < (int)FloorIndicatorArray.size(); i++)
269         {
270                 if (FloorIndicatorArray[i])
271                 {
272                         FloorIndicatorArray[i]->object->parent_deleting = true;
273                         delete FloorIndicatorArray[i];
274                 }
275         }
276         FloorIndicatorArray.clear();
277
278         //delete panels
279         if (sbs->Verbose)
280                 Report("deleting button panels");
281
282         for (int i = 0; i < (int)PanelArray.size(); i++)
283         {
284                 if (PanelArray[i])
285                 {
286                         PanelArray[i]->object->parent_deleting = true;
287                         delete PanelArray[i];
288                 }
289                 PanelArray[i] = 0;
290         }
291         PanelArray.clear();
292
293         //delete doors
294         if (sbs->Verbose)
295                 Report("deleting standard doors");
296
297         for (int i = 0; i < (int)StdDoorArray.size(); i++)
298         {
299                 if (StdDoorArray[i])
300                 {
301                         StdDoorArray[i]->object->parent_deleting = true;
302                         delete StdDoorArray[i];
303                 }
304                 StdDoorArray[i] = 0;
305         }
306         StdDoorArray.clear();
307
308         //Destructor
309         if (sbs->Verbose)
310                 Report("deleting objects");
311         if (mainsound)
312         {
313                 mainsound->object->parent_deleting = true;
314                 delete mainsound;
315         }
316         mainsound = 0;
317         if (alarm)
318         {
319                 alarm->object->parent_deleting = true;
320                 delete alarm;
321         }
322         alarm = 0;
323         if (floorbeep)
324         {
325                 floorbeep->object->parent_deleting = true;
326                 delete floorbeep;
327         }
328         floorbeep = 0;
329         if (floorsound)
330         {
331                 floorsound->object->parent_deleting = true;
332                 delete floorsound;
333         }
334         floorsound = 0;
335         if (motorsound)
336         {
337                 motorsound->object->parent_deleting = true;
338                 delete motorsound;
339         }
340         motorsound = 0;
341         if (idlesound)
342         {
343                 idlesound->object->parent_deleting = true;
344                 delete idlesound;
345         }
346         idlesound = 0;
347         if (messagesnd)
348         {
349                 messagesnd->object->parent_deleting = true;
350                 delete messagesnd;
351         }
352         messagesnd = 0;
353         if (musicsound)
354         {
355                 musicsound->object->parent_deleting = true;
356                 delete musicsound;
357         }
358         musicsound = 0;
359
360         //delete sounds
361         if (sbs->Verbose)
362                 Report("deleting sounds");
363
364         for (int i = 0; i < (int)sounds.size(); i++)
365         {
366                 if (sounds[i])
367                 {
368                         sounds[i]->object->parent_deleting = true;
369                         delete sounds[i];
370                 }
371                 sounds[i] = 0;
372         }
373         sounds.clear();
374
375         //delete wall objects
376         for (int i = 0; i < (int)elevator_walls.size(); i++)
377         {
378                 if (elevator_walls[i])
379                 {
380                         elevator_walls[i]->parent_deleting = true;
381                         delete elevator_walls[i];
382                 }
383                 elevator_walls[i] = 0;
384         }
385
386         if (ElevatorMesh)
387                 delete ElevatorMesh;
388         ElevatorMesh = 0;
389
390         //unregister from parent
391         if (sbs->FastDelete == false)
392         {
393                 if (object->parent_deleting == false)
394                         sbs->RemoveElevator(this);
395         }
396         delete object;
397 }
398
399 Object* Elevator::CreateElevator(bool relative, float x, float z, int floor)
400 {
401         //Creates elevator at specified location and floor
402         //x and z are the center coordinates
403         //if relative is true, then x and z coordinates are relative
404         //to the assigned shaft's center
405
406         if (Created == true)
407         {
408                 ReportError("Has already been created");
409                 return 0;
410         }
411
412         //make sure required values are set
413         if (ElevatorSpeed <= 0)
414         {
415                 ReportError("Speed not set or invalid");
416                 return 0;
417         }
418         if (Acceleration <= 0)
419         {
420                 ReportError("Acceleration not set or invalid");
421                 return 0;
422         }
423         if (Deceleration <= 0)
424         {
425                 ReportError("Deceleration not set or invalid");
426                 return 0;
427         }
428         if (NumDoors < 0)
429         {
430                 ReportError("Number of doors invalid");
431                 return 0;
432         }
433         if (AccelJerk <= 0)
434         {
435                 ReportError("Invalid value for AccelJerk");
436                 return 0;
437         }
438         if (DecelJerk <= 0)
439         {
440                 ReportError("Invalid value for DecelJerk");
441                 return 0;
442         }
443         if (AssignedShaft <= 0)
444         {
445                 ReportError("Not assigned to a shaft");
446                 return 0;
447         }
448         if (!sbs->GetShaft(AssignedShaft))
449         {
450                 std::string num = Ogre::StringConverter::toString(AssignedShaft);
451                 ReportError(std::string("Shaft " + num + " doesn't exist").c_str());
452                 return 0;
453         }
454         if (floor < sbs->GetShaft(AssignedShaft)->startfloor || floor > sbs->GetShaft(AssignedShaft)->endfloor)
455         {
456                 std::string num = Ogre::StringConverter::toString(floor);
457                 ReportError(std::string("Invalid starting floor " + num).c_str());
458                 return 0;
459         }
460
461         //add elevator's starting floor to serviced floor list - this also ensures that the list is populated to prevent errors
462         if (IsServicedFloor(floor) == false)
463                 AddServicedFloor(floor);
464
465         //set data
466         Origin.y = sbs->GetFloor(floor)->GetBase();
467         if (relative == false)
468         {
469                 Origin.x = x;
470                 Origin.z = z;
471         }
472         else
473         {
474                 Origin.x = sbs->GetShaft(AssignedShaft)->origin.x + x;
475                 Origin.z = sbs->GetShaft(AssignedShaft)->origin.z + z;
476         }
477         OriginFloor = floor;
478
479         //add elevator to associated shaft's list
480         sbs->GetShaft(AssignedShaft)->AddElevator(Number);
481
482         //set recall/ACP floors if not already set
483         if (RecallSet == false)
484                 SetRecallFloor(GetBottomFloor());
485         if (RecallAltSet == false)
486                 SetAlternateRecallFloor(GetTopFloor());
487         if (ACPFloorSet == false)
488                 SetACPFloor(GetBottomFloor());
489
490         //create door objects
491         if (sbs->Verbose)
492                 Report("creating doors");
493         if (NumDoors > 0)
494         {
495                 DoorArray.resize(NumDoors);
496                 for (int i = 0; i < NumDoors; i++)
497                         DoorArray[i] = new ElevatorDoor(i, this);
498         }
499
500         //move objects to positions
501         if (sbs->Verbose)
502                 Report("moving elevator to origin position");
503         ElevatorMesh->Move(Origin, false, false, false);
504
505         //create sound objects
506         if (sbs->Verbose)
507                 Report("creating sound objects");
508         mainsound = new Sound(this->object, "Main", true);
509         mainsound->SetPosition(Origin);
510         idlesound = new Sound(this->object, "Idle", true);
511         idlesound->SetPosition(Origin);
512         idlesound->Load(CarIdleSound.c_str());
513         motorsound = new Sound(this->object, "Motor", true);
514         motorsound->SetPosition(Origin);
515         //move motor to top of shaft if location not specified, or to location
516         if (MotorPosition != Ogre::Vector3(0, 0, 0))
517                 motorsound->SetPosition(Ogre::Vector3(MotorPosition.x + Origin.x, MotorPosition.y, MotorPosition.z + Origin.z));
518         else
519                 motorsound->SetPositionY(sbs->GetFloor(sbs->GetShaft(AssignedShaft)->endfloor)->GetBase());
520         MotorPosition = Ogre::Vector3(motorsound->GetPosition().x - Origin.x, motorsound->GetPosition().y, motorsound->GetPosition().z - Origin.z);
521         alarm = new Sound(this->object, "Alarm", true);
522         alarm->SetPosition(Origin);
523         floorbeep = new Sound(this->object, "Floor Beep", true);
524         floorbeep->SetPosition(Origin);
525         floorsound = new Sound(this->object, "Floor Sound", true);
526         floorsound->SetPosition(Origin);
527         messagesnd = new Sound(this->object, "Message Sound", true);
528         messagesnd->SetPosition(Origin);
529         musicsound = new Sound(this->object, "Music Sound", true);
530         musicsound->SetPosition(Origin + MusicPosition);
531         musicsound->Load(Music.c_str());
532
533         //set elevator's floor
534         ElevatorFloor = floor;
535
536         //create test light
537         //AddLight("light", 0, Ogre::Vector3(0, 6, 0), Ogre::Vector3(0, 0, 0), 1, 1, 1, 1, 1, 1, 0, 0, 0, 1000, 1, 1, 1);
538
539         Created = true;
540
541         Report("created at " + std::string(sbs->TruncateNumber(x, 4)) + ", " + std::string(sbs->TruncateNumber(x, 4)) + ", " + std::string(_itoa(floor, buffer, 12)));
542         return object;
543 }
544
545 void Elevator::AddRoute(int floor, int direction, bool change_light)
546 {
547         //Add call route to elevator routing table, in sorted order
548         //directions are either 1 for up, or -1 for down
549         //SBS_PROFILE("Elevator::AddRoute");
550
551         if (Running == false)
552         {
553                 Report("Elevator not running");
554                 return;
555         }
556
557         //if doors are open or moving in independent service mode, quit
558         if (IndependentService == true && (AreDoorsOpen() == false || CheckOpenDoor() == true))
559         {
560                 Report("floor button must be pressed before closing doors while in independent service");
561                 return;
562         }
563
564         //do not add routes if in inspection service or fire phase 1 modes
565         if (InspectionService == true)
566         {
567                 Report("cannot add route while in inspection service mode");
568                 return;
569         }
570         if (FireServicePhase2 == 2)
571         {
572                 Report("cannot add route while in held state");
573                 return;
574         }
575
576         if (direction == 1)
577         {
578                 int loc = -1;
579                 for (int i = 0; i < (int)UpQueue.size(); i++)
580                 {
581                         if (UpQueue[i] == floor)
582                                 loc = i;
583                 }
584
585                 if (loc != -1)
586                 {
587                         //exit if entry already exits
588                         Report("route to floor " + std::string(_itoa(floor, intbuffer, 10)) + " (" + sbs->GetFloor(floor)->ID + ") already exists");
589                         return;
590                 }
591
592                 UpQueue.push_back(floor);
593                 std::sort(UpQueue.begin(), UpQueue.end());
594
595                 LastQueueFloor[0] = floor;
596                 LastQueueFloor[1] = 1;
597                 Report("adding route to floor " + std::string(_itoa(floor, intbuffer, 10)) + " (" + sbs->GetFloor(floor)->ID + ") direction up");
598         }
599         else
600         {
601                 int loc = -1;
602                 for (int i = 0; i < (int)DownQueue.size(); i++)
603                 {
604                         if (DownQueue[i] == floor)
605                                 loc = i;
606                 }
607
608                 if (loc != -1)
609                 {
610                         //exit if entry already exits
611                         Report("route to floor " + std::string(_itoa(floor, intbuffer, 10)) + " (" + sbs->GetFloor(floor)->ID + ") already exists");
612                         return;
613                 }
614                 
615                 DownQueue.push_back(floor);
616                 std::sort(DownQueue.begin(), DownQueue.end());
617                 
618                 LastQueueFloor[0] = floor;
619                 LastQueueFloor[1] = -1;
620                 Report("adding route to floor " + std::string(_itoa(floor, intbuffer, 10)) + " (" + sbs->GetFloor(floor)->ID + ") direction down");
621         }
622
623         //turn on button lights
624         if (change_light == true)
625         {
626                 if (sbs->Verbose)
627                         Report("AddRoute: turning on button lights for floor " + std::string(_itoa(floor, intbuffer, 10)));
628                 for (int i = 0; i < (int)PanelArray.size(); i++)
629                         PanelArray[i]->ChangeLight(floor, true);
630         }
631
632         //add ACP route recursively if mode is enabled
633         if (ACP == true && floor != ACPFloor)
634         {
635                 //only add ACP route if original route will pass ACP floor
636                 if ((GetFloor() < ACPFloor && floor > ACPFloor) || (GetFloor() > ACPFloor && floor < ACPFloor))
637                 {
638                         if (sbs->Verbose)
639                                 Report("Adding ACP route");
640                         AddRoute(ACPFloor, direction, false);
641                 }
642         }
643 }
644
645 void Elevator::DeleteRoute(int floor, int direction)
646 {
647         //Delete call route from elevator routing table
648         //directions are either 1 for up, or -1 for down
649         //SBS_PROFILE("Elevator::DeleteRoute");
650
651         if (Running == false)
652         {
653                 Report("Elevator not running");
654                 return;
655         }
656
657         if (direction == 1)
658         {
659                 //delete floor entry from up queue
660                 for (int i = 0; i < (int)UpQueue.size(); i++)
661                 {
662                         if (UpQueue[i] == floor)
663                                 UpQueue.erase(UpQueue.begin() + i);
664                 }
665                 Report("deleting route to floor " + std::string(_itoa(floor, intbuffer, 10)) + " (" + sbs->GetFloor(floor)->ID + ") direction up");
666         }
667         else
668         {
669                 //delete floor entry from down queue
670                 for (int i = 0; i < (int)DownQueue.size(); i++)
671                 {
672                         if (DownQueue[i] == floor)
673                                 DownQueue.erase(DownQueue.begin() + i);
674                 }
675                 Report("deleting route to floor " + std::string(_itoa(floor, intbuffer, 10)) + " (" + sbs->GetFloor(floor)->ID + ") direction down");
676         }
677
678         //turn off button lights
679         if (sbs->Verbose)
680                 Report("DeleteRoute: turning off button lights for floor " + std::string(_itoa(floor, intbuffer, 10)));
681         for (int i = 0; i < (int)PanelArray.size(); i++)
682                 PanelArray[i]->ChangeLight(floor, false);
683 }
684
685 void Elevator::CancelLastRoute()
686 {
687         //cancels the last added route
688
689         //LastQueueFloor holds the floor and direction of the last route; array element 0 is the floor and 1 is the direction
690
691         if (Running == false)
692         {
693                 Report("Elevator not running");
694                 return;
695         }
696
697         if (LastQueueFloor[1] == 0)
698         {
699                 if (sbs->Verbose)
700                         Report("CancelLastRoute: route not valid");
701                 return;
702         }
703
704         Report("canceling last route");
705         DeleteRoute(LastQueueFloor[0], LastQueueFloor[1]);
706         LastQueueFloor[0] = 0;
707         LastQueueFloor[1] = 0;
708 }
709
710 void Elevator::Alarm()
711 {
712         //elevator alarm code
713
714         if (AlarmActive == false)
715         {
716                 //ring alarm
717                 AlarmActive = true;
718                 Report("alarm on");
719                 if (AlarmSound != "")
720                 {
721                         alarm->Load(AlarmSound.c_str());
722                         alarm->Loop(true);
723                         alarm->Play();
724                 }
725         }
726         if (AlarmActive == true && sbs->camera->MouseDown == false)
727         {
728                 //stop alarm
729                 AlarmActive = false;
730                 if (AlarmSound != "")
731                 {
732                         alarm->Stop();
733                         alarm->Load(AlarmSoundStop.c_str());
734                         alarm->Loop(false);
735                         alarm->Play();
736                 }
737                 Report("alarm off");
738         }
739 }
740
741 void Elevator::Stop(bool emergency)
742 {
743         //Tells elevator to stop moving, no matter where it is
744
745         //exit if in inspection mode
746         if (InspectionService == true && emergency == true)
747         {
748                 if (sbs->Verbose)
749                         Report("cannot stop while in inspection service");
750                 return;
751         }
752
753         if (IsMoving == false)
754         {
755                 if (sbs->Verbose)
756                         Report("Stop: not moving");
757                 return;
758         }
759
760         EmergencyStop = true;
761
762         if (emergency == true)
763         {
764                 Report("emergency stop");
765
766                 //clear elevator queues
767                 ResetQueue(true, true);
768         }
769         else
770                 Report("Stopping elevator");
771 }
772
773 void Elevator::OpenHatch()
774 {
775         //Opens the elevator's upper escape hatch, allowing access to the shaft
776
777         Report("opening hatch");
778 }
779
780 void Elevator::ProcessCallQueue()
781 {
782         //Processes the elevator's call queue, and sends elevators to called floors
783         SBS_PROFILE("Elevator::ProcessCallQueue");
784
785         //exit if elevator is not running
786         if (Running == false)
787                 return;
788
789         //exit if in inspection service mode
790         if (InspectionService == true)
791                 return;
792
793         //exit if stopping
794         if (EmergencyStop == true)
795                 return;
796
797         //if both queues are empty
798         if (UpQueue.size() == 0 && DownQueue.size() == 0)
799         {
800                 int TopFloor = GetTopFloor();
801                 int BottomFloor = GetBottomFloor();
802
803                 if (DownPeak == true || UpPeak == true)
804                 {
805                         //if DownPeak mode is active, send elevator to the top serviced floor if not already there
806                         if (GetFloor() != TopFloor && DownPeak == true && IsMoving == false)
807                         {
808                                 if (sbs->Verbose)
809                                         Report("ProcessCallQueue: sending elevator to top floor for DownPeak mode");
810                                 AddRoute(TopFloor, 1, false);
811                                 return;
812                         }
813                         //if UpPeak mode is active, send elevator to the bottom serviced floor if not already there
814                         else if (GetFloor() != BottomFloor && UpPeak == true && IsMoving == false)
815                         {
816                                 if (sbs->Verbose)
817                                         Report("ProcessCallQueue: sending elevator to bottom floor for UpPeak mode");
818                                 AddRoute(BottomFloor, -1, false);
819                                 return;
820                         }
821                 }
822
823                 if (IsIdle() == true && QueuePositionDirection != 0)
824                 {
825                         //set search direction to 0 if idle
826                         if (sbs->Verbose)
827                                 Report("ProcessCallQueue: resetting search direction due to idle");
828                         LastQueueDirection = QueuePositionDirection;
829                         QueuePositionDirection = 0;
830                 }
831                 return;
832         }
833         else if (QueuePositionDirection == 0)
834         {
835                 LastQueueDirection = 0;
836
837                 if (UpQueue.size() != 0)
838                 {
839                         if (sbs->Verbose)
840                                 Report("ProcessCallQueue: setting search direction to up");
841                         QueuePositionDirection = 1;
842                 }
843                 if (DownQueue.size() != 0)
844                 {
845                         if (sbs->Verbose)
846                                 Report("ProcessCallQueue: setting search direction to down");
847                         QueuePositionDirection = -1;
848                 }
849         }
850
851         //set search direction to 0 if any related queue is empty, and if doors are not open or moving
852         if (AreDoorsOpen() == false && CheckOpenDoor() == false)
853         {
854                 if (QueuePositionDirection == 1 && UpQueue.size() == 0)
855                 {
856                         if (sbs->Verbose)
857                                 Report("ProcessCallQueue: resetting search direction due to empty up queue");
858                         QueuePositionDirection = 0;
859                         LastQueueDirection = 1;
860                 }
861                 if (QueuePositionDirection == -1 && DownQueue.size() == 0)
862                 {
863                         if (sbs->Verbose)
864                                 Report("ProcessCallQueue: resetting search direction due to empty down queue");
865                         QueuePositionDirection = 0;
866                         LastQueueDirection = -1;
867                 }
868         }
869         else if (UpPeak == false && DownPeak == false)
870                 return; //don't process the main queue code if doors are open or moving
871
872         //Search through queue lists and find next valid floor call
873         if (QueuePositionDirection == 1)
874         {
875                 //search through up queue
876                 for (int i = 0; i < (int)UpQueue.size(); i++)
877                 {
878                         //if the queued floor number is a higher floor, dispatch the elevator to that floor
879                         if (UpQueue[i] >= ElevatorFloor)
880                         {
881                                 if (MoveElevator == false)
882                                 {
883                                         if (sbs->Verbose)
884                                                 Report("ProcessCallQueue up: standard dispatch, floor " + std::string(_itoa(UpQueue[i], intbuffer, 10)));
885                                         ActiveCallFloor = UpQueue[i];
886                                         ActiveCallDirection = 1;
887                                         GotoFloor = UpQueue[i];
888                                         if (FireServicePhase2 == 0 || UpPeak == true || DownPeak == true)
889                                         {
890                                                 CloseDoors();
891                                                 WaitForDoors = true;
892                                         }
893                                         MoveElevator = true;
894                                         LastQueueDirection = 1;
895                                 }
896                                 else if (Leveling == false && ActiveDirection == 1)
897                                 {
898                                         //if elevator is moving and not leveling, change destination floor if not beyond decel marker of that floor
899                                         if (GotoFloor != UpQueue[i])
900                                         {
901                                                 float tmpdestination = sbs->GetFloor(UpQueue[i])->GetBase();
902                                                 if (BeyondDecelMarker(1, tmpdestination) == false)
903                                                 {
904                                                         ActiveCallFloor = UpQueue[i];
905                                                         GotoFloor = UpQueue[i];
906                                                         Destination = tmpdestination;
907                                                         Report("changing destination floor to " + std::string(_itoa(GotoFloor, intbuffer, 10)) + " (" + sbs->GetFloor(GotoFloor)->ID + ")");
908                                                 }
909                                                 else if (sbs->Verbose)
910                                                         Report("ProcessCallQueue up: cannot change destination floor to " + std::string(_itoa(UpQueue[i], intbuffer, 10)));
911                                         }
912                                 }
913                                 return;
914                         }
915                         //if the queued floor number is a lower floor
916                         if (UpQueue[i] < ElevatorFloor && MoveElevator == false)
917                         {
918                                 //dispatch elevator if it's idle
919                                 if (IsIdle() == true && LastQueueDirection == 0)
920                                 {
921                                         if (sbs->Verbose)
922                                                 Report("ProcessCallQueue up: dispatching idle lower elevator, floor " + std::string(_itoa(UpQueue[i], intbuffer, 10)));
923                                         ActiveCallFloor = UpQueue[i];
924                                         ActiveCallDirection = 1;
925                                         GotoFloor = UpQueue[i];
926                                         if (FireServicePhase2 == 0 || UpPeak == true || DownPeak == true)
927                                         {
928                                                 CloseDoors();
929                                                 WaitForDoors = true;
930                                         }
931                                         MoveElevator = true;
932                                         LastQueueDirection = 1;
933                                         return;
934                                 }
935                                 //reset search direction or queue if it's the last entry
936                                 if (i == UpQueue.size() - 1)
937                                 {
938                                         if (QueueResets == true)
939                                         {
940                                                 if (sbs->Verbose)
941                                                         Report("ProcessCallQueue up: last entry (" + std::string(_itoa(UpQueue[i], intbuffer, 10)) + ") is lower; resetting queue");
942                                                 ResetQueue(true, false);
943                                                 return;
944                                         }
945                                         else if (IsIdle() == true && QueuePositionDirection != 0)
946                                         {
947                                                 //set search direction to 0 if idle
948                                                 if (sbs->Verbose)
949                                                         Report("ProcessCallQueue up: resetting search direction since last entry is lower");
950                                                 LastQueueDirection = QueuePositionDirection;
951                                                 QueuePositionDirection = 0;
952                                                 return;
953                                         }
954                                 }
955                                 //otherwise skip it if it's not the last entry
956                                 if (sbs->Verbose)
957                                         Report("ProcessCallQueue up: skipping floor entry " + std::string(_itoa(UpQueue[i], intbuffer, 10)));
958                         }
959                 }
960         }
961         else if (QueuePositionDirection == -1)
962         {
963                 //search through down queue (search order is reversed since calls need to be processed in decending order)
964                 for (int i = (int)DownQueue.size() - 1; i >= 0; i--)
965                 {
966                         //if the queued floor number is a lower floor, dispatch the elevator to that floor
967                         if (DownQueue[i] <= ElevatorFloor)
968                         {
969                                 if (MoveElevator == false)
970                                 {
971                                         if (sbs->Verbose)
972                                                 Report("ProcessCallQueue down: standard dispatch, floor " + std::string(_itoa(DownQueue[i], intbuffer, 10)));
973                                         ActiveCallFloor = DownQueue[i];
974                                         ActiveCallDirection = -1;
975                                         GotoFloor = DownQueue[i];
976                                         if (FireServicePhase2 == 0 || UpPeak == true || DownPeak == true)
977                                         {
978                                                 CloseDoors();
979                                                 WaitForDoors = true;
980                                         }
981                                         MoveElevator = true;
982                                         LastQueueDirection = -1;
983                                 }
984                                 else if (Leveling == false && ActiveDirection == -1)
985                                 {
986                                         //if elevator is moving and not leveling, change destination floor if not beyond decel marker of that floor
987                                         if (GotoFloor != DownQueue[i])
988                                         {
989                                                 float tmpdestination = sbs->GetFloor(DownQueue[i])->GetBase();
990                                                 if (BeyondDecelMarker(-1, tmpdestination) == false)
991                                                 {
992                                                         ActiveCallFloor = DownQueue[i];
993                                                         GotoFloor = DownQueue[i];
994                                                         Destination = tmpdestination;
995                                                         Report("changing destination floor to " + std::string(_itoa(GotoFloor, intbuffer, 10)) + " (" + sbs->GetFloor(GotoFloor)->ID + ")");
996                                                 }
997                                                 else if (sbs->Verbose)
998                                                         Report("ProcessCallQueue down: cannot change destination floor to " + std::string(_itoa(DownQueue[i], intbuffer, 10)));
999                                         }
1000                                 }
1001                                 return;
1002                         }
1003                         //if the queued floor number is an upper floor
1004                         if (DownQueue[i] > ElevatorFloor && MoveElevator == false)
1005                         {
1006                                 //dispatch elevator if idle
1007                                 if (IsIdle() == true && LastQueueDirection == 0)
1008                                 {
1009                                         if (sbs->Verbose)
1010                                                 Report("ProcessCallQueue down: dispatching idle higher elevator, floor " + std::string(_itoa(DownQueue[i], intbuffer, 10)));
1011                                         ActiveCallFloor = DownQueue[i];
1012                                         ActiveCallDirection = -1;
1013                                         GotoFloor = DownQueue[i];
1014                                         if (FireServicePhase2 == 0 || UpPeak == true || DownPeak == true)
1015                                         {
1016                                                 CloseDoors();
1017                                                 WaitForDoors = true;
1018                                         }
1019                                         MoveElevator = true;
1020                                         LastQueueDirection = -1;
1021                                         return;
1022                                 }
1023                                 //reset search direction or queue if it's the first entry
1024                                 if (i == 0)
1025                                 {
1026                                         if (QueueResets == true)
1027                                         {
1028                                                 if (sbs->Verbose)
1029                                                         Report("ProcessCallQueue down: last entry (" + std::string(_itoa(DownQueue[i], intbuffer, 10)) + ") is higher; resetting queue");
1030                                                 ResetQueue(false, true);
1031                                                 return;
1032                                         }
1033                                         else if (IsIdle() == true && QueuePositionDirection != 0)
1034                                         {
1035                                                 //set search direction to 0 if idle
1036                                                 if (sbs->Verbose)
1037                                                         Report("ProcessCallQueue down: resetting search direction since last entry is higher");
1038                                                 LastQueueDirection = QueuePositionDirection;
1039                                                 QueuePositionDirection = 0;
1040                                                 return;
1041                                         }
1042                                 }
1043                                 //otherwise skip it if it's not the last entry
1044                                 if (sbs->Verbose)
1045                                         Report("ProcessCallQueue down: skipping floor entry " + std::string(_itoa(DownQueue[i], intbuffer, 10)));
1046                         }
1047                 }
1048         }
1049 }
1050
1051 int Elevator::GetFloor()
1052 {
1053         //Determine floor that the elevator is on
1054         //SBS_PROFILE("Elevator::GetFloor");
1055
1056         int newlastfloor;
1057
1058         if (lastfloorset == true)
1059                 newlastfloor = sbs->GetFloorNumber(GetPosition().y, lastfloor, true);
1060         else
1061                 newlastfloor = sbs->GetFloorNumber(GetPosition().y);
1062
1063         lastfloor = newlastfloor;
1064         lastfloorset = true;
1065         return lastfloor;
1066 }
1067
1068 void Elevator::MonitorLoop()
1069 {
1070         //Monitors elevator and starts actions if needed
1071         SBS_PROFILE("Elevator::MonitorLoop");
1072
1073         //make sure height value is set
1074         if (HeightSet == false)
1075         {
1076                 Height = 0;
1077                 //search through mesh geometry to find actual height
1078                 for (int i = 0; i < (int)ElevatorMesh->MeshGeometry.size(); i++)
1079                 {
1080                         //set height value
1081                         if (sbs->ToLocal(ElevatorMesh->MeshGeometry[i].vertex.y) > Height)
1082                                 Height = sbs->ToLocal(ElevatorMesh->MeshGeometry[i].vertex.y);
1083                 }
1084                 HeightSet = true;
1085         }
1086
1087         //set random lobby level if not set
1088         if (RandomLobbySet == false)
1089                 SetRandomLobby(GetBottomFloor());
1090
1091         //perform first-run tasks
1092         if (FirstRun == true && Running == true)
1093         {
1094                 FirstRun = false;
1095
1096                 if (UpPeak == true)
1097                 {
1098                         UpPeak = false;
1099                         EnableUpPeak(true);
1100                 }
1101                 if (DownPeak == true)
1102                 {
1103                         DownPeak = false;
1104                         EnableDownPeak(true);
1105                 }
1106                 if (IndependentService == true)
1107                 {
1108                         IndependentService = false;
1109                         EnableIndependentService(true);
1110                 }
1111                 if (InspectionService == true)
1112                 {
1113                         InspectionService = false;
1114                         EnableInspectionService(true);
1115                 }
1116                 if (FireServicePhase1 > 0)
1117                 {
1118                         int value = FireServicePhase1;
1119                         FireServicePhase1 = 0;
1120                         EnableFireService1(value);
1121                 }
1122                 if (FireServicePhase2 > 0)
1123                 {
1124                         int value = FireServicePhase2;
1125                         FireServicePhase2 = 0;
1126                         EnableFireService2(value);
1127                 }
1128                 if (ACP == true)
1129                 {
1130                         ACP = false;
1131                         EnableACP(true);
1132                 }
1133                 if (ACPFloor != 0)
1134                 {
1135                         int tmp = ACPFloor;
1136                         ACPFloor = 0;
1137                         SetACPFloor(tmp);
1138                 }
1139                 UpdateFloorIndicators();
1140         }
1141
1142         //play idle sound if in elevator, or if doors open
1143         if (CarIdleSound != "")
1144         {
1145                 if (idlesound->IsPlaying() == false && Fan == true)
1146                 {
1147                         if ((sbs->InElevator == true && sbs->ElevatorNumber == Number) || AreDoorsOpen() == true || CheckOpenDoor() == true)
1148                         {
1149                                 if (sbs->Verbose)
1150                                         Report("playing idle sound");
1151                                 idlesound->Loop(true);
1152                                 idlesound->Play();
1153                         }
1154                 }
1155                 else
1156                 {
1157                         if (Fan == false && idlesound->IsPlaying() == true)
1158                         {
1159                                 if (sbs->Verbose)
1160                                         Report("stopping idle sound");
1161                                 idlesound->Stop();
1162                         }
1163                         else if ((sbs->InElevator == false || sbs->ElevatorNumber != Number) && AreDoorsOpen() == false && CheckOpenDoor() == false)
1164                         {
1165                                 if (sbs->Verbose)
1166                                         Report("stopping idle sound");
1167                                 idlesound->Stop();
1168                         }
1169                 }
1170         }
1171
1172         //play music sound if in elevator, or if doors open
1173         if (Music != "")
1174         {
1175                 if (musicsound->IsPlaying() == false && MusicOn == true && ((MusicOnMove == true && IsMoving == true) || MusicOnMove == false))
1176                 {
1177                         if (InServiceMode() == false)
1178                         {
1179                                 if ((sbs->InElevator == true && sbs->ElevatorNumber == Number) || AreDoorsOpen() == true || CheckOpenDoor() == true)
1180                                 {
1181                                         if (sbs->Verbose)
1182                                                 Report("playing music");
1183
1184                                         if (MusicPosition == Ogre::Vector3(0, 0, 0) && Height > 0)
1185                                                 MusicPosition = Ogre::Vector3(0, Height, 0); //set default music position to elevator height
1186
1187                                         musicsound->SetPosition(GetPosition() + MusicPosition);
1188                                         musicsound->Loop(true);
1189                                         musicsound->Play(false);
1190                                 }
1191                         }
1192                 }
1193                 else
1194                 {
1195                         if ((MusicOn == false || InServiceMode() == true || (MusicOnMove == true && IsMoving == false)) && musicsound->IsPlaying() == true)
1196                         {
1197                                 if (sbs->Verbose)
1198                                         Report("stopping music");
1199                                 musicsound->Pause();
1200                         }
1201                         else if ((sbs->InElevator == false || sbs->ElevatorNumber != Number) && AreDoorsOpen() == false && CheckOpenDoor() == false)
1202                         {
1203                                 if (sbs->Verbose)
1204                                         Report("stopping music");
1205                                 musicsound->Pause();
1206                         }
1207                 }
1208         }
1209
1210         //process alarm
1211         if (AlarmActive == true)
1212                 Alarm();
1213
1214         //call queue processor
1215         ProcessCallQueue();
1216
1217         //door operations
1218         for (int i = 1; i <= NumDoors; i++)
1219         {
1220                 ElevatorDoor *door = GetDoor(i);
1221                 if (door)
1222                         door->Loop();
1223
1224                 //reset door timer if peak mode is enabled and a movement is pending
1225                 if ((UpPeak == true || DownPeak == true))
1226                 {
1227                         if ((UpQueue.size() != 0 || DownQueue.size() != 0) && (AreDoorsOpen() == true && CheckOpenDoor() == false))
1228                         {
1229                                 if (door)
1230                                 {
1231                                         if (door->TimerIsRunning() == false)
1232                                                 door->ResetDoorTimer();
1233                                 }
1234                         }
1235                 }
1236         }
1237
1238         //enable auto-park timer if specified
1239         if (ParkingDelay > 0 && Running == true && IsIdle() == true)
1240         {
1241                 if (timer->IsRunning() == false)
1242                         timer->Start(ParkingDelay * 1000, true);
1243         }
1244
1245         //enable random call timer
1246         if (random_timer->IsRunning() == false && RandomActivity == true && Running == true)
1247                 random_timer->Start(RandomFrequency * 1000, false);
1248
1249         //elevator movement
1250         if (MoveElevator == true)
1251                 MoveElevatorToFloor();
1252
1253 }
1254
1255 void Elevator::MoveElevatorToFloor()
1256 {
1257         //Main processing routine; sends elevator to floor specified in GotoFloor
1258         //if InspectionService is enabled, this function ignores GotoFloor values, since the elevator is manually moved
1259         SBS_PROFILE("Elevator::MoveElevatorToFloor");
1260
1261         Ogre::Vector3 movement = Ogre::Vector3(0, 0, 0);
1262
1263         //wait until doors are fully closed if WaitForDoors is true
1264         if (WaitForDoors == true)
1265         {
1266                 if (AreDoorsOpen() == true || CheckOpenDoor() == true)
1267                         return;
1268                 else
1269                         WaitForDoors = false;
1270         }
1271
1272         //exit if waiting for arrival or departure timers
1273         if (WaitForTimer == true)
1274                 return;
1275
1276         if (ElevatorIsRunning == false)
1277         {
1278                 if (Running == false)
1279                 {
1280                         Report("Elevator not running");
1281                         return;
1282                 }
1283
1284                 if (sbs->Verbose)
1285                         Report("starting elevator movement procedure");
1286
1287                 ElevatorIsRunning = true;
1288                 FinishedMove = false;
1289                 std::string dir_string;
1290
1291                 Notified = false;
1292
1293                 //get elevator's current altitude
1294                 elevposition = GetPosition();
1295                 ElevatorStart = elevposition.y;
1296
1297                 //get elevator's current floor
1298                 ElevatorFloor = GetFloor();
1299                 oldfloor = ElevatorFloor;
1300
1301                 //Determine direction
1302                 if (InspectionService == false)
1303                 {
1304                         if (GotoFloor < ElevatorFloor)
1305                         {
1306                                 Direction = -1;
1307                                 dir_string = "down";
1308                         }
1309                         if (GotoFloor > ElevatorFloor)
1310                         {
1311                                 Direction = 1;
1312                                 dir_string = "up";
1313                         }
1314                 }
1315                 else
1316                 {
1317                         if (Direction == -1)
1318                                 dir_string = "down";
1319                         else if (Direction == 1)
1320                                 dir_string = "up";
1321                 }
1322
1323                 ActiveDirection = Direction;
1324
1325                 //If elevator is already on specified floor, open doors and exit
1326                 if (ElevatorFloor == GotoFloor && InspectionService == false)
1327                 {
1328                         sbs->Report("Elevator already on specified floor");
1329                         DeleteActiveRoute();
1330                         goto finish; //skip main processing and run cleanup section
1331                 }
1332
1333                 //if destination floor is not a serviced floor, reset and exit
1334                 if (IsServicedFloor(GotoFloor) == false && InspectionService == false)
1335                 {
1336                         sbs->Report("Destination floor not in ServicedFloors list");
1337                         MoveElevator = false;
1338                         ElevatorIsRunning = false;
1339                         DeleteActiveRoute();
1340                         return;
1341                 }
1342
1343                 //Determine distance to destination floor
1344                 if (InspectionService == false)
1345                 {
1346                         Destination = sbs->GetFloor(GotoFloor)->GetBase();
1347                         DistanceToTravel = fabs(fabs(Destination) - fabs(ElevatorStart));
1348                 }
1349                 else
1350                 {
1351                         //otherwise if inspection service is on, choose the altitude of the top/bottom floor
1352                         if (Direction == 1)
1353                         {
1354                                 Destination = sbs->GetFloor(GetTopFloor())->GetBase();
1355                                 if (ElevatorStart >= Destination)
1356                                 {
1357                                         //don't go above top floor
1358                                         Report("cannot go above top floor");
1359                                         Destination = 0;
1360                                         Direction = 0;
1361                                         MoveElevator = false;
1362                                         ElevatorIsRunning = false;
1363                                         DeleteActiveRoute();
1364                                         return;
1365                                 }
1366                         }
1367                         else
1368                         {
1369                                 Destination = sbs->GetFloor(GetBottomFloor())->GetBase();
1370                                 if (ElevatorStart <= Destination)
1371                                 {
1372                                         //don't go below bottom floor
1373                                         Report("cannot go below bottom floor");
1374                                         Destination = 0;
1375                                         Direction = 0;
1376                                         MoveElevator = false;
1377                                         ElevatorIsRunning = false;
1378                                         DeleteActiveRoute();
1379                                         return;
1380                                 }
1381                         }
1382                         DistanceToTravel = fabs(fabs(Destination) - fabs(ElevatorStart));
1383                 }
1384                 CalculateStoppingDistance = true;
1385
1386                 //If user is riding this elevator, then turn off objects
1387                 if (sbs->ElevatorSync == true && sbs->ElevatorNumber == Number && InspectionService == false)
1388                 {
1389                         if (sbs->Verbose)
1390                                 Report("user in elevator - turning off objects");
1391
1392                         //turn off floor
1393                         if (sbs->GetShaft(AssignedShaft)->ShowFloors == false)
1394                         {
1395                                 sbs->GetFloor(sbs->camera->CurrentFloor)->Enabled(false);
1396                                 sbs->GetFloor(sbs->camera->CurrentFloor)->EnableGroup(false);
1397                         }
1398                         else
1399                         {
1400                                 int loc = -1;
1401                                 for (int i = 0; i < (int)sbs->GetShaft(AssignedShaft)->ShowFloorsList.size(); i++)
1402                                 {
1403                                         if (sbs->GetShaft(AssignedShaft)->ShowFloorsList[i] == sbs->camera->CurrentFloor)
1404                                                 loc = i;
1405                                 }
1406
1407                                 if (loc == -1)
1408                                 {
1409                                         sbs->GetFloor(sbs->camera->CurrentFloor)->Enabled(false);
1410                                         sbs->GetFloor(sbs->camera->CurrentFloor)->EnableGroup(false);
1411                                 }
1412                         }
1413
1414                         //Turn off sky, buildings, and landscape
1415                         if (sbs->GetShaft(AssignedShaft)->ShowOutside == false)
1416                         {
1417                                 sbs->EnableSkybox(false);
1418                                 sbs->EnableBuildings(false);
1419                                 sbs->EnableLandscape(false);
1420                                 sbs->EnableExternal(false);
1421                         }
1422                         else
1423                         {
1424                                 int loc = -1;
1425                                 for (int i = 0; i < (int)sbs->GetShaft(AssignedShaft)->ShowOutsideList.size(); i++)
1426                                 {
1427                                         if (sbs->GetShaft(AssignedShaft)->ShowOutsideList[i] == sbs->camera->CurrentFloor)
1428                                                 loc = i;
1429                                 }
1430                                 if (loc == -1)
1431                                 {
1432                                         sbs->EnableSkybox(false);
1433                                         sbs->EnableBuildings(false);
1434                                         sbs->EnableLandscape(false);
1435                                         sbs->EnableExternal(false);
1436                                 }
1437                         }
1438                 }
1439
1440                 //set interior directional indicators
1441                 UpdateDirectionalIndicators();
1442
1443                 //set external active-direction indicators
1444                 sbs->GetFloor(sbs->camera->CurrentFloor)->UpdateDirectionalIndicators(Number);
1445
1446                 //notify about movement
1447                 if (InspectionService == false)
1448                         Report("moving " + dir_string + " to floor " + std::string(_itoa(GotoFloor, intbuffer, 10)) + " (" + sbs->GetFloor(GotoFloor)->ID + ")");
1449                 else
1450                         Report("moving " + dir_string);
1451                 IsMoving = true;
1452                 OnFloor = false;
1453                 SoundsQueued = true;
1454
1455                 //start departure timer
1456                 if (DepartureDelay > 0 && WaitForTimer == false)
1457                 {
1458                         if (sbs->Verbose)
1459                                 Report("started departure delay");
1460                         WaitForTimer = true;
1461                         departure_delay->Start(DepartureDelay * 1000, false);
1462                         return;
1463                 }
1464         }
1465
1466         if (SoundsQueued == true)
1467         {
1468                 SoundsQueued = false;
1469
1470                 if (DepartureDelay > 0)
1471                 {
1472                         if (sbs->Verbose)
1473                                 Report("finished departure delay");
1474                         departure_delay->Stop();
1475                 }
1476
1477                 //Play starting sounds
1478                 if (sbs->Verbose)
1479                         Report("playing starting sounds");
1480                 mainsound->Stop();
1481                 mainsound->Load(CarStartSound.c_str());
1482                 mainsound->Loop(false);
1483                 mainsound->Play();
1484                 motorsound->Stop();
1485                 motorsound->Load(MotorStartSound.c_str());
1486                 motorsound->Loop(false);
1487                 motorsound->Play();
1488         }
1489
1490         if (EmergencyStop == true && Brakes == false)
1491         {
1492                 //emergency stop
1493                 if (sbs->Verbose)
1494                         Report("handling emergency stop");
1495                 CalculateStoppingDistance = false;
1496                 TempDeceleration = Deceleration;
1497                 if (Direction == 1)
1498                         Direction = -1;
1499                 else
1500                         Direction = 1;
1501                 Brakes = true;
1502                 //stop sounds
1503                 mainsound->Stop();
1504                 motorsound->Stop();
1505                 //play stopping sounds
1506                 if (sbs->Verbose)
1507                         Report("playing stopping sounds");
1508                 mainsound->Load(CarStopSound.c_str());
1509                 mainsound->Loop(false);
1510                 bool adjust = sbs->GetConfigBool("Skyscraper.SBS.Elevator.AutoAdjustSound", false);
1511                 //set play position to current percent of the total speed
1512                 if (adjust == true)
1513                         mainsound->SetPlayPosition(1 - (ElevatorRate / ElevatorSpeed));
1514                 else
1515                         mainsound->Reset();
1516                 mainsound->Play(false);
1517                 motorsound->Load(MotorStopSound.c_str());
1518                 motorsound->Loop(false);
1519                 if (adjust == true)
1520                         motorsound->SetPlayPosition(1 - (ElevatorRate / ElevatorSpeed));
1521                 else
1522                         motorsound->Reset();
1523                 motorsound->Play(false);
1524         }
1525
1526         if (mainsound->IsPlaying() == false && Brakes == false && CarMoveSound.empty() == false && CarMoveSound != "")
1527         {
1528                 //Movement sound
1529                 if (sbs->Verbose)
1530                         Report("playing car movement sound");
1531                 mainsound->Load(CarMoveSound.c_str());
1532                 mainsound->Loop(true);
1533                 mainsound->Play();
1534         }
1535
1536         if (motorsound->IsPlaying() == false && Brakes == false && MotorRunSound.empty() == false && MotorRunSound != "")
1537         {
1538                 //Motor sound
1539                 if (sbs->Verbose)
1540                         Report("playing motor run sound");
1541                 motorsound->Load(MotorRunSound.c_str());
1542                 motorsound->Loop(true);
1543                 motorsound->Play();
1544         }
1545
1546         //move elevator objects and camera
1547         movement.y = ElevatorRate * sbs->delta;
1548
1549         ElevatorMesh->Move(Ogre::Vector3(0, movement.y, 0), true, true, true);
1550         elevposition = GetPosition();
1551         if (sbs->ElevatorSync == true && sbs->ElevatorNumber == Number)
1552                 sbs->camera->SetPosition(Ogre::Vector3(sbs->camera->GetPosition().x, elevposition.y + CameraOffset, sbs->camera->GetPosition().z));
1553         MoveDoors(0, movement, true, true, true);
1554         MoveLights(movement, true, true, true);
1555         MoveModels(movement, true, true, true);
1556         for (int i = 0; i < (int)FloorIndicatorArray.size(); i++)
1557         {
1558                 if (FloorIndicatorArray[i])
1559                         FloorIndicatorArray[i]->MovePosition(movement);
1560         }
1561         for (int i = 0; i < (int)PanelArray.size(); i++)
1562         {
1563                 if (PanelArray[i])
1564                         PanelArray[i]->Move(movement);
1565         }
1566         for (int i = 0; i < (int)DirIndicatorArray.size(); i++)
1567         {
1568                 if (DirIndicatorArray[i])
1569                         DirIndicatorArray[i]->Move(movement);
1570         }
1571         for (int i = 0; i < (int)StdDoorArray.size(); i++)
1572         {
1573                 if (StdDoorArray[i])
1574                         StdDoorArray[i]->Move(movement, true, true, true);
1575         }
1576
1577         //move sounds
1578         mainsound->SetPosition(elevposition);
1579         idlesound->SetPosition(elevposition);
1580         MoveDoorSound(0, elevposition, false, false, false);
1581         alarm->SetPosition(elevposition);
1582         floorbeep->SetPosition(elevposition);
1583         floorsound->SetPosition(elevposition);
1584         messagesnd->SetPosition(elevposition);
1585         musicsound->SetPosition(elevposition + MusicPosition);
1586         for (int i = 0; i < (int)sounds.size(); i++)
1587         {
1588                 if (sounds[i])
1589                         sounds[i]->SetPositionY(elevposition.y + sounds[i]->PositionOffset.y);
1590         }
1591
1592         //motion calculation
1593         if (Brakes == false)
1594         {
1595                 //calculate jerk rate
1596                 if (JerkRate < 1)
1597                 {
1598                         JerkRate += AccelJerk * sbs->delta;
1599                         JerkPos = ElevatorRate;
1600                 }
1601
1602                 //regular motion
1603                 float limit = 0;
1604                 if (InspectionService == false)
1605                         limit = ElevatorSpeed;
1606                 else
1607                         limit = ElevatorSpeed * 0.6;
1608
1609                 if (Direction == 1 && ElevatorRate < limit)
1610                         ElevatorRate += ElevatorSpeed * ((Acceleration * JerkRate) * sbs->delta);
1611                 else if (Direction == -1 && ElevatorRate > -limit)
1612                         ElevatorRate -= ElevatorSpeed * ((Acceleration * JerkRate) * sbs->delta);
1613                 else
1614                         CalculateStoppingDistance = false;
1615         }
1616         else if (Leveling == false)
1617         {
1618                 //slow down
1619
1620                 //calculate jerk rate
1621                 //check if the elevator rate is less than the amount that was stored in JerkPos
1622                 //(the elevator rate at the end of the JerkRate increments), adjusted to the ratio of acceljerk to deceljerk
1623
1624                 double tmppos = JerkPos * (AccelJerk / DecelJerk);
1625                 if ((Direction == -1 && ElevatorRate <= tmppos) || (Direction == 1 && ElevatorRate >= tmppos))
1626                 {
1627                         if (tmpDecelJerk == 0)
1628                                 tmpDecelJerk = DecelJerk * (tmppos / ElevatorRate);
1629                         JerkRate -= tmpDecelJerk * sbs->delta;
1630                 }
1631                 //prevent jerkrate from reaching 0
1632                 if (JerkRate < 0)
1633                 {
1634                         JerkRate = 0;
1635                         ElevatorRate = 0;
1636                 }
1637
1638                 if (Direction == 1)
1639                         ElevatorRate += ElevatorSpeed * ((TempDeceleration * JerkRate) * sbs->delta);
1640                 if (Direction == -1)
1641                         ElevatorRate -= ElevatorSpeed * ((TempDeceleration * JerkRate) * sbs->delta);
1642         }
1643
1644         //prevent the rate from going beyond 0
1645         if (Direction == 1 && Brakes == true && ElevatorRate > 0)
1646                 ElevatorRate = 0;
1647         if (Direction == -1 && Brakes == true && ElevatorRate < 0)
1648                 ElevatorRate = 0;
1649
1650         //get distance needed to stop elevator
1651         if (CalculateStoppingDistance == true)
1652         {
1653                 if (Direction == 1)
1654                         //stopping distance is the distance the elevator has traveled (usually to reach max speed), times
1655                         //the ratio of acceleration to deceleration (so if the deceleration is half of the acceleration,
1656                         //it will take twice the distance to stop)
1657                         StoppingDistance = (elevposition.y - ElevatorStart) * (Acceleration / Deceleration);
1658                 else if (Direction == -1)
1659                         StoppingDistance = (ElevatorStart - elevposition.y) * (Acceleration / Deceleration);
1660         }
1661
1662         //Deceleration routines with floor overrun correction (there's still problems, but it works pretty good)
1663         //since this function cycles at a slower/less granular rate (cycles according to frames per sec), an error factor is present where the elevator overruns the dest floor,
1664         //even though the algorithms are all correct. Since the elevator moves by "jumping" to a new altitude every frame - and usually jumps right over the altitude value where it is supposed to
1665         //start the deceleration process, causing the elevator to decelerate too late, and end up passing/overrunning the dest floor's altitude.  This code corrects this problem
1666         //by determining if the next "jump" will overrun the deceleration marker (which is Dest's Altitude minus Stopping Distance), and temporarily altering the deceleration rate according to how far off the mark it is
1667         //and then starting the deceleration process immediately.
1668
1669         //determine if next jump altitude is over deceleration marker
1670         if (Brakes == false)
1671         {
1672                 if (BeyondDecelMarker(Direction, Destination) == true)
1673                 {
1674                         if (sbs->Verbose)
1675                                 Report("beyond deceleration marker - slowing down");
1676
1677                         //up movement
1678                         if (Direction == 1)
1679                         {
1680                                 CalculateStoppingDistance = false;
1681                                 //recalculate deceleration value based on distance from marker, and store result in tempdeceleration
1682                                 //TempDeceleration = Deceleration;
1683                                 TempDeceleration = Deceleration * (StoppingDistance / ((Destination - LevelingOffset) - elevposition.y));
1684                                 //start deceleration
1685                                 Direction = -1;
1686                                 Brakes = true;
1687                                 if (InspectionService == false)
1688                                         ElevatorRate -= ElevatorSpeed * ((TempDeceleration * JerkRate) * sbs->delta);
1689                                 else
1690                                         ElevatorRate -= (ElevatorSpeed * 0.6) * ((TempDeceleration * JerkRate) * sbs->delta);
1691                         }
1692                         //down movement
1693                         else if (Direction == -1)
1694                         {
1695                                 CalculateStoppingDistance = false;
1696                                 //recalculate deceleration value based on distance from marker, and store result in tempdeceleration
1697                                 //TempDeceleration = Deceleration;
1698                                 TempDeceleration = Deceleration * (StoppingDistance / (elevposition.y - (Destination + LevelingOffset)));
1699                                 //start deceleration
1700                                 Direction = 1;
1701                                 Brakes = true;
1702                                 if (InspectionService == false)
1703                                         ElevatorRate += ElevatorSpeed * ((TempDeceleration * JerkRate) * sbs->delta);
1704                                 else
1705                                         ElevatorRate += (ElevatorSpeed * 0.6) * ((TempDeceleration * JerkRate) * sbs->delta);
1706                         }
1707
1708                         //stop sounds
1709                         mainsound->Stop();
1710                         motorsound->Stop();
1711                         //play elevator stopping sounds
1712                         if (sbs->Verbose)
1713                                 Report("playing stopping sounds");
1714                         bool adjust = sbs->GetConfigBool("Skyscraper.SBS.Elevator.AutoAdjustSound", false);
1715                         mainsound->Load(CarStopSound.c_str());
1716                         mainsound->Loop(false);
1717                         //set play position to current percent of the total speed
1718                         if (adjust == true)
1719                                 mainsound->SetPlayPosition(1 - (ElevatorRate / ElevatorSpeed));
1720                         else
1721                                 mainsound->Reset();
1722                         mainsound->Play(false);
1723                         motorsound->Load(MotorStopSound.c_str());
1724                         motorsound->Loop(false);
1725                         if (adjust == true)
1726                                 motorsound->SetPlayPosition(1 - (ElevatorRate / ElevatorSpeed));
1727                         else
1728                                 motorsound->Reset();
1729                         motorsound->Play(false);
1730
1731                         if (NotifyEarly == 2 && Parking == false)
1732                                 NotifyArrival(GotoFloor);
1733                 }
1734         }
1735         else if (Leveling == false && EmergencyStop == false)
1736         {
1737                 if (fabs(ElevatorRate) <= LevelingSpeed)
1738                 {
1739                         //turn on leveling if elevator's rate is less than or equal to the leveling speed value
1740                         if (sbs->Verbose)
1741                                 Report("leveling enabled");
1742                         Leveling = true;
1743
1744                         if (NotifyEarly == 1 && Parking == false)
1745                                 NotifyArrival(GotoFloor);
1746                 }
1747         }
1748
1749         if (Leveling == true)
1750         {
1751                 //floor leveling routine
1752                 if (Direction == -1 && (Destination - elevposition.y) > 0)
1753                         ElevatorRate = LevelingSpeed;
1754                 else if (Direction == 1 && (elevposition.y - Destination) > 0)
1755                         ElevatorRate = -LevelingSpeed;
1756                 else
1757                 {
1758                         if (sbs->Verbose)
1759                                 Report("arrived at floor");
1760                         ElevatorRate = 0; //stop if on floor
1761                 }
1762
1763                 //open doors if leveling open offset is not zero
1764                 if (LevelingOpen > 0 && FinishedMove == false && ArrivalDelay == 0)
1765                 {
1766                         if (Direction == -1 && (Destination - elevposition.y) < LevelingOpen)
1767                                 FinishMove();
1768                         else if (Direction == 1 && (elevposition.y - Destination) < LevelingOpen)
1769                                 FinishMove();
1770                 }
1771         }
1772
1773         if (GetFloor() != oldfloor)
1774         {
1775                 if (sbs->Verbose)
1776                         Report("on floor " + std::string(_itoa(GetFloor(), intbuffer, 10)));
1777
1778                 //play floor beep sound if floor is a serviced floor
1779                 if (IsServicedFloor(GetFloor()) == true)
1780                         PlayFloorBeep();
1781
1782                 //update floor indicators
1783                 UpdateFloorIndicators();
1784
1785                 //update floor indicators on current camera floor
1786                 sbs->GetFloor(sbs->camera->CurrentFloor)->UpdateFloorIndicators(Number);
1787         }
1788
1789         oldfloor = GetFloor();
1790
1791         //exit if elevator's running
1792         if (fabs(ElevatorRate) != 0)
1793                 return;
1794
1795         //start arrival timer
1796         if (ArrivalDelay > 0)
1797         {
1798                 if (WaitForTimer == false && arrival_delay->IsRunning() == false)
1799                 {
1800                         if (sbs->Verbose)
1801                                 Report("started arrival delay");
1802                         WaitForTimer = true;
1803                         arrival_delay->Start(ArrivalDelay * 1000, false);
1804                         return;
1805                 }
1806                 else
1807                 {
1808                         if (sbs->Verbose)
1809                                 Report("finished arrival delay");
1810                         arrival_delay->Stop();
1811                 }
1812         }
1813
1814         //finish move
1815         if (EmergencyStop == false)
1816         {
1817                 if (sbs->Verbose)
1818                         Report("storing error offset");
1819
1820                 //store error offset value
1821                 if (Direction == -1)
1822                         ErrorOffset = elevposition.y - Destination;
1823                 else if (Direction == 1)
1824                         ErrorOffset = Destination - elevposition.y;
1825                 else
1826                         ErrorOffset = 0;
1827
1828                 //set elevator and objects to floor altitude (corrects offset errors)
1829                 //move elevator objects
1830                 if (sbs->Verbose)
1831                         Report("setting elevator to floor altitude");
1832                 ElevatorMesh->Move(Ogre::Vector3(elevposition.x, Destination, elevposition.z), false, false, false);
1833                 if (sbs->ElevatorSync == true && sbs->ElevatorNumber == Number)
1834                         sbs->camera->SetPosition(Ogre::Vector3(sbs->camera->GetPosition().x, GetPosition().y + CameraOffset, sbs->camera->GetPosition().z));
1835                 MoveDoors(0, Ogre::Vector3(0, Destination, 0), true, false, true);
1836                 MoveLights(Ogre::Vector3(0, Destination, 0), true, false, true);
1837                 MoveModels(Ogre::Vector3(0, Destination, 0), true, false, true);
1838                 for (int i = 0; i < (int)FloorIndicatorArray.size(); i++)
1839                 {
1840                         if (FloorIndicatorArray[i])
1841                                 FloorIndicatorArray[i]->SetPosition(Ogre::Vector3(FloorIndicatorArray[i]->GetPosition().x, Destination, FloorIndicatorArray[i]->GetPosition().z));
1842                 }
1843                 for (int i = 0; i < (int)PanelArray.size(); i++)
1844                 {
1845                         if (PanelArray[i])
1846                                 PanelArray[i]->SetToElevatorAltitude();
1847                 }
1848                 for (int i = 0; i < (int)DirIndicatorArray.size(); i++)
1849                 {
1850                         if (DirIndicatorArray[i])
1851                                 DirIndicatorArray[i]->SetPosition(Ogre::Vector3(DirIndicatorArray[i]->GetPosition().x, Destination, DirIndicatorArray[i]->GetPosition().z));
1852                 }
1853                 for (int i = 0; i < (int)StdDoorArray.size(); i++)
1854                 {
1855                         if (StdDoorArray[i])
1856                                 StdDoorArray[i]->Move(Ogre::Vector3(0, GetPosition().y, 0), true, false, true);
1857                 }
1858
1859                 //move sounds
1860                 mainsound->SetPosition(GetPosition());
1861                 idlesound->SetPosition(GetPosition());
1862                 MoveDoorSound(0, GetPosition(), false, false, false);
1863                 alarm->SetPosition(GetPosition());
1864                 floorbeep->SetPosition(GetPosition());
1865                 floorsound->SetPosition(GetPosition());
1866                 messagesnd->SetPosition(GetPosition());
1867                 musicsound->SetPosition(GetPosition() + MusicPosition);
1868                 for (int i = 0; i < (int)sounds.size(); i++)
1869                 {
1870                         if (sounds[i])
1871                                 sounds[i]->SetPositionY(GetPosition().y + sounds[i]->PositionOffset.y);
1872                 }
1873         }
1874
1875         //reset values if at destination floor
1876 finish:
1877         if (sbs->Verbose)
1878                 Report("resetting elevator motion values");
1879         ElevatorRate = 0;
1880         JerkRate = 0;
1881         Direction = 0;
1882         Brakes = false;
1883         Destination = 0;
1884         DistanceToTravel = 0;
1885         ElevatorStart = 0;
1886         ElevatorIsRunning = false;
1887         MoveElevator = false;
1888         IsMoving = false;
1889         Leveling = false;
1890         mainsound->Stop();
1891         motorsound->Stop();
1892         tmpDecelJerk = 0;
1893
1894         if (FinishedMove == false)
1895                 FinishMove();
1896 }
1897
1898 void Elevator::FinishMove()
1899 {
1900         //post-move operations, such as chimes, opening doors, indicator updates, etc
1901
1902         if (EmergencyStop == false)
1903         {
1904                 //the elevator is now stopped on a valid floor; set OnFloor to true
1905                 OnFloor = true;
1906                 Report("arrived at floor " + std::string(_itoa(GotoFloor, intbuffer, 10)) + " (" + sbs->GetFloor(GotoFloor)->ID + ")");
1907
1908                 //dequeue floor route
1909                 DeleteActiveRoute();
1910         }
1911
1912         //turn off interior directional indicators
1913         ActiveDirection = 0;
1914         UpdateDirectionalIndicators();
1915
1916         //update external active-direction indicators
1917         sbs->GetFloor(sbs->camera->CurrentFloor)->UpdateDirectionalIndicators(Number);
1918
1919         if (EmergencyStop == false && InspectionService == false)
1920         {
1921                 //update floor indicators on current camera floor
1922                 sbs->GetFloor(sbs->camera->CurrentFloor)->UpdateFloorIndicators(Number);
1923
1924                 UpdateFloorIndicators();
1925
1926                 //turn on objects if user is in elevator
1927                 if (sbs->ElevatorSync == true && sbs->ElevatorNumber == Number && CameraOffset < Height)
1928                 {
1929                         if (sbs->Verbose)
1930                                 Report("user in elevator - turning on objects");
1931
1932                         //turn on floor
1933                         sbs->GetFloor(GotoFloor)->Enabled(true);
1934                         sbs->GetFloor(GotoFloor)->EnableGroup(true);
1935
1936                         //Turn on sky, buildings, and landscape
1937                         sbs->EnableSkybox(true);
1938                         sbs->EnableBuildings(true);
1939                         sbs->EnableLandscape(true);
1940                         sbs->EnableExternal(true);
1941
1942                         //reset shaft doors
1943                         for (int i = 1; i <= sbs->Shafts(); i++)
1944                         {
1945                                 sbs->GetShaft(i)->EnableRange(GotoFloor, sbs->ShaftDisplayRange, false, true);
1946                                 sbs->GetShaft(i)->EnableRange(GotoFloor, sbs->ShaftDisplayRange, true, true);
1947                         }
1948                 }
1949                 else if (sbs->Verbose)
1950                         Report("user not in elevator - not turning on objects");
1951
1952                 //notify arrival and disable call button light
1953                 if (InServiceMode() == false)
1954                 {
1955                         //reverse queue if at end of current queue, and if elevator was moving in the correct direction (not moving up for a down call, etc)
1956                         if ((QueuePositionDirection == 1 && UpQueue.size() == 0 && ElevatorFloor < GotoFloor) || (QueuePositionDirection == -1 && DownQueue.size() == 0 && ElevatorFloor > GotoFloor))
1957                         {
1958                                 std::vector<int> buttons = sbs->GetFloor(GotoFloor)->GetCallButtons(Number);
1959                                 if (buttons.size() > 0)
1960                                 {
1961                                         CallButton *button =  sbs->GetFloor(GotoFloor)->CallButtonArray[buttons[0]];
1962                                         //only reverse the queue direction if no related active call is on the floor
1963                                         if ((button->UpStatus == false && QueuePositionDirection == 1) || (button->DownStatus == false && QueuePositionDirection == -1))
1964                                         {
1965                                                 if (sbs->Verbose)
1966                                                         Report("reversing queue search direction");
1967                                                 LastQueueDirection = QueuePositionDirection;
1968                                                 QueuePositionDirection = -QueuePositionDirection;
1969                                         }
1970                                 }
1971                         }
1972
1973                         if ((NotifyEarly == 0 || Notified == false) && Parking == false)
1974                                 NotifyArrival(GotoFloor);
1975
1976                         //disable call button lights
1977                         SetCallButtons(GotoFloor, GetArrivalDirection(GotoFloor), false);
1978                 }
1979
1980                 //open doors
1981                 //do not automatically open doors if in fire service phase 2
1982                 if (FireServicePhase2 == 0)
1983                 {
1984                         if (Parking == false)
1985                                 OpenDoors();
1986
1987                         PlayFloorSound();
1988                 }
1989         }
1990         else
1991         {
1992                 if (sbs->Verbose)
1993                         Report("stop complete");
1994                 EmergencyStop = false;
1995                 if (sbs->ElevatorSync == true && sbs->ElevatorNumber == Number)
1996                 {
1997                         //reset shaft doors
1998                         for (int i = 1; i <= sbs->Shafts(); i++)
1999                         {
2000                                 sbs->GetShaft(i)->EnableRange(GotoFloor, sbs->ShaftDisplayRange, false, true);
2001                                 sbs->GetShaft(i)->EnableRange(GotoFloor, sbs->ShaftDisplayRange, true, true);
2002                         }
2003                 }
2004         }
2005
2006         //update elevator's floor number
2007         ElevatorFloor = GotoFloor;
2008
2009         Parking = false;
2010         FinishedMove = true;
2011 }
2012
2013 WallObject* Elevator::AddWall(const char *name, const char *texture, float thickness, float x1, float z1, float x2, float z2, float height1, float height2, float voffset1, float voffset2, float tw, float th)
2014 {
2015         //Adds a wall with the specified dimensions
2016
2017         WallObject *wall = ElevatorMesh->CreateWallObject(this->object, name);
2018         sbs->AddWallMain(wall, name, texture, thickness, x1, z1, x2, z2, height1, height2, voffset1, voffset2, tw, th, true);
2019         return wall;
2020 }
2021
2022 WallObject* Elevator::AddFloor(const char *name, const char *texture, float thickness, float x1, float z1, float x2, float z2, float voffset1, float voffset2, float tw, float th)
2023 {
2024         //Adds a floor with the specified dimensions and vertical offset
2025
2026         WallObject *wall = ElevatorMesh->CreateWallObject(this->object, name);
2027         sbs->AddFloorMain(wall, name, texture, thickness, x1, z1, x2, z2, voffset1, voffset2, tw, th, true);
2028         return wall;
2029 }
2030
2031 Object* Elevator::AddFloorIndicator(const char *texture_prefix, const char *direction, float CenterX, float CenterZ, float width, float height, float voffset)
2032 {
2033         //Creates a floor indicator at the specified location
2034
2035         int size = (int)FloorIndicatorArray.size();
2036         FloorIndicatorArray.resize(size + 1);
2037         FloorIndicatorArray[size] = new FloorIndicator(object, Number, texture_prefix, direction, CenterX, CenterZ, width, height, voffset, true);
2038         FloorIndicatorArray[size]->SetPosition(Origin);
2039         return FloorIndicatorArray[size]->object;
2040 }
2041
2042 const Ogre::Vector3 Elevator::GetPosition()
2043 {
2044         //returns the elevator's position
2045         return ElevatorMesh->GetPosition();
2046 }
2047
2048 void Elevator::DumpQueues()
2049 {
2050         //dump both (up and down) elevator queues
2051
2052         sbs->Report("--- Elevator " + std::string(_itoa(Number, intbuffer, 10)) + " Queues ---\n");
2053         sbs->Report("Up:");
2054         for (int i = 0; i < (int)UpQueue.size(); i++)
2055                 sbs->Report(std::string(_itoa(i, intbuffer, 10)) + " - " + std::string(_itoa(UpQueue[i], intbuffer, 10)));
2056         sbs->Report("Down:");
2057         for (int i = 0; i < (int)DownQueue.size(); i++)
2058                 sbs->Report(std::string(_itoa(i, intbuffer, 10)) + " - " + std::string(_itoa(DownQueue[i], intbuffer, 10)));
2059 }
2060
2061 void Elevator::Enabled(bool value)
2062 {
2063         //shows/hides elevator
2064
2065         if (IsEnabled == value)
2066                 return;
2067
2068         //SBS_PROFILE("Elevator::Enabled");
2069         if (sbs->Verbose)
2070         {
2071                 if (value == true)
2072                         Report("enabling elevator");
2073                 else
2074                         Report("disabling elevator");
2075         }
2076
2077         ElevatorMesh->Enable(value);
2078         EnableDoors(value);
2079         IsEnabled = value;
2080 }
2081
2082 void Elevator::EnableObjects(bool value)
2083 {
2084         //enable or disable interior objects, such as floor indicators and button panels
2085
2086         //SBS_PROFILE("Elevator::EnableObjects");
2087         if (sbs->Verbose)
2088         {
2089                 if (value == true)
2090                         Report("enabling objects");
2091                 else
2092                         Report("disabling objects");
2093         }
2094
2095         //floor indicators
2096         /*for (int i = 0; i < FloorIndicatorArray.size(); i++)
2097         {
2098                 if (FloorIndicatorArray[i])
2099                         FloorIndicatorArray[i]->Enabled(value);
2100         }*/
2101
2102         //interior directional indicators
2103         //EnableDirectionalIndicators(value);
2104
2105         //models
2106         for (size_t i = 0; i < (int)ModelArray.size(); i++)
2107         {
2108                 if (ModelArray[i])
2109                         ModelArray[i]->Enable(value);
2110         }
2111
2112         //panels
2113         for (int i = 0; i < (int)PanelArray.size(); i++)
2114                 PanelArray[i]->Enabled(value);
2115
2116         //sounds
2117         for (int i = 0; i < (int)sounds.size(); i++)
2118         {
2119                 if (sounds[i])
2120                 {
2121                         if (value == false)
2122                                 sounds[i]->Stop();
2123                         else
2124                                 sounds[i]->Play();
2125                 }
2126         }
2127 }
2128
2129 bool Elevator::IsElevator(Ogre::MeshPtr test)
2130 {
2131         if (test == ElevatorMesh->MeshWrapper)
2132                 return true;
2133
2134         return false;
2135 }
2136
2137 bool Elevator::IsInElevator(const Ogre::Vector3 &position)
2138 {
2139         //determine if the given 3D position is inside the elevator
2140
2141         //SBS_PROFILE("Elevator::IsInElevator");
2142         bool inelevator = false;
2143
2144         //if last position is the same as new, return previous result
2145         if ((position.x >= (lastposition.x - 0.01)) &&
2146                 (position.y >= (lastposition.y - 0.01)) &&
2147                 (position.z >= (lastposition.z - 0.01)) &&
2148                 (position.x <= (lastposition.x + 0.01)) &&
2149                 (position.y <= (lastposition.y + 0.01)) &&
2150                 (position.z <= (lastposition.z + 0.01)) &&
2151                 checkfirstrun == false)
2152                 return lastcheckresult;
2153
2154         checkfirstrun = false;
2155
2156         if (position.y > GetPosition().y && position.y < GetPosition().y + (Height * 2))
2157         {
2158                 if (ElevatorMesh->InBoundingBox(position, false) == true)
2159                 {
2160                         if (ElevatorMesh->HitBeam(position, Ogre::Vector3::NEGATIVE_UNIT_Y, Height) >= 0)
2161                         {
2162                                 if (IsMoving == false)
2163                                 {
2164                                         //store camera offset if elevator is not moving
2165                                         CameraOffset = position.y - GetPosition().y;
2166                                 }
2167                                 else if (CameraOffset == 0)
2168                                 {
2169                                         //reposition camera with a moving elevator if the offset is 0
2170                                         if (position.y < GetPosition().y + Height)
2171                                                 CameraOffset = sbs->camera->GetHeight(); //if below ceiling, set to camera height
2172                                         else
2173                                                 CameraOffset = Height + sbs->camera->GetHeight(); //if above ceiling, set to elev height + camera height
2174                                 }
2175                                 inelevator = true;
2176                         }
2177                 }
2178                 else
2179                         CameraOffset = 0;
2180
2181                 if (position.y < GetPosition().y + Height)
2182                 {
2183                         //cache values
2184                         lastcheckresult = inelevator;
2185                         lastposition = position;
2186
2187                         return inelevator;
2188                 }
2189         }
2190
2191         //cache values
2192         lastcheckresult = false;
2193         lastposition = position;
2194
2195         return false;
2196 }
2197
2198 float Elevator::GetElevatorStart()
2199 {
2200         //returns the internal elevator starting position
2201         return ElevatorStart;
2202 }
2203
2204 float Elevator::GetDestination()
2205 {
2206         //returns the internal destination value
2207         return Destination;
2208 }
2209
2210 float Elevator::GetStoppingDistance()
2211 {
2212         //returns the internal stopping distance value
2213         return StoppingDistance;
2214 }
2215
2216 bool Elevator::GetBrakeStatus()
2217 {
2218         //returns the internal brake status value
2219         return Brakes;
2220 }
2221
2222 bool Elevator::GetEmergencyStopStatus()
2223 {
2224         //returns the internal emergency stop status
2225         return EmergencyStop;
2226 }
2227
2228 void Elevator::DumpServicedFloors()
2229 {
2230         //dump serviced floors list
2231
2232         sbs->Report("--- Elevator " + std::string(_itoa(Number, intbuffer, 10)) + "'s Serviced Floors ---\n");
2233         for (int i = 0; i < (int)ServicedFloors.size(); i++)
2234                 sbs->Report(std::string(_itoa(i, intbuffer, 10)) + " - " + std::string(_itoa(ServicedFloors[i], intbuffer, 10)));
2235 }
2236
2237 bool Elevator::AddServicedFloor(int number)
2238 {
2239         if (sbs->Verbose)
2240                 Report("adding serviced floor " + std::string(_itoa(number, intbuffer, 10)));
2241
2242         //check if floor is outside valid floor range
2243         if (sbs->IsValidFloor(number) == false)
2244         {
2245                 std::string floor = Ogre::StringConverter::toString(number);
2246                 return ReportError("AddServicedFloor: Invalid floor " + floor);
2247         }
2248
2249         if (IsServicedFloor(number) == false)
2250         {
2251                 ServicedFloors.push_back(number);
2252                 std::sort(ServicedFloors.begin(), ServicedFloors.end());
2253         }
2254         return true;
2255 }
2256
2257 void Elevator::RemoveServicedFloor(int number)
2258 {
2259         if (sbs->Verbose)
2260                 Report("removing serviced floor " + std::string(_itoa(number, intbuffer, 10)));
2261         if (IsServicedFloor(number) == true)
2262         {
2263                 for (int i = 0; i < (int)ServicedFloors.size(); i++)
2264                 {
2265                         if (ServicedFloors[i] == number)
2266                                 ServicedFloors.erase(ServicedFloors.begin() + i);
2267                 }
2268         }
2269 }
2270
2271 Object* Elevator::CreateButtonPanel(const char *texture, int rows, int columns, const char *direction, float CenterX, float CenterZ, float buttonwidth, float buttonheight, float spacingX, float spacingY, float voffset, float tw, float th)
2272 {
2273         //create a new button panel object and store the pointer
2274
2275         int index = (int)PanelArray.size();
2276         std::string number = Ogre::StringConverter::toString(index + 1);
2277         TrimString(number);
2278         PanelArray.resize(index + 1);
2279
2280         if (sbs->Verbose)
2281                 Report("creating button panel " + number);
2282
2283         PanelArray[index] = new ButtonPanel(Number, index + 1, texture, rows, columns, direction, CenterX, CenterZ, buttonwidth, buttonheight, spacingX, spacingY, voffset, tw, th);
2284         if (PanelArray[index])
2285                 return PanelArray[index]->object;
2286         else
2287                 return 0;
2288 }
2289
2290 void Elevator::UpdateFloorIndicators()
2291 {
2292         //changes the number texture on the floor indicators to the elevator's current floor
2293
2294         SBS_PROFILE("Elevator::UpdateFloorIndicators");
2295         std::string value;
2296         if (UseFloorSkipText == true && IsServicedFloor(GetFloor()) == false)
2297                 value = FloorSkipText;
2298         else
2299         {
2300                 if (DisplayFloors.size() > 0)
2301                 {
2302                         for (int i = 0; i < DisplayFloors.size(); i++)
2303                         {
2304                                 if (GetFloor() == DisplayFloors[i])
2305                                         value = sbs->GetFloor(GetFloor())->ID;
2306                         }
2307                 }
2308                 else
2309                         value = sbs->GetFloor(GetFloor())->ID;
2310         }
2311         TrimString(value);
2312
2313         if (value == "")
2314                 value = "null";
2315
2316         for (int i = 0; i < (int)FloorIndicatorArray.size(); i++)
2317         {
2318                 if (FloorIndicatorArray[i])
2319                         FloorIndicatorArray[i]->Update(value.c_str());
2320         }
2321 }
2322
2323 double Elevator::GetJerkRate()
2324 {
2325         return JerkRate;
2326 }
2327
2328 double Elevator::GetJerkPosition()
2329 {
2330         return JerkPos;
2331 }
2332
2333 void Elevator::SetFloorSkipText(const char *id)
2334 {
2335         //sets the text shown in the floor indicator while skipping floors (an express zone)
2336
2337         if (sbs->Verbose)
2338                 Report("setting floor skip text to " + std::string(id));
2339         UseFloorSkipText = true;
2340         FloorSkipText = id;
2341         TrimString(FloorSkipText);
2342 }
2343
2344 const char* Elevator::GetFloorSkipText()
2345 {
2346         //get the floor skip text
2347         return FloorSkipText.c_str();
2348 }
2349
2350 bool Elevator::IsServicedFloor(int floor)
2351 {
2352         //returns true if floor is in serviced floor list, otherwise false
2353
2354         //SBS_PROFILE("Elevator::IsServicedFloor");
2355         int index = -1;
2356         for (int i = 0; i < (int)ServicedFloors.size(); i++)
2357         {
2358                 if (ServicedFloors[i] == floor)
2359                         index = i;
2360         }
2361         if (index == -1)
2362         {
2363                 if (sbs->Verbose)
2364                         Report("Floor " + std::string(_itoa(floor, intbuffer, 10)) + " is not a serviced floor");
2365                 return false;
2366         }
2367         else
2368         {
2369                 if (sbs->Verbose)
2370                         Report("Floor " + std::string(_itoa(floor, intbuffer, 10)) + " is a serviced floor");
2371                 return true;
2372         }
2373 }
2374
2375 bool Elevator::InServiceMode()
2376 {
2377         //report if an elevator is in a service mode
2378         if (IndependentService == true || InspectionService == true || FireServicePhase1 == 1 || FireServicePhase2 != 0)
2379                 return true;
2380         else
2381                 return false;
2382 }
2383
2384 void Elevator::Go(int floor)
2385 {
2386         //go to specified floor, bypassing the queuing system
2387
2388         if (Running == false)
2389         {
2390                 Report("Elevator not running");
2391                 return;
2392         }
2393
2394         //exit if in inspection mode
2395         if (InspectionService == true)
2396         {
2397                 if (sbs->Verbose)
2398                         Report("Go: in inspection mode");
2399                 return;
2400         }
2401
2402         if (sbs->Verbose)
2403                 Report("Go: proceeding to floor " + std::string(_itoa(floor, intbuffer, 10)));
2404         GotoFloor = floor;
2405         WaitForDoors = true;
2406         CloseDoors();
2407         MoveElevator = true;
2408 }
2409
2410 void Elevator::GoToRecallFloor()
2411 {
2412         //for fire service modes; tells the elevator to go to the recall floor (or the alternate recall floor
2413         //if the other is not available)
2414
2415         if (Running == false)
2416         {
2417                 Report("Elevator not running");
2418                 return;
2419         }
2420
2421         //stop elevator if moving
2422         if (IsMoving == true)
2423                 Stop(false);
2424
2425         //reset queues
2426         ResetQueue(true, true);
2427
2428         if (RecallUnavailable == false)
2429         {
2430                 Report("Proceeding to recall floor");
2431                 if (RecallFloor > GetFloor())
2432                         AddRoute(RecallFloor, 1, false);
2433                 else
2434                         AddRoute(RecallFloor, -1, false);
2435         }
2436         else
2437         {
2438                 Report("Proceeding to alternate recall floor");
2439                 if (RecallFloor > GetFloor())
2440                         AddRoute(RecallFloorAlternate, 1, false);
2441                 else
2442                         AddRoute(RecallFloorAlternate, -1, false);
2443         }
2444 }
2445
2446 void Elevator::EnableACP(bool value)
2447 {
2448         //enable Anti-Crime Protection (ACP) mode
2449
2450         if (Running == false)
2451         {
2452                 Report("Elevator not running");
2453                 return;
2454         }
2455
2456         //exit if no change
2457         if (ACP == value)
2458         {
2459                 if (sbs->Verbose)
2460                         Report("EnableACP: mode is the same");
2461                 return;
2462         }
2463
2464         ACP = value;
2465
2466         if (value == true)
2467         {
2468                 EnableIndependentService(false);
2469                 EnableInspectionService(false);
2470                 EnableFireService1(0);
2471                 EnableFireService2(0);
2472                 Report("ACP mode enabled");
2473         }
2474         else
2475                 Report("ACP mode disabled");
2476
2477 }
2478
2479 void Elevator::EnableUpPeak(bool value)
2480 {
2481         //enable Up-Peak mode
2482
2483         if (Running == false)
2484         {
2485                 Report("Elevator not running");
2486                 return;
2487         }
2488
2489         //exit if no change
2490         if (UpPeak == value)
2491         {
2492                 if (sbs->Verbose)
2493                         Report("EnableUpPeak: mode is the same");
2494                 return;
2495         }
2496
2497         UpPeak = value;
2498
2499         if (value == true)
2500         {
2501                 EnableDownPeak(false);
2502                 EnableIndependentService(false);
2503                 EnableInspectionService(false);
2504                 EnableFireService1(0);
2505                 EnableFireService2(0);
2506                 if (IsMoving == false && GetFloor() == GetBottomFloor())
2507                 {
2508                         sbs->GetFloor(GetFloor())->SetDirectionalIndicators(Number, true, false);
2509                         SetDirectionalIndicators(true, false);
2510                         OpenDoors();
2511                 }
2512                 Report("Up Peak mode enabled");
2513         }
2514         else
2515         {
2516                 ResetDoorTimer();
2517                 Report("Up Peak mode disabled");
2518         }
2519 }
2520
2521 void Elevator::EnableDownPeak(bool value)
2522 {
2523         //enable Down-Peak mode
2524
2525         if (Running == false)
2526         {
2527                 Report("Elevator not running");
2528                 return;
2529         }
2530
2531         //exit if no change
2532         if (DownPeak == value)
2533         {
2534                 if (sbs->Verbose)
2535                         Report("EnableDownPeak: mode is the same");
2536                 return;
2537         }
2538
2539         DownPeak = value;
2540
2541         if (value == true)
2542         {
2543                 EnableUpPeak(false);
2544                 EnableIndependentService(false);
2545                 EnableInspectionService(false);
2546                 EnableFireService1(0);
2547                 EnableFireService2(0);
2548                 if (IsMoving == false && GetFloor() == GetTopFloor())
2549                 {
2550                         sbs->GetFloor(GetFloor())->SetDirectionalIndicators(Number, false, true);
2551                         SetDirectionalIndicators(false, true);
2552                         OpenDoors();
2553                 }
2554                 Report("Down Peak mode enabled");
2555         }
2556         else
2557         {
2558                 ResetDoorTimer();
2559                 Report("Down Peak mode disabled");
2560         }
2561 }
2562
2563 void Elevator::EnableIndependentService(bool value)
2564 {
2565         //enable Independent Service (ISC) mode
2566
2567         if (Running == false)
2568         {
2569                 Report("Elevator not running");
2570                 return;
2571         }
2572
2573         //exit if no change
2574         if (IndependentService == value)
2575         {
2576                 if (sbs->Verbose)
2577                         Report("EnableIndependentService: mode is the same");
2578                 return;
2579         }
2580
2581         IndependentService = value;
2582
2583         if (value == true)
2584         {
2585                 EnableACP(false);
2586                 EnableUpPeak(false);
2587                 EnableDownPeak(false);
2588                 EnableInspectionService(false);
2589                 EnableFireService1(0);
2590                 EnableFireService2(0);
2591                 ResetQueue(true, true);
2592                 EnableNudgeMode(false);
2593                 if (IsMoving == false)
2594                         OpenDoors();
2595                 Report("Independent Service mode enabled");
2596         }
2597         else
2598         {
2599                 ResetDoorTimer();
2600                 Report("Independent Service mode disabled");
2601         }
2602 }
2603
2604 void Elevator::EnableInspectionService(bool value)
2605 {
2606         //enable Inspection Service (INS) mode
2607
2608         //exit if no change
2609         if (InspectionService == value)
2610         {
2611                 if (sbs->Verbose)
2612                         Report("EnableInspectionService: mode is the same");
2613                 return;
2614         }
2615
2616         if (value == true)
2617         {
2618                 EnableACP(false);
2619                 EnableUpPeak(false);
2620                 EnableDownPeak(false);
2621                 EnableIndependentService(false);
2622                 EnableFireService1(0);
2623                 EnableFireService2(0);
2624                 EnableNudgeMode(false);
2625                 ResetQueue(true, true);
2626                 if (IsMoving == true)
2627                         Stop(false);
2628                 Report("Inspection Service mode enabled");
2629         }
2630         else
2631         {
2632                 ResetDoorTimer();
2633                 Report("Inspection Service mode disabled");
2634
2635                 UpdateFloorIndicators();
2636
2637                 //turn on objects if user is in elevator
2638                 if (sbs->ElevatorSync == true && sbs->ElevatorNumber == Number && IsMoving == false)
2639                 {
2640                         if (sbs->Verbose)
2641                                 Report("user in elevator - turning on objects");
2642
2643                         //turn on floor
2644                         sbs->GetFloor(GetFloor())->Enabled(true);
2645                         sbs->GetFloor(GetFloor())->EnableGroup(true);
2646
2647                         //Turn on sky, buildings, and landscape
2648                         sbs->EnableSkybox(true);
2649                         sbs->EnableBuildings(true);
2650                         sbs->EnableLandscape(true);
2651                         sbs->EnableExternal(true);
2652
2653                         //reset shaft doors
2654                         for (int i = 1; i <= sbs->Shafts(); i++)
2655                         {
2656                                 sbs->GetShaft(i)->EnableRange(GetFloor(), sbs->ShaftDisplayRange, false, true);
2657                                 sbs->GetShaft(i)->EnableRange(GetFloor(), sbs->ShaftDisplayRange, true, true);
2658                         }
2659                 }
2660
2661                 if (IsMoving == true)
2662                         Stop(false);
2663         }
2664
2665         InspectionService = value;
2666 }
2667
2668 void Elevator::EnableFireService1(int value)
2669 {
2670         //enable Fire Service Phase 1 mode
2671         //valid values are 0 (off), 1 (on) or 2 (bypass)
2672
2673         if (Running == false)
2674         {
2675                 Report("Elevator not running");
2676                 return;
2677         }
2678
2679         //exit if no change
2680         if (FireServicePhase1 == value)
2681         {
2682                 if (sbs->Verbose)
2683                         Report("EnableFireService1: mode is the same");
2684                 return;
2685         }
2686
2687         if (value >= 0 && value <= 2)
2688                 FireServicePhase1 = value;
2689         else
2690         {
2691                 if (sbs->Verbose)
2692                         Report("EnableFireService1: invalid value");
2693                 return;
2694         }
2695
2696         if (value > 0)
2697         {
2698                 EnableACP(false);
2699                 EnableUpPeak(false);
2700                 EnableDownPeak(false);
2701                 EnableIndependentService(false);
2702                 EnableInspectionService(false);
2703                 EnableFireService2(0);
2704                 if (value == 1)
2705                 {
2706                         Report("Fire Service Phase 1 mode set to On");
2707                         
2708                         //enable nudge mode on all doors if any are open
2709                         if (GetFloor() != RecallFloor)
2710                                 EnableNudgeMode(true);
2711
2712                         //goto recall floor
2713                         GoToRecallFloor();
2714                 }
2715                 else
2716                         Report("Fire Service Phase 1 mode set to Bypass");
2717         }
2718         else
2719         {
2720                 ResetDoorTimer();
2721                 Report("Fire Service Phase 1 mode set to Off");
2722         }
2723 }
2724
2725 void Elevator::EnableFireService2(int value)
2726 {
2727         //enable Fire Service Phase 2 mode
2728         //valid values are 0 (off), 1 (on) or 2 (hold)
2729
2730         if (Running == false)
2731         {
2732                 Report("Elevator not running");
2733                 return;
2734         }
2735
2736         //exit if no change
2737         if (FireServicePhase2 == value)
2738         {
2739                 if (sbs->Verbose)
2740                         Report("EnableFireService2: mode is the same");
2741                 return;
2742         }
2743
2744         if (value >= 0 && value <= 2)
2745                 FireServicePhase2 = value;
2746         else
2747         {
2748                 if (sbs->Verbose)
2749                         Report("EnableFireService2: invalid value");
2750                 return;
2751         }
2752
2753         if (value > 0)
2754         {
2755                 EnableACP(false);
2756                 EnableUpPeak(false);
2757                 EnableDownPeak(false);
2758                 EnableIndependentService(false);
2759                 EnableInspectionService(false);
2760                 EnableFireService1(0);
2761                 EnableNudgeMode(false);
2762                 ResetQueue(true, true);
2763                 if (value == 1)
2764                         Report("Fire Service Phase 2 mode set to On");
2765                 else
2766                 {
2767                         if (IsMoving == false)
2768                                 OpenDoors();
2769                         Report("Fire Service Phase 2 mode set to Hold");
2770                 }
2771         }
2772         else
2773         {
2774                 ResetDoorTimer();
2775                 Report("Fire Service Phase 2 mode set to Off");
2776                 GoToRecallFloor();
2777         }
2778 }
2779
2780 bool Elevator::SetRecallFloor(int floor)
2781 {
2782         //set elevator's fire recall floor
2783         if (ServicedFloors.size() == 0)
2784         {
2785                 ReportError("No serviced floors assigned");
2786                 return false;
2787         }
2788         if (IsServicedFloor(floor) == false)
2789         {
2790                 ReportError("Invalid recall floor");
2791                 return false;
2792         }
2793
2794         if (sbs->Verbose)
2795                 Report("setting recall floor to " + std::string(_itoa(floor, intbuffer, 10)));
2796         RecallFloor = floor;
2797         RecallSet = true;
2798         return true;
2799 }
2800
2801 bool Elevator::SetAlternateRecallFloor(int floor)
2802 {
2803         //set elevator's alternate fire recall floor
2804         if (ServicedFloors.size() == 0)
2805         {
2806                 ReportError("No serviced floors assigned");
2807                 return false;
2808         }
2809         if (IsServicedFloor(floor) == false)
2810         {
2811                 ReportError("Invalid alternate recall floor");
2812                 return false;
2813         }
2814
2815         if (sbs->Verbose)
2816                 Report("setting alternate recall floor to " + std::string(_itoa(floor, intbuffer, 10)));
2817         RecallFloorAlternate = floor;
2818         RecallAltSet = true;
2819         return true;
2820 }
2821
2822 bool Elevator::SetACPFloor(int floor)
2823 {
2824         //set elevator's ACP floor
2825         if (ServicedFloors.size() == 0)
2826         {
2827                 ReportError("No serviced floors assigned");
2828                 return false;
2829         }
2830         if (IsServicedFloor(floor) == false)
2831         {
2832                 ReportError("Invalid ACP floor");
2833                 return false;
2834         }
2835
2836         if (sbs->Verbose)
2837                 Report("setting ACP floor to " + std::string(_itoa(floor, intbuffer, 10)));
2838         ACPFloor = floor;
2839         ACPFloorSet = true;
2840         return true;
2841 }
2842
2843 bool Elevator::MoveUp()
2844 {
2845         if (Running == false)
2846         {
2847                 Report("Elevator not running");
2848                 return false;
2849         }
2850
2851         //moves elevator upwards if in Inspection Service mode
2852         if (InspectionService == false)
2853         {
2854                 Report("Not in inspection service mode");
2855                 return false;
2856         }
2857
2858         //make sure Go button is on
2859         if (ManualGo == false)
2860         {
2861                 if (sbs->Verbose)
2862                         Report("MoveUp: go button is off");
2863                 return false;
2864         }
2865
2866         if (IsMoving == true)
2867         {
2868                 if (sbs->Verbose)
2869                         Report("MoveUp: already moving");
2870                 return false;
2871         }
2872
2873         //set direction
2874         Direction = 1;
2875         MoveElevator = true;
2876         if (sbs->Verbose)
2877                 Report("MoveUp: moving elevator");
2878         return true;
2879 }
2880
2881 bool Elevator::MoveDown()
2882 {
2883         if (Running == false)
2884         {
2885                 Report("Elevator not running");
2886                 return false;
2887         }
2888
2889         //moves elevator downwards if in Inspection Service mode
2890         if (InspectionService == false)
2891         {
2892                 Report("Not in inspection service mode");
2893                 return false;
2894         }
2895
2896         //make sure Go button is on
2897         if (ManualGo == false)
2898         {
2899                 if (sbs->Verbose)
2900                         Report("MoveDown: go button is off");
2901                 return false;
2902         }
2903
2904         if (IsMoving == true)
2905         {
2906                 if (sbs->Verbose)
2907                         Report("MoveDown: already moving");
2908                 return false;
2909         }
2910
2911         //set direction
2912         Direction = -1;
2913         MoveElevator = true;
2914         if (sbs->Verbose)
2915                 Report("MoveDown: moving elevator");
2916         return true;
2917 }
2918
2919 void Elevator::SetGoButton(bool value)
2920 {
2921         //sets the value of the Go button (for Inspection Service mode)
2922
2923         if (Running == false)
2924         {
2925                 Report("Elevator not running");
2926                 return;
2927         }
2928
2929         if (InspectionService == false)
2930                 return;
2931
2932         if (ManualGo == true && value == false)
2933                 Stop(false);
2934
2935         ManualGo = value;
2936
2937         if (sbs->Verbose)
2938         {
2939                 if (value == true)
2940                         Report("setting go button status to true");
2941                 else
2942                         Report("setting go button status to false");
2943         }
2944 }
2945
2946 int Elevator::GetTopFloor()
2947 {
2948         //returns highest serviced floor
2949         return ServicedFloors[ServicedFloors.size() - 1];
2950 }
2951
2952 int Elevator::GetBottomFloor()
2953 {
2954         //returns lowest serviced floor
2955         return ServicedFloors[0];
2956 }
2957
2958 void Elevator::AddDirectionalIndicators(bool relative, bool active_direction, bool single, bool vertical, const char *BackTexture, const char *uptexture, const char *uptexture_lit, const char *downtexture, const char *downtexture_lit, float CenterX, float CenterZ, float voffset, const char *direction, float BackWidth, float BackHeight, bool ShowBack, float tw, float th)
2959 {
2960         //create external directional indicators on all serviced floors
2961
2962         if (sbs->Verbose)
2963                 Report("adding directional indicators");
2964
2965         for (size_t i = 0; i < ServicedFloors.size(); i++)
2966                 sbs->GetFloor(ServicedFloors[i])->AddDirectionalIndicator(Number, relative, active_direction, single, vertical, BackTexture, uptexture, uptexture_lit, downtexture, downtexture_lit, CenterX, CenterZ, voffset, direction, BackWidth, BackHeight, ShowBack, tw, th);
2967 }
2968
2969 Object* Elevator::AddDirectionalIndicator(bool active_direction, bool single, bool vertical, const char *BackTexture, const char *uptexture, const char *uptexture_lit, const char *downtexture, const char *downtexture_lit, float CenterX, float CenterZ, float voffset, const char *direction, float BackWidth, float BackHeight, bool ShowBack, float tw, float th)
2970 {
2971         //create a directional indicator inside the elevator
2972
2973         if (sbs->Verbose)
2974                 Report("adding interior directional indicator");
2975
2976         float x = Origin.x + CenterX;
2977         float z = Origin.z + CenterZ;
2978
2979         int index = (int)DirIndicatorArray.size();
2980         DirIndicatorArray.resize(index + 1);
2981         DirIndicatorArray[index] = new DirectionalIndicator(object, Number, 0, active_direction, single, vertical, BackTexture, uptexture, uptexture_lit, downtexture, downtexture_lit, x, z, voffset, direction, BackWidth, BackHeight, ShowBack, tw, th, true);
2982         DirIndicatorArray[index]->SetPosition(Ogre::Vector3(DirIndicatorArray[index]->GetPosition().x, sbs->GetFloor(OriginFloor)->GetBase(), DirIndicatorArray[index]->GetPosition().z));
2983         return DirIndicatorArray[index]->object;
2984 }
2985
2986 void Elevator::SetDirectionalIndicators(bool UpLight, bool DownLight)
2987 {
2988         //set light status of interior directional indicators
2989
2990         for (int i = 0; i < (int)DirIndicatorArray.size(); i++)
2991         {
2992                 if (DirIndicatorArray[i])
2993                 {
2994                         if (DirIndicatorArray[i]->ActiveDirection == false)
2995                         {
2996                                 DirIndicatorArray[i]->DownLight(DownLight);
2997                                 DirIndicatorArray[i]->UpLight(UpLight);
2998                         }
2999                 }
3000         }
3001 }
3002
3003 void Elevator::UpdateDirectionalIndicators()
3004 {
3005         //updates all interior active direction indicators
3006
3007         for (int i = 0; i < (int)DirIndicatorArray.size(); i++)
3008         {
3009                 if (DirIndicatorArray[i])
3010                 {
3011                         if (DirIndicatorArray[i]->ActiveDirection == true)
3012                         {
3013                                 if (ActiveDirection == 1)
3014                                 {
3015                                         DirIndicatorArray[i]->DownLight(false);
3016                                         DirIndicatorArray[i]->UpLight(true);
3017                                 }
3018                                 if (ActiveDirection == 0)
3019                                 {
3020                                         DirIndicatorArray[i]->DownLight(false);
3021                                         DirIndicatorArray[i]->UpLight(false);
3022                                 }
3023                                 if (ActiveDirection == -1)
3024                                 {
3025                                         DirIndicatorArray[i]->DownLight(true);
3026                                         DirIndicatorArray[i]->UpLight(false);
3027                                 }
3028                         }
3029                 }
3030         }
3031 }
3032
3033 void Elevator::EnableDirectionalIndicators(bool value)
3034 {
3035         //turn on/off all interior directional indicators
3036
3037         if (sbs->Verbose)
3038         {
3039                 if (value == true)
3040                         Report("enabling interior directional indicators");
3041                 else
3042                         Report("disabling interior directional indicators");
3043         }
3044
3045         for (int i = 0; i < (int)DirIndicatorArray.size(); i++)
3046         {
3047                 if (DirIndicatorArray[i])
3048                         DirIndicatorArray[i]->Enabled(value);
3049         }
3050 }
3051
3052 ElevatorDoor* Elevator::GetDoor(int number)
3053 {
3054         //get elevator door object
3055
3056         //return cached check if number is the same
3057         if (lastdoor_number == number)
3058                 return lastdoor_result;
3059
3060         if (number > 0 && number <= (int)DoorArray.size())
3061         {
3062                 if (DoorArray[number - 1])
3063                 {
3064                         lastdoor_result = DoorArray[number - 1];
3065                         lastdoor_number = number;
3066                         return lastdoor_result;
3067                 }
3068         }
3069         return 0;
3070 }
3071
3072 void Elevator::OpenDoorsEmergency(int number, int whichdoors, int floor)
3073 {
3074         //Simulates manually prying doors open.
3075         //Slowly opens the elevator doors no matter where elevator is.
3076         //If lined up with shaft doors, then opens the shaft doors also
3077
3078         //WhichDoors is the doors to move:
3079         //1 = both shaft and elevator doors
3080         //2 = only elevator doors
3081         //3 = only shaft doors
3082
3083         OpenDoors(number, whichdoors, floor, true);
3084 }
3085
3086 void Elevator::CloseDoorsEmergency(int number, int whichdoors, int floor)
3087 {
3088         //Simulates manually closing doors.
3089         //Slowly closes the elevator doors no matter where elevator is.
3090         //If lined up with shaft doors, then closes the shaft doors also
3091
3092         //WhichDoors is the doors to move:
3093         //1 = both shaft and elevator doors
3094         //2 = only elevator doors
3095         //3 = only shaft doors
3096
3097         CloseDoors(number, whichdoors, floor, true);
3098 }
3099
3100 void Elevator::OpenDoors(int number, int whichdoors, int floor, bool manual)
3101 {
3102         //Opens elevator doors
3103
3104         //if manual is true, then it simulates manually prying doors open,
3105         //Slowly opens the elevator doors no matter where elevator is,
3106         //and if lined up with shaft doors, then opens the shaft doors also
3107
3108         //WhichDoors is the doors to move:
3109         //1 = both shaft and elevator doors
3110         //2 = only elevator doors
3111         //3 = only shaft doors
3112
3113         int start, end;
3114         if (number == 0)
3115         {
3116                 start = 1;
3117                 end = NumDoors;
3118         }
3119         else
3120         {
3121                 start = number;
3122                 end = number;
3123         }
3124         for (int i = start; i <= end; i++)
3125         {
3126                 if (GetDoor(i))
3127                         GetDoor(i)->OpenDoors(whichdoors, floor, manual);
3128                 else
3129                         Report("Invalid door " + std::string(_itoa(i, intbuffer, 10)));
3130         }
3131 }
3132
3133 void Elevator::CloseDoors(int number, int whichdoors, int floor, bool manual)
3134 {
3135         //Closes elevator doors
3136
3137         //WhichDoors is the doors to move:
3138         //1 = both shaft and elevator doors
3139         //2 = only elevator doors
3140         //3 = only shaft doors
3141
3142         int start, end;
3143         if (number == 0)
3144         {
3145                 start = 1;
3146                 end = NumDoors;
3147         }
3148         else
3149         {
3150                 start = number;
3151                 end = number;
3152         }
3153         for (int i = start; i <= end; i++)
3154         {
3155                 if (GetDoor(i))
3156                         GetDoor(i)->CloseDoors(whichdoors, floor, manual);
3157                 else
3158                         Report("Invalid door " + std::string(_itoa(i, intbuffer, 10)));
3159         }
3160 }
3161
3162 void Elevator::StopDoors(int number)
3163 {
3164         //stops doors that are currently moving; can only be used for manual/emergency movements
3165         //this basically just resets the door internals
3166
3167         int start, end;
3168         if (number == 0)
3169         {
3170                 start = 1;
3171                 end = NumDoors;
3172         }
3173         else
3174         {
3175                 start = number;
3176                 end = number;
3177         }
3178         for (int i = start; i <= end; i++)
3179         {
3180                 if (GetDoor(i))
3181                         GetDoor(i)->StopDoors();
3182                 else
3183                         Report("Invalid door " + std::string(_itoa(i, intbuffer, 10)));
3184         }
3185 }
3186
3187 Object* Elevator::AddDoors(int number, const char *lefttexture, const char *righttexture, float thickness, float CenterX, float CenterZ, float width, float height, bool direction, float tw, float th)
3188 {
3189         //adds elevator doors specified at a relative central position (off of elevator origin)
3190         //if direction is false, doors are on the left/right side; otherwise front/back
3191
3192         if (GetDoor(number))
3193                 return GetDoor(number)->AddDoors(lefttexture, righttexture, thickness, CenterX, CenterZ, width, height, direction, tw, th);
3194         else
3195                 ReportError("Invalid door " + std::string(_itoa(number, intbuffer, 10)));
3196         return 0;
3197 }
3198
3199 bool Elevator::AddShaftDoors(int number, const char *lefttexture, const char *righttexture, float thickness, float CenterX, float CenterZ, float tw, float th)
3200 {
3201         //adds shaft's elevator doors specified at a relative central position (off of elevator origin)
3202         //uses some parameters (width, height, direction) from AddDoors function
3203
3204         if (GetDoor(number))
3205                 return GetDoor(number)->AddShaftDoors(lefttexture, righttexture, thickness, CenterX, CenterZ, tw, th);
3206         else
3207                 ReportError("Invalid door " + std::string(_itoa(number, intbuffer, 10)));
3208         return false;
3209 }
3210
3211 Object* Elevator::AddShaftDoor(int floor, int number, const char *lefttexture, const char *righttexture, float tw, float th)
3212 {
3213         //adds a single elevator shaft door on the specified floor, with position and thickness parameters first specified
3214         //by the SetShaftDoors command.
3215
3216         int index = -1;
3217         for (int i = 0; i < (int)ServicedFloors.size(); i++)
3218         {
3219                 if (ServicedFloors[i] == floor)
3220                         index = i;
3221         }
3222
3223         if (index != -1 && GetDoor(number))
3224                 return GetDoor(number)->AddShaftDoor(floor, lefttexture, righttexture, tw, th);
3225         else
3226                 return 0;
3227 }
3228
3229 void Elevator::ShaftDoorsEnabled(int number, int floor, bool value)
3230 {
3231         //turns shaft elevator doors on/off
3232
3233         SBS_PROFILE("Elevator::ShaftDoorsEnabled");
3234         int start, end;
3235         if (number == 0)
3236         {
3237                 start = 1;
3238                 end = NumDoors;
3239         }
3240         else
3241         {
3242                 start = number;
3243                 end = number;
3244         }
3245         for (int i = start; i <= end; i++)
3246         {
3247                 ElevatorDoor *door = GetDoor(i);
3248                 if (door)
3249                         door->ShaftDoorsEnabled(floor, value);
3250                 else
3251                         Report("Invalid door " + std::string(_itoa(i, intbuffer, 10)));
3252         }
3253 }
3254
3255 void Elevator::ShaftDoorsEnabledRange(int number, int floor, int range)
3256 {
3257         //turn on a range of floors
3258         //if range is 3, show shaft door on current floor (floor), and 1 floor below and above (3 total floors)
3259         //if range is 1, show door on only the current floor (floor)
3260
3261         SBS_PROFILE("Elevator::ShaftDoorsEnabledRange");
3262         int start, end;
3263         if (number == 0)
3264         {
3265                 start = 1;
3266                 end = NumDoors;
3267         }
3268         else
3269         {
3270                 start = number;
3271                 end = number;
3272         }
3273         for (int i = start; i <= end; i++)
3274         {
3275                 ElevatorDoor *door = GetDoor(i);
3276                 if (door)
3277                         door->ShaftDoorsEnabledRange(floor, range);
3278                 else
3279                         Report("Invalid door " + std::string(_itoa(i, intbuffer, 10)));
3280         }
3281 }
3282
3283 bool Elevator::AreDoorsOpen(int number)
3284 {
3285         //returns the internal door state
3286
3287         SBS_PROFILE("Elevator::AreDoorsOpen");
3288         int start, end;
3289         if (number == 0)
3290         {
3291                 start = 1;
3292                 end = NumDoors;
3293         }
3294         else
3295         {
3296                 start = number;
3297                 end = number;
3298         }
3299         for (int i = start; i <= end; i++)
3300         {
3301                 ElevatorDoor *door = GetDoor(i);
3302                 if (door)
3303                 {
3304                         if (door->AreDoorsOpen() == true)
3305                                 return true;
3306                 }
3307                 else
3308                         Report("Invalid door " + std::string(_itoa(i, intbuffer, 10)));
3309         }
3310         return false;
3311 }
3312
3313 bool Elevator::AreShaftDoorsOpen(int number, int floor)
3314 {
3315         //returns the internal shaft door state
3316
3317         SBS_PROFILE("Elevator::AreShaftDoorsOpen");
3318         ElevatorDoor *door = GetDoor(number);
3319         if (door)
3320                 return door->AreShaftDoorsOpen(floor);
3321         else
3322                 Report("Invalid door " + std::string(_itoa(number, intbuffer, 10)));
3323         return false;
3324 }
3325
3326 void Elevator::Chime(int number, int floor, bool direction)
3327 {
3328         //play chime sound on specified floor
3329
3330         SBS_PROFILE("Elevator::Chime");
3331         int start, end;
3332         if (number == 0)
3333         {
3334                 start = 1;
3335                 end = NumDoors;
3336         }
3337         else
3338         {
3339                 start = number;
3340                 end = number;
3341         }
3342         for (int i = start; i <= end; i++)
3343         {
3344                 ElevatorDoor *door = GetDoor(i);
3345                 if (door)
3346                         door->Chime(floor, direction);
3347                 else
3348                         Report("Invalid door " + std::string(_itoa(i, intbuffer, 10)));
3349         }
3350 }
3351
3352 void Elevator::ResetDoorTimer(int number)
3353 {
3354         //reset elevator door timer
3355
3356         int start, end;
3357         if (number == 0)
3358         {
3359                 start = 1;
3360                 end = NumDoors;
3361         }
3362         else
3363         {
3364                 start = number;
3365                 end = number;
3366         }
3367         for (int i = start; i <= end; i++)
3368         {
3369                 ElevatorDoor *door = GetDoor(i);
3370                 if (door)
3371                         door->ResetDoorTimer();
3372                 else
3373                         Report("Invalid door " + std::string(_itoa(i, intbuffer, 10)));
3374         }
3375 }
3376
3377 bool Elevator::DoorsStopped(int number)
3378 {
3379         int start, end;
3380         if (number == 0)
3381         {
3382                 start = 1;
3383                 end = NumDoors;
3384         }
3385         else
3386         {
3387                 start = number;
3388                 end = number;
3389         }
3390         for (int i = start; i <= end; i++)
3391         {
3392                 ElevatorDoor *door = GetDoor(i);
3393                 if (door)
3394                         return door->DoorsStopped();
3395                 else
3396                         Report("Invalid door " + std::string(_itoa(i, intbuffer, 10)));
3397         }
3398         return false;
3399 }
3400
3401 bool Elevator::CheckOpenDoor()
3402 {
3403         //check each door's OpenDoor value and return true if any are not zero, or
3404         //false if all are zero
3405
3406         for (int i = 1; i <= NumDoors; i++)
3407         {
3408                 ElevatorDoor *door = GetDoor(i);
3409                 if (door)
3410                 {
3411                         if (door->OpenDoor != 0)
3412                                 return true;
3413                 }
3414         }
3415         return false;
3416 }
3417
3418 void Elevator::MoveDoors(int number, const Ogre::Vector3 position, bool relative_x, bool relative_y, bool relative_z)
3419 {
3420         //move all doors
3421
3422         SBS_PROFILE("Elevator::MoveDoors");
3423         if (number == 0)
3424         {
3425                 for (int i = 1; i <= NumDoors; i++)
3426                 {
3427                         if (GetDoor(i))
3428                                 GetDoor(i)->Move(position, relative_x, relative_y, relative_z);
3429                 }
3430         }
3431         else
3432         {
3433                 if (GetDoor(number))
3434                         GetDoor(number)->Move(position, relative_x, relative_y, relative_z);
3435         }
3436 }
3437
3438 void Elevator::EnableDoors(bool value)
3439 {
3440         //enable/disable all doors
3441
3442         SBS_PROFILE("Elevator::EnableDoors");
3443         if (sbs->Verbose)
3444         {
3445                 if (value == true)
3446                         Report("enabling doors");
3447                 else
3448                         Report("disabling doors");
3449         }
3450
3451         for (int i = 1; i <= NumDoors; i++)
3452         {
3453                 ElevatorDoor *door = GetDoor(i);
3454                 if (door)
3455                         door->Enabled(value);
3456         }
3457 }
3458
3459 void Elevator::MoveDoorSound(int number, const Ogre::Vector3 position, bool relative_x, bool relative_y, bool relative_z)
3460 {
3461         //move all doors
3462
3463         if (number == 0)
3464         {
3465                 for (int i = 1; i <= NumDoors; i++)
3466                 {
3467                         if (GetDoor(i))
3468                                 GetDoor(i)->MoveSound(position, relative_x, relative_y, relative_z);
3469                 }
3470         }
3471         else
3472         {
3473                 if (GetDoor(number))
3474                         GetDoor(number)->MoveSound(position, relative_x, relative_y, relative_z);
3475         }
3476 }
3477
3478 void Elevator::SetShaftDoors(int number, float thickness, float CenterX, float CenterZ)
3479 {
3480         if (number == 0)
3481         {
3482                 for (int i = 1; i <= NumDoors; i++)
3483                 {
3484                         if (GetDoor(i))
3485                                 GetDoor(i)->SetShaftDoors(thickness, CenterX, CenterZ);
3486                 }
3487         }
3488         else
3489         {
3490                 if (GetDoor(number))
3491                         GetDoor(number)->SetShaftDoors(thickness, CenterX, CenterZ);
3492         }
3493 }
3494
3495 bool Elevator::AddFloorSigns(int door_number, bool relative, const char *texture_prefix, const char *direction, float CenterX, float CenterZ, float width, float height, float voffset)
3496 {
3497         //adds floor signs at the specified position and direction for each serviced floor,
3498         //depending on if the given door number services the floor or not (unless door_number is 0)
3499
3500         float x, z;
3501         if (relative == true)
3502         {
3503                 x = Origin.x + CenterX;
3504                 z = Origin.z + CenterZ;
3505         }
3506         else
3507         {
3508                 x = CenterX;
3509                 z = CenterZ;
3510         }
3511
3512         //make sure specified door exists before continuing
3513         if (door_number != 0)
3514         {
3515                 if (DoorExists(door_number) == false)
3516                 {
3517                         std::string doornum = Ogre::StringConverter::toString(door_number);
3518                         ReportError("AddFloorSigns: door " + doornum + " does not exist");
3519                         return false;
3520                 }
3521         }
3522
3523         bool autosize_x, autosize_y;
3524         sbs->GetAutoSize(autosize_x, autosize_y);
3525         sbs->SetAutoSize(false, false);
3526
3527         for (int i = 0; i < (int)ServicedFloors.size(); i++)
3528         {
3529                 bool door_result = false;
3530                 if (door_number != 0)
3531                         door_result = GetDoor(door_number)->ShaftDoorsExist(ServicedFloors[i]);
3532
3533                 if (door_number == 0 || door_result == true)
3534                 {
3535                         std::string texture = texture_prefix + sbs->GetFloor(ServicedFloors[i])->ID;
3536                         std::string tmpdirection = direction;
3537                         SetCase(tmpdirection, false);
3538
3539                         if (tmpdirection == "front" || tmpdirection == "left")
3540                                 sbs->DrawWalls(true, false, false, false, false, false);
3541                         else
3542                                 sbs->DrawWalls(false, true, false, false, false, false);
3543
3544                         if (tmpdirection == "front" || tmpdirection == "back")
3545                                 sbs->GetFloor(ServicedFloors[i])->AddWall("Floor Sign", texture.c_str(), 0, x - (width / 2), z, x + (width / 2), z, height, height, voffset, voffset, 1, 1, false);
3546                         else
3547                                 sbs->GetFloor(ServicedFloors[i])->AddWall("Floor Sign", texture.c_str(), 0, x, z - (width / 2), x, z + (width / 2), height, height, voffset, voffset, 1, 1, false);
3548                         sbs->ResetWalls();
3549                 }
3550         }
3551         sbs->SetAutoSize(autosize_x, autosize_y);
3552         return true;
3553 }
3554
3555 void Elevator::SetCallButtons(int floor, bool direction, bool value)
3556 {
3557         //sets light status of all associated call buttons on the specified floor
3558         //for direction, true is up and false is down
3559
3560         //get call buttons associated with this elevator
3561         if (sbs->Verbose)
3562                 Report("SetCallButtons: getting associated call buttons");
3563
3564         std::vector<int> buttons = sbs->GetFloor(floor)->GetCallButtons(Number);
3565
3566         for (int i = 0; i < (int)buttons.size(); i++)
3567         {
3568                 CallButton *button = 0;
3569                 if ((int)sbs->GetFloor(floor)->CallButtonArray.size() > buttons[i])
3570                         button = sbs->GetFloor(floor)->CallButtonArray[buttons[i]];
3571                 if (button)
3572                 {
3573                         if (direction == true)
3574                                 button->UpLight(value);
3575                         else
3576                                 button->DownLight(value);
3577                 }
3578         }
3579
3580 }
3581
3582 bool Elevator::IsIdle()
3583 {
3584         //return true if elevator is idle (not moving, doors are closed (unless in a peak mode) and not moving)
3585         if (MoveElevator == false && (AreDoorsOpen() == false || UpPeak == true || DownPeak == true) && CheckOpenDoor() == false && Running == true)
3586                 return true;
3587         else
3588                 return false;
3589 }
3590
3591 void Elevator::ResetQueue(bool up, bool down)
3592 {
3593         //reset queues
3594         if (up == true)
3595         {
3596                 if (sbs->Verbose)
3597                         Report("QueueReset: resetting up queue");
3598                 UpQueue.clear();
3599         }
3600         if (down == true)
3601         {
3602                 if (sbs->Verbose)
3603                         Report("QueueReset: resetting down queue");
3604                 DownQueue.clear();
3605         }
3606
3607         //turn off button lights
3608         if (sbs->Verbose)
3609                 Report("QueueReset: turning off button lights for queue reset");
3610         for (int i = 0; i < (int)ServicedFloors.size(); i++)
3611         {
3612                 for (int j = 0; j < (int)PanelArray.size(); j++)
3613                         PanelArray[j]->ChangeLight(ServicedFloors[i], false);
3614         }
3615 }
3616
3617 void Elevator::SetBeepSound(const char *filename)
3618 {
3619         //set sound used for floor beeps
3620         if (sbs->Verbose)
3621                 Report("setting beep sound");
3622         BeepSound = filename;
3623         TrimString(BeepSound);
3624         UseFloorBeeps = true;
3625 }
3626
3627 void Elevator::SetFloorSound(const char *prefix)
3628 {
3629         //set prefix of floor sound
3630         if (sbs->Verbose)
3631                 Report("setting floor sound");
3632         FloorSound = prefix;
3633         TrimString(FloorSound);
3634         UseFloorSounds = true;
3635 }
3636
3637 void Elevator::SetMessageSound(bool direction, const char *filename)
3638 {
3639         //if direction is true, set up message sound; otherwise set down message sound
3640
3641         if (direction == true)
3642         {
3643                 if (sbs->Verbose)
3644                         Report("setting up message sound");
3645                 UpMessageSound = filename;
3646                 TrimString(UpMessageSound);
3647         }
3648         else
3649         {
3650                 if (sbs->Verbose)
3651                         Report("setting down message sound");
3652                 DownMessageSound = filename;
3653                 TrimString(DownMessageSound);
3654         }
3655         UseMessageSounds = true;
3656 }
3657
3658 Object* Elevator::AddSound(const char *name, const char *filename, Ogre::Vector3 position, int volume, int speed, float min_distance, float max_distance, float dir_radiation, Ogre::Vector3 direction)
3659 {
3660         //create a looping sound object
3661         sounds.resize(sounds.size() + 1);
3662         Sound *sound = sounds[sounds.size() - 1];
3663         sound = new Sound(this->object, name, false);
3664
3665         //set parameters and play sound
3666         sound->PositionOffset = position;
3667         sound->SetPosition(Origin + position);
3668         sound->SetDirection(direction);
3669         sound->SetVolume(volume);
3670         sound->SetSpeed(speed);
3671         sound->SetDistances(min_distance, max_distance);
3672         sound->SetDirection(direction);
3673         sound->SetDirectionalRadiation(dir_radiation);
3674         sound->Load(filename);
3675         sound->Loop(true);
3676         sound->Play();
3677
3678         return sound->object;
3679 }
3680
3681 void Elevator::DeleteActiveRoute()
3682 {
3683         if (Running == false)
3684         {
3685                 Report("Elevator not running");
3686                 return;
3687         }
3688
3689         //deletes the active route
3690         if (sbs->Verbose)
3691                 Report("deleting active route");
3692         DeleteRoute(ActiveCallFloor, ActiveCallDirection);
3693         ActiveCallFloor = 0;
3694         ActiveCallDirection = 0;
3695 }
3696
3697 bool Elevator::IsQueueActive()
3698 {
3699         if (QueuePositionDirection != 0)
3700                 return true;
3701         return false;
3702 }
3703
3704 bool Elevator::BeyondDecelMarker(int direction, float destination)
3705 {
3706         //return true if elevator is beyond the deceleration marker for the specified direction
3707         //directions are 1 for up, -1 for down
3708
3709         if (direction == 1)
3710         {
3711                 if (((GetPosition().y + (ElevatorRate * sbs->delta)) > (destination - StoppingDistance)))
3712                         return true;
3713         }
3714         if (direction == -1)
3715         {
3716                 if (((GetPosition().y - (ElevatorRate * sbs->delta)) < (destination + StoppingDistance)))
3717                         return true;
3718         }
3719         return false;
3720 }
3721
3722 void Elevator::Report(std::string message)
3723 {
3724         //general reporting function
3725         sbs->Report("Elevator " + std::string(_itoa(Number, intbuffer, 10)) + ": " + message);
3726
3727 }
3728
3729 bool Elevator::ReportError(std::string message)
3730 {
3731         //general reporting function
3732         return sbs->ReportError("Elevator " + std::string(_itoa(Number, intbuffer, 10)) + ": " + message);
3733 }
3734
3735 Object* Elevator::AddDoorComponent(int number, const char *name, const char *texture, const char *sidetexture, float thickness, const char *direction, float OpenSpeed, float CloseSpeed, float x1, float z1, float x2, float z2, float height, float voffset, float tw, float th, float side_tw, float side_th)
3736 {
3737         //adds an elevator door component to the specified door at a relative central position (off of elevator origin)
3738
3739         if (GetDoor(number))
3740                 return GetDoor(number)->AddDoorComponent(name, texture, sidetexture, thickness, direction, OpenSpeed, CloseSpeed, x1, z1, x2, z2, height, voffset, tw, th, side_tw, side_th);
3741         else
3742                 Report("Invalid door " + std::string(_itoa(number, intbuffer, 10)));
3743         return 0;
3744 }
3745
3746 Object* Elevator::AddShaftDoorComponent(int number, int floor, const char *name, const char *texture, const char *sidetexture, float thickness, const char *direction, float OpenSpeed, float CloseSpeed, float x1, float z1, float x2, float z2, float height, float voffset, float tw, float th, float side_tw, float side_th)
3747 {
3748         //adds a single elevator shaft door component on the specified floor
3749
3750         int index = -1;
3751         for (int i = 0; i < (int)ServicedFloors.size(); i++)
3752         {
3753                 if (ServicedFloors[i] == floor)
3754                         index = i;
3755         }
3756         if (index != -1 && GetDoor(number))
3757                 return GetDoor(number)->AddShaftDoorComponent(floor, name, texture, sidetexture, thickness, direction, OpenSpeed, CloseSpeed, x1, z1, x2, z2, height, voffset, tw, th, side_tw, side_th);
3758         else
3759                 return 0;
3760 }
3761
3762 void Elevator::AddShaftDoorsComponent(int number, const char *name, const char *texture, const char *sidetexture, float thickness, const char *direction, float OpenSpeed, float CloseSpeed, float x1, float z1, float x2, float z2, float height, float voffset, float tw, float th, float side_tw, float side_th)
3763 {
3764         //adds shaft's elevator door components specified at a relative central position (off of elevator origin)
3765
3766         if (GetDoor(number))
3767                 GetDoor(number)->AddShaftDoorsComponent(name, texture, sidetexture, thickness, direction, OpenSpeed, CloseSpeed, x1, z1, x2, z2, height, voffset, tw, th, side_tw, side_th);
3768         else
3769                 Report("Invalid door " + std::string(_itoa(number, intbuffer, 10)));
3770 }
3771
3772 Object* Elevator::FinishDoors(int number)
3773 {
3774         //finishes elevator door
3775         if (GetDoor(number))
3776                 return GetDoor(number)->FinishDoors();
3777         else
3778                 Report("Invalid door " + std::string(_itoa(number, intbuffer, 10)));
3779         return 0;
3780 }
3781
3782 Object* Elevator::FinishShaftDoor(int number, int floor)
3783 {
3784         //finishes a single shaft door
3785
3786         int index = -1;
3787         for (int i = 0; i < (int)ServicedFloors.size(); i++)
3788         {
3789                 if (ServicedFloors[i] == floor)
3790                         index = i;
3791         }
3792         if (index != -1 && GetDoor(number))
3793                 return GetDoor(number)->FinishShaftDoor(floor);
3794         else
3795                 return 0;
3796 }
3797
3798 bool Elevator::FinishShaftDoors(int number)
3799 {
3800         //finishes all shaft doors
3801         if (GetDoor(number))
3802                 return GetDoor(number)->FinishShaftDoors();
3803         else
3804                 ReportError("Invalid door " + std::string(_itoa(number, intbuffer, 10)));
3805         return false;
3806 }
3807
3808 void Elevator::Timer::Notify()
3809 {
3810         if (elevator->IsRunning() == false)
3811                 return;
3812
3813         SBS_PROFILE("Elevator::Timer::Notify");
3814         if (type == 0)
3815         {
3816                 //parking timer
3817
3818                 if (elevator->ParkingDelay > 0 && elevator->IsIdle() == true)
3819                 {
3820                         int floor = elevator->GetFloor();
3821                         if (elevator->ParkingFloor > floor)
3822                         {
3823                                 elevator->AddRoute(elevator->ParkingFloor, 1, false);
3824                                 elevator->Parking = true;
3825                         }
3826                         else if (elevator->ParkingFloor < floor)
3827                         {
3828                                 elevator->AddRoute(elevator->ParkingFloor, -1, false);
3829                                 elevator->Parking = true;
3830                         }
3831                         Stop();
3832                 }
3833         }
3834         else if (type == 1 && elevator->RandomActivity == true)
3835         {
3836                 //random call timer
3837                 
3838                 RandomGen rnd_main(time(0) + elevator->Number);
3839                 RandomGen rnd_floor(sbs->GetRunTime() + elevator->Number);
3840
3841                 int num, floor;
3842
3843                 //get call probability
3844                 if (elevator->RandomProbability > 1)
3845                         num = rnd_main.Get(elevator->RandomProbability - 1);
3846                 else
3847                         num = 0;
3848
3849                 //get call floor
3850                 int index = rnd_floor.Get(elevator->ServicedFloors.size());
3851                 floor = elevator->ServicedFloors[index];
3852
3853                 //if probability number matched, press selected floor button
3854                 if (num == 0 && elevator->IsQueued(floor) == false && floor != elevator->GetFloor())
3855                         elevator->SelectFloor(floor);
3856         }
3857         else if (type > 1)
3858         {
3859                 //arrival and departure timers
3860                 elevator->WaitForTimer = false;
3861         }
3862 }
3863
3864 ButtonPanel* Elevator::GetPanel(int index)
3865 {
3866         //get a button panel object
3867
3868         if (index > (int)PanelArray.size() || index < 1)
3869                 return 0;
3870
3871         return PanelArray[index - 1];
3872 }
3873
3874 int Elevator::GetRandomLobby()
3875 {
3876         //return random lobby floor value
3877         return RandomLobby;
3878 }
3879
3880 void Elevator::SetRandomLobby(int floor)
3881 {
3882         //set random lobby floor
3883         RandomLobby = floor;
3884         RandomLobbySet = true;
3885 }
3886
3887 void Elevator::SelectFloor(int floor)
3888 {
3889         //press a floor button
3890
3891         if (Running == false)
3892         {
3893                 Report("Elevator not running");
3894                 return;
3895         }
3896
3897         int index = 0;
3898
3899         if (PanelArray.size() > 0)
3900         {
3901                 for (int i = 0; i < (int)PanelArray.size(); i++)
3902                 {
3903                         index = PanelArray[i]->GetFloorButtonIndex(floor);
3904                         if (index >= 0)
3905                         {
3906                                 PanelArray[i]->Press(index);
3907                                 return;
3908                         }
3909                 }
3910         }
3911 }
3912
3913 bool Elevator::IsQueued(int floor)
3914 {
3915         //return true if the given floor is in either queue
3916         
3917         int index = -1;
3918         for (int i = 0; i < (int)UpQueue.size(); i++)
3919         {
3920                 if (UpQueue[i] == floor)
3921                         index = i;
3922         }
3923         if (index > -1)
3924                 return true;
3925
3926         index = -1;
3927         for (int i = 0; i < (int)DownQueue.size(); i++)
3928         {
3929                 if (DownQueue[i] == floor)
3930                         index = i;
3931         }
3932         if (index > -1)
3933                 return true;
3934         return false;
3935 }
3936
3937 void Elevator::HoldDoors(int number)
3938 {
3939         //hold specified door, or all if "0" is given
3940
3941         int start, end;
3942         if (number == 0)
3943         {
3944                 start = 1;
3945                 end = NumDoors;
3946         }
3947         else
3948         {
3949                 start = number;
3950                 end = number;
3951         }
3952         for (int i = start; i <= end; i++)
3953         {
3954                 if (GetDoor(i))
3955                         GetDoor(i)->Hold();
3956                 else
3957                         Report("Invalid door " + std::string(_itoa(i, intbuffer, 10)));
3958         }
3959 }
3960
3961 Object* Elevator::AddDoor(const char *open_sound, const char *close_sound, bool open_state, const char *texture, float thickness, int direction, float speed, float CenterX, float CenterZ, float width, float height, float voffset, float tw, float th)
3962 {
3963         //interface to the SBS AddDoor function
3964
3965         if (direction > 8 || direction < 1)
3966         {
3967                 ReportError("Door direction out of range");
3968                 return 0;
3969         }
3970
3971         float x1, z1, x2, z2;
3972         //set up coordinates
3973         if (direction < 5)
3974         {
3975                 x1 = CenterX;
3976                 x2 = CenterX;
3977                 z1 = CenterZ - (width / 2);
3978                 z2 = CenterZ + (width / 2);
3979         }
3980         else
3981         {
3982                 x1 = CenterX - (width / 2);
3983                 x2 = CenterX + (width / 2);
3984                 z1 = CenterZ;
3985                 z2 = CenterZ;
3986         }
3987
3988         //cut area
3989         /*if (direction < 5)
3990                 CutAll(Ogre::Vector3(x1 - 1, GetBase(true) + voffset, z1), Ogre::Vector3(x2 + 1, GetBase(true) + voffset + height, z2), true, false);
3991         else
3992                 CutAll(Ogre::Vector3(x1, GetBase(true) + voffset, z1 - 1), Ogre::Vector3(x2, GetBase(true) + voffset + height, z2 + 1), true, false);
3993         */
3994
3995         StdDoorArray.resize(StdDoorArray.size() + 1);
3996         std::string elevnum = _itoa(Number, intbuffer, 10);
3997         std::string num = _itoa((int)StdDoorArray.size() - 1, intbuffer, 10);
3998         StdDoorArray[StdDoorArray.size() - 1] = new Door(this->object, std::string("Elevator " + elevnum + ":Door " + num).c_str(), open_sound, close_sound, open_state, texture, thickness, direction, speed, GetPosition().x + CenterX, GetPosition().z + CenterZ, width, height, voffset + GetPosition().y, tw, th);
3999         return StdDoorArray[StdDoorArray.size() - 1]->object;
4000 }
4001
4002 void Elevator::OpenDoor(int number)
4003 {
4004         //open door
4005         if (number < (int)StdDoorArray.size())
4006         {
4007                 if (StdDoorArray[number])
4008                         StdDoorArray[number]->Open();
4009         }
4010         else
4011                 Report("Invalid door " + std::string(_itoa(number, intbuffer, 10)));
4012 }
4013
4014 void Elevator::CloseDoor(int number)
4015 {
4016         //close door
4017         if (number < (int)StdDoorArray.size())
4018         {
4019                 if (StdDoorArray[number])
4020                         StdDoorArray[number]->Close();
4021         }
4022         else
4023                 Report("Invalid door " + std::string(_itoa(number, intbuffer, 10)));
4024 }
4025
4026 bool Elevator::IsDoorOpen(int number)
4027 {
4028         //check to see if door is open
4029         if (number < (int)StdDoorArray.size())
4030         {
4031                 if (StdDoorArray[number])
4032                         return StdDoorArray[number]->IsOpen();
4033         }
4034         else
4035                 Report("Invalid door " + std::string(_itoa(number, intbuffer, 10)));
4036         return false;
4037 }
4038
4039 bool Elevator::IsDoorMoving(int number)
4040 {
4041         //check to see if door is moving
4042         if (number < (int)StdDoorArray.size())
4043         {
4044                 if (StdDoorArray[number])
4045                         return StdDoorArray[number]->IsMoving;
4046         }
4047         else
4048                 Report("Invalid door " + std::string(_itoa(number, intbuffer, 10)));
4049         return false;
4050 }
4051
4052 void Elevator::RemovePanel(ButtonPanel* panel)
4053 {
4054         //remove a button panel reference (does not delete the object itself)
4055         for (int i = 0; i < (int)PanelArray.size(); i++)
4056         {
4057                 if (PanelArray[i] == panel)
4058                         PanelArray.erase(PanelArray.begin() + i);
4059         }
4060 }
4061
4062 void Elevator::RemoveDirectionalIndicator(DirectionalIndicator* indicator)
4063 {
4064         //remove a directional indicator reference (does not delete the object itself)
4065         for (int i = 0; i < (int)DirIndicatorArray.size(); i++)
4066         {
4067                 if (DirIndicatorArray[i] == indicator)
4068                         DirIndicatorArray.erase(DirIndicatorArray.begin() + i);
4069         }
4070 }
4071
4072 void Elevator::RemoveElevatorDoor(ElevatorDoor* door)
4073 {
4074         //remove an elevator door reference (does not delete the object itself)
4075         for (int i = 0; i < (int)DoorArray.size(); i++)
4076         {
4077                 if (DoorArray[i] == door)
4078                         DoorArray.erase(DoorArray.begin() + i);
4079         }
4080 }
4081
4082 void Elevator::RemoveFloorIndicator(FloorIndicator* indicator)
4083 {
4084         //remove a floor indicator reference (does not delete the object itself)
4085         for (int i = 0; i < (int)FloorIndicatorArray.size(); i++)
4086         {
4087                 if (FloorIndicatorArray[i] == indicator)
4088                         FloorIndicatorArray.erase(FloorIndicatorArray.begin() + i);
4089         }
4090 }
4091
4092 void Elevator::RemoveDoor(Door* door)
4093 {
4094         //remove a door reference (does not delete the object itself)
4095         for (int i = 0; i < (int)StdDoorArray.size(); i++)
4096         {
4097                 if (StdDoorArray[i] == door)
4098                         StdDoorArray.erase(StdDoorArray.begin() + i);
4099         }
4100 }
4101
4102 void Elevator::RemoveSound(Sound *sound)
4103 {
4104         //remove a sound reference (does not delete the object itself)
4105         for (int i = 0; i < (int)sounds.size(); i++)
4106         {
4107                 if (sounds[i] == sound)
4108                         sounds.erase(sounds.begin() + i);
4109         }
4110 }
4111
4112 void Elevator::NotifyArrival(int floor)
4113 {
4114         //notify on elevator arrival (play chime and turn on related directional indicator lantern)
4115
4116         bool LightDirection = GetArrivalDirection(floor); //true for up, false for down
4117
4118         //play chime sound and change indicator
4119         if (LightDirection == true)
4120         {
4121                 Chime(0, floor, true);
4122                 sbs->GetFloor(floor)->SetDirectionalIndicators(Number, true, false);
4123                 SetDirectionalIndicators(true, false);
4124         }
4125         else
4126         {
4127                 Chime(0, floor, false);
4128                 sbs->GetFloor(floor)->SetDirectionalIndicators(Number, false, true);
4129                 SetDirectionalIndicators(false, true);
4130         }
4131
4132         Notified = true;
4133 }
4134
4135 bool Elevator::GetArrivalDirection(int floor)
4136 {
4137         //determine if the directional lantern should show up or down on arrival to the specified floor
4138
4139         bool LightDirection = false; //true for up, false for down
4140
4141         if (floor == GetTopFloor())
4142                 LightDirection = false; //turn on down light if on top floor
4143         else if (floor == GetBottomFloor())
4144                 LightDirection = true; //turn on up light if on bottom floor
4145         else if (QueuePositionDirection == 1)
4146                 LightDirection = true; //turn on up light if queue direction is up
4147         else if (QueuePositionDirection == -1)
4148                 LightDirection = false; //turn on down light if queue direction is down
4149
4150         return LightDirection;
4151 }
4152
4153 void Elevator::SetRunState(bool value)
4154 {
4155         //set elevator run state (true for run, false for stop)
4156
4157         if (Running == value)
4158                 return;
4159
4160         if (value == false && IsMoving == true)
4161         {
4162                 if (InspectionService == true)
4163                         Stop(false);
4164                 else
4165                         Stop(true);
4166         }
4167
4168         if (value == false)
4169                 Report("Elevator stopped");
4170         else
4171                 Report("Elevator running");
4172
4173         Running = value;
4174 }
4175
4176 bool Elevator::IsRunning()
4177 {
4178         //return elevator running state
4179         return Running;
4180 }
4181
4182 bool Elevator::PlayFloorBeep()
4183 {
4184         //play floor beep sound
4185
4186         if (BeepSound == "" || UseFloorBeeps == false)
4187                 return false;
4188
4189         if (sbs->Verbose)
4190                 Report("playing floor beep sound");
4191
4192         std::string newsound = BeepSound;
4193         //change the asterisk into the current floor number
4194         ReplaceAll(newsound, "*", _itoa(GetFloor(), intbuffer, 10));
4195         TrimString(newsound);
4196         floorbeep->Stop();
4197         floorbeep->Load(newsound.c_str());
4198         floorbeep->Loop(false);
4199         floorbeep->Play();
4200         return true;
4201 }
4202
4203 bool Elevator::PlayFloorSound()
4204 {
4205         //play floor sound
4206
4207         if (FloorSound == "" || UseFloorSounds == false)
4208                 return false;
4209
4210         if (sbs->Verbose)
4211                 Report("playing floor sound");
4212
4213         std::string newsound = FloorSound;
4214         //change the asterisk into the current floor number
4215         ReplaceAll(newsound, "*", _itoa(GotoFloor, intbuffer, 10));
4216         TrimString(newsound);
4217         floorsound->Stop();
4218         floorsound->Load(newsound.c_str());
4219         floorsound->Loop(false);
4220         floorsound->Play();
4221         return true;
4222 }
4223
4224 bool Elevator::PlayMessageSound(bool direction)
4225 {
4226         //play message sound
4227         //if direction is true, play up sound; otherwise play down sound
4228
4229         if (UseMessageSounds == false)
4230                 return false;
4231         if (direction == true && UpMessageSound == "")
4232                 return false;
4233         if (direction == false && DownMessageSound == "")
4234                 return false;
4235
4236         std::string newsound;
4237
4238         if (direction == true)
4239         {
4240                 if (sbs->Verbose)
4241                         Report("playing up message sound");
4242
4243                 newsound = UpMessageSound;
4244         }
4245         else
4246         {
4247                 if (sbs->Verbose)
4248                         Report("playing down message sound");
4249
4250                 newsound = DownMessageSound;
4251         }
4252
4253         //change the asterisk into the current floor number
4254         ReplaceAll(newsound, "*", _itoa(GetFloor(), intbuffer, 10));
4255         TrimString(newsound);
4256         messagesnd->Stop();
4257         messagesnd->Load(newsound.c_str());
4258         messagesnd->Loop(false);
4259         messagesnd->Play();
4260         return true;
4261 }
4262
4263 bool Elevator::DoorExists(int number)
4264 {
4265         //check if the specified door exists
4266         //if number is 0, return true if any door exists
4267
4268         if (number > 0)
4269                 return (GetDoor(number) > 0);
4270
4271         if (number == 0)
4272         {
4273                 for (int i = 0; i < (int)DoorArray.size(); i++)
4274                 {
4275                         if (GetDoor(i))
4276                                 return true;
4277                 }
4278         }
4279         return false;
4280 }
4281
4282 bool Elevator::IsNudgeModeActive(int number)
4283 {
4284         //checks doors and returns true if any (or the specified door) have nudge mode active
4285
4286         if (number > 0 && number < (int)DoorArray.size())
4287         {
4288                 if (DoorArray[number])
4289                         return DoorArray[number]->GetNudgeStatus();
4290         }
4291         else if (number == 0)
4292         {
4293                 for (int i = 0; i < (int)DoorArray.size(); i++)
4294                 {
4295                         if (DoorArray[number]->GetNudgeStatus() == true)
4296                                 return true;
4297                 }
4298         }
4299         return false;
4300 }
4301
4302 void Elevator::EnableNudgeMode(bool value, int number)
4303 {
4304         //enables nudge mode on all doors or the specified door
4305
4306         if (number > 0 && number < (int)DoorArray.size())
4307         {
4308                 if (DoorArray[number])
4309                         DoorArray[number]->EnableNudgeMode(value);
4310         }
4311         else if (number == 0)
4312         {
4313                 for (int i = 0; i < (int)DoorArray.size(); i++)
4314                         DoorArray[number]->EnableNudgeMode(value);
4315         }
4316 }
4317
4318 Object* Elevator::AddLight(const char *name, int type, Ogre::Vector3 position, 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)
4319 {
4320         //add a global light
4321
4322         Light* light = new Light(name, type, position + Origin, 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);
4323         lights.push_back(light);
4324         return light->object;
4325 }
4326
4327 void Elevator::MoveLights(Ogre::Vector3 position, bool relative_x, bool relative_y, bool relative_z)
4328 {
4329         //move lights
4330         for (int i = 0; i < (int)lights.size(); i++)
4331                 lights[i]->Move(position, relative_x, relative_y, relative_z);
4332 }
4333
4334 Object* Elevator::AddModel(const char *name, const char *filename, Ogre::Vector3 position, Ogre::Vector3 rotation, float max_render_distance, float scale_multiplier, bool enable_physics, float restitution, float friction, float mass)
4335 {
4336         //add a model
4337         Model* model = new Model(name, filename, position + Origin, rotation, max_render_distance, scale_multiplier, enable_physics, restitution, friction, mass);
4338         if (model->load_error == true)
4339         {
4340                 delete model;
4341                 return 0;
4342         }
4343         ModelArray.push_back(model);
4344         return model->object;
4345 }
4346
4347 void Elevator::MoveModels(Ogre::Vector3 position, bool relative_x, bool relative_y, bool relative_z)
4348 {
4349         //move models
4350         for (int i = 0; i < (int)ModelArray.size(); i++)
4351                 ModelArray[i]->Move(position, relative_x, relative_y, relative_z);
4352 }
4353
4354 void Elevator::AddDisplayFloor(int floor)
4355 {
4356         //add a floor to the display floors list
4357         DisplayFloors.push_back(floor);
4358 }