Skyscraper 2.0
elevator.cpp
Go to the documentation of this file.
1/*
2 Scalable Building Simulator - Elevator Object
3 The Skyscraper Project - Version 2.0
4 Copyright (C)2004-2024 Ryan Thoryk
5 https://www.skyscrapersim.net
6 https://sourceforge.net/projects/skyscraper/
7 Contact - ryan@skyscrapersim.net
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22*/
23
24#include "globals.h"
25#include "sbs.h"
26#include "floor.h"
27#include "dynamicmesh.h"
28#include "mesh.h"
29#include "camera.h"
30#include "shaft.h"
31#include "control.h"
32#include "sound.h"
33#include "elevatorcar.h"
34#include "timer.h"
35#include "profiler.h"
36#include "texture.h"
37#include "controller.h"
38#include "random.h"
39#include "elevator.h"
40
41#include <time.h>
42
43namespace SBS {
44
45//elevator parking timer
47{
48public:
50 int type; //0 = parking timer, 1 = arrival/departure, 2 = random malfunctions
51 Timer(const std::string &name, Elevator *parent, int Type) : TimerObject(parent, name)
52 {
53 elevator = parent;
54 type = Type;
55 }
56 virtual void Notify();
57};
58
59Elevator::Elevator(Object *parent, int number) : Object(parent)
60{
61 //set up SBS object
62 SetValues("Elevator", "", false);
63
64 //set elevator number
65 Number = number;
66
67 //init variables
68 Name = "";
69 Type = "Local";
73 LastQueueFloor[0] = 0;
74 LastQueueFloor[1] = 0;
75 UpSpeed = 0;
76 DownSpeed = 0;
77 MoveElevator = false;
78 GotoFloor = 0;
79 GotoFloorCar = 0;
80 Acceleration = 0;
81 Deceleration = 0;
82 AccelJerk = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.AccelJerk", 1);
83 DecelJerk = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.DecelJerk", 1);
84 ElevatorStart = 0;
85 Direction = 0;
87 Destination = 0;
88 ElevatorRate = 0;
91 Brakes = false;
92 EmergencyStop = 0;
93 AssignedShaft = 0;
94 IsEnabled = true;
96 ErrorOffset = 0;
97 JerkRate = 0;
98 JerkPos = 0;
99 MovementRunning = false;
100 oldfloor = 0;
101 IsMoving = false;
102 lastfloor = 0;
103 lastfloorset = false;
104 MotorUpStartSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorUpStartSound", "motor_start.wav");
105 MotorUpRunSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorUpRunSound", "motor_running.wav");
106 MotorUpStopSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorUpStopSound", "motor_stop.wav");
107 MotorDownStartSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorDownStartSound", "motor_start.wav");
108 MotorDownRunSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorDownRunSound", "motor_running.wav");
109 MotorDownStopSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorDownStopSound", "motor_stop.wav");
110 MotorIdleSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorIdleSound", "");
111 UseFloorSkipText = false;
112 ACP = sbs->GetConfigBool("Skyscraper.SBS.Elevator.ACP", false);
113 ACPFloor = sbs->GetConfigInt("Skyscraper.SBS.Elevator.ACPFloor", 0);
114 UpPeak = sbs->GetConfigBool("Skyscraper.SBS.Elevator.UpPeak", false);
115 DownPeak = sbs->GetConfigBool("Skyscraper.SBS.Elevator.DownPeak", false);
116 IndependentService = sbs->GetConfigBool("Skyscraper.SBS.Elevator.IndependentService", false);
118 InspectionService = sbs->GetConfigBool("Skyscraper.SBS.Elevator.InspectionService", false);
119 FireServicePhase1 = sbs->GetConfigInt("Skyscraper.SBS.Elevator.FireService1", 0);
120 FireServicePhase2 = sbs->GetConfigInt("Skyscraper.SBS.Elevator.FireService2", 0);
122 RecallFloor = 0;
124 OnFloor = true;
125 RecallSet = false;
126 RecallAltSet = false;
127 ACPFloorSet = false;
128 RecallUnavailable = false;
129 ManualGo = false;
130 Created = false;
131 MotorPosition = Vector3::ZERO;
132 QueueResets = sbs->GetConfigBool("Skyscraper.SBS.Elevator.QueueResets", false);
133 FirstRun = true;
134 ParkingFloor = 0;
135 ParkingDelay = 0;
136 Leveling = false;
137 LevelingSpeed = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.LevelingSpeed", 0.2);
138 LevelingOffset = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.LevelingOffset", 0.5);
139 LevelingOpen = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.LevelingOpen", 0);
140 tmpDecelJerk = 0;
141 FinishedMove = false;
142 WaitForDoors = false;
143 ActiveDirection = 0;
144 motorsound = 0;
145 motoridlesound = 0;
146 NotifyEarly = sbs->GetConfigInt("Skyscraper.SBS.Elevator.NotifyEarly", 0);
147 NotifyLate = sbs->GetConfigBool("Skyscraper.SBS.Elevator.NotifyLate", false);
148 Running = sbs->GetConfigBool("Skyscraper.SBS.Elevator.Run", true);
149 Notified = false;
150 Parking = false;
151 DepartureDelay = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.DepartureDelay", 0.0);
152 ArrivalDelay = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.ArrivalDelay", 0.0);
153 WaitForTimer = false;
154 SoundsQueued = false;
155 HeightSet = false;
156 elevposition = Vector3::ZERO;
157 ManualUp = false;
158 ManualDown = false;
159 InspectionSpeed = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.InspectionSpeed", 0.6);
160 LimitQueue = sbs->GetConfigBool("Skyscraper.SBS.Elevator.LimitQueue", false);
161 UpQueueEmpty = false;
162 DownQueueEmpty = false;
163 UpCall = false;
164 DownCall = false;
165 QueuePending = false;
166 ReOpen = sbs->GetConfigBool("Skyscraper.SBS.Elevator.ReOpen", true);
168 AutoDoors = sbs->GetConfigBool("Skyscraper.SBS.Elevator.AutoDoors", true);
169 OpenOnStart = sbs->GetConfigBool("Skyscraper.SBS.Elevator.OpenOnStart", false);
170 ManualMove = 0;
171 ManualMoveHold = false;
172 Interlocks = sbs->GetConfigBool("Skyscraper.SBS.Elevator.Interlocks", true);
173 GoActive = false;
174 GoActiveFloor = 0;
175 FloorHold = sbs->GetConfigBool("Skyscraper.SBS.Elevator.FloorHold", false);
176 GoPending = false;
177 EmergencyStopSpeed = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.EmergencyStopSpeed", 3.0);
178 MotorEmergencyStopSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.MotorEmergencyStopSound", "");
179 AutoAdjustSound = sbs->GetConfigBool("Skyscraper.SBS.Elevator.AutoAdjustSound", false);
180 SkipFloorSound = false;
181 ManualStop = false;
182 ChimeOnArrival = sbs->GetConfigBool("Skyscraper.SBS.Elevator.ChimeOnArrival", false);
183 HoistwayAccess = 0;
185 HoistwayAccessHold = sbs->GetConfigBool("Skyscraper.SBS.Elevator.HoistwayAccessHold", true);
186 RopePosition = Vector3::ZERO;
187 CounterweightStartSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.CounterweightStartSound", "");
188 CounterweightMoveSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.CounterweightMoveSound", "");
189 CounterweightStopSound = sbs->GetConfigString("Skyscraper.SBS.Elevator.CounterweightStopSound", "");
191 Counterweight = false;
192 WeightMesh = 0;
193 WeightRopeMesh = 0;
194 RopeMesh = 0;
195 Error = false;
196 RandomProbability = sbs->GetConfigInt("Skyscraper.SBS.Elevator.RandomProbability", 20);
197 RandomFrequency = sbs->GetConfigFloat("Skyscraper.SBS.Elevator.RandomFrequency", 5);
198
199 //initialize random number generators
200 rnd_time = new RandomGen((unsigned int)(time(0) + GetNumber()));
201 rnd_type = new RandomGen((unsigned int)(time(0) + GetNumber() + 1));
202
203 //create timers
204 parking_timer = new Timer("Parking Timer", this, 0);
205 arrival_delay = new Timer("Arrival Delay Timer", this, 1);
206 departure_delay = new Timer("Departure Delay Timer", this, 1);
207 malfunction_timer = new Timer("Malfunction Timer", this, 2);
208
209 //create object meshes
210 std::string name = "Elevator " + ToString(Number);
211 SetName(name);
212
213 //create a primary car object
214 AddCar();
215
216 //create a dynamic mesh for elevator doors
217 DoorContainer = new DynamicMesh(this, GetSceneNode(), name + " Door Container", 0, true);
219
220 if (sbs->Verbose)
221 Report("elevator object created");
222
223 EnableLoop(true);
224}
225
227{
228 //delete counterweight and rope meshes
229 if (sbs->Verbose)
230 Report("deleting meshes");
231
233 {
235 delete counterweightsound;
236 }
238
239 if (WeightMesh)
240 {
242 delete WeightMesh;
243 }
244 WeightMesh = 0;
245
246 if (WeightRopeMesh)
247 {
249 delete WeightRopeMesh;
250 }
251 WeightRopeMesh = 0;
252
253 if (RopeMesh)
254 {
256 delete RopeMesh;
257 }
258 RopeMesh = 0;
259
260 //delete timers
261 if (sbs->Verbose)
262 Report("deleting timers");
263
264 if (parking_timer)
265 {
267 delete parking_timer;
268 }
269 parking_timer = 0;
270
271 if (arrival_delay)
272 {
274 delete arrival_delay;
275 }
276 arrival_delay = 0;
277
278 if (departure_delay)
279 {
281 delete departure_delay;
282 }
283 departure_delay = 0;
284
286 {
288 delete malfunction_timer;
289 }
291
292 //delete random number generators
293 if (rnd_time)
294 delete rnd_time;
295 rnd_time = 0;
296
297 if (rnd_type)
298 delete rnd_type;
299 rnd_type = 0;
300
301 //delete cars
302 if (sbs->Verbose)
303 Report("deleting cars");
304
305 for (size_t i = 0; i < Cars.size(); i++)
306 {
307 if (Cars[i])
308 {
309 Cars[i]->parent_deleting = true;
310 delete Cars[i];
311 }
312 Cars[i] = 0;
313 }
314 Cars.clear();
315
316 //delete door container
317 if (DoorContainer)
318 delete DoorContainer;
319 DoorContainer = 0;
320
321 //delete sounds
322 if (sbs->Verbose)
323 Report("deleting motor sounds");
324
325 if (motorsound)
326 {
328 delete motorsound;
329 }
330 motorsound = 0;
331 if (motoridlesound)
332 {
334 delete motoridlesound;
335 }
336 motoridlesound = 0;
337
338 //unregister from parent
339 if (sbs->FastDelete == false && parent_deleting == false)
340 sbs->RemoveElevator(this);
341}
342
343bool Elevator::CreateElevator(bool relative, Real x, Real z, int floor)
344{
345 //Creates elevator at specified location and floor
346 //x and z are the center coordinates
347 //if relative is true, then x and z coordinates are relative
348 //to the assigned shaft's center
349
350 if (Created == true)
351 return ReportError("Has already been created");
352
353 //make sure required values are set
354 if (UpSpeed <= 0 || DownSpeed <= 0)
355 return ReportError("Speed not set or invalid");
356
357 if (Acceleration <= 0)
358 return ReportError("Acceleration not set or invalid");
359
360 if (Deceleration <= 0)
361 return ReportError("Deceleration not set or invalid");
362
363 if (AccelJerk <= 0)
364 return ReportError("Invalid value for AccelJerk");
365
366 if (DecelJerk <= 0)
367 return ReportError("Invalid value for DecelJerk");
368
369 if (AssignedShaft <= 0)
370 return ReportError("Not assigned to a shaft");
371
372 for (size_t i = 0; i < Controllers.size(); i++)
373 {
374 if (Controllers[i] < 0)
375 return ReportError("Invalid value for Controller");
376 }
377
378 if (!GetShaft())
379 return ReportError("Shaft " + ToString(AssignedShaft) + " doesn't exist");
380
381 //set starting position
382 Vector3 position = Vector3::ZERO;
383
384 if (relative == false)
385 {
386 position.x = x;
387 position.z = z;
388 }
389 else
390 {
391 position.x = GetShaft()->GetPosition().x + x;
392 position.z = GetShaft()->GetPosition().z + z;
393 }
394
395 //add elevator to associated shaft
397
398 //add elevator to associated dispatch controllers
399 for (size_t i = 0; i < (size_t)Controllers.size(); i++)
400 {
403 }
404
405 //set recall/ACP floors if not already set
406 if (RecallSet == false)
408 if (RecallAltSet == false)
410 if (ACPFloorSet == false)
412
413 //move objects to positions
414 if (sbs->Verbose)
415 Report("moving elevator to origin position");
416 SetPosition(position);
417
418 //create motor sounds
419 std::string motorname = "Motor " + ToString(Number);
420 motorsound = new Sound(GetShaft(), motorname, true);
421 motorname += " Idle";
422 motoridlesound = new Sound(GetShaft(), motorname, true);
423
424 //move motor to top of shaft if location not specified, or to location
425 if (MotorPosition != Vector3(0, 0, 0))
427 else
428 {
429 Floor *floor = sbs->GetFloor(GetShaft()->endfloor);
430 if (floor)
432 }
435
436 Created = true;
437
438 Report("created at " + TruncateNumber(position.x, 4) + ", " + TruncateNumber(position.z, 4));
439
440 //set up primary car
441 if (!GetCar(1)->CreateCar(floor))
442 return false;
443
445
446 return true;
447}
448
449Wall* Elevator::CreateCounterweight(const std::string &frame_texture, const std::string &weight_texture, Real x, Real z, const Vector3 &size, Real weight_voffset)
450{
451 //create a counterweight and ropes for this elevator
452
453 for (int i = 0; i < GetCarCount(); i++)
454 {
455 if (GetCar(i)->Created == false)
456 {
457 ReportError("Car " + ToString(i) + " not created yet");
458 return 0;
459 }
460 }
461
462 //create counterweight and rope meshes
463 WeightMesh = new MeshObject(this, GetName() + " Counterweight");
464 WeightRopeMesh = new MeshObject(this, GetName() + " Counterweight Rope", 0, "", "", 0, 1, false, false);
465 RopeMesh = new MeshObject(this, GetName() + " Rope", 0, "", "", 0, 1, false, false);
466
467 //save and change autosizing
468 bool autosize_x, autosize_y;
469 sbs->GetTextureManager()->GetAutoSize(autosize_x, autosize_y);
470 sbs->GetTextureManager()->SetAutoSize(false, false);
471
472 Wall *frame = sbs->CreateWallBox(WeightMesh, "Counterweight Frame", frame_texture, -size.x / 2, size.x / 2, -size.z / 2, size.z / 2, size.y, 0, 1, 1, true, true, false, false, false);
473 Wall *counterweight = sbs->CreateWallBox(WeightMesh, "Counterweight", weight_texture, (-size.x / 2) + 0.01, (size.x / 2) - 0.01, (-size.z / 2) + 0.01, (size.z / 2) - 0.01, size.y / 2, weight_voffset, 1, 1);
474
475 Floor *topfloor, *carfloor, *topcarfloor, *bottomfloor;
476 topfloor = sbs->GetFloor(GetTopFloor());
477 carfloor = sbs->GetFloor(GetCar(1)->GetFloor());
478 topcarfloor = sbs->GetFloor(GetCar(GetCarCount() - 1)->GetFloor());
479 bottomfloor = sbs->GetFloor(GetBottomFloor());
480
481 x += GetCar(1)->GetPosition().x;
482 z += GetCar(1)->GetPosition().z;
483
484 WeightMesh->SetPosition(Vector3(x, topfloor->GetBase() - (carfloor->GetBase() - bottomfloor->GetBase()), z));
485 WeightRopeMesh->SetPosition(Vector3(x, WeightMesh->GetPosition().y + (size.y - 0.5), z));
487
488 Real counterweight_rope_height = MotorPosition.y - (WeightMesh->GetPosition().y + (size.y - 0.5));
489 Wall *c_rope;
490 if (size.x > size.z)
491 c_rope = sbs->AddWall(WeightRopeMesh, "Counterweight Rope", RopeTexture, 0, -size.x / 4, 0, size.x / 4, 0, counterweight_rope_height, counterweight_rope_height, 0, 0, 1, 1);
492 else
493 c_rope = sbs->AddWall(WeightRopeMesh, "Counterweight Rope", RopeTexture, 0, 0, -size.z / 4, 0, size.z / 4, counterweight_rope_height, counterweight_rope_height, 0, 0, 1, 1);
494
495 Real rope_height = MotorPosition.y - RopeMesh->GetPosition().y;
496 Wall *rope;
497 if (size.x > size.z)
498 rope = sbs->AddWall(RopeMesh, "Rope", RopeTexture, 0, -size.x / 4, 0, size.x / 4, 0, rope_height, rope_height, 0, 0, 1, 1);
499 else
500 rope = sbs->AddWall(RopeMesh, "Rope", RopeTexture, 0, 0, -size.z / 4, 0, size.z / 4, rope_height, rope_height, 0, 0, 1, 1);
501
502 counterweightsound = new Sound(WeightMesh, "Counterweight Sound", true);
503
504 weight_size = size;
505
506 //restore autosize
507 sbs->GetTextureManager()->SetAutoSize(autosize_x, autosize_y);
508
509 Counterweight = true;
510 return counterweight;
511}
512
513bool Elevator::AddRails(const std::string &main_texture, const std::string &edge_texture, Real x, Real z, bool orientation, Real rail_distance, Real rail_width)
514{
515 //creates rails for the elevator cars or counterweight
516 //if orientation is false, create rails along the X axis, otherwise the Z axis
517
518 //check if shaft is associated
519 if (!GetShaft())
520 return ReportError("AddRails: no shaft available");
521
522 Vector3 offset;
523 offset = GetPosition() - GetShaft()->GetPosition();
524
525 MeshObject *mesh = GetShaft()->GetLevel(GetShaft()->startfloor)->GetMeshObject();
526 Real height = MotorPosition.y - sbs->GetFloor(GetShaft()->startfloor)->Altitude;
527
528 if (orientation == false)
529 {
530 //left side
531 sbs->CreateWallBox2(mesh, "Rails L1", edge_texture, -rail_distance + x + offset.x + (rail_width / 4), z + offset.z, rail_width / 2, rail_width / 8, height, 0, 1, 1);
532 sbs->CreateWallBox2(mesh, "Rails L2", main_texture, -rail_distance + x + offset.x - (rail_width / 4), z + offset.z, rail_width / 2, rail_width / 8, height, 0, 1, 1);
533 sbs->CreateWallBox2(mesh, "Rails L3", main_texture, -rail_distance + x + offset.x - (rail_width / 2), z + offset.z, rail_width / 8, rail_width, height, 0, 1, 1);
534
535 //right side
536 sbs->CreateWallBox2(mesh, "Rails R1", edge_texture, rail_distance + x + offset.x - (rail_width / 4), z + offset.z, rail_width / 2, rail_width / 8, height, 0, 1, 1);
537 sbs->CreateWallBox2(mesh, "Rails R2", main_texture, rail_distance + x + offset.x + (rail_width / 4), z + offset.z, rail_width / 2, rail_width / 8, height, 0, 1, 1);
538 sbs->CreateWallBox2(mesh, "Rails R3", main_texture, rail_distance + x + offset.x + (rail_width / 2), z + offset.z, rail_width / 8, rail_width, height, 0, 1, 1);
539 }
540 else
541 {
542 //front side
543 sbs->CreateWallBox2(mesh, "Rails F1", edge_texture, x + offset.x, -rail_distance + z + offset.z + (rail_width / 4), rail_width / 8, rail_width / 2, height, 0, 1, 1);
544 sbs->CreateWallBox2(mesh, "Rails F2", main_texture, x + offset.x, -rail_distance + z + offset.z - (rail_width / 4), rail_width / 8, rail_width / 2, height, 0, 1, 1);
545 sbs->CreateWallBox2(mesh, "Rails F3", main_texture, x + offset.x, -rail_distance + z + offset.z - (rail_width / 2), rail_width, rail_width / 8, height, 0, 1, 1);
546
547 //back side
548 sbs->CreateWallBox2(mesh, "Rails B1", edge_texture, x + offset.x, rail_distance + z + offset.z - (rail_width / 4), rail_width / 8, rail_width / 2, height, 0, 1, 1);
549 sbs->CreateWallBox2(mesh, "Rails B2", main_texture, x + offset.x, rail_distance + z + offset.z + (rail_width / 4), rail_width / 8, rail_width / 2, height, 0, 1, 1);
550 sbs->CreateWallBox2(mesh, "Rails B3", main_texture, x + offset.x, rail_distance + z + offset.z + (rail_width / 2), rail_width, rail_width / 8, height, 0, 1, 1);
551 }
552
553 return true;
554}
555
556bool Elevator::AddRoute(int floor, int direction, int call_type)
557{
558 //Add call route to elevator routing table, in sorted order
559 //directions are either 1 for up, or -1 for down
560 //call type is 0 for a car call, 1 for a hall call, 2 for a system call
561
562 //only run if power is enabled
563 if (sbs->GetPower() == false)
564 return false;
565
566 if (Running == false)
567 return ReportError("Elevator not running");
568
569 Floor *floorobj = sbs->GetFloor(floor);
570
571 if (!floorobj)
572 return ReportError("Invalid floor " + ToString(floor));
573
574 //if doors are open or moving in independent service mode, quit
575 if (IndependentService == true && (AreDoorsOpen() == false || AreDoorsMoving() != 0))
576 return ReportError("floor button must be pressed before closing doors while in independent service");
577
578 //do not add routes if in inspection service or fire phase 1 modes
579 if (InspectionService == true)
580 return ReportError("cannot add route while in inspection service mode");
581
582 if (FireServicePhase2 == 2)
583 return ReportError("cannot add route while hold is enabled");
584
585 //discard route if direction opposite queue search direction
586 if (LimitQueue == true && direction != QueuePositionDirection && QueuePositionDirection != 0)
587 {
588 //only allow if any queue entries exist
589 if ((QueuePositionDirection == 1 && UpQueue.size() > 0) || (QueuePositionDirection == -1 && DownQueue.size() > 0))
590 return ReportError("cannot add route in opposite direction of queue search");
591 }
592
593 //get related car number
594 ElevatorCar *car = GetCarForFloor(floor);
595
596 if (!car)
597 return ReportError("floor " + ToString(floor) + " is not a serviced floor");
598
599 //if car is on the same floor, perform an arrival
600 if (car->IsOnFloor(floor))
601 {
602 if (QueuePositionDirection == direction ||
604 (QueuePositionDirection == 1 && UpQueue.size() == 0) ||
605 (QueuePositionDirection == -1 && DownQueue.size() == 0))
606 {
607 SameFloorArrival(floor, direction);
608 return true;
609 }
610 }
611
612 //add route in related direction queue
613 if (direction == 1)
614 {
615 //exit if entry already exists
616 if (RouteExists(true, floor) == true)
617 return ReportError("route to floor " + ToString(floor) + " (" + floorobj->ID + ") already exists");
618
619 //add floor to up queue
620 UpQueue.emplace_back(QueueEntry(floor, call_type, car->Number, 1));
621 //sort queue
622 std::sort(UpQueue.begin(), UpQueue.end());
623 QueuePending = true;
624
625 LastQueueFloor[0] = floor;
626 LastQueueFloor[1] = 1;
627
628 //add car number info if needed
629 std::string car_msg = "";
630 if (GetCarCount() > 1)
631 car_msg = " for car " + ToString(car->Number);
632
633 Report("adding route to floor " + ToString(floor) + " (" + floorobj->ID + ") direction Up" + car_msg);
634 }
635 else
636 {
637 //exit if entry already exists
638 if (RouteExists(false, floor) == true)
639 return ReportError("route to floor " + ToString(floor) + " (" + floorobj->ID + ") already exists");
640
641 //add floor to down queue
642 DownQueue.emplace_back(QueueEntry(floor, call_type, car->Number, -1));
643 //sort queue
644 std::sort(DownQueue.begin(), DownQueue.end());
645 QueuePending = true;
646
647 LastQueueFloor[0] = floor;
648 LastQueueFloor[1] = -1;
649
650 //add car number info if needed
651 std::string car_msg = "";
652 if (GetCarCount() > 1)
653 car_msg = " for car " + ToString(car->Number);
654
655 Report("adding route to floor " + ToString(floor) + " (" + floorobj->ID + ") direction Down" + car_msg);
656 }
657
658 //make sure the car's GotoFloor status is set, if this is an additional car heading the same route
659 ProcessGotoFloor(floor, direction);
660
661 //turn on button lights for a car call, or a Destination Dispatch hall call
662 if (call_type == 0)
663 ChangeLight(floor, true);
664
665 //go to ACP floor if ACP mode is enabled
666 if (ACP == true && floor != ACPFloor)
667 {
668 //only add ACP route if original route will pass ACP floor
669 if ((car->GetFloor() < ACPFloor && floor > ACPFloor) || (car->GetFloor() > ACPFloor && floor < ACPFloor))
670 {
671 Report("adding ACP route");
672 AddRoute(ACPFloor, direction, 2);
673 }
674 }
675
676 return true;
677}
678
679bool Elevator::DeleteRoute(int floor, int direction)
680{
681 //Delete call route from elevator routing table
682 //directions are either 1 for up, or -1 for down
683
684 //only run if power is enabled
685 if (sbs->GetPower() == false)
686 return false;
687
688 if (Running == false)
689 return ReportError("Elevator not running");
690
691 Floor *floorobj = sbs->GetFloor(floor);
692
693 if (!floorobj)
694 return ReportError("Invalid floor " + ToString(floor));
695
696 if (direction == 1)
697 {
698 //delete floor entry from up queue
699 for (size_t i = 0; i < UpQueue.size(); i++)
700 {
701 if (UpQueue[i].floor == floor)
702 {
703 Report("deleting route to floor " + ToString(floor) + " (" + floorobj->ID + ") direction Up");
704 UpQueue.erase(UpQueue.begin() + i);
705 break;
706 }
707 }
708 if (UpQueue.size() == 0)
709 UpQueueEmpty = true;
710 }
711 else
712 {
713 //delete floor entry from down queue
714 for (size_t i = 0; i < DownQueue.size(); i++)
715 {
716 if (DownQueue[i].floor == floor)
717 {
718 Report("deleting route to floor " + ToString(floor) + " (" + floorobj->ID + ") direction Down");
719 DownQueue.erase(DownQueue.begin() + i);
720 break;
721 }
722 }
723 if (DownQueue.size() == 0)
724 DownQueueEmpty = true;
725 }
726
727 HandleDequeue(direction);
728
729 //turn off button lights
730 if (sbs->Verbose)
731 Report("DeleteRoute: turning off button lights for floor " + ToString(floor));
732 ChangeLight(floor, false);
733 return true;
734}
735
736bool Elevator::RouteExists(bool direction, int floor)
737{
738 //return true if a floor route exists in the specified directional queue
739
740 //only run if power is enabled
741 if (sbs->GetPower() == false)
742 return false;
743
744 if (direction == true)
745 {
746 for (size_t i = 0; i < UpQueue.size(); i++)
747 {
748 if (UpQueue[i].floor == floor)
749 return true;
750 }
751 }
752 else
753 {
754 for (size_t i = 0; i < DownQueue.size(); i++)
755 {
756 if (DownQueue[i].floor == floor)
757 return true;
758 }
759 }
760
761 return false;
762}
763
765{
766 //cancel the last route
767
768 //only run if power is enabled
769 if (sbs->GetPower() == false)
770 return false;
771
772 if (Running == false)
773 return ReportError("Elevator not running");
774
775 if (LastQueueFloor[1] == 0)
776 {
777 if (sbs->Verbose)
778 ReportError("CallCancel: no valid routes");
779 return false;
780 }
781
783 Report("cancelled last call");
784
785 return true;
786}
787
789{
790 //cancels all added routes
791
792 //only run if power is enabled
793 if (sbs->GetPower() == false)
794 return false;
795
796 if (Running == false)
797 return ReportError("Elevator not running");
798
799 if (LastQueueFloor[1] == 0)
800 {
801 if (sbs->Verbose)
802 ReportError("CallCancel: no valid routes");
803 return false;
804 }
805
806 Report("cancelled all calls");
807 ResetQueue(true, true);
808
809 return true;
810}
811
812bool Elevator::Stop(bool emergency)
813{
814 //Tells elevator to stop moving, no matter where it is
815
816 if (EmergencyStop > 0)
817 return false;
818
819 if (emergency == true)
820 {
821 //exit if in inspection mode
822 if (InspectionService == true)
823 return ReportError("cannot stop while in inspection service mode");
824
825 //exit if in fire service phase 1 recall
826 if (FireServicePhase1 == 1 && FireServicePhase2 == 0)
827 return ReportError("cannot stop while in fire service 1 recall mode");
828 }
829
830 if (IsMoving == false)
831 {
832 if (sbs->Verbose)
833 ReportError("Stop: not moving");
834 return false;
835 }
836
837 if (emergency == true)
838 {
839 EmergencyStop = 2;
840 Report("emergency stop");
841
842 //clear elevator queues
843 ResetQueue(true, true);
844 }
845 else
846 {
847 EmergencyStop = 1;
848 Report("stopping elevator");
849 }
850 return true;
851}
852
854{
855 //Processes the elevator's call queue, and sends elevators to called floors
856 SBS_PROFILE("Elevator::ProcessCallQueue");
857
858 //only run if power is enabled
859 if (sbs->GetPower() == false)
860 return;
861
862 //exit if elevator is not running
863 if (Running == false)
864 return;
865
866 //exit if in inspection service mode
867 if (InspectionService == true)
868 return;
869
870 //exit if stopping
871 if (EmergencyStop > 0)
872 return;
873
874 //exit if moving manually
875 if (ManualMove > 0)
876 return;
877
878 //exit if Go function is active
879 if (GoPending == true)
880 return;
881
882 //if both queues are empty
883 if (UpQueue.empty() && DownQueue.empty())
884 {
885 UpQueueEmpty = false;
886 DownQueueEmpty = false;
887
888 if (DownPeak == true || UpPeak == true)
889 {
890 int TopFloor = GetCar(1)->GetTopFloor();
891 int BottomFloor = GetCar(1)->GetBottomFloor();
892
893 //if DownPeak mode is active, send elevator to the top serviced floor if not already there
894 if (GetCar(1)->GetFloor() != TopFloor && DownPeak == true && IsMoving == false)
895 {
896 if (sbs->Verbose)
897 Report("ProcessCallQueue: sending elevator to top floor for DownPeak mode");
898 AddRoute(TopFloor, 1, 2);
899 return;
900 }
901 //if UpPeak mode is active, send elevator to the bottom serviced floor if not already there
902 else if (GetCar(1)->GetFloor() != BottomFloor && UpPeak == true && IsMoving == false)
903 {
904 if (sbs->Verbose)
905 Report("ProcessCallQueue: sending elevator to bottom floor for UpPeak mode");
906 AddRoute(BottomFloor, -1, 2);
907 return;
908 }
909 }
910
911 if (IsIdle() == true && QueuePositionDirection != 0)
912 {
913 //set search direction to 0 if idle
914 if (sbs->Verbose)
915 Report("ProcessCallQueue: resetting search direction due to idle");
918 }
919 return;
920 }
921 else if (QueuePositionDirection == 0)
922 {
923 if (UpQueue.empty() == false)
924 {
925 if (sbs->Verbose)
926 Report("ProcessCallQueue: setting search direction to up");
928 }
929 else if (DownQueue.empty() == false)
930 {
931 if (sbs->Verbose)
932 Report("ProcessCallQueue: setting search direction to down");
934 }
936 }
937
938 //reverse queues if related queue empty flag is set
939 if (QueuePositionDirection == 1 && UpQueueEmpty == true && DownQueue.empty() == false && (NotifyEarly <= 0 || NotifyEarly == 3))
940 {
941 if (UpCall == false)
942 {
943 if (sbs->Verbose)
944 Report("ProcessCallQueue: setting search direction to down");
947 }
948 }
949 if (QueuePositionDirection == -1 && DownQueueEmpty == true && UpQueue.empty() == false && (NotifyEarly <= 0 || NotifyEarly == 3))
950 {
951 if (DownCall == false)
952 {
953 if (sbs->Verbose)
954 Report("ProcessCallQueue: setting search direction to up");
957 }
958 }
959
960 UpQueueEmpty = false;
961 DownQueueEmpty = false;
962 UpCall = false;
963 DownCall = false;
964
965 //set search direction to 0 if any related queue is empty, and if doors are not open or moving
966 if (AreDoorsOpen() == false && AreDoorsMoving() == 0)
967 {
968 if (QueuePositionDirection == 1 && UpQueue.empty())
969 {
970 if (sbs->Verbose)
971 Report("ProcessCallQueue: resetting search direction due to empty up queue");
974 }
975 if (QueuePositionDirection == -1 && DownQueue.empty())
976 {
977 if (sbs->Verbose)
978 Report("ProcessCallQueue: resetting search direction due to empty down queue");
981 }
982 }
983 else if (UpPeak == false && DownPeak == false)
984 return; //don't process the main queue code if doors are open or moving
985
986 //Search through queue lists and find next valid floor call
987 if (QueuePositionDirection == 1)
988 {
989 //search through up queue
990 for (size_t i = 0; i < UpQueue.size(); i++)
991 {
992 ElevatorCar *car = GetCarForFloor(UpQueue[i].floor);
993 if (!car)
994 return;
995
996 std::string car_msg = "";
997 if (GetCarCount() > 1)
998 car_msg = " for car " + ToString(car->Number);
999
1000 //if the queued floor number is a higher floor, dispatch the elevator to that floor
1001 if (UpQueue[i].floor >= car->CurrentFloor)
1002 {
1003 if (MoveElevator == false)
1004 {
1005 if (sbs->Verbose)
1006 Report("ProcessCallQueue up: standard dispatch, floor " + ToString(UpQueue[i].floor) + car_msg);
1007 ActiveCall = UpQueue[i];
1008 GotoFloor = UpQueue[i].floor;
1009 GotoFloorCar = car->Number;
1010 car->GotoFloor = true;
1011 if (FireServicePhase2 == 0 || UpPeak == true || DownPeak == true)
1012 {
1013 WaitForDoors = true;
1014 CloseDoors();
1015 }
1016 MoveElevator = true;
1018 QueuePending = false;
1020 }
1021 else if (Leveling == false && ActiveDirection == 1)
1022 {
1023 //if elevator is moving and not leveling, change destination floor if not beyond decel marker of that floor
1024 if (GotoFloor != UpQueue[i].floor)
1025 {
1026 if (car == GetCar(GotoFloorCar)) //make sure car is the same
1027 {
1028 Real tmpdestination = GetDestinationAltitude(UpQueue[i].floor);
1029 if (BeyondDecelMarker(1, tmpdestination) == false && sbs->GetFloor(GotoFloor))
1030 {
1031 ActiveCall = UpQueue[i];
1032 GotoFloor = UpQueue[i].floor;
1033 GotoFloorCar = car->Number;
1034 Destination = tmpdestination;
1035 Report("changing destination floor to " + ToString(GotoFloor) + " (" + sbs->GetFloor(GotoFloor)->ID + ")" + car_msg);
1036 }
1037 else if (sbs->Verbose)
1038 Report("ProcessCallQueue up: cannot change destination floor to " + ToString(UpQueue[i].floor) + car_msg);
1039 }
1040 }
1041 }
1042 return;
1043 }
1044 //if the queued floor number is a lower floor
1045 if (UpQueue[i].floor < car->CurrentFloor && MoveElevator == false)
1046 {
1047 //dispatch elevator if it's idle
1048 if (IsIdle() == true && LastQueueDirection == 0)
1049 {
1050 if (sbs->Verbose)
1051 Report("ProcessCallQueue up: dispatching idle lower elevator, floor " + ToString(UpQueue[i].floor) + car_msg);
1052 ActiveCall = UpQueue[i];
1053 GotoFloor = UpQueue[i].floor;
1054 GotoFloorCar = car->Number;
1055 car->GotoFloor = true;
1056 if (FireServicePhase2 == 0 || UpPeak == true || DownPeak == true)
1057 {
1058 WaitForDoors = true;
1059 CloseDoors();
1060 }
1061 MoveElevator = true;
1063 QueuePending = false;
1065 return;
1066 }
1067 //reset search direction if it's the last entry and idle
1068 if (i == UpQueue.size() - 1 && IsIdle() == true && QueuePositionDirection != 0)
1069 {
1070 if (sbs->Verbose)
1071 Report("ProcessCallQueue up: resetting search direction since last entry is lower" + car_msg);
1074 return;
1075 }
1076 //otherwise skip it if it's not the last entry
1077 if (sbs->Verbose)
1078 Report("ProcessCallQueue up: skipping floor entry " + ToString(UpQueue[i].floor) + car_msg);
1079 }
1080 }
1081 }
1082 else if (QueuePositionDirection == -1)
1083 {
1084 //search through down queue (search order is reversed since calls need to be processed in descending order)
1085 for (size_t i = DownQueue.size() - 1; i < DownQueue.size(); --i)
1086 {
1087 ElevatorCar *car = GetCarForFloor(DownQueue[i].floor);
1088 if (!car)
1089 return;
1090
1091 std::string car_msg = "";
1092 if (GetCarCount() > 1)
1093 car_msg = " for car " + ToString(car->Number);
1094
1095 //if the queued floor number is a lower floor, dispatch the elevator to that floor
1096 if (DownQueue[i].floor <= car->CurrentFloor)
1097 {
1098 if (MoveElevator == false)
1099 {
1100 if (sbs->Verbose)
1101 Report("ProcessCallQueue down: standard dispatch, floor " + ToString(DownQueue[i].floor) + car_msg);
1102 ActiveCall = DownQueue[i];
1104 GotoFloorCar = car->Number;
1105 car->GotoFloor = true;
1106 if (FireServicePhase2 == 0 || UpPeak == true || DownPeak == true)
1107 {
1108 WaitForDoors = true;
1109 CloseDoors();
1110 }
1111 MoveElevator = true;
1112 LastQueueDirection = -1;
1113 QueuePending = false;
1115 }
1116 else if (Leveling == false && ActiveDirection == -1)
1117 {
1118 //if elevator is moving and not leveling, change destination floor if not beyond decel marker of that floor
1119 if (GotoFloor != DownQueue[i].floor)
1120 {
1121 if (car == GetCar(GotoFloorCar)) //make sure car is the same
1122 {
1123 Real tmpdestination = GetDestinationAltitude(DownQueue[i].floor);
1124 if (BeyondDecelMarker(-1, tmpdestination) == false && sbs->GetFloor(GotoFloor))
1125 {
1126 ActiveCall = DownQueue[i];
1128 GotoFloorCar = car->Number;
1129 Destination = tmpdestination;
1130 Report("changing destination floor to " + ToString(GotoFloor) + " (" + sbs->GetFloor(GotoFloor)->ID + ")" + car_msg);
1131 }
1132 else if (sbs->Verbose)
1133 Report("ProcessCallQueue down: cannot change destination floor to " + ToString(DownQueue[i].floor) + car_msg);
1134 }
1135 }
1136 }
1137 return;
1138 }
1139 //if the queued floor number is an upper floor
1140 if (DownQueue[i].floor > car->CurrentFloor && MoveElevator == false)
1141 {
1142 //dispatch elevator if idle
1143 if (IsIdle() == true && LastQueueDirection == 0)
1144 {
1145 if (sbs->Verbose)
1146 Report("ProcessCallQueue down: dispatching idle higher elevator, floor " + ToString(DownQueue[i].floor) + car_msg);
1147 ActiveCall = DownQueue[i];
1149 GotoFloorCar = car->Number;
1150 car->GotoFloor = true;
1151 if (FireServicePhase2 == 0 || UpPeak == true || DownPeak == true)
1152 {
1153 WaitForDoors = true;
1154 CloseDoors();
1155 }
1156 MoveElevator = true;
1157 LastQueueDirection = -1;
1158 QueuePending = false;
1160 return;
1161 }
1162 //reset search direction if it's the last entry and idle
1163 if (i == 0 && IsIdle() == true && QueuePositionDirection != 0)
1164 {
1165 if (sbs->Verbose)
1166 Report("ProcessCallQueue down: resetting search direction since last entry is higher" + car_msg);
1169 return;
1170 }
1171 //otherwise skip it if it's not the last entry
1172 if (sbs->Verbose)
1173 Report("ProcessCallQueue down: skipping floor entry " + ToString(DownQueue[i].floor) + car_msg);
1174 }
1175 }
1176 }
1177}
1178
1180{
1181 //Monitors elevator and starts actions if needed
1182
1183 SBS_PROFILE("Elevator::Loop");
1184
1185 if (Created == false)
1186 return;
1187
1188 //only run if power is enabled
1189 if (sbs->GetPower() == false)
1190 {
1191 Stop(true);
1192 }
1193
1194 //make sure height value is set
1195 if (HeightSet == false)
1196 {
1197 for (int i = 1; i <= GetCarCount(); i++)
1198 {
1199 GetCar(i)->SetHeight();
1200 }
1201 HeightSet = true;
1202 }
1203
1204 //perform first-run tasks
1205 if (FirstRun == true && Running == true)
1206 {
1207 FirstRun = false;
1208
1209 if (UpPeak == true)
1210 {
1211 UpPeak = false;
1212 EnableUpPeak(true);
1213 }
1214 if (DownPeak == true)
1215 {
1216 DownPeak = false;
1217 EnableDownPeak(true);
1218 }
1219 if (IndependentService == true)
1220 {
1221 IndependentService = false;
1223 }
1224 if (InspectionService == true)
1225 {
1226 InspectionService = false;
1228 }
1229 if (FireServicePhase1 > 0)
1230 {
1231 int value = FireServicePhase1;
1233 EnableFireService1(value);
1234 }
1235 if (FireServicePhase2 > 0)
1236 {
1237 int value = FireServicePhase2;
1240 }
1241 if (ACP == true)
1242 {
1243 ACP = false;
1244 EnableACP(true);
1245 }
1246 if (ACPFloor != 0)
1247 {
1248 int tmp = ACPFloor;
1249 ACPFloor = 0;
1250 SetACPFloor(tmp);
1251 }
1252
1254 }
1255
1256 DoSetControls();
1257
1258 if (MotorIdleSound != "")
1259 {
1260 //play motor idle sound
1261 if (motoridlesound->IsPlaying() == false && Running == true)
1262 {
1263 if (sbs->Verbose)
1264 Report("playing motor idle sound");
1265
1266 if (motoridlesound->IsLoaded() == false)
1268
1271 }
1272
1273 //stop motor sound if elevator is stopped and not running
1274 if (motoridlesound->IsPlaying() == true && Running == false)
1275 {
1276 if (sbs->Verbose)
1277 Report("stopping motor idle sound");
1279 }
1280 }
1281
1282 //process up/down buttons
1283 if (ManualMoveHold == true)
1284 {
1285 if (ManualMove == 1)
1286 Up();
1287 if (ManualMove == -1)
1288 Down();
1289 }
1290
1291 //process Go function hold
1292 if (GoActive == true)
1293 Go(GoActiveFloor, true);
1294
1295 if (HoistwayAccess != 0 && HoistwayAccessHold == true)
1297
1298 //call queue processor
1300
1301 //enable auto-park timer if specified
1302 if (parking_timer->IsRunning() == false && ParkingDelay > 0 && Running == true && IsIdle() == true && InServiceMode() == false && AutoDoors == true)
1303 parking_timer->Start(int(ParkingDelay * 1000), true);
1304
1305 //run per-car loops
1306 for (int i = 1; i <= GetCarCount(); i++)
1307 {
1308 GetCar(i)->Loop();
1309 }
1310
1311 //elevator movement
1312 if (MoveElevator == true)
1314}
1315
1317{
1318 //Main processing routine; sends elevator to floor specified in GotoFloor
1319 //if InspectionService or manual movements are enabled, this function ignores GotoFloor values, since the elevator is manually moved
1320
1321 SBS_PROFILE("Elevator::MoveElevatorToFloor");
1322
1323 Vector3 movement = Vector3(0, 0, 0);
1324 bool StartLeveling = false;
1325 Error = false;
1326
1327 //wait until doors are fully closed if WaitForDoors is true
1328 if (WaitForDoors == true)
1329 {
1330 if (AreDoorsOpen() == true || AreDoorsMoving() != 0)
1331 return;
1332 else
1333 WaitForDoors = false;
1334 }
1335
1336 //exit if waiting for arrival or departure timers
1337 if (WaitForTimer == true)
1338 return;
1339
1340 //validate car object
1341 if (!GetCar(GotoFloorCar))
1342 {
1343 ReportError("Invalid elevator car");
1344 Destination = 0;
1345 Direction = 0;
1346 MoveElevator = false;
1347 MovementRunning = false;
1348 Error = true;
1350 return;
1351 }
1352
1353 if (MovementRunning == false)
1354 {
1355 if (Running == false)
1356 {
1357 ReportError("Elevator not running");
1358 Error = true;
1359 return;
1360 }
1361
1362 if (sbs->Verbose)
1363 Report("starting elevator movement procedure");
1364
1365 MovementRunning = true;
1366 FinishedMove = false;
1367 ManualStop = false;
1368 std::string dir_string;
1369
1370 Notified = false;
1371
1372 //reset arrival status on controllers
1373 for (size_t i = 0; i < Controllers.size(); i++)
1374 {
1375 if (sbs->GetController(Controllers[i]))
1377 }
1378
1379 //get elevator's current altitude
1382
1383 //get elevator's current floor (first car)
1384 GetCar(1)->CurrentFloor = GetCar(1)->GetFloor();
1386
1387 //switch off directional indicators on current floor(s) if not already done so
1389
1390 //exit if floor doesn't exist
1391 if (!sbs->GetFloor(GotoFloor) && ManualMove == 0)
1392 {
1393 ReportError("Destination floor does not exist");
1394 MoveElevator = false;
1395 MovementRunning = false;
1396 Error = true;
1398 return;
1399 }
1400
1401 //if destination floor is not a serviced floor, reset and exit
1402 if (GetCar(GotoFloorCar)->IsServicedFloor(GotoFloor) == false && InspectionService == false && ManualMove == 0)
1403 {
1404 ReportError("Destination floor not a serviced floor");
1405 MoveElevator = false;
1406 MovementRunning = false;
1407 Error = true;
1409 return;
1410 }
1411
1412 //if elevator is already on specified floor, open doors and finish
1413 if (GetCar(GotoFloorCar)->CurrentFloor == GotoFloor && InspectionService == false && IsLeveled() == true && ManualMove == 0)
1414 {
1415 ReportError("Elevator already on specified floor");
1416 MoveElevator = false;
1417 MovementRunning = false;
1418 SkipFloorSound = true; //don't play floor announcement if on same floor
1419 Error = true;
1420 goto finish; //skip main processing and run cleanup section
1421 }
1422
1423 //determine direction
1424 if (InspectionService == false && ManualMove == 0)
1425 {
1428 {
1429 Direction = -1;
1430 dir_string = "down";
1431 }
1433 {
1434 Direction = 1;
1435 dir_string = "up";
1436 }
1437 }
1438 else
1439 {
1440 if (Direction == -1)
1441 dir_string = "down";
1442 else if (Direction == 1)
1443 dir_string = "up";
1444 }
1445
1447
1448 bool skip_interlock = false;
1449 if (InspectionService == true && HoistwayAccess == Direction)
1450 {
1451 //skip the interlock check if Hoistway Access is enabled
1452 //for the floor and direction, and the shaft doors are open
1453 if (GetCar(GotoFloorCar)->AreShaftDoorsOpen(0, HoistwayAccessFloor) == true)
1454 {
1455 skip_interlock = true;
1456 Report("skipping interlock check for floor " + ToString(HoistwayAccessFloor));
1457 }
1458 }
1459
1460 //exit if doors are not fully closed while interlocks enabled
1461 if (CheckInterlocks() == false && skip_interlock == false)
1462 {
1463 ReportError("Doors must be closed before moving when interlocks are enabled");
1464 MoveElevator = false;
1465 MovementRunning = false;
1466 Error = true;
1467 Direction = 0;
1469 return;
1470 }
1471
1472 //determine distance to destination floor
1473 if (InspectionService == false && ManualMove == 0)
1474 DistanceToTravel = std::abs(std::abs(Destination) - std::abs(ElevatorStart));
1475 else
1476 {
1477 //otherwise if inspection service is on, choose the altitude of the top/bottom floor
1478 if (Direction == 1)
1479 {
1482 {
1483 //don't go above top floor
1484 ReportError("cannot go above top floor");
1485 Destination = 0;
1486 Direction = 0;
1487 MoveElevator = false;
1488 MovementRunning = false;
1489 Error = true;
1491 return;
1492 }
1493 }
1494 else
1495 {
1498 {
1499 //don't go below bottom floor
1500 ReportError("cannot go below bottom floor");
1501 Destination = 0;
1502 Direction = 0;
1503 MoveElevator = false;
1504 MovementRunning = false;
1505 Error = true;
1507 return;
1508 }
1509 }
1510 DistanceToTravel = std::abs(std::abs(Destination) - std::abs(ElevatorStart));
1511 }
1513
1514 //if user is riding this elevator, then turn off objects
1515 if (sbs->ElevatorSync == true && sbs->ElevatorNumber == Number && InspectionService == false && ManualMove == 0)
1516 {
1517 if (sbs->Verbose)
1518 Report("user in elevator - turning off objects");
1519
1520 //turn off floor
1521 if (GetShaft()->ShowFloors == 0)
1522 {
1525 }
1526 else if (GetShaft()->IsShowFloor(sbs->camera->CurrentFloor) == false)
1527 {
1530 }
1531
1532 //turn off sky, buildings, and landscape
1533 if (GetShaft()->ShowOutside == false)
1534 {
1535 sbs->EnableSkybox(false);
1536 sbs->EnableBuildings(false);
1537 sbs->EnableLandscape(false);
1538 sbs->EnableExternal(false);
1539 }
1540 else if (GetShaft()->IsShowOutside(sbs->camera->CurrentFloor) == false)
1541 {
1542 sbs->EnableSkybox(false);
1543 sbs->EnableBuildings(false);
1544 sbs->EnableLandscape(false);
1545 sbs->EnableExternal(false);
1546 }
1547
1548 //reset shaft doors
1550 }
1551
1552 //set interior directional indicators
1554
1555 //set external active-direction indicators
1558
1559 std::string car_msg = "";
1560 if (GetCarCount() > 1)
1561 car_msg = " for car " + ToString(GotoFloorCar);
1562
1563 //notify about movement
1564 if (InspectionService == false && ManualMove == 0)
1565 Report("moving " + dir_string + " to floor " + ToString(GotoFloor) + " (" + sbs->GetFloor(GotoFloor)->ID + ")" + car_msg);
1566 else
1567 Report("moving " + dir_string);
1568 IsMoving = true;
1569 OnFloor = false;
1570 SoundsQueued = true;
1571
1572 //start departure timer
1573 if (DepartureDelay > 0 && WaitForTimer == false)
1574 {
1575 if (sbs->Verbose)
1576 Report("started departure delay");
1577 WaitForTimer = true;
1578 departure_delay->Start(int(DepartureDelay * 1000), false);
1579 return;
1580 }
1581 }
1582
1583 if (SoundsQueued == true)
1584 {
1585 SoundsQueued = false;
1586
1587 if (DepartureDelay > 0)
1588 {
1589 if (sbs->Verbose)
1590 Report("finished departure delay");
1592 }
1593
1594 //play directional message sound if MessageOnDoor is false
1595 for (size_t i = 0; i < Cars.size(); i++)
1596 {
1597 if (Cars[i]->MessageOnMove == true)
1598 Cars[i]->PlayMessageSound(true);
1599 }
1600
1602 }
1603
1604 if (EmergencyStop > 0 && Brakes == false)
1605 {
1606 //stop
1607 if (sbs->Verbose)
1608 {
1609 if (EmergencyStop == 2)
1610 Report("handling emergency stop");
1611 else
1612 Report("handling stop");
1613 }
1615
1616 if (EmergencyStop == 2)
1618 else
1620
1621 if (Direction == 1)
1622 Direction = -1;
1623 else
1624 Direction = 1;
1625 Brakes = true;
1626
1627 //play stopping sounds
1628 if (EmergencyStop == 1)
1630 else
1631 PlayStoppingSounds(true);
1632 }
1633
1634 if (Brakes == false)
1636
1637 //move elevator objects and camera
1638 movement.y = ElevatorRate * sbs->delta;
1639
1640 MoveObjects(movement.y);
1641
1642 //motion calculation
1643 if (Brakes == false)
1644 {
1645 //calculate jerk rate
1646 if (JerkRate < 1)
1647 {
1650 }
1651
1652 //regular motion
1653 Real limit = 0;
1654 if (InspectionService == false)
1655 {
1656 if (ActiveDirection == 1)
1657 limit = UpSpeed;
1658 else
1659 limit = DownSpeed;
1660 }
1661 else if (HoistwayAccess == 0)
1662 {
1663 if (ActiveDirection == 1)
1664 limit = UpSpeed * InspectionSpeed;
1665 else
1666 limit = DownSpeed * InspectionSpeed;
1667 }
1668 else
1669 limit = LevelingSpeed; //if hoistway access is on, run at leveling speed
1670
1671 Real Speed;
1672 if (ActiveDirection == 1)
1673 Speed = UpSpeed;
1674 else
1675 Speed = DownSpeed;
1676
1677 if (Direction == 1 && ElevatorRate < limit)
1678 ElevatorRate += Speed * ((Acceleration * JerkRate) * sbs->delta);
1679 else if (Direction == -1 && ElevatorRate > -limit)
1680 ElevatorRate -= Speed * ((Acceleration * JerkRate) * sbs->delta);
1681 else
1683 }
1684 else if (Leveling == false)
1685 {
1686 //slow down
1687
1688 //calculate jerk rate
1689 //check if the elevator rate is less than the amount that was stored in JerkPos
1690 //(the elevator rate at the end of the JerkRate increments), adjusted to the ratio of acceljerk to deceljerk
1691
1692 Real tmppos = JerkPos * (AccelJerk / DecelJerk);
1693 if ((Direction == -1 && ElevatorRate <= tmppos) || (Direction == 1 && ElevatorRate >= tmppos))
1694 {
1695 if (ElevatorRate != 0)
1696 {
1697 if (tmpDecelJerk == 0)
1698 tmpDecelJerk = DecelJerk * (tmppos / ElevatorRate);
1700 }
1701 }
1702 //prevent jerkrate from reaching 0
1703 if (JerkRate < 0)
1704 {
1705 JerkRate = 0;
1706 ElevatorRate = 0;
1707 }
1708
1709 Real Speed;
1710 if (ActiveDirection == 1)
1711 Speed = UpSpeed;
1712 else
1713 Speed = DownSpeed;
1714
1715 if (Direction == 1)
1716 ElevatorRate += Speed * ((TempDeceleration * JerkRate) * sbs->delta);
1717 if (Direction == -1)
1718 ElevatorRate -= Speed * ((TempDeceleration * JerkRate) * sbs->delta);
1719 }
1720
1721 //prevent the rate from going beyond 0
1722 if (Direction == 1 && Brakes == true && ElevatorRate > 0)
1723 ElevatorRate = 0;
1724 if (Direction == -1 && Brakes == true && ElevatorRate < 0)
1725 ElevatorRate = 0;
1726
1727 //get distance needed to stop elevator
1728 if (CalculateStoppingDistance == true)
1729 {
1730 if (Direction == 1)
1731 //stopping distance is the distance the elevator has traveled (usually to reach max speed), times
1732 //the ratio of acceleration to deceleration (so if the deceleration is half of the acceleration,
1733 //it will take twice the distance to stop)
1735 else if (Direction == -1)
1737 }
1738
1739 //Deceleration routines with floor overrun correction (there's still problems, but it works pretty good)
1740 //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,
1741 //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
1742 //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
1743 //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
1744 //and then starting the deceleration process immediately.
1745
1746 //determine if next jump altitude is over deceleration marker
1747 if (Brakes == false)
1748 {
1750 {
1751 if (sbs->Verbose)
1752 Report("beyond deceleration marker - slowing down");
1753
1754 //up movement
1755 if (Direction == 1)
1756 {
1758
1759 //recalculate deceleration value based on distance from marker, and store result in TempDeceleration
1762 //if elevator is beyond leveling offset, ignore the offset
1763 else if (Destination > elevposition.y)
1765 else
1766 {
1767 //if elevator is at destination
1768 TempDeceleration = 0;
1769 ElevatorRate = 0;
1770 }
1771
1772 //start deceleration
1773 Direction = -1;
1774 Brakes = true;
1775 Real Speed;
1776 if (ActiveDirection == 1)
1777 Speed = UpSpeed;
1778 else
1779 Speed = DownSpeed;
1780
1781 if (InspectionService == false)
1782 ElevatorRate -= Speed * ((TempDeceleration * JerkRate) * sbs->delta);
1783 else
1785 }
1786 //down movement
1787 else if (Direction == -1)
1788 {
1790
1791 //recalculate deceleration value based on distance from marker, and store result in TempDeceleration
1794 //if elevator is beyond leveling offset, ignore the offset
1795 else if (Destination < elevposition.y)
1797 else
1798 {
1799 //if elevator is at destination
1800 TempDeceleration = 0;
1801 ElevatorRate = 0;
1802 }
1803
1804 //start deceleration
1805 Direction = 1;
1806 Brakes = true;
1807 Real Speed;
1808 if (ActiveDirection == 1)
1809 Speed = UpSpeed;
1810 else
1811 Speed = DownSpeed;
1812
1813 if (InspectionService == false)
1814 ElevatorRate += Speed * ((TempDeceleration * JerkRate) * sbs->delta);
1815 else
1817 }
1818
1819 if (ElevatorRate != 0.0)
1820 {
1822
1823 if ((NotifyEarly == 2 || NotifyEarly == 3) && Parking == false)
1824 NotifyArrival(true);
1825 }
1826 }
1827 }
1828 else if (Leveling == false && EmergencyStop == 0)
1829 {
1830 if (std::abs(ElevatorRate) <= LevelingSpeed)
1831 {
1832 //turn on leveling if elevator's rate is less than or equal to the leveling speed value
1833 if (sbs->Verbose)
1834 Report("leveling enabled");
1835 Leveling = true;
1836 StartLeveling = true;
1837
1838 if (NotifyEarly == 1 && Parking == false)
1839 NotifyArrival(true);
1840 }
1841 }
1842
1843 //play floor beep and update indicators, if passing by or arriving at a floor
1844 if ((GetCar(1)->GetFloor() != oldfloor && Leveling == false) || StartLeveling == true)
1845 {
1846 Real alt = sbs->GetFloor(GetCar(1)->GetFloor())->Altitude;
1847 bool pass = false;
1848
1849 //determine if elevator will pass floor, only for down movement
1850 if (ActiveDirection == -1 && elevposition.y >= alt && elevposition.y + movement.y < alt)
1851 pass = true;
1852 if (ActiveDirection == 1)
1853 pass = true;
1854
1855 ElevatorCar *gotocar = GetCar(GotoFloorCar);
1856
1857 //if elevator hasn't started leveling, and is about to arrive at the destination, cancel any update
1858 if (gotocar->GetFloor() == GotoFloor && StartLeveling == false)
1859 pass = false;
1860
1861 if (pass == true || StartLeveling == true)
1862 {
1863 if (sbs->Verbose)
1864 {
1865 if (GotoFloorCar == 1)
1866 Report("on floor " + ToString(GetCar(1)->GetFloor()));
1867 else
1868 Report("on floor " + ToString(GetCar(1)->GetFloor()) + " (" + ToString(GetCar(GotoFloorCar)->GetFloor()) + " for car " + ToString(GotoFloorCar) + ")");
1869 }
1870
1871 //play floor beep sound if floor is a serviced floor
1872 if (gotocar->IsServicedFloor(gotocar->GetFloor()) == true)
1873 gotocar->PlayFloorBeep();
1874
1875 //update floor indicators
1877
1878 //update floor indicators on current camera floor
1881
1882 oldfloor = GetCar(1)->GetFloor();
1883 }
1884 StartLeveling = false;
1885 }
1886
1887 if (Leveling == true)
1888 {
1889 //floor leveling routine
1890 if (Direction == -1 && (Destination - elevposition.y) > 0)
1892 else if (Direction == 1 && (elevposition.y - Destination) > 0)
1894 else
1895 {
1896 if (sbs->Verbose)
1897 Report("arrived at floor");
1898 ElevatorRate = 0; //stop if on floor
1899 }
1900
1901 //open doors if leveling open offset is not zero
1902 if (LevelingOpen > 0 && FinishedMove == false && ArrivalDelay == 0)
1903 {
1904 if (Direction == -1 && (Destination - elevposition.y) < LevelingOpen)
1905 FinishMove();
1906 else if (Direction == 1 && (elevposition.y - Destination) < LevelingOpen)
1907 FinishMove();
1908 }
1909 }
1910
1911 //exit if elevator's running
1912 if (ElevatorRate != 0)
1913 return;
1914
1915 //start arrival timer
1916 if (ArrivalDelay > 0)
1917 {
1918 if (WaitForTimer == false && arrival_delay->IsRunning() == false)
1919 {
1920 if (sbs->Verbose)
1921 Report("started arrival delay");
1922 WaitForTimer = true;
1923 arrival_delay->Start(int(ArrivalDelay * 1000), false);
1924 return;
1925 }
1926 else
1927 {
1928 if (sbs->Verbose)
1929 Report("finished arrival delay");
1931 }
1932 }
1933
1934 //finish move
1935 if (EmergencyStop == 0)
1936 {
1937 if (sbs->Verbose)
1938 Report("storing error offset");
1939
1940 //store error offset value
1941 if (Direction == -1)
1943 else if (Direction == 1)
1945 else
1946 ErrorOffset = 0;
1947
1948 //set elevator and objects to floor altitude (corrects offset errors)
1949 //move elevator objects
1950 if (sbs->Verbose)
1951 Report("setting elevator to floor altitude");
1952
1954 }
1955
1956 //reset values if at destination floor
1957finish:
1958 if (sbs->Verbose)
1959 Report("resetting elevator motion values");
1960
1961 if (GoPending == true)
1962 {
1963 GoPending = false;
1964 GetCar(GotoFloorCar)->ChangeLight(GetCar(GotoFloorCar)->GetFloor(), false);
1965 }
1966 ElevatorRate = 0;
1967 JerkRate = 0;
1968 Direction = 0;
1969 Brakes = false;
1970 Destination = 0;
1971 DistanceToTravel = 0;
1972 ElevatorStart = 0;
1973 MovementRunning = false;
1974 MoveElevator = false;
1975 IsMoving = false;
1976 Leveling = false;
1977 tmpDecelJerk = 0;
1978
1979 StopSounds();
1980
1981 if (FinishedMove == false)
1982 FinishMove();
1983 else
1984 EmergencyStop = 0; //make sure emergency stop status is cleared
1985
1986 //dequeue floor route
1987 if (EmergencyStop == 0 && IsManuallyStopped() == false)
1989
1990 //reset cars' GotoFloor states
1991 for (int i = 1; i <= GetCarCount(); i++)
1992 {
1993 GetCar(i)->GotoFloor = false;
1994 }
1995}
1996
1998{
1999 //move elevator and objects vertically
2000
2001 SBS_PROFILE("Elevator::MoveObjects");
2002
2003 Vector3 vector (0, offset, 0);
2004
2005 Move(vector);
2006 elevposition.y = GetPosition().y;
2007
2008 //move counterweight
2009 if (WeightMesh)
2010 {
2011 WeightMesh->Move(-vector * 2);
2012 WeightRopeMesh->Move(-vector * 2);
2013 Floor *topfloor = sbs->GetFloor(GetTopFloor());
2014 Real counterweight_rope_height = MotorPosition.y - (WeightMesh->GetPosition().y + (weight_size.y - 0.5));
2015 WeightRopeMesh->ChangeHeight(counterweight_rope_height);
2016 Real rope_height = MotorPosition.y - RopeMesh->GetPosition().y;
2017 RopeMesh->ChangeHeight(rope_height);
2018 }
2019
2020 //move camera
2021 if (sbs->ElevatorSync == true && sbs->ElevatorNumber == Number)
2022 sbs->camera->MovePosition(vector);
2023}
2024
2026{
2027 //post-move operations, such as chimes, opening doors, indicator updates, etc
2028
2029 if (IsManuallyStopped() == true || InspectionService == true)
2030 {
2031 GotoFloor = GetCar(1)->GetFloor();
2032 GotoFloorCar = 1;
2033 }
2034
2035 if (EmergencyStop == 0 || IsManuallyStopped() == true)
2036 {
2037 //the elevator is now stopped on a valid floor; set OnFloor to true
2038 OnFloor = true;
2039 if (GetCarCount() == 1)
2040 Report("arrived at floor " + ToString(GotoFloor) + " (" + sbs->GetFloor(GotoFloor)->ID + ")");
2041 else
2042 Report("arrived at floor " + ToString(GotoFloor) + " (" + sbs->GetFloor(GotoFloor)->ID + ") in car " + ToString(GotoFloorCar));
2043 }
2044
2045 //turn off interior directional indicators
2046 ActiveDirection = 0;
2048
2049 //update external active-direction indicators
2052
2053 if ((EmergencyStop == 0 || IsManuallyStopped() == true) && InspectionService == false)
2054 {
2055 //update floor indicators on current camera floor
2058
2060
2062
2063 if (!cameracar)
2064 cameracar = GetCar(GotoFloorCar);
2065
2066 //turn on objects if user is in elevator
2067 if (sbs->ElevatorSync == true && sbs->ElevatorNumber == Number && cameracar->CameraOffset < cameracar->Height)
2068 {
2069 if (sbs->Verbose)
2070 Report("user in elevator - turning on objects");
2071
2072 //turn on floor
2073 int floor = GotoFloor;
2075 floor = sbs->camera->CurrentFloor;
2076 sbs->GetFloor(floor)->Enabled(true);
2077 sbs->GetFloor(floor)->EnableGroup(true);
2078
2079 //Turn on sky, buildings, and landscape
2080 sbs->EnableSkybox(true);
2081 sbs->EnableBuildings(true);
2082 sbs->EnableLandscape(true);
2083 sbs->EnableExternal(true);
2084
2085 //reset shaft doors
2086 for (int i = 1; i <= GetCarCount(); i++)
2087 {
2088 if (GetCar(i)->GotoFloor == true)
2089 {
2090 int floor = GetFloorForCar(i, GotoFloor);
2091 ResetShaftDoors(floor);
2092 }
2093 }
2094 }
2095 else if (sbs->Verbose)
2096 Report("user not in elevator - not turning on objects");
2097
2098 //notify arrival and disable call button light
2099 if (InServiceMode() == false)
2100 {
2101 //notify on arrival
2102 if (((NotifyEarly == 0 || NotifyEarly == 3) || Notified == false) && Parking == false && NotifyEarly != -1)
2103 NotifyArrival(false);
2104
2105 //get status of call buttons before switching off
2107
2108 for (int i = 1; i <= GetCarCount(); i++)
2109 {
2110 if (GetCar(i)->GotoFloor == true)
2111 {
2112 int floor = GetFloorForCar(i, GotoFloor);
2113
2114 //notify dispatch controllers of arrival
2115 for (size_t i = 0; i < Controllers.size(); i++)
2116 {
2117 if (sbs->GetController(Controllers[i]))
2119 }
2120 }
2121 }
2122 }
2123
2124 //reset queues if specified
2125 if (QueueResets == true)
2126 {
2127 //if last entry in current queue, reset opposite queue
2128 if (QueuePositionDirection == 1 && UpQueue.size() <= 1 && DownQueue.empty() == false)
2129 ResetQueue(false, true, false);
2130 else if (QueuePositionDirection == -1 && DownQueue.size() <= 1 && UpQueue.empty() == false)
2131 ResetQueue(true, false, false);
2132 }
2133
2134 //reverse queues if at either top or bottom of serviced floors
2136 {
2137 if (sbs->Verbose)
2138 Report("at top floor; setting queue search direction to down");
2141 }
2143 {
2144 if (sbs->Verbose)
2145 Report("at bottom floor; setting queue search direction to up");
2148 }
2149
2150 //open doors
2151 //do not automatically open doors if fire service phase 2 is on
2152 if (FireServicePhase2 != 1 || OnRecallFloor() == true)
2153 {
2154 if (Parking == false)
2155 if (AutoDoors == true)
2156 {
2157 if (FireServicePhase2 == 1)
2158 {
2161 }
2162 else
2163 {
2164 if ((OnRecallFloor() == true && FireServicePhase1 == 1) || OnPeakFloor() == true)
2165 OpenDoors(); //automatically open doors in Fire Phase 1 and Peak modes
2166 else
2167 {
2168 for (int i = 1; i <= GetCarCount(); i++)
2169 {
2170 if (GetCar(i)->GotoFloor == true)
2171 GetCar(i)->OpenDoors();
2172 }
2173 }
2174 }
2175 }
2176 }
2177 }
2178 else
2179 {
2180 if (sbs->Verbose)
2181 Report("stop complete");
2182
2183 //reset shaft doors
2184 if (sbs->ElevatorSync == true && sbs->ElevatorNumber == Number)
2185 {
2186 for (int i = 1; i <= GetCarCount(); i++)
2187 {
2188 if (GetCar(i)->GotoFloor == true)
2189 {
2190 int floor = GetFloorForCar(i, GotoFloor);
2191 ResetShaftDoors(floor);
2192 }
2193 }
2194 }
2195 }
2196
2197 //update car floor numbers
2198 for (int i = 1; i <= GetCarCount(); i++)
2199 {
2200 GetCar(i)->CurrentFloor = GetCar(i)->GetFloor();
2201 }
2202
2203 EmergencyStop = 0;
2204 ManualStop = false;
2205 SkipFloorSound = false;
2206 Parking = false;
2207 FinishedMove = true;
2208}
2209
2211{
2212 //dump both (up and down) elevator queues
2213
2214 Object::Report("\n--- " + GetName() + "'s Queues ---\n");
2215
2216 if (UpQueue.size() > 0)
2217 Object::Report("Up:");
2218
2219 for (size_t i = 0; i < UpQueue.size(); i++)
2220 {
2221 std::string type = "Car";
2222 if (UpQueue[i].call_type == 1)
2223 type = "Hall";
2224 if (UpQueue[i].call_type == 2)
2225 type = "System";
2226 if (UpQueue[i].call_type == 3)
2227 type = "Destination";
2228
2229 std::string car;
2230 if (GetCarCount() > 1)
2231 car = "\t-\tCar: " + ToString(UpQueue[i].car);
2232
2233 Object::Report("Entry: " + ToString((int)i) + "\t-\tFloor: " + ToString(UpQueue[i].floor) + "\t-\tCall type: " + type + car);
2234 }
2235
2236 if (DownQueue.size() > 0)
2237 Object::Report("Down:");
2238
2239 for (size_t i = 0; i < DownQueue.size(); i++)
2240 {
2241 std::string type = "Car";
2242 if (DownQueue[i].call_type == 1)
2243 type = "Hall";
2244 if (DownQueue[i].call_type == 2)
2245 type = "System";
2246 if (DownQueue[i].call_type == 3)
2247 type = "Destination";
2248
2249 std::string car;
2250 if (GetCarCount() > 1)
2251 car = "\t-\tCar: " + ToString(DownQueue[i].car);
2252
2253 Object::Report("Entry: " + ToString((int)i) + "\t-\tFloor: " + ToString(DownQueue[i].floor) + "\t-\tCall type: " + type + car);
2254 }
2255 Object::Report("");
2256}
2257
2258void Elevator::Enabled(bool value)
2259{
2260 //shows/hides elevator
2261
2262 if (IsEnabled == value)
2263 return;
2264
2265 EnableLoop(value);
2266
2267 if (sbs->Verbose)
2268 {
2269 if (value == true)
2270 Report("enabling elevator");
2271 else
2272 Report("disabling elevator");
2273 }
2274
2275 for (size_t i = 0; i < Cars.size(); i++)
2276 {
2277 Cars[i]->Enabled(value);
2278 }
2279 IsEnabled = value;
2280}
2281
2282ElevatorCar* Elevator::IsInElevator(const Vector3 &position, bool camera)
2283{
2284 //determine if the given 3D position is inside the elevator
2285 //returns the related car object, or 0 if not found
2286
2287 //first checks to see if camera is within a car's height range, and then
2288 //checks for a collision with the car's floor below
2289
2290 //if camera is true, set associated camera offset
2291
2292 //SBS_PROFILE("Elevator::IsInElevator");
2293
2294 if (IsEnabled == false)
2295 return 0;
2296
2297 for (size_t i = 0; i < Cars.size(); i++)
2298 {
2299 if (Cars[i]->IsInCar(position, camera) == true)
2300 return Cars[i];
2301 }
2302 return 0;
2303}
2304
2306{
2307 //returns the internal elevator starting position
2308 return ElevatorStart;
2309}
2310
2312{
2313 //returns the internal destination value
2314 return Destination;
2315}
2316
2318{
2319 //returns the internal stopping distance value
2320 return StoppingDistance;
2321}
2322
2324{
2325 //returns the internal brake status value
2326 return Brakes;
2327}
2328
2330{
2331 //returns the internal emergency stop status
2332 return EmergencyStop;
2333}
2334
2336{
2337 //updates all floor indicators
2338
2339 SBS_PROFILE("Elevator::UpdateFloorIndicators");
2340
2341 for (size_t i = 0; i < Cars.size(); i++)
2342 {
2343 Cars[i]->UpdateFloorIndicators();
2344 }
2345}
2346
2348{
2349 return JerkRate;
2350}
2351
2353{
2354 return JerkPos;
2355}
2356
2357void Elevator::SetFloorSkipText(const std::string &id)
2358{
2359 //sets the text shown in the floor indicator while skipping floors (an express zone)
2360
2361 std::string text = id;
2362 TrimString(text);
2363
2364 if (text == "")
2365 {
2366 UseFloorSkipText = false;
2367 FloorSkipText = text;
2368 return;
2369 }
2370
2371 if (sbs->Verbose)
2372 Report("setting floor skip text to " + text);
2373 UseFloorSkipText = true;
2374 FloorSkipText = id;
2375}
2376
2378{
2379 //get the floor skip text
2380 return FloorSkipText;
2381}
2382
2384{
2385 //report if an elevator is in a service mode
2386 if (IndependentService == true || InspectionService == true || FireServicePhase1 == 1 || FireServicePhase2 > 0)
2387 return true;
2388 else
2389 return false;
2390}
2391
2392bool Elevator::Go(int floor, bool hold)
2393{
2394 //go to specified floor, bypassing the queuing system
2395
2396 //only run if power is enabled
2397 if (sbs->GetPower() == false)
2398 return false;
2399
2400 if (Running == false)
2401 return ReportError("Elevator not running");
2402
2403 if (!sbs->GetFloor(floor))
2404 return ReportError("Invalid floor " + ToString(floor));
2405
2406 //exit if in inspection mode
2407 if (InspectionService == true)
2408 {
2409 if (sbs->Verbose)
2410 ReportError("Go: in inspection mode");
2411 return false;
2412 }
2413
2414 if (GoActive == false || hold == false)
2415 {
2416 //exit if elevator is moving
2417 if (MoveElevator == true)
2418 return false;
2419
2420 ElevatorCar *car = GetCarForFloor(floor);
2421 if (!car)
2422 return false;
2423
2424 if (hold == true)
2425 {
2426 GoActive = true;
2427 GoActiveFloor = floor;
2428 }
2429 Report("Go: proceeding to floor " + ToString(floor) + " (" + sbs->GetFloor(floor)->ID + ")");
2430 ChangeLight(floor, true);
2431 GotoFloor = floor;
2432 GotoFloorCar = car->Number;
2433 GoPending = true;
2434 if (AutoDoors == true)
2435 {
2436 WaitForDoors = true;
2437 CloseDoors();
2438 }
2439 MoveElevator = true;
2440 }
2441 else if (GoActive == true && hold == true && sbs->camera->MouseDown() == false)
2442 {
2443 //stop go movement
2444 GoActive = false;
2445 GoActiveFloor = 0;
2446 Stop();
2447 ChangeLight(floor, false);
2448 }
2449 return true;
2450}
2451
2453{
2454 //for fire service modes; tells the elevator to go to the recall floor (or the alternate recall floor
2455 //if the other is not available)
2456
2457 //only run if power is enabled
2458 if (sbs->GetPower() == false)
2459 return;
2460
2461 if (Running == false)
2462 {
2463 ReportError("Elevator not running");
2464 return;
2465 }
2466
2467 //reset queues (this will also stop the elevator)
2468 ResetQueue(true, true);
2469
2470 if (OnRecallFloor() == true)
2471 {
2472 if (RecallUnavailable == false)
2473 Report("On recall floor");
2474 else
2475 Report("On alternate recall floor");
2476 if (AutoDoors == true)
2477 {
2478 OpenDoors();
2479 }
2480 return;
2481 }
2482
2483 if (RecallUnavailable == false)
2484 {
2485 Report("Proceeding to recall floor");
2486 if (RecallFloor > GetCar(1)->GetFloor())
2487 AddRoute(RecallFloor, 1, 2);
2488 else
2489 AddRoute(RecallFloor, -1, 2);
2490 }
2491 else
2492 {
2493 Report("Proceeding to alternate recall floor");
2494 if (RecallFloorAlternate > GetCar(1)->GetFloor())
2496 else
2498 }
2499}
2500
2502{
2503 //returns the recall floor of the elevator
2504
2505 if (RecallUnavailable == true)
2506 return RecallFloorAlternate;
2507 return RecallFloor;
2508}
2509
2510bool Elevator::EnableACP(bool value)
2511{
2512 //enable Anti-Crime Protection (ACP) mode
2513
2514 //only run if power is enabled
2515 if (sbs->GetPower() == false)
2516 return false;
2517
2518 if (Running == false)
2519 return ReportError("Elevator not running");
2520
2521 //exit if no change
2522 if (ACP == value)
2523 {
2524 if (sbs->Verbose)
2525 ReportError("EnableACP: mode is the same");
2526 return true;
2527 }
2528
2529 if (value == true)
2530 {
2531 if (IndependentService == true)
2532 return ReportError("EnableACP: cannot enable while in independent service mode");
2533 if (InspectionService == true)
2534 return ReportError("EnableACP: cannot enable while in inspection service mode");
2535 if (FireServicePhase1 > 0 || FireServicePhase2 > 0)
2536 return ReportError("EnableACP: cannot enable while in a fire service mode");
2537 }
2538
2539 ACP = value;
2540
2541 if (value == true)
2542 {
2543 SetControls("acpon");
2544 Report("ACP mode enabled");
2545 }
2546 else
2547 {
2548 SetControls("acpoff");
2549 Report("ACP mode disabled");
2550 }
2551
2552 return true;
2553}
2554
2556{
2557 //enable Up-Peak mode
2558
2559 //only run if power is enabled
2560 if (sbs->GetPower() == false)
2561 return false;
2562
2563 if (Running == false)
2564 return ReportError("Elevator not running");
2565
2566 //exit if no change
2567 if (UpPeak == value)
2568 {
2569 if (sbs->Verbose)
2570 ReportError("EnableUpPeak: mode is the same");
2571 return true;
2572 }
2573
2574 if (value == true)
2575 {
2576 if (IndependentService == true)
2577 return ReportError("EnableUpPeak: cannot enable while in independent service mode");
2578 if (InspectionService == true)
2579 return ReportError("EnableUpPeak: cannot enable while in inspection service mode");
2580 if (FireServicePhase1 > 0 || FireServicePhase2 > 0)
2581 return ReportError("EnableUpPeak: cannot enable while in a fire service mode");
2582 }
2583
2584 UpPeak = value;
2585
2586 if (value == true)
2587 {
2588 EnableDownPeak(false);
2589 if (IsMoving == false && OnBottomFloor() == true)
2590 {
2591 //set directional indicators on all cars
2592 for (size_t i = 0; i < Cars.size(); i++)
2593 {
2594 if (sbs->GetFloor(Cars[i]->GetFloor()))
2595 {
2596 Cars[i]->SetDirectionalIndicators(Cars[i]->GetFloor(), true, false);
2597
2598 if (AutoDoors == true)
2599 Cars[i]->OpenDoors();
2600 }
2601 }
2602 }
2603 SetControls("uppeakon");
2604 Report("Up Peak mode enabled");
2605 }
2606 else
2607 {
2608 ResetDoors();
2610 SetControls("uppeakoff");
2611 Report("Up Peak mode disabled");
2612 }
2613
2614 return true;
2615}
2616
2618{
2619 //enable Down-Peak mode
2620
2621 //only run if power is enabled
2622 if (sbs->GetPower() == false)
2623 return false;
2624
2625 if (Running == false)
2626 return ReportError("Elevator not running");
2627
2628 //exit if no change
2629 if (DownPeak == value)
2630 {
2631 if (sbs->Verbose)
2632 ReportError("EnableDownPeak: mode is the same");
2633 return true;
2634 }
2635
2636 if (value == true)
2637 {
2638 if (IndependentService == true)
2639 return ReportError("EnableDownPeak: cannot enable while in independent service mode");
2640 if (InspectionService == true)
2641 return ReportError("EnableDownPeak: cannot enable while in inspection service mode");
2642 if (FireServicePhase1 > 0 || FireServicePhase2 > 0)
2643 return ReportError("EnableDownPeak: cannot enable while in a fire service mode");
2644 }
2645
2646 DownPeak = value;
2647
2648 if (value == true)
2649 {
2650 EnableUpPeak(false);
2651 if (IsMoving == false && OnTopFloor() == true)
2652 {
2653 //set directional indicators on all cars
2654 for (size_t i = 0; i < Cars.size(); i++)
2655 {
2656 if (sbs->GetFloor(Cars[i]->GetFloor()))
2657 {
2658 Cars[i]->SetDirectionalIndicators(Cars[i]->GetFloor(), false, true);
2659
2660 if (AutoDoors == true)
2661 Cars[i]->OpenDoors();
2662 }
2663 }
2664 }
2665 SetControls("downpeakon");
2666 Report("Down Peak mode enabled");
2667 }
2668 else
2669 {
2670 ResetDoors();
2672 SetControls("downpeakoff");
2673 Report("Down Peak mode disabled");
2674 }
2675
2676 return true;
2677}
2678
2679bool Elevator::EnableIndependentService(bool value, int car_number)
2680{
2681 //enable Independent Service (ISC) mode
2682 //car_number is the car number to use for independent service; only used when enabling the mode
2683
2684 //only run if power is enabled
2685 if (sbs->GetPower() == false)
2686 return false;
2687
2688 if (Running == false)
2689 return ReportError("Elevator not running");
2690
2691 //exit if mode is already active for another car
2692 if (IndependentService == true && car_number > 0 && IndependentServiceCar != car_number)
2693 {
2694 ReportError("EnableIndependentService: mode already active for car " + ToString(IndependentServiceCar));
2695 return !value; //succeed if disabling mode
2696 }
2697
2698 //exit if no change
2699 if (IndependentService == value)
2700 {
2701 if (sbs->Verbose)
2702 ReportError("EnableIndependentService: mode is the same");
2703 return true;
2704 }
2705
2706 if (car_number == 0)
2707 car_number = 1;
2708
2709 if (value == true)
2710 {
2711 if (InspectionService == true)
2712 return ReportError("EnableIndependentService: cannot enable while in inspection service mode");
2713 if (FireServicePhase1 > 0 || FireServicePhase2 > 0)
2714 return ReportError("EnableIndependentService: cannot enable while in a fire service mode");
2715
2716 ElevatorCar *car = GetCar(car_number);
2717 if (!car)
2718 return ReportError("EnableIndependentService: invalid car " + ToString(car_number));
2719
2720 IndependentService = true;
2721 IndependentServiceCar = car_number;
2722 EnableACP(false);
2723 EnableUpPeak(false);
2724 EnableDownPeak(false);
2725 ResetQueue(true, true); //this will also stop the elevator
2726 car->HoldDoors(); //turn off door timers for selected car
2727 car->ResetNudgeTimer(false); //switch off nudge timer for selected car
2728 DirectionalIndicatorsOff(); //switch off directional indicators on current floor
2729 if (IsMoving == false)
2730 if (AutoDoors == true)
2731 car->OpenDoors();
2732 SetControls("indon");
2733 Report("Independent Service mode enabled for car " + ToString(car_number));
2734 }
2735 else
2736 {
2737 IndependentService = false;
2739 ResetQueue(true, true); //this will also stop the elevator
2740 ResetDoors();
2742 SetControls("indoff");
2743 Report("Independent Service mode disabled");
2744 }
2745
2746 return true;
2747}
2748
2750{
2751 //enable Inspection Service (INS) mode
2752
2753 //only run if power is enabled
2754 if (sbs->GetPower() == false)
2755 return false;
2756
2757 //exit if no change
2758 if (InspectionService == value)
2759 {
2760 if (sbs->Verbose)
2761 ReportError("EnableInspectionService: mode is the same");
2762 return true;
2763 }
2764
2765 if (value == true)
2766 {
2767 EnableACP(false);
2768 EnableUpPeak(false);
2769 EnableDownPeak(false);
2772 EnableFireService2(0, true);
2773 ResetQueue(true, true); //this will also stop the elevator
2774 HoldDoors(); //turn off door timers
2775 ResetNudgeTimers(false); //switch off nudge timer
2776 DirectionalIndicatorsOff(); //switch off directional indicators on current floor
2777 SetControls("inson");
2778 Report("Inspection Service mode enabled");
2779 HoistwayAccess = 0;
2781 InspectionService = true;
2782 }
2783 else
2784 {
2785 ResetDoors();
2787 SetControls("insoff");
2788 Report("Inspection Service mode disabled");
2789
2791
2792 //turn on objects if user is in elevator
2793 if (sbs->ElevatorSync == true && sbs->ElevatorNumber == Number && IsMoving == false && GetCar(sbs->CarNumber))
2794 {
2795 if (sbs->Verbose)
2796 Report("user in elevator - turning on objects");
2797
2798 int carfloor = GetCar(sbs->CarNumber)->GetFloor();
2799
2800 //turn on floor
2801 if (sbs->GetFloor(carfloor))
2802 {
2803 sbs->GetFloor(carfloor)->Enabled(true);
2804 sbs->GetFloor(carfloor)->EnableGroup(true);
2805 }
2806
2807 //Turn on sky, buildings, and landscape
2808 sbs->EnableSkybox(true);
2809 sbs->EnableBuildings(true);
2810 sbs->EnableLandscape(true);
2811 sbs->EnableExternal(true);
2812
2813 //reset shaft doors
2814 ResetShaftDoors(carfloor);
2815 }
2816
2817 HoistwayAccess = 0;
2819 InspectionService = false;
2820
2821 if (IsMoving == true)
2822 Stop();
2823 else
2825 }
2826
2827 return true;
2828}
2829
2831{
2832 //enable Fire Service Phase 1 mode
2833 //valid values are 0 (off), 1 (on) or 2 (bypass)
2834
2835 //only run if power is enabled
2836 if (sbs->GetPower() == false)
2837 return false;
2838
2839 if (Running == false)
2840 return ReportError("Elevator not running");
2841
2842 //exit if no change
2843 if (FireServicePhase1 == value)
2844 {
2845 if (sbs->Verbose)
2846 ReportError("EnableFireService1: mode is the same");
2847 return true;
2848 }
2849
2850 //exit if in inspection mode
2851 if (InspectionService == true && value > 0)
2852 return ReportError("EnableFireService1: cannot enable while in inspection service mode");
2853
2854 if (value < 0 || value > 2)
2855 return ReportError("EnableFireService1: invalid value");
2856
2857 FireServicePhase1 = value;
2858
2859 if (value > 0)
2860 {
2861 EnableACP(false);
2862 EnableUpPeak(false);
2863 EnableDownPeak(false);
2865 if (value == 1)
2866 {
2867 Report("Fire Service Phase 1 mode set to On");
2868
2869 //switch off directional indicators on current floor
2871
2872 //recall elevator if not in phase 2 hold
2873 if (FireServicePhase2 != 2)
2874 {
2875 //turn off all door timers
2876 HoldDoors();
2877 ResetNudgeTimers(false); //switch off nudge timer
2878
2879 //enable nudge mode on all doors if any are open
2880 if (OnRecallFloor() == false)
2881 EnableNudgeMode(true);
2882
2883 //goto recall floor
2885 }
2886 }
2887 else
2888 {
2889 if (FireServicePhase2 == 0)
2890 {
2891 ResetDoors(); //enable door timers
2893 }
2894 Report("Fire Service Phase 1 mode set to Bypass");
2895 }
2896 }
2897 else
2898 {
2899 if (FireServicePhase2 == 0)
2900 {
2901 ResetDoors(); //enable door timers
2903 }
2904 Report("Fire Service Phase 1 mode set to Off");
2905 }
2906
2907 return true;
2908}
2909
2910bool Elevator::EnableFireService2(int value, int car_number, bool force)
2911{
2912 //enable Fire Service Phase 2 mode
2913 //valid values are 0 (off), 1 (on) or 2 (hold)
2914 //if force is true, does not require doors to be opened to change value
2915
2916 //only run if power is enabled
2917 if (sbs->GetPower() == false)
2918 return false;
2919
2920 if (Running == false)
2921 return ReportError("Elevator not running");
2922
2923 if (value > 0)
2924 {
2925 //exit if in inspection mode
2926 if (InspectionService == true)
2927 return ReportError("EnableFireService2: cannot enable while in inspection service mode");
2928
2929 //require fire service phase 1 to be enabled first
2930 if (FireServicePhase1 != 1 && FireServicePhase2 == 0)
2931 return ReportError("EnableFireService2: not in fire service phase 1 mode");
2932 }
2933
2934 //exit if mode is already active for another car
2935 if (FireServicePhase2 > 0 && car_number > 0 && FireServicePhase2Car != car_number)
2936 {
2937 ReportError("EnableFireService2: mode already active for car " + ToString(FireServicePhase2Car));
2938 return !value; //succeed if disabling mode
2939 }
2940
2941 //require doors to be open to change modes
2942 if (AreDoorsOpen() == false && force == false)
2943 return ReportError("EnableFireService2: doors must be open to change phase 2 modes");
2944
2945 //exit if no change
2946 if (FireServicePhase2 == value)
2947 {
2948 if (sbs->Verbose)
2949 ReportError("EnableFireService2: mode is the same");
2950 return true;
2951 }
2952
2953 if (car_number == 0)
2954 car_number = 1;
2955
2956 if (value >= 0 && value <= 2)
2957 FireServicePhase2 = value;
2958 else
2959 return ReportError("EnableFireService2: invalid value");
2960
2961 if (value > 0)
2962 {
2963 ElevatorCar *car = GetCar(car_number);
2964 if (!car)
2965 return ReportError("EnableFireService2: invalid car " + ToString(car_number));
2966
2967 FireServicePhase2Car = car_number;
2968 EnableACP(false);
2969 EnableUpPeak(false);
2970 EnableDownPeak(false);
2972 ResetQueue(true, true); //this will also stop the elevator
2973 car->HoldDoors(); //disable all door timers for selected car
2974 car->ResetNudgeTimer(false); //switch off nudge timer for selected car
2975
2976 //close other doors
2977 for (int i = 1; i <= GetCarCount(); i++)
2978 {
2979 if (i != car_number)
2980 GetCar(i)->CloseDoors();
2981 }
2982
2983 if (value == 1)
2984 {
2985 SetControls("fire2on");
2986 Report("Fire Service Phase 2 mode set to On for car " + ToString(car_number));
2987 }
2988 else
2989 {
2990 SetControls("fire2hold");
2991 Report("Fire Service Phase 2 mode set to Hold for car " + ToString(car_number));
2992 }
2993 }
2994 else
2995 {
2996 SetControls("fire2off");
2997 Report("Fire Service Phase 2 mode set to Off");
2998
3000
3001 if (FireServicePhase1 == 0)
3002 {
3003 ResetDoors(); //enable door timers
3005 }
3006 else if (FireServicePhase1 == 1 && OnRecallFloor() == false)
3007 {
3008 //enable nudge mode on all doors if any are open
3009 EnableNudgeMode(true);
3010
3011 //recall elevator
3013 }
3014 }
3015
3016 return true;
3017}
3018
3020{
3021 //set elevator's fire recall floor
3022
3023 //only run if power is enabled
3024 if (sbs->GetPower() == false)
3025 return false;
3026
3027 if (GetCar(1)->IsServicedFloor(floor) == false)
3028 return ReportError("Invalid recall floor");
3029
3030 if (sbs->Verbose)
3031 Report("setting recall floor to " + ToString(floor));
3032 RecallFloor = floor;
3033 RecallSet = true;
3034 return true;
3035}
3036
3038{
3039 //set elevator's alternate fire recall floor
3040
3041 //only run if power is enabled
3042 if (sbs->GetPower() == false)
3043 return false;
3044
3045 if (GetCar(1)->IsServicedFloor(floor) == false)
3046 return ReportError("Invalid alternate recall floor");
3047
3048 if (sbs->Verbose)
3049 Report("setting alternate recall floor to " + ToString(floor));
3050 RecallFloorAlternate = floor;
3051 RecallAltSet = true;
3052 return true;
3053}
3054
3056{
3057 //set elevator's ACP floor
3058
3059 //only run if power is enabled
3060 if (sbs->GetPower() == false)
3061 return false;
3062
3063 ElevatorCar *car = GetCarForFloor(floor);
3064 if (!car)
3065 return ReportError("Invalid ACP floor");
3066 if (car->IsServicedFloor(floor) == false)
3067 return ReportError("Invalid ACP floor");
3068
3069 if (sbs->Verbose)
3070 Report("setting ACP floor to " + ToString(floor));
3071 ACPFloor = floor;
3072 ACPFloorSet = true;
3073 return true;
3074}
3075
3077{
3078 //manual up movement for inspection service mode
3079
3080 //only run if power is enabled
3081 if (sbs->GetPower() == false)
3082 return false;
3083
3084 if (Running == false)
3085 return ReportError("Elevator not running");
3086
3087 //moves elevator upwards if in Inspection Service mode
3088 if (InspectionService == false)
3089 return ReportError("Not in inspection service mode");
3090
3091 //make sure Go button is on
3092 if (ManualGo == false)
3093 {
3094 if (sbs->Verbose)
3095 ReportError("MoveUp: go button is off");
3096 return false;
3097 }
3098
3099 if (IsMoving == true)
3100 {
3101 if (sbs->Verbose)
3102 ReportError("MoveUp: already moving");
3103 return false;
3104 }
3105
3106 //set direction
3107 Direction = 1;
3108 MoveElevator = true;
3109 if (sbs->Verbose)
3110 Report("MoveUp: moving elevator");
3111 return true;
3112}
3113
3115{
3116 //manual down movement for inspection service mode
3117
3118 //only run if power is enabled
3119 if (sbs->GetPower() == false)
3120 return false;
3121
3122 if (Running == false)
3123 return ReportError("Elevator not running");
3124
3125 //moves elevator downwards if in Inspection Service mode
3126 if (InspectionService == false)
3127 return ReportError("Not in inspection service mode");
3128
3129 //make sure Go button is on
3130 if (ManualGo == false)
3131 {
3132 if (sbs->Verbose)
3133 ReportError("MoveDown: go button is off");
3134 return false;
3135 }
3136
3137 if (IsMoving == true)
3138 {
3139 if (sbs->Verbose)
3140 ReportError("MoveDown: already moving");
3141 return false;
3142 }
3143
3144 //set direction
3145 Direction = -1;
3146 MoveElevator = true;
3147 if (sbs->Verbose)
3148 Report("MoveDown: moving elevator");
3149 return true;
3150}
3151
3153{
3154 //sets the value of the Go button (for Inspection Service mode)
3155
3156 //only run if power is enabled
3157 if (sbs->GetPower() == false)
3158 return false;
3159
3160 if (Running == false)
3161 return ReportError("Elevator not running");
3162
3163 if (InspectionService == false)
3164 return false;
3165
3166 if (ManualGo == true && value == false)
3167 Stop();
3168
3169 ManualGo = value;
3170
3171 if (value == true)
3172 SetControls("goon");
3173 else
3174 SetControls("gooff");
3175
3176 if (sbs->Verbose)
3177 {
3178 if (value == true)
3179 Report("setting go button status to on");
3180 else
3181 Report("setting go button status to off");
3182 }
3183
3184 if (ManualGo == true)
3185 {
3186 if (ManualUp == true)
3187 MoveUp();
3188 if (ManualDown == true)
3189 MoveDown();
3190 }
3191
3192 return true;
3193}
3194
3196{
3197 //sets the value of the Up button (for Inspection Service mode)
3198
3199 //only run if power is enabled
3200 if (sbs->GetPower() == false)
3201 return false;
3202
3203 if (Running == false)
3204 return ReportError("Elevator not running");
3205
3206 if (InspectionService == false)
3207 return false;
3208
3209 if (ManualUp == true && value == false)
3210 Stop();
3211
3212 ManualUp = value;
3213
3214 if (value == true)
3215 SetControls("upon");
3216 else
3217 SetControls("upoff");
3218
3219 if (sbs->Verbose)
3220 {
3221 if (value == true)
3222 Report("setting up button status to on");
3223 else
3224 Report("setting up button status to off");
3225 }
3226
3227 if (ManualGo == true && value == true)
3228 MoveUp();
3229
3230 return true;
3231}
3232
3234{
3235 //sets the value of the Go button (for Inspection Service mode)
3236
3237 //only run if power is enabled
3238 if (sbs->GetPower() == false)
3239 return false;
3240
3241 if (Running == false)
3242 return ReportError("Elevator not running");
3243
3244 if (InspectionService == false)
3245 return false;
3246
3247 if (ManualDown == true && value == false)
3248 Stop();
3249
3250 ManualDown = value;
3251
3252 if (value == true)
3253 SetControls("downon");
3254 else
3255 SetControls("downoff");
3256
3257 if (sbs->Verbose)
3258 {
3259 if (value == true)
3260 Report("setting down button status to on");
3261 else
3262 Report("setting down button status to off");
3263 }
3264
3265 if (ManualGo == true && value == true)
3266 MoveDown();
3267
3268 return true;
3269}
3270
3272{
3273 //returns highest serviced floor
3274
3275 return GetCar(GetCarCount())->GetTopFloor();
3276}
3277
3279{
3280 //returns lowest serviced floor
3281
3282 return GetCar(1)->GetBottomFloor();
3283}
3284
3286{
3287 //updates all interior active direction indicators
3288
3289 SBS_PROFILE("Elevator::UpdateDirectionalIndicators");
3290
3291 for (size_t i = 0; i < Cars.size(); i++)
3292 {
3293 Cars[i]->UpdateDirectionalIndicators();
3294 }
3295}
3296
3298{
3299 //return true if elevator is idle (not moving, doors are closed (unless in a peak mode) and not moving)
3300
3301 //only run if power is enabled
3302 if (sbs->GetPower() == false)
3303 return false;
3304
3305 if (MoveElevator == false && (AreDoorsOpen() == false || UpPeak == true || DownPeak == true) && AreDoorsMoving() == 0 && Running == true)
3306 return true;
3307 else
3308 return false;
3309}
3310
3311void Elevator::ResetQueue(bool up, bool down, bool stop_if_empty)
3312{
3313 //reset queues
3314 //if stop_if_empty is true, the elevator will stop when the related queue is empty
3315
3316 QueuePending = false;
3317
3318 if (up == true)
3319 {
3320 if (sbs->Verbose)
3321 Report("QueueReset: resetting up queue");
3322 UpQueue.clear();
3323 HandleDequeue(1, stop_if_empty);
3324 }
3325 if (down == true)
3326 {
3327 if (sbs->Verbose)
3328 Report("QueueReset: resetting down queue");
3329 DownQueue.clear();
3330 HandleDequeue(-1, stop_if_empty);
3331 }
3332
3333 ResetLights();
3334}
3335
3337{
3338 //only run if power is enabled
3339 if (sbs->GetPower() == false)
3340 return;
3341
3342 if (Running == false)
3343 {
3344 ReportError("Elevator not running");
3345 return;
3346 }
3347
3348 //deletes the active route
3349 if (sbs->Verbose)
3350 Report("deleting active route");
3352
3353 //delete associated routes for any other cars
3354 for (int i = 1; i <= GetCarCount(); i++)
3355 {
3356 int floor = GetFloorForCar(i, ActiveCall.floor);
3357 if (IsQueued(floor, ActiveCall.direction) == true)
3359 }
3360
3361 ActiveCall.floor = 0;
3364 ActiveCall.car = 0;
3365}
3366
3368{
3369 if (QueuePositionDirection != 0)
3370 return true;
3371 return false;
3372}
3373
3374bool Elevator::BeyondDecelMarker(int direction, Real destination)
3375{
3376 //return true if elevator is beyond the deceleration marker for the specified direction
3377 //directions are 1 for up, -1 for down
3378
3379 if (direction == 1)
3380 {
3381 if (((GetPosition().y + (ElevatorRate * sbs->delta)) > (destination - StoppingDistance)))
3382 return true;
3383 }
3384 if (direction == -1)
3385 {
3386 if (((GetPosition().y - (ElevatorRate * sbs->delta)) < (destination + StoppingDistance)))
3387 return true;
3388 }
3389 return false;
3390}
3391
3392void Elevator::Report(const std::string &message)
3393{
3394 //general reporting function
3395 Object::Report(GetName() + ": " + message);
3396}
3397
3398bool Elevator::ReportError(const std::string &message)
3399{
3400 //general reporting function
3401 return Object::ReportError(GetName() + ": " + message);
3402}
3403
3405{
3406 //elevator timer loop
3407
3408 //only run if power is enabled
3409 if (sbs->GetPower() == false)
3410 return;
3411
3412 if (elevator->IsRunning() == false)
3413 return;
3414
3415 SBS_PROFILE("Elevator::Timer::Notify");
3416 if (type == 0)
3417 {
3418 //parking timer
3419
3420 if (elevator->ParkingDelay > 0 && elevator->IsIdle() == true && elevator->InServiceMode() == false)
3421 {
3423 if (car)
3424 {
3425 int floor = car->GetFloor();
3426 if (elevator->ParkingFloor != floor)
3427 {
3428 elevator->Report("parking to floor " + ToString(elevator->ParkingFloor));
3429 elevator->Parking = true;
3430 }
3431
3432 if (elevator->ParkingFloor > floor)
3434 else if (elevator->ParkingFloor < floor)
3436
3437 Stop();
3438 }
3439 }
3440 else if (elevator->InServiceMode() == true)
3441 Stop(); //stop timer if in service mode
3442 }
3443 else if (type == 1)
3444 {
3445 //arrival and departure timers
3446 elevator->WaitForTimer = false;
3447 }
3448 else if (type == 2)
3449 {
3450 //malfunction timer
3451 int result = (int)elevator->rnd_time->Get(elevator->RandomProbability - 1);
3452 if (result == 0)
3454 }
3455}
3456
3457bool Elevator::IsQueued(int floor, int queue)
3458{
3459 //return true if the given floor is in either queue
3460 //if queue is 0, check both queues; otherwise up queue with 1, and down queue with -1
3461
3462 if (queue == 0 || queue == 1)
3463 {
3464 for (size_t i = 0; i < UpQueue.size(); i++)
3465 {
3466 if (UpQueue[i].floor == floor)
3467 return true;
3468 }
3469 }
3470
3471 if (queue == 0 || queue == -1)
3472 {
3473 for (size_t i = 0; i < DownQueue.size(); i++)
3474 {
3475 if (DownQueue[i].floor == floor)
3476 return true;
3477 }
3478 }
3479
3480 return false;
3481}
3482
3484{
3485 //notify on elevator arrival (play chime and turn on related directional indicator lantern)
3486 //for all cars
3487
3488 //only run if power is enabled
3489 if (sbs->GetPower() == false)
3490 return;
3491
3492 for (int i = 1; i <= GetCarCount(); i++)
3493 {
3494 if (GetCar(i)->GotoFloor == true)
3495 {
3496 int floor = GetFloorForCar(i, GotoFloor);
3497 GetCar(i)->NotifyArrival(floor, early);
3498 }
3499 }
3500
3501 Notified = true;
3502}
3503
3505{
3506 //determine if the directional lantern should show up or down on arrival to the specified floor
3507 //true for up, false for down
3508
3509 ElevatorCar *car = GetCarForFloor(floor);
3510
3511 if (!car)
3512 return false;
3513
3514 int newfloor = floor;
3515
3516 if (floor == car->GetTopFloor())
3517 return false; //turn on down light if on top floor
3518 if (floor == car->GetBottomFloor())
3519 return true; //turn on up light if on bottom floor
3520
3521 //chime queue direction if queue resets are on
3522 if (QueueResets == true)
3523 {
3524 if (QueuePositionDirection == 1)
3525 return true;
3526 else if (QueuePositionDirection == -1)
3527 return false;
3528 }
3529
3530 //check for active hall calls
3531 bool UpStatus, DownStatus;
3532 GetCallStatus(floor, UpStatus, DownStatus);
3533 if (UpStatus == true && QueuePositionDirection == 1)
3534 return true;
3535 if (DownStatus == true && QueuePositionDirection == -1)
3536 return false;
3537
3538 if (NotifyEarly <= 0 || NotifyEarly == 3)
3539 {
3540 if (QueuePositionDirection == 1 && UpQueue.size() > 0 && UpQueueEmpty == false)
3541 newfloor = UpQueue[0].floor;
3542
3543 if (QueuePositionDirection == -1 && DownQueue.size() > 0 && DownQueueEmpty == false)
3544 newfloor = DownQueue[DownQueue.size() - 1].floor;
3545
3546 if (QueuePositionDirection == 1 && DownQueue.size() > 0 && UpQueueEmpty == true)
3547 newfloor = DownQueue[DownQueue.size() - 1].floor;
3548
3549 if (QueuePositionDirection == -1 && UpQueue.size() > 0 && DownQueueEmpty == true)
3550 newfloor = UpQueue[0].floor;
3551 }
3552 else
3553 {
3554 if (QueuePositionDirection == 1 && UpQueue.size() > 1)
3555 newfloor = UpQueue[1].floor;
3556
3557 if (QueuePositionDirection == -1 && DownQueue.size() > 1)
3558 newfloor = DownQueue[DownQueue.size() - 2].floor;
3559
3560 if (QueuePositionDirection == 1 && UpQueue.size() == 1)
3561 {
3562 if (DownQueue.size() > 0)
3563 newfloor = DownQueue[DownQueue.size() - 1].floor;
3564 else
3565 return true;
3566 }
3567
3568 if (QueuePositionDirection == -1 && DownQueue.size() == 1)
3569 {
3570 if (UpQueue.size() > 0)
3571 newfloor = UpQueue[0].floor;
3572 else
3573 return false;
3574 }
3575 }
3576
3577 if (QueuePositionDirection == 1 && UpQueue.size() == 0 && DownQueue.size() == 0)
3578 return true;
3579
3580 if (QueuePositionDirection == -1 && UpQueue.size() == 0 && DownQueue.size() == 0)
3581 return false;
3582
3583 //return direction of queue if floors are the same
3584 if (newfloor == floor)
3585 {
3586 if (QueuePositionDirection == 1 && UpQueue.size() > 0)
3587 return true;
3588 else if (QueuePositionDirection == -1 && DownQueue.size() > 0)
3589 return false;
3590 }
3591
3592 if (newfloor >= floor)
3593 return true; //turn on up for entry above current floor
3594 else
3595 return false; //turn on down for entry below current floor
3596}
3597
3599{
3600 //set elevator run state (true for run, false for stop)
3601
3602 if (Running == value)
3603 return;
3604
3605 if (value == false && IsMoving == true)
3606 {
3607 if (InspectionService == true)
3608 Stop();
3609 else
3610 Stop(true);
3611 }
3612
3613 //switch off directional indicators on current floor
3614 if (value == false && IsMoving == false)
3616
3617 if (value == false)
3618 {
3619 SetControls("stop");
3620 Report("Elevator stopped");
3621 }
3622 else
3623 {
3624 SetControls("run");
3625 Report("Elevator running");
3626 }
3627
3628 Running = value;
3629}
3630
3632{
3633 //return elevator running state
3634 return Running;
3635}
3636
3638{
3639 //returns the destination altitude of the specified floor, based on shaft door positioning
3640 //this adjusts the value based on the car offset, so that it returns the altitude that
3641 //car 1 would have to be at, to match an upper car's destination
3642
3643 ElevatorCar *car = GetCarForFloor(floor);
3644 if (!car)
3645 return 0.0;
3646
3647 return car->GetDestinationAltitude(floor) - GetCarOffset(car->Number);
3648}
3649
3651{
3652 //returns the offset distance from the floor's base altitude the elevator destination is
3653
3654 ElevatorCar *car = GetCarForFloor(floor);
3655 if (!car)
3656 return 0.0;
3657
3658 return car->GetDestinationOffset(floor);
3659}
3660
3662{
3663 //startup elevator initialization
3664
3665 //exit if not created properly
3666 if (Created == false)
3667 return;
3668
3669 bool enable_elevators = sbs->GetConfigBool("Skyscraper.SBS.Elevator.IsEnabled", true);
3670
3671 if (enable_elevators == false)
3672 Enabled(false);
3673}
3674
3675int Elevator::AvailableForCall(bool destination, int floor, int direction, bool report_on_failure)
3676{
3677 //determines if the elevator is available for the specified hall call
3678 //if report_on_failure is true, and verbose mode is enabled, report the reason for call rejection
3679
3680 //return codes:
3681 //0 - busy and will eventually be available
3682 //1 - available for call
3683 //2 - unavailable due to a service mode or error
3684
3685 SBS_PROFILE("Elevator::AvailableForCall");
3686
3687 //only run if power is enabled
3688 if (sbs->GetPower() == false)
3689 return 2;
3690
3691 ElevatorCar *car = GetCarForFloor(floor, report_on_failure);
3692
3693 //if floor is a serviced floor (valid car found)
3694 if (car)
3695 {
3696 //if direction doesn't go beyond elevator's range
3697 if ((direction == 1 && floor < car->GetTopFloor()) || (direction == -1 && floor > car->GetBottomFloor()))
3698 {
3699 //if elevator is running
3700 if (IsRunning() == true)
3701 {
3702 //and if it's not in any service mode
3703 if (InServiceMode() == false)
3704 {
3705 //and if no queue changes are pending, unless doors are open on the same floor as call
3706 if (QueuePending == false || ((AreDoorsOpen() == true || AreDoorsOpening() == true) && car->GetFloor() == floor))
3707 {
3708 //and if elevator either has limitqueue off, or has limitqueue on and queue direction is the same
3709 if (LimitQueue == false || (LimitQueue == true && (QueuePositionDirection == direction || QueuePositionDirection == 0)))
3710 {
3711 //and if elevator either has queueresets off, or has queueresets on and queue direction is the same
3712 if (QueueResets == false || (QueueResets == true && (QueuePositionDirection == direction || QueuePositionDirection == 0)))
3713 {
3714 //and if doors are not being held or elevator is waiting in a peak mode
3715 if (GetHoldStatus() == false || PeakWaiting() == true)
3716 {
3717 //and if the interlock check passes, unless waiting in a peak mode
3718 if (CheckInterlocks(true) == true || PeakWaiting() == true)
3719 {
3720 //and if nudge mode is off on all doors
3721 if (IsNudgeModeActive() == false)
3722 {
3723 //and if it's above the current floor and should be called down, or below the
3724 //current floor and called up, or on the same floor and not moving, or idle, or proceeding to call floor
3725 if ((car->GetFloor() > floor && direction == -1) || (car->GetFloor() < floor && direction == 1) || (car->GetFloor() == floor && MoveElevator == false) || IsIdle() || car->RespondingToCall(floor, direction) == true)
3726 {
3727 //and if it's either going the same direction as the call, or queue is not active, or idle, or on the same floor with the queues empty
3728 if (QueuePositionDirection == direction || QueuePositionDirection == 0 || IsIdle() ||
3729 (destination == true && car->GetFloor() == floor && MoveElevator == false && QueuePositionDirection != 0 && UpQueue.size() == 0 && DownQueue.size() == 0)) //this is an exception if a controller is used
3730 {
3731 if (sbs->Verbose)
3732 Report("Available for call");
3733 return 1;
3734 }
3735 else
3736 {
3737 if (sbs->Verbose == true && report_on_failure == true)
3738 Report("Not available for call - going a different direction and is not idle");
3739 return 0;
3740 }
3741 }
3742 else
3743 {
3744 if (sbs->Verbose == true && report_on_failure == true)
3745 Report("Not available for call - position/direction wrong for call and is not idle");
3746 return 0;
3747 }
3748 }
3749 else
3750 {
3751 if (sbs->Verbose == true && report_on_failure == true)
3752 Report("Not available for call - in nudge mode");
3753 return 0;
3754 }
3755 }
3756 else
3757 {
3758 if (sbs->Verbose == true && report_on_failure == true)
3759 Report("Not available for call - interlock check failed");
3760 return 2;
3761 }
3762 }
3763 else
3764 {
3765 if (sbs->Verbose == true && report_on_failure == true)
3766 Report("Not available for call - door hold is enabled");
3767 return 0;
3768 }
3769 }
3770 else
3771 {
3772 if (sbs->Verbose == true && report_on_failure == true)
3773 Report("Not available for call - queueresets is on and opposite queue direction is active");
3774 return 0;
3775 }
3776 }
3777 else
3778 {
3779 if (sbs->Verbose == true && report_on_failure == true)
3780 Report("Not available for call - limitqueue is on and queue is active");
3781 return 0;
3782 }
3783 }
3784 else
3785 {
3786 if (sbs->Verbose == true && report_on_failure == true)
3787 Report("Not available for call - queue change is pending");
3788 return 0;
3789 }
3790 }
3791 else
3792 {
3793 if (sbs->Verbose == true && report_on_failure == true)
3794 Report("Not available for call - in service mode");
3795 return 2;
3796 }
3797 }
3798 else
3799 {
3800 if (sbs->Verbose == true && report_on_failure == true)
3801 Report("Not available for call - elevator not running");
3802 return 2;
3803 }
3804 }
3805 else
3806 {
3807 if (sbs->Verbose == true && report_on_failure == true)
3808 Report("Not available for call - direction beyond serviced range");
3809 return 2;
3810 }
3811 }
3812
3813 if (sbs->Verbose == true && report_on_failure == true)
3814 Report("Not available for call - not a serviced floor");
3815 return 2;
3816}
3817
3819{
3820 //select a floor (in-elevator floor selections)
3821
3822 //only run if power is enabled
3823 if (sbs->GetPower() == false)
3824 return false;
3825
3826 //exit if in inspection mode or in fire service mode or is not running
3827 if (InspectionService == true)
3828 return ReportError("Cannot select floor while in inspection mode");
3829 else if (FireServicePhase1 == 1 && FireServicePhase2 == 0)
3830 return ReportError("Cannot select floor while in fire service recall mode");
3831 else if (Running == false)
3832 return false;
3833
3834 //use Go routine instead if floorhold parameter is enabled
3835 if (FloorHold == true)
3836 return Go(floor, true);
3837
3838 ElevatorCar *car = GetCarForFloor(floor);
3839 if (!car)
3840 return ReportError("Floor " + ToString(floor) + " not a serviced floor");
3841
3842 //SelectFloor won't work if Destination Dispatch is enabled, if set
3843 if (Controllers.size() > 0)
3844 {
3846
3847 if (controller)
3848 {
3849 if (controller->DestinationDispatch == true && controller->Hybrid == false && InServiceMode() == false)
3850 return ReportError("Cannot select floor " + ToString(floor) + ": Hybrid mode is not enabled");
3851 }
3852 }
3853
3854 bool result = false;
3855
3856 //elevator is above floor
3857 if (car->GetFloor() > floor)
3858 result = AddRoute(floor, -1, 0);
3859
3860 //elevator is below floor
3861 if (car->GetFloor() < floor)
3862 result = AddRoute(floor, 1, 0);
3863
3864 //elevator is on floor
3865 if (car->GetFloor() == floor)
3866 {
3867 if (IsLeveled() == false)
3868 {
3869 //relevel elevator if needed
3870 ReturnToNearestFloor(false, car->Number);
3871 }
3872 else if (Direction == 0)
3873 {
3874 //stopped - play chime and reopen doors
3875 if (ReOpen == true)
3876 {
3877 if (InServiceMode() == false)
3878 {
3879 int dir = 0;
3880
3881 if (IsQueueActive() == true)
3882 {
3883 dir = LastChimeDirection;
3884 if (dir == 0)
3885 dir = LastQueueDirection;
3886 }
3887
3888 if (ChimeOnArrival == true)
3889 {
3890 if (dir == -1)
3891 car->Chime(0, floor, false);
3892 else if (dir == 1)
3893 car->Chime(0, floor, true);
3894 }
3895 }
3896 if (FireServicePhase2 == 0)
3897 if (AutoDoors == true)
3898 car->OpenDoors();
3899 return false;
3900 }
3901 }
3902 else
3903 {
3904 //add a route to the current floor if elevator is moving
3905 result = AddRoute(floor, -Direction, 0);
3906 }
3907 }
3908
3909 return result;
3910}
3911
3913{
3914 //check to see if user (camera) is in the elevator
3915
3916 if (IsEnabled == false)
3917 return false;
3918
3919 SBS_PROFILE("Elevator::Check");
3920
3921 for (size_t i = Cars.size() - 1; i < Cars.size(); --i)
3922 {
3923 if (Cars[i]->Check(position) == true)
3924 return true;
3925 }
3926
3927 return false;
3928}
3929
3930bool Elevator::ReturnToNearestFloor(bool parking, int car)
3931{
3932 //return and relevel to nearest floor
3933
3934 //only run if power is enabled
3935 if (sbs->GetPower() == false)
3936 return false;
3937
3938 if (!GetCar(car))
3939 return false;
3940
3941 if (IsIdle() == true && InServiceMode() == false)
3942 {
3943 int floor = GetCar(car)->GetNearestServicedFloor();
3944 Report("returning to nearest floor for car " + ToString(car));
3945 if (parking == true)
3946 Parking = true; //enable parking mode to prevent arrival notification
3947
3948 if (floor >= GetCar(car)->GetFloor())
3949 AddRoute(floor, 1, 2);
3950 else
3951 AddRoute(floor, -1, 2);
3952 return true;
3953 }
3954 return false;
3955}
3956
3958{
3959 //return and relevel to bottom floor
3960
3961 //only run if power is enabled
3962 if (sbs->GetPower() == false)
3963 return false;
3964
3965 if (IsIdle() == true && InServiceMode() == false)
3966 {
3967 int floor = GetCar(1)->GetBottomFloor();
3968 Report("returning to bottom floor");
3969 Parking = true; //enable parking mode to prevent arrival notification
3970
3971 AddRoute(floor, -1, 2);
3972 return true;
3973 }
3974 return false;
3975}
3976
3978{
3979 //return true if elevator is leveled at a serviced floor
3980
3981 return GetCar(1)->IsLeveled();
3982}
3983
3985{
3986 //move elevator up (manual)
3987 //this function also stops the up movement when button is depressed
3988
3989 //only run if power is enabled
3990 if (sbs->GetPower() == false)
3991 return false;
3992
3993 if (ManualMove == 0)
3994 {
3995 ManualMoveHold = true;
3996 return Up(true);
3997 }
3998 else if (ManualMove == 1 && sbs->camera->MouseDown() == false)
3999 {
4000 ManualMoveHold = false;
4001 return Up(false);
4002 }
4003 return false;
4004}
4005
4007{
4008 //move elevator down (manual)
4009 //this function also stops the down movement when button is depressed
4010
4011 //only run if power is enabled
4012 if (sbs->GetPower() == false)
4013 return false;
4014
4015 if (ManualMove == 0)
4016 {
4017 ManualMoveHold = true;
4018 return Down(true);
4019 }
4020 else if (ManualMove == -1 && sbs->camera->MouseDown() == false)
4021 {
4022 ManualMoveHold = false;
4023 return Down(false);
4024 }
4025 return false;
4026}
4027
4028bool Elevator::Up(bool value)
4029{
4030 //move elevator up (manual)
4031
4032 //only run if power is enabled
4033 if (sbs->GetPower() == false)
4034 return false;
4035
4036 if (Running == false)
4037 {
4038 ReportError("Elevator not running");
4039 return false;
4040 }
4041
4042 if (ManualMove == 0 && value == true && IsMoving == false)
4043 {
4044 ManualMove = 1;
4045
4046 //set direction
4047 Direction = 1;
4048 MoveElevator = true;
4049 if (sbs->Verbose)
4050 Report("Up: moving elevator");
4051 return true;
4052 }
4053 else if (value == false && ManualMove != 0)
4054 {
4055 //stop movement
4056 ManualMove = 0;
4057 ManualStop = true;
4058 Stop();
4059 return true;
4060 }
4061 return false;
4062}
4063
4064bool Elevator::Down(bool value)
4065{
4066 //move elevator down (manual)
4067
4068 //only run if power is enabled
4069 if (sbs->GetPower() == false)
4070 return false;
4071
4072 if (Running == false)
4073 {
4074 ReportError("Elevator not running");
4075 return false;
4076 }
4077
4078 if (ManualMove == 0 && value == true && IsMoving == false)
4079 {
4080 ManualMove = -1;
4081
4082 //set direction
4083 Direction = -1;
4084 MoveElevator = true;
4085 if (sbs->Verbose)
4086 Report("Up: moving elevator");
4087 return true;
4088 }
4089 else if (value == false && ManualMove != 0)
4090 {
4091 //stop movement
4092 ManualMove = 0;
4093 ManualStop = true;
4094 Stop();
4095 return true;
4096 }
4097 return false;
4098}
4099
4101{
4102 //get associated shaft object
4103 return sbs->GetShaft(AssignedShaft);
4104}
4105
4107{
4108 //returns the first call station associated with this elevator, on the fire recall (lobby) floor
4109 Floor *floor = sbs->GetFloor(RecallFloor);
4110 if (floor)
4111 return floor->GetCallStationForElevator(Number);
4112 return 0;
4113}
4114
4116{
4117 return ActiveCall.floor;
4118}
4119
4124
4126{
4127 return ActiveCall.call_type;
4128}
4129
4131{
4132 //return true if user is currently in elevator
4133
4134 return (sbs->InElevator == true && sbs->ElevatorNumber == Number);
4135}
4136
4138{
4139 //play elevator starting sounds
4140
4141 //car sound
4142 for (size_t i = 0; i < Cars.size(); i++)
4143 {
4144 Cars[i]->PlayStartingSounds();
4145 }
4146
4147 //motor sound
4148 motorsound->Stop();
4149 if (Direction == 1 && MotorUpStartSound.empty() == false && MotorUpStartSound != "")
4150 {
4151 if (sbs->Verbose)
4152 Report("playing motor up start sound");
4153
4155 motorsound->SetLoopState(false);
4156 motorsound->Play();
4157 }
4158 if (Direction == -1 && MotorDownStartSound.empty() == false && MotorDownStartSound != "")
4159 {
4160 if (sbs->Verbose)
4161 Report("playing motor down start sound");
4162
4164 motorsound->SetLoopState(false);
4165 motorsound->Play();
4166 }
4167
4168 //counterweight sound
4170 {
4172 if (CounterweightStartSound.empty() == false && CounterweightStartSound != "")
4173 {
4174 if (sbs->Verbose)
4175 Report("playing counterweight start sound");
4176
4180 }
4181 }
4182}
4183
4185{
4186 //play elevator stopping sounds
4187 //if emergency is true, plays emergency stop sounds with a fallback to standard sounds
4188
4189 bool motorsound_play = false;
4190 std::string MotorSoundFile;
4191
4192 //car sounds
4193 for (size_t i = 0; i < Cars.size(); i++)
4194 {
4195 Cars[i]->PlayStoppingSounds(emergency);
4196 }
4197
4198 if (emergency == true)
4199 {
4200 //motor sound
4201 if (MotorEmergencyStopSound.empty() == false && MotorEmergencyStopSound != "")
4202 {
4203 if (sbs->Verbose)
4204 Report("playing motor emergency stop sound");
4205
4206 MotorSoundFile = MotorEmergencyStopSound;
4207 motorsound_play = true;
4208 }
4209 }
4210
4211 //motor sound
4212 if (motorsound_play == false)
4213 {
4214 if (Direction == -1 && MotorUpStopSound.empty() == false && MotorUpStopSound != "")
4215 {
4216 if (sbs->Verbose)
4217 Report("playing motor up stop sound");
4218
4219 MotorSoundFile = MotorUpStopSound;
4220 motorsound_play = true;
4221 }
4222 if (Direction == 1 && MotorDownStopSound.empty() == false && MotorDownStopSound != "")
4223 {
4224 if (sbs->Verbose)
4225 Report("playing motor down stop sound");
4226
4227 MotorSoundFile = MotorDownStopSound;
4228 motorsound_play = true;
4229 }
4230 }
4231
4232 motorsound->Stop();
4233
4234 if (motorsound_play == true)
4235 {
4236 motorsound->Load(MotorSoundFile);
4237 motorsound->SetLoopState(false);
4238
4239 //set play position to current percent of the total speed
4240 if (AutoAdjustSound == true)
4241 {
4242 if (ActiveDirection == 1)
4244 else
4246 }
4247 else
4248 motorsound->Reset();
4249
4250 motorsound->Play(false);
4251 }
4252
4253 //counterweight sound
4255 {
4257 if (CounterweightStopSound.empty() == false && CounterweightStopSound != "")
4258 {
4259 if (sbs->Verbose)
4260 Report("[playing counterweight stop sound");
4261
4265 }
4266 }
4267}
4268
4270{
4271 //play elevator movement sounds
4272
4273 SBS_PROFILE("Elevator::PlayMovingSounds");
4274
4275 //car sounds
4276 for (size_t i = 0; i < Cars.size(); i++)
4277 {
4278 Cars[i]->PlayMovingSounds();
4279 }
4280
4281 //motor sound
4282 if (motorsound->IsPlaying() == false)
4283 {
4284 if (Direction == 1 && MotorUpRunSound.empty() == false && MotorUpRunSound != "")
4285 {
4286 if (sbs->Verbose)
4287 Report("playing motor up run sound");
4288
4290 motorsound->SetLoopState(true);
4291 motorsound->Play();
4292 }
4293 else if (Direction == -1 && MotorDownRunSound.empty() == false && MotorDownRunSound != "")
4294 {
4295 if (sbs->Verbose)
4296 Report("playing motor down run sound");
4297
4299 motorsound->SetLoopState(true);
4300 motorsound->Play();
4301 }
4302 }
4303
4304 //counterweight sound
4306 {
4307 if (counterweightsound->IsPlaying() == false && CounterweightMoveSound.empty() == false && CounterweightMoveSound != "")
4308 {
4309 if (sbs->Verbose)
4310 Report("playing counterweight move sound");
4314 }
4315 }
4316}
4317
4319{
4320 //returns true if elevator is waiting in UpPeak or DownPeak mode
4321
4322 if (GetHoldStatus() == false)
4323 return false;
4324
4325 if ((OnTopFloor() && DownPeak == true && IsMoving == false) ||
4326 (OnBottomFloor() && UpPeak == true && IsMoving == false))
4327 return true;
4328 return false;
4329}
4330
4332{
4333 //similar to PeakWaiting(), but returns true if elevator is on the related peak mode floor
4334
4335 if ((OnTopFloor() && DownPeak == true) ||
4336 (OnBottomFloor() && UpPeak == true))
4337 return true;
4338 return false;
4339}
4340
4342{
4343 //returns false if not on a recall floor,
4344 //returns true if on the standard recall floor and normal recall is available
4345 //returns true if on the alternate recall floor and normal recall is unavailable
4346
4347 if (RecallUnavailable == false)
4348 return GetCar(1)->IsOnFloor(RecallFloor, false);
4349
4350 return GetCar(1)->IsOnFloor(RecallFloorAlternate, false);
4351}
4352
4354{
4355 //returns floor number of recall floor that's available
4356
4357 if (RecallUnavailable == true)
4358 return RecallFloorAlternate;
4359
4360 return RecallFloor;
4361}
4362
4363std::vector<Floor*> Elevator::GetLobbies()
4364{
4365 //returns a list of lobbies/skylobbies that service this elevator
4366
4367 std::vector<Floor*> list;
4368
4369 for (size_t i = 0; i < Cars.size(); i++)
4370 {
4371 for (size_t j = 0; j < Cars[i]->GetServicedFloorCount(); j++)
4372 {
4373 int num = Cars[i]->GetServicedFloor(j);
4374
4375 Floor *floor = sbs->GetFloor(num);
4376 if (floor)
4377 {
4378 std::string type = SetCaseCopy(floor->FloorType, false);
4379 if (type == "lobby" || type == "skylobby")
4380 list.emplace_back(floor);
4381 }
4382 }
4383 }
4384
4385 if (list.size() == 0 && sbs->GetFloor(RecallFloor))
4386 list.emplace_back(sbs->GetFloor(RecallFloor));
4387
4388 return list;
4389}
4390
4392{
4393 //true if elevator has stopped without reaching a floor, usually due to an emergency stop
4394
4395 return (IsMoving == false && OnFloor == false && FinishedMove == true);
4396}
4397
4398void Elevator::CancelHallCall(int floor, int direction)
4399{
4400 //delete a route if the route is a hall call response, and no floor button has been pressed
4401
4402 //only run if power is enabled
4403 if (sbs->GetPower() == false)
4404 return;
4405
4406 for (size_t i = 0; i < Cars.size(); i++)
4407 {
4408 Control *control = Cars[i]->GetFloorButton(floor);
4409
4410 if (control)
4411 {
4412 //exit if a floor button has been pressed
4413 if (control->GetLightStatus() == true)
4414 return;
4415 }
4416 }
4417
4418 DeleteRoute(floor, direction);
4419}
4420
4421void Elevator::HandleDequeue(int direction, bool stop_if_empty)
4422{
4423 //handle elevator behavior on dequeue
4424 //if stop_if_empty is true, this will stop the elevator if the related queue is empty
4425
4426 if (stop_if_empty == true && MoveElevator == true && EmergencyStop == 0)
4427 {
4428 if ((direction == 1 && UpQueue.empty()) ||
4429 (direction == -1 && DownQueue.empty()))
4430 Stop();
4431 }
4432
4433 //reset active call status if queues are empty
4434 if (DownQueue.empty() == true && UpQueue.empty() == true)
4435 {
4436 ActiveCall.floor = 0;
4439 ActiveCall.car = 0;
4440 }
4441}
4442
4444{
4445 //this will return true if elevator is stopped within 18 inches of the nearest landing
4446
4447 return (InServiceMode() == false && ManualStop == true && std::abs(GetCar(1)->GetDestinationAltitude(GetCar(1)->GetFloor()) - GetCar(1)->GetPosition().y) < 1.5);
4448}
4449
4450bool Elevator::CheckInterlocks(bool skip_current_floor)
4451{
4452 //return true if interlock checks pass, or interlocks are disabled
4453
4454 bool status;
4455
4456 if (skip_current_floor == false)
4457 status = (Interlocks == true && (AreDoorsOpen() == true || AreShaftDoorsClosed() == false || DoorsStopped() == true));
4458 else
4459 status = (Interlocks == true && (AreShaftDoorsClosed(true) == false || DoorsStopped() == true));
4460
4461 return !status;
4462}
4463
4465{
4466 //create a new elevator car object
4467
4468 int number = (int)Cars.size() + 1;
4469 ElevatorCar *car = new ElevatorCar(this, number);
4470 Cars.emplace_back(car);
4471 return car;
4472}
4473
4475{
4476 if (number < 0 || number >(int)Cars.size())
4477 return 0;
4478
4479 if (number == 0)
4480 number = 1;
4481
4482 return Cars[number - 1];
4483}
4484
4486{
4487 return (int)Cars.size();
4488}
4489
4491{
4492 //remove a car reference (does not delete the object itself)
4493 for (size_t i = 0; i < Cars.size(); i++)
4494 {
4495 if (Cars[i] == car)
4496 {
4497 Cars.erase(Cars.begin() + i);
4498 return;
4499 }
4500 }
4501}
4502
4503ElevatorCar* Elevator::GetCarForFloor(int number, bool report_on_failure)
4504{
4505 //return car that services specified floor
4506
4507 for (size_t i = 0; i < Cars.size(); i++)
4508 {
4509 if (Cars[i]->IsServicedFloor(number, report_on_failure) == true)
4510 return Cars[i];
4511 }
4512
4513 return 0;
4514}
4515
4517{
4518 //returns true if any door is open
4519
4520 for (size_t i = 0; i < Cars.size(); i++)
4521 {
4522 if (Cars[i]->AreDoorsOpen() == true)
4523 return true;
4524 }
4525 return false;
4526}
4527
4528int Elevator::AreDoorsMoving(bool car_doors, bool shaft_doors)
4529{
4530 //returns status if any door is moving
4531
4532 for (size_t i = 0; i < Cars.size(); i++)
4533 {
4534 int result = Cars[i]->AreDoorsMoving(0, car_doors, shaft_doors);
4535 if (result != 0)
4536 return result;
4537 }
4538 return 0;
4539}
4540
4541bool Elevator::AreShaftDoorsClosed(bool skip_current_floor)
4542{
4543 //returns true if all shaft doors are closed and not moving
4544
4545 for (size_t i = 0; i < Cars.size(); i++)
4546 {
4547 if (Cars[i]->AreShaftDoorsClosed(skip_current_floor) == false)
4548 return false;
4549 }
4550 return true;
4551}
4552
4554{
4555 //returns true if any of the doors are stopped
4556
4557 for (size_t i = 0; i < Cars.size(); i++)
4558 {
4559 if (Cars[i]->DoorsStopped() == true)
4560 return true;
4561 }
4562 return false;
4563}
4564
4565bool Elevator::AreDoorsOpening(bool car_doors, bool shaft_doors)
4566{
4567 //returns true if doors are opening
4568
4569 for (size_t i = 0; i < Cars.size(); i++)
4570 {
4571 if (Cars[i]->AreDoorsOpening(0, car_doors, shaft_doors) == true)
4572 return true;
4573 }
4574 return false;
4575}
4576
4577bool Elevator::AreDoorsClosing(bool car_doors, bool shaft_doors)
4578{
4579 //returns true if doors are closing
4580
4581 for (size_t i = 0; i < Cars.size(); i++)
4582 {
4583 if (Cars[i]->AreDoorsClosing(0, car_doors, shaft_doors) == true)
4584 return true;
4585 }
4586 return false;
4587}
4588
4589bool Elevator::IsServicedFloor(int floor, bool report)
4590{
4591 //returns true if floor is in any of the serviced floor lists, otherwise false
4592
4593 ElevatorCar* car = GetCarForFloor(floor);
4594
4595 bool result = false;
4596
4597 if (car)
4598 result = true;
4599
4600 if (sbs->Verbose && report == true)
4601 {
4602 if (result == false)
4603 Report("Floor " + ToString(floor) + " is not a serviced floor");
4604 else
4605 Report("Floor " + ToString(floor) + " is a serviced floor");
4606 }
4607
4608 return result;
4609}
4610
4612{
4613 //stop movement sounds
4614
4615 for (size_t i = 0; i < Cars.size(); i++)
4616 {
4617 Cars[i]->StopCarSound();
4618 }
4619 motorsound->Stop();
4620}
4621
4622void Elevator::ChangeLight(int floor, bool value)
4623{
4624 //turn on or off specified button lights
4625
4626 for (size_t i = 0; i < Cars.size(); i++)
4627 {
4628 Cars[i]->ChangeLight(floor, value);
4629 }
4630}
4631
4633{
4634 //turn off all button lights
4635
4636 for (size_t i = 0; i < Cars.size(); i++)
4637 {
4638 Cars[i]->ResetLights();
4639 }
4640}
4641
4643{
4644 //open all doors
4645
4646 //only run if power is enabled
4647 if (sbs->GetPower() == false)
4648 return;
4649
4650 for (size_t i = 0; i < Cars.size(); i++)
4651 {
4652 Cars[i]->OpenDoors();
4653 }
4654}
4655
4657{
4658 //close all doors
4659
4660 //only run if power is enabled
4661 if (sbs->GetPower() == false)
4662 return;
4663
4664 for (size_t i = 0; i < Cars.size(); i++)
4665 {
4666 Cars[i]->CloseDoors();
4667 }
4668}
4669
4671{
4672 //checks doors and returns true if any have door hold enabled
4673
4674 for (size_t i = 0; i < Cars.size(); i++)
4675 {
4676 if (Cars[i]->GetHoldStatus() == true)
4677 return true;
4678 }
4679 return false;
4680}
4681
4683{
4684 return GetCar(1)->OnTopFloor();
4685}
4686
4688{
4689 return GetCar(1)->OnBottomFloor();
4690}
4691
4693{
4694 //returns true if nudge mode is active in any car
4695
4696 for (size_t i = 0; i < Cars.size(); i++)
4697 {
4698 if (Cars[i]->IsNudgeModeActive() == true)
4699 return true;
4700 }
4701 return false;
4702}
4703
4705{
4706 //reset shaft doors
4707
4708 //this might not be needed, due to addition of full-shaft enable check to
4709 //floor object's EnableGroup function, needs testing
4710
4711 for (int i = 1; i <= sbs->GetShaftCount(); i++)
4712 {
4713 Shaft *shaft = sbs->GetShaft(i);
4714 if (shaft)
4715 {
4716 if (shaft->IsEnabled == false)
4717 {
4718 shaft->EnableRange(floor, sbs->ShaftDisplayRange, false, true);
4719 shaft->EnableRange(floor, sbs->ShaftDisplayRange, true, true);
4720 }
4721 }
4722 }
4723}
4724
4726{
4727 //reset all doors
4728 for (size_t i = 0; i < Cars.size(); i++)
4729 {
4730 Cars[i]->ResetDoors();
4731 }
4732}
4733
4735{
4736 //reset all nudge timers
4737 for (size_t i = 0; i < Cars.size(); i++)
4738 {
4739 Cars[i]->ResetNudgeTimer(start);
4740 }
4741}
4742
4744{
4745 //hold all doors
4746
4747 //only run if power is enabled
4748 if (sbs->GetPower() == false)
4749 return;
4750
4751 for (size_t i = 0; i < Cars.size(); i++)
4752 {
4753 Cars[i]->HoldDoors();
4754 }
4755}
4756
4758{
4759 //switch off all directional indicators
4760
4761 for (size_t i = 0; i < Cars.size(); i++)
4762 {
4763 Cars[i]->SetDirectionalIndicators(Cars[i]->CurrentFloor, false, false);
4764 }
4765}
4766
4768{
4769 //enable nudge mode on all cars
4770
4771 //only run if power is enabled
4772 if (sbs->GetPower() == false)
4773 return;
4774
4775 for (size_t i = 0; i < Cars.size(); i++)
4776 {
4777 Cars[i]->EnableNudgeMode(value);
4778 }
4779}
4780
4782{
4783 //get vertical offset of specified car
4784
4785 if (!GetCar(GotoFloorCar))
4786 return 0.0;
4787
4788 return GetCar(GotoFloorCar)->GetPosition().y - GetPosition().y;
4789}
4790
4791int Elevator::GetFloorForCar(int car, int number)
4792{
4793 if (!GetCar(car))
4794 return 0;
4795
4796 ElevatorCar *obj = GetCarForFloor(number);
4797 if (!obj)
4798 return 0;
4799
4800 //subtract car's offset to get base car's value
4801 number -= obj->Offset;
4802
4803 return number += GetCar(car)->Offset;
4804}
4805
4806void Elevator::ProcessGotoFloor(int floor, int direction)
4807{
4808 //set GotoFloor status on other cars, if queue entries exists for those cars
4809
4810 if (MoveElevator == false)
4811 return;
4812
4813 for (int i = 1; i <= GetCarCount(); i++)
4814 {
4815 if (i == GotoFloorCar)
4816 continue;
4817
4818 if (IsQueued(GetFloorForCar(i, floor), direction) == true)
4819 GetCar(i)->GotoFloor = true;
4820 }
4821}
4822
4824{
4825 //returns true if the elevator is on the parking floor
4826
4828 if (!car)
4829 return false;
4830
4831 return (car->GetFloor() == ParkingFloor);
4832}
4833
4834bool Elevator::SetHoistwayAccess(int floor, int access)
4835{
4836 //sets the hoistway access direction for the specified floor, for Inspection Service mode
4837 //access is -1 for Down, 0 for Off, and 1 for Up
4838
4839 //with a direction enabled and held, this moves the elevator
4840 //at leveling speed, in that direction while the shaft doors are open
4841
4842 if (Running == false)
4843 return ReportError("SetHoistwayAccess: Elevator not running");
4844
4845 if (InspectionService == false)
4846 return ReportError("SetHoistwayAccess: Not in inspection service mode");
4847
4848 if (access == 0 || (HoistwayAccessHold == true && HoistwayAccess != 0 && sbs->camera->MouseDown() == false))
4849 {
4850 //disable mode
4851 HoistwayAccess = 0;
4853 if (IsMoving == true)
4854 Stop();
4855
4856 Report("Hoistway Access set to Off for floor " + ToString(floor));
4857 return true;
4858 }
4859 else if (HoistwayAccess == 0)
4860 {
4861 if (IsMoving == true)
4862 {
4863 if (sbs->Verbose)
4864 ReportError("SetHoistwayAccess: already moving");
4865 return false;
4866 }
4867
4868 ElevatorCar *car = GetCarForFloor(floor);
4869 if (!car)
4870 return ReportError("SetHoistwayAccess: no car found that services floor " + ToString(floor));
4871
4872 if (car->AreShaftDoorsOpen(0, floor) == false)
4873 return ReportError("Shaft doors need to be open on floor " + ToString(floor) + " to enable Hoistway Access");
4874
4875 //enable mode
4876 HoistwayAccess = access;
4877 HoistwayAccessFloor = floor;
4878
4879 std::string direction = "Down";
4880 if (HoistwayAccess == 1)
4881 direction = "Up";
4882 Report("Hoistway Access set to " + direction + " for floor " + ToString(floor));
4883
4884 //set direction
4886 MoveElevator = true;
4887
4888 return true;
4889 }
4890
4891 return false;
4892}
4893
4894void Elevator::SetControls(const std::string &action_name)
4895{
4896 //queue a setcontrols action
4897
4898 ControlQueue.push(action_name);
4899}
4900
4902{
4903 //process existing setcontrols actions
4904
4905 while (ControlQueue.empty() == false)
4906 {
4907 std::string action_name = ControlQueue.front();
4908
4909 for (size_t i = 0; i < Cars.size(); i++)
4910 {
4911 Cars[i]->SetControls(action_name);
4912 }
4913
4914 ControlQueue.pop();
4915 }
4916}
4917
4919{
4920 //return true if Destination Dispatch is enabled
4921
4922 if (Controllers.size() > 0)
4923 {
4924 if (sbs->GetController(Controllers[0]))
4926 }
4927 return false;
4928}
4929
4930void Elevator::SameFloorArrival(int floor, int direction)
4931{
4932 if (sbs->Verbose)
4933 Report("Elevator active on current floor - opening");
4934
4935 //get related car number
4936 ElevatorCar *car = GetCarForFloor(floor);
4937
4938 if (!car)
4939 {
4940 ReportError("floor " + ToString(floor) + " is not a serviced floor");
4941 return;
4942 }
4943
4944 for (size_t i = 0; i < Controllers.size(); i++)
4945 {
4947
4948 //notify controllers of movement
4949 if (controller)
4950 controller->ResetArrival(Number);
4951
4952 //update arrival information
4953 if (direction == -1)
4954 {
4955 if (controller)
4956 controller->ElevatorArrived(Number, floor, false);
4957 }
4958 else
4959 {
4960 if (controller)
4961 controller->ElevatorArrived(Number, floor, true);
4962 }
4963 }
4964
4965 //notify on arrival
4966 if (NotifyEarly >= 0)
4967 car->NotifyArrival(floor, false, direction);
4968
4969 //store call direction for NotifyLate feature
4970 if (NotifyLate == true)
4971 car->LateDirection = direction;
4972
4973 //open elevator if it's on the same floor
4974 car->OpenDoors();
4975}
4976
4977void Elevator::AddController(int controller)
4978{
4979 //add a controller to this elevator
4980
4981 if (!sbs->GetController(controller))
4982 return;
4983
4984 //exit if already in table
4985 for (size_t i = 0; i < Controllers.size(); i++)
4986 {
4987 if (Controllers[i] == controller)
4988 return;
4989 }
4990
4991 Controllers.emplace_back(controller);
4992}
4993
4994void Elevator::RemoveController(int controller)
4995{
4996 //remove a controller from this elevator
4997
4998 for (size_t i = 0; i < Controllers.size(); i++)
4999 {
5000 if (Controllers[i] == controller)
5001 {
5002 Controllers.erase(Controllers.begin() + i);
5003 return;
5004 }
5005 }
5006}
5007
5008bool Elevator::GetCallStatus(int floor, bool &up, bool &down)
5009{
5010 //returns call status for the specified floor
5011
5012 for (size_t i = 0; i < Controllers.size(); i++)
5013 {
5014 bool result = false;
5015 if (sbs->GetController(Controllers[i]))
5016 result = sbs->GetController(Controllers[i])->GetCallStatus(Number, floor, up, down);
5017
5018 if (result == true)
5019 return result;
5020 }
5021 return false;
5022}
5023
5025{
5026 return sbs->GetFloorNumber(MotorPosition.y);
5027}
5028
5030{
5031 //enable random malfunctions on this elevator
5032
5033 //only run if power is enabled
5034 if (sbs->GetPower() == false)
5035 return;
5036
5037 if (!malfunction_timer)
5038 return;
5039
5040 if (value == true)
5041 {
5042 Report("Enabling malfunctions");
5043 malfunction_timer->Start(int(RandomFrequency) * 1000, false);
5044 }
5045 else
5046 {
5047 Report("Disabling malfunctions");
5049 }
5050}
5051
5053{
5054 //elevator malfunction
5055
5056 Report("Malfunction");
5057
5058 size_t type = rnd_type->Get(2);
5059 if (type == 0)
5060 {
5061 //full malfunction
5062 Stop(true);
5063 SetRunState(false);
5064 ResetQueue(true, true);
5065
5066 for (size_t i = 0; i < Cars.size(); i++)
5067 {
5068 if (Cars[i])
5069 {
5070 //turn off car fans
5071 Cars[i]->Fan = false;
5072 }
5073 }
5074 }
5075 else if (type == 1)
5076 {
5077 //partial malfunction (stop)
5078 Stop(true);
5079 }
5080 else if (type == 2)
5081 {
5082 //partial malfunction (reset queues)
5083 ResetQueue(true, true);
5084 }
5085}
5086
5087}
bool MovePosition(Vector3 vector, Real speed=1.0)
Definition camera.cpp:353
int CurrentFloor
Definition camera.h:49
bool MouseDown()
Definition camera.cpp:1497
bool GetLightStatus()
Definition control.h:61
bool AddElevator(int elevator)
bool GetCallStatus(int elevator, int floor, bool &up, bool &down)
void ResetArrival(int number)
void ElevatorArrived(int number, int floor, bool direction)
void ResetNudgeTimer(bool start=true, int number=0)
void ChangeLight(int floor, bool value)
void HoldDoors(int number=0, bool sensor=false)
Real GetDestinationOffset(int floor)
bool IsOnFloor(int floor, bool leveled=true)
bool OpenDoors(int number=0, int whichdoors=1, int floor=0, bool manual=false, bool hold=false)
Real GetDestinationAltitude(int floor)
void NotifyArrival(int floor, bool early=false, int direction=0)
bool IsServicedFloor(int floor, bool report=true)
bool RespondingToCall(int floor, int direction)
void Chime(int number, int floor, bool direction, bool early=false)
int AreDoorsMoving(int number=0, bool car_doors=true, bool shaft_doors=true)
bool AreShaftDoorsOpen(int number, int floor)
void CloseDoors(int number=0, int whichdoors=1, int floor=0, bool manual=false, bool hold=false)
Elevator * elevator
Definition elevator.cpp:49
virtual void Notify()
Timer(const std::string &name, Elevator *parent, int Type)
Definition elevator.cpp:51
bool CreateElevator(bool relative, Real x, Real z, int floor)
Definition elevator.cpp:343
int AvailableForCall(bool destination, int floor, int direction, bool report_on_failure=true)
Real TempDeceleration
Definition elevator.h:52
bool EnableACP(bool value)
Real GetStoppingDistance()
int RecallFloor
Definition elevator.h:80
bool AreShaftDoorsClosed(bool skip_current_floor=false)
bool GetCallStatus(int floor, bool &up, bool &down)
bool BeyondDecelMarker(int direction, Real destination)
void HandleDequeue(int direction, bool stop_if_empty=true)
Real DepartureDelay
Definition elevator.h:100
void EnableNudgeMode(bool value)
bool ManualMoveHold
Definition elevator.h:110
void ChangeLight(int floor, bool value)
bool FinishedMove
Definition elevator.h:372
bool Interlocks
Definition elevator.h:111
Real ErrorOffset
Definition elevator.h:53
Vector3 elevposition
Definition elevator.h:370
friend class ElevatorCar
Definition elevator.h:32
void FinishMove()
std::string MotorUpRunSound
Definition elevator.h:62
bool AreDoorsClosing(bool car_doors=true, bool shaft_doors=true)
bool Notified
Definition elevator.h:98
Vector3 MotorPosition
Definition elevator.h:86
void ResetNudgeTimers(bool start=true)
Real DecelJerk
Definition elevator.h:51
void Report(const std::string &message)
std::string CounterweightStopSound
Definition elevator.h:124
bool OnParkingFloor()
int GetFloorForCar(int car, int number)
std::string MotorEmergencyStopSound
Definition elevator.h:68
std::string FloorSkipText
Definition elevator.h:365
bool QueueResets
Definition elevator.h:87
bool CallCancelAll()
Definition elevator.cpp:788
bool HeightSet
Definition elevator.h:59
Real LevelingOpen
Definition elevator.h:93
Real Destination
Definition elevator.h:305
bool EnableFireService1(int value)
bool GetArrivalDirection(int floor)
bool OnRecallFloor()
Real InspectionSpeed
Definition elevator.h:103
bool EnableIndependentService(bool value, int car_number=0)
int GotoFloorCar
Definition elevator.h:47
bool SetDownButton(bool value)
bool SetUpButton(bool value)
std::string GetFloorSkipText()
bool RecallAltSet
Definition elevator.h:367
Real ParkingDelay
Definition elevator.h:89
Real LevelingOffset
Definition elevator.h:92
DynamicMesh * DoorContainer
Definition elevator.h:351
void CancelHallCall(int floor, int direction)
bool OnTopFloor()
int GetActiveRecallFloor()
bool EnableUpPeak(bool value)
bool IsNudgeModeActive()
void SetFloorSkipText(const std::string &id)
bool UseFloorSkipText
Definition elevator.h:69
int HoistwayAccessFloor
Definition elevator.h:323
Real EmergencyStopSpeed
Definition elevator.h:115
bool lastfloorset
Definition elevator.h:364
ElevatorCar * AddCar()
Real ElevatorStart
Definition elevator.h:304
std::string MotorDownStartSound
Definition elevator.h:64
Real GetJerkPosition()
int QueuePositionDirection
Definition elevator.h:40
int GetEmergencyStopStatus()
bool IsQueued(int floor, int queue=0)
void ProcessCallQueue()
Definition elevator.cpp:853
std::vector< QueueEntry > UpQueue
Definition elevator.h:302
bool MoveElevator
Definition elevator.h:45
bool GetDestinationDispatch()
ElevatorCar * GetCar(int number)
void MoveElevatorToFloor()
void SetControls(const std::string &action_name)
int GetActiveCallFloor()
bool AddRoute(int floor, int direction, int call_type)
Definition elevator.cpp:556
Sound * motorsound
Definition elevator.h:344
bool SetRecallFloor(int floor)
std::vector< Floor * > GetLobbies()
Timer * departure_delay
Definition elevator.h:293
void EnableMalfunctions(bool value)
MeshObject * WeightMesh
Definition elevator.h:355
void ResetShaftDoors(int floor)
std::string Type
Definition elevator.h:38
void Malfunction()
bool EnableDownPeak(bool value)
bool WaitForDoors
Definition elevator.h:94
bool SelectFloor(int floor)
Real ArrivalDelay
Definition elevator.h:101
std::string MotorDownRunSound
Definition elevator.h:65
void ResetQueue(bool up, bool down, bool stop_if_empty=true)
bool IsQueueActive()
bool ReturnToNearestFloor(bool parking=true, int car=1)
bool Counterweight
Definition elevator.h:125
Timer * malfunction_timer
Definition elevator.h:296
bool Stop(bool emergency=false)
Definition elevator.cpp:812
void NotifyArrival(bool early=false)
bool ManualDown
Definition elevator.h:85
void SetRunState(bool value)
int FireServicePhase2Car
Definition elevator.h:79
bool AutoAdjustSound
Definition elevator.h:116
void CloseDoors()
bool AreDoorsOpen()
bool HoistwayAccessHold
Definition elevator.h:119
bool LimitQueue
Definition elevator.h:104
int GetRecallFloor()
Elevator(Object *parent, int number)
Definition elevator.cpp:59
void RemoveController(int controller)
bool Go(int floor, bool hold=false)
void ResetDoors()
Real LevelingSpeed
Definition elevator.h:91
bool InElevator()
bool DownPeak
Definition elevator.h:73
std::string Name
Definition elevator.h:36
bool WaitForTimer
Definition elevator.h:102
bool OnPeakFloor()
bool IndependentService
Definition elevator.h:74
bool CallCancel()
Definition elevator.cpp:764
void UpdateFloorIndicators()
Vector3 weight_size
Definition elevator.h:357
void ProcessGotoFloor(int floor, int direction)
std::vector< ElevatorCar * > Cars
Definition elevator.h:301
void RemoveCar(ElevatorCar *car)
bool NotifyLate
Definition elevator.h:97
bool CheckInterlocks(bool skip_current_floor=false)
int ActiveDirection
Definition elevator.h:95
void Enabled(bool value)
Real StoppingDistance
Definition elevator.h:306
Wall * CreateCounterweight(const std::string &frame_texture, const std::string &weight_texture, Real x, Real z, const Vector3 &size, Real weight_voffset)
Definition elevator.cpp:449
ElevatorCar * GetCarForFloor(int number, bool report_on_failure=false)
bool EnableInspectionService(bool value)
Sound * motoridlesound
Definition elevator.h:345
void SameFloorArrival(int floor, int direction)
bool AreDoorsOpening(bool car_doors=true, bool shaft_doors=true)
bool SetACPFloor(int floor)
bool ReturnToBottomFloor()
CallStation * GetPrimaryCallStation()
bool RouteExists(bool direction, int floor)
Definition elevator.cpp:736
bool PeakWaiting()
bool Leveling
Definition elevator.h:90
bool InServiceMode()
void DeleteActiveRoute()
std::string MotorUpStopSound
Definition elevator.h:63
bool ManualStop
Definition elevator.h:321
int LastQueueFloor[2]
Definition elevator.h:42
RandomGen * rnd_time
Definition elevator.h:325
int AssignedShaft
Definition elevator.h:56
Real AccelJerk
Definition elevator.h:50
int LastChimeDirection
Definition elevator.h:106
bool CalculateStoppingDistance
Definition elevator.h:307
void PlayMovingSounds()
void MoveObjects(Real offset)
Real DistanceToTravel
Definition elevator.h:54
void PlayStoppingSounds(bool emergency=false)
bool DoorsStopped()
void GoToRecallFloor()
int EmergencyStop
Definition elevator.h:309
std::string ID
Definition elevator.h:37
void DumpQueues()
Real Deceleration
Definition elevator.h:49
Real GetCarOffset(int number)
std::queue< std::string > ControlQueue
Definition elevator.h:353
std::string RopeTexture
Definition elevator.h:121
std::string MotorIdleSound
Definition elevator.h:67
bool SetHoistwayAccess(int floor, int access)
std::string MotorUpStartSound
Definition elevator.h:61
bool Check(Vector3 position)
bool InspectionService
Definition elevator.h:76
Timer * parking_timer
Definition elevator.h:289
int AreDoorsMoving(bool car_doors=true, bool shaft_doors=true)
int FireServicePhase2
Definition elevator.h:78
std::vector< QueueEntry > DownQueue
Definition elevator.h:303
bool IsMoving
Definition elevator.h:60
Shaft * GetShaft()
int RecallFloorAlternate
Definition elevator.h:81
void UpdateDirectionalIndicators()
int GoActiveFloor
Definition elevator.h:320
int GetActiveCallDirection()
int IndependentServiceCar
Definition elevator.h:75
bool ACPFloorSet
Definition elevator.h:368
bool UpQueueEmpty
Definition elevator.h:315
Timer * arrival_delay
Definition elevator.h:292
QueueEntry ActiveCall
Definition elevator.h:312
int RandomProbability
Definition elevator.h:297
bool SkipFloorSound
Definition elevator.h:117
bool IsServicedFloor(int floor, bool report=true)
bool IsEnabled
Definition elevator.h:57
int GetBottomFloor()
bool ReportError(const std::string &message)
Sound * counterweightsound
Definition elevator.h:348
bool QueuePending
Definition elevator.h:319
int LastQueueDirection
Definition elevator.h:41
bool ManualUp
Definition elevator.h:84
bool SetAlternateRecallFloor(int floor)
Real tmpDecelJerk
Definition elevator.h:371
Real GetDestinationAltitude(int floor)
Real GetJerkRate()
MeshObject * RopeMesh
Definition elevator.h:358
std::string MotorDownStopSound
Definition elevator.h:66
Real GetElevatorStart()
bool DeleteRoute(int floor, int direction)
Definition elevator.cpp:679
Real Acceleration
Definition elevator.h:48
int GetActiveCallType()
std::string CounterweightStartSound
Definition elevator.h:122
Real ElevatorRate
Definition elevator.h:55
ElevatorCar * IsInElevator(const Vector3 &position, bool camera=false)
bool EnableFireService2(int value, int car_number=0, bool force=false)
Real GetDestinationOffset(int floor)
std::vector< int > Controllers
Definition elevator.h:324
bool MovementRunning
Definition elevator.h:361
bool SetGoButton(bool value)
RandomGen * rnd_type
Definition elevator.h:325
bool GetBrakeStatus()
bool SoundsQueued
Definition elevator.h:373
bool DownQueueEmpty
Definition elevator.h:316
bool ChimeOnArrival
Definition elevator.h:118
bool AddRails(const std::string &main_texture, const std::string &edge_texture, Real x, Real z, bool orientation, Real rail_distance, Real rail_width)
Definition elevator.cpp:513
int ParkingFloor
Definition elevator.h:88
Real GetDestination()
int FireServicePhase1
Definition elevator.h:77
int NotifyEarly
Definition elevator.h:96
Real RandomFrequency
Definition elevator.h:298
bool IsManuallyStopped()
Real DownSpeed
Definition elevator.h:44
Vector3 RopePosition
Definition elevator.h:120
bool OnBottomFloor()
bool RecallUnavailable
Definition elevator.h:369
MeshObject * WeightRopeMesh
Definition elevator.h:356
bool OpenOnStart
Definition elevator.h:108
bool GetHoldStatus()
void AddController(int controller)
bool ManualGo
Definition elevator.h:83
int HoistwayAccess
Definition elevator.h:322
void DirectionalIndicatorsOff()
void DoSetControls()
void ResetLights()
void PlayStartingSounds()
std::string CounterweightMoveSound
Definition elevator.h:123
void StopSounds()
void UpdateDirectionalIndicators(int elevator)
Definition floor.cpp:1215
void UpdateFloorIndicators(int elevator)
Definition floor.cpp:928
Real Altitude
Definition floor.h:43
std::string FloorType
Definition floor.h:40
Real GetBase(bool relative=false)
Definition floor.cpp:1140
std::string ID
Definition floor.h:38
void EnableGroup(bool value)
Definition floor.cpp:706
void Enabled(bool value)
Definition floor.cpp:377
CallStation * GetCallStationForElevator(int elevator)
Definition floor.cpp:985
void ChangeHeight(Real newheight)
Definition mesh.cpp:654
const std::string & GetName()
Definition object.cpp:53
virtual bool ReportError(const std::string &message)
Definition object.cpp:84
virtual void Report(const std::string &message)
Definition object.cpp:78
void SetName(const std::string &name)
Definition object.cpp:72
bool parent_deleting
Definition object.h:64
virtual void Move(const Vector3 &vector, Real speed=1.0)
Definition object.cpp:253
virtual void SetPositionY(Real value)
Definition object.cpp:313
virtual Vector3 GetPosition(bool relative=false)
Definition object.cpp:321
int GetNumber()
Definition object.cpp:183
SceneNode * GetSceneNode()
Definition object.cpp:240
void SetValues(const std::string &type, const std::string &name, bool is_permanent, bool is_movable=true)
Definition object.cpp:144
void EnableLoop(bool value)
Definition object.cpp:521
virtual void SetPosition(const Vector3 &position)
Definition object.cpp:274
float Get()
Definition random.cpp:135
Shaft * GetShaft(int number)
Definition sbs.cpp:1753
bool GetPower()
Definition sbs.cpp:4701
void EnableBuildings(bool value)
Definition sbs.cpp:1475
std::string GetConfigString(const std::string &key, const std::string &default_value)
Definition sbs.cpp:3238
void RemoveElevator(Elevator *elevator)
Definition sbs.cpp:2780
int CarNumber
Definition sbs.h:166
Real GetConfigFloat(const std::string &key, Real default_value)
Definition sbs.cpp:3249
DispatchController * GetController(int number)
Definition sbs.cpp:1774
int ShaftDisplayRange
Definition sbs.h:178
Floor * GetFloor(int number)
Definition sbs.cpp:1739
bool FastDelete
Definition sbs.h:188
TextureManager * GetTextureManager()
Definition sbs.cpp:4558
Camera * camera
Definition sbs.h:160
void EnableLandscape(bool value)
Definition sbs.cpp:1482
Wall * CreateWallBox2(MeshObject *mesh, const std::string &name, const std::string &texture, Real CenterX, Real CenterZ, Real WidthX, Real LengthZ, Real height_in, Real voffset, Real tw, Real th, bool inside=true, bool outside=true, bool top=true, bool bottom=true, bool autosize=true)
Definition sbs.cpp:1369
void EnableSkybox(bool value)
Definition sbs.cpp:1498
int GetFloorNumber(Real altitude, int lastfloor=0, bool checklastfloor=false)
Definition sbs.cpp:1584
int ElevatorNumber
Definition sbs.h:165
bool GetConfigBool(const std::string &key, bool default_value)
Definition sbs.cpp:3243
Wall * AddWall(MeshObject *mesh, const std::string &name, const std::string &texture, Real thickness, Real x1, Real z1, Real x2, Real z2, Real height_in1, Real height_in2, Real altitude1, Real altitude2, Real tw, Real th)
Definition sbs.cpp:1899
int GetConfigInt(const std::string &key, int default_value)
Definition sbs.cpp:3232
void EnableExternal(bool value)
Definition sbs.cpp:1489
bool InElevator
Definition sbs.h:163
Real delta
Definition sbs.h:134
int GetShaftCount()
Definition sbs.cpp:1721
Wall * CreateWallBox(MeshObject *mesh, const std::string &name, const std::string &texture, Real x1, Real x2, Real z1, Real z2, Real height_in, Real voffset, Real tw, Real th, bool inside=true, bool outside=true, bool top=true, bool bottom=true, bool autosize=true)
Definition sbs.cpp:1162
bool ElevatorSync
Definition sbs.h:167
bool Verbose
Definition sbs.h:186
MeshObject * GetMeshObject()
Definition shaft.cpp:1048
void AddElevator(int number)
Definition shaft.cpp:446
void EnableRange(int floor, int range, bool value, bool EnableShaftDoors)
Definition shaft.cpp:270
Level * GetLevel(int floor)
Definition shaft.cpp:135
bool IsEnabled
Definition shaft.h:43
void Reset()
Definition sound.cpp:378
bool Load(const std::string &filename, bool force=false)
Definition sound.cpp:386
void Stop()
Definition sound.cpp:292
bool Play(bool reset=true)
Definition sound.cpp:321
bool IsLoaded()
Definition sound.cpp:475
void SetLoopState(bool value)
Definition sound.cpp:204
void SetPlayPosition(Real percent)
Definition sound.cpp:439
bool IsPlaying()
Definition sound.cpp:254
void SetAutoSize(bool x, bool y)
Definition texture.cpp:1075
void GetAutoSize(bool &x, bool &y)
Definition texture.cpp:1082
bool IsRunning()
Definition timer.cpp:74
void Start(int milliseconds=-1, bool oneshot=false)
Definition timer.cpp:49
Ogre::Vector3 Vector3
Definition globals.h:58
Ogre::Real Real
Definition globals.h:57
std::string TruncateNumber(float value, int decimals)
Definition globals.cpp:416
std::string ToString(int number)
Definition globals.cpp:279
std::string SetCaseCopy(std::string string, bool uppercase)
Definition globals.cpp:165
void TrimString(std::string &string)
Definition globals.cpp:188
#define SBS_PROFILE(name)
Definition profiler.h:131