Skyscraper 2.0
scriptproc.cpp
Go to the documentation of this file.
1/*
2 Skyscraper 2.0 Alpha - File I/O and Script Processing Routines
3 Copyright (C)2003-2024 Ryan Thoryk
4 https://www.skyscrapersim.net
5 https://sourceforge.net/projects/skyscraper/
6 Contact - ryan@skyscrapersim.net
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21*/
22
23#include "globals.h"
24#include "sbs.h"
25#ifdef USING_WX
26#include "wx/wx.h"
27#endif
28#include <OgreFileSystem.h>
29#include <OgreArchive.h>
30#include <OgreArchiveManager.h>
31#include <OgreException.h>
32#include <stdlib.h>
33#include <cmath>
34#include "vm.h"
35#include "sky.h"
36#include "enginecontext.h"
37#include "texture.h"
38#include "floor.h"
39#include "camera.h"
40#include "random.h"
41#include "scriptproc.h"
42#include "section.h"
43
44using namespace SBS;
45
46namespace Skyscraper {
47
49{
50 if (!instance)
51 return;
52
53 engine = instance;
54 Simcore = instance->GetSystem();
55
56 //create configuration handler
57 config = new ConfigHandler();
58
59 //create section objects
64 floor_section = new FloorSection(this);
70
71 NoModels = false;
72 Reset();
73}
74
100
102{
103 line = 0; //line number
104 LineData = ""; //line contents
105 wall = 0;
106 startpos = 0;
107 getfloordata = false;
108 BuildingData.clear();
109 BuildingDataOrig.clear();
110 BuildingData.reserve(1024);
111 BuildingDataOrig.reserve(1024);
112 InFunction = 0;
113 FunctionStack.clear();
114 ReplaceLine = false;
115 ReplaceLineData = "";
116 nonexistent_files.clear();
117 show_percent = true;
118 IsFinished = false;
119 progress_marker = 0;
120 functions.clear();
121 includes.clear();
122 variables.clear();
123 in_runloop = false;
124 processed_runloop = false;
125
126 //reset configuration
127 config->Reset();
128
129 //set CheckScript state
131
132 //reset section objects
143}
144
146{
147 //building loader/script interpreter
148
149 bool status = false;
150 int returncode = sContinue;
151 IsFinished = false;
152
153 if (engine->IsRunning() == true && processed_runloop == false)
155
156 //wait until end of script before processing runloop again
157 if (processed_runloop == true)
158 processed_runloop = false;
159
160 if (line < (int)BuildingData.size() && line >= 0)
161 {
162 if (InRunloop() == false)
163 engine->ResetPrepare(); //reset prepare flag
166
167 if (ReplaceLine == true)
168 {
170 ReplaceLine = false;
171 }
172
173 //skip blank lines
174 if (LineData == "")
175 goto Nextline;
176
177 //process comment markers
178 {
179 int marker = LineData.find("#", 0);
180 if (marker > -1)
181 LineData.erase(marker);
182 }
183
184 //skip blank lines
185 if (LineData == "")
186 goto Nextline;
187
188 //expand runloop variables
189 if (in_runloop == true)
190 {
191 ReplaceAll(LineData, "%uptime%", ToString((int)Simcore->GetRunTime()));
192 int hour, minute, second;
193 engine->GetVM()->GetSkySystem()->GetTime(hour, minute, second);
194 ReplaceAll(LineData, "%hour%", ToString(hour));
195 ReplaceAll(LineData, "%minute%", ToString(minute));
196 ReplaceAll(LineData, "%second%", ToString(second));
197 }
198
199 //process function parameters
200 status = ProcessFunctionParameters();
201
202 //handle cancel if requested
203 if (status == false)
204 goto Error;
205
206 //process user variables
208
209 //process sections
210 returncode = ProcessSections();
211 if (returncode != sContinue)
212 goto handlecodes;
213
214 //process floor object conversions
215checkfloors:
216 returncode = ProcessFloorObjects();
217 if (returncode != sContinue)
218 goto handlecodes;
219
220 //process extent variables
222
223 //process For loops
224 returncode = ProcessForLoops();
225 if (returncode != sContinue)
226 goto handlecodes;
227
228 //reset return code
229 returncode = sContinue;
230
231 //Global parameters
232 if (config->SectionNum == 1)
233 returncode = globals_section->Run(LineData);
234
235 //Process floors
236 else if (config->SectionNum == 2)
237 {
238 //create floor if not created already
240recalc:
241 returncode = floor_section->Run(LineData);
242 }
243
244 //Process external buildings
245 else if (config->SectionNum == 3)
246 returncode = buildings_section->Run(LineData);
247
248 //process elevators
249 else if (config->SectionNum == 4)
250 returncode = elevator_section->Run(LineData);
251
252 //Process textures
253 else if (config->SectionNum == 5)
254 returncode = textures_section->Run(LineData);
255
256 //process elevator cars
257 else if (config->SectionNum == 6)
258 returncode = elevatorcar_section->Run(LineData);
259
260 //process vehicles
261 else if (config->SectionNum == 7)
262 returncode = vehicle_section->Run(LineData);
263
264 //process controllers
265 else if (config->SectionNum == 8)
266 returncode = controller_section->Run(LineData);
267
268 //process call stations
269 else if (config->SectionNum == 9)
270 returncode = callstation_section->Run(LineData);
271
272 //Global commands
273 if (returncode == sContinue)
274 returncode = commands_section->Run(LineData);
275
276 //handle return values
277handlecodes:
278 if (returncode == sError)
279 goto Error;
280 else if (returncode == sCheckFloors)
281 goto checkfloors;
282 else if (returncode == sBreak)
283 {
284 Breakpoint();
285 goto Nextline;
286 }
287 else if (returncode == sRecalc)
288 goto recalc;
289 else if (returncode == sSkipReset)
290 goto Nextline;
291 else if (returncode == sExit)
292 return true;
293 else if (returncode == sLoopFor)
294 {
295 if (ForLoops.size() > 0)
296 {
297 ForInfo &info = ForLoops.back();
298 line = info.line;
299 }
300 goto Nextline;
301 }
302
303 //reset temporary states
306
307Nextline:
308 if (config->InWhile == true && InFunction == 0)
309 config->InWhile = false;
310 else
311 line++;
312
313 if (line == (int)BuildingData.size())
314 {
315 //free text texture memory
317
318 IsFinished = true;
319 show_percent = false;
320 }
321 }
322
323 return true;
324
325Error:
326 line++;
327 return false;
328}
329
330bool ScriptProcessor::LoadDataFile(const std::string &filename, bool insert, int insert_line)
331{
332 //loads a building data file into the runtime buffer
333 int location = insert_line;
334 std::string Filename = Simcore->VerifyFile(filename);
335
336 //if insert location is greater than array size, return with error
337 if (insert == true)
338 {
339 if (location > (int)BuildingData.size() - 1 || location < 0)
340 {
341 ScriptError("Cannot insert file beyond end of script");
342 return false;
343 }
344 }
345
346 //make sure file exists
347 if (Simcore->FileExists(Filename) == false)
348 {
349 if (insert == false)
350 engine->ReportFatalError("Error loading building file:\nFile '" + Filename + "' does not exist");
351 else
352 ScriptError("File not found");
353 return false;
354 }
355
356 if (Simcore->Verbose)
357 Simcore->Report("Filename: '" + Filename + "'");
358
359 Ogre::Archive *filesystem = 0;
360 Ogre::ArchiveManager::ArchiveMapIterator it = Ogre::ArchiveManager::getSingleton().getArchiveIterator();
361 Ogre::DataStreamPtr filedata;
362 while(it.hasMoreElements())
363 {
364 //search each filesystem entry for the file
365 const std::string& key = it.peekNextKey();
366 filesystem = it.getNext();
367
368 if (!filesystem)
369 return "";
370
371 //check for a mount point
372 std::string shortname;
373 std::string group = Simcore->GetMountPath(Filename, shortname);
374
375 if (group == "General")
376 {
377 //for the General group, check the native filesystem
378 if (filesystem->exists(Filename) == true)
379 {
380 try
381 {
382 filedata = filesystem->open(Filename, true);
383 }
384 catch (Ogre::Exception &e)
385 {
386 std::string msg = "Error loading building file\nDetails: " + e.getDescription();
387 if (insert == false)
389 else
390 ScriptError(msg);
391 return false;
392 }
393 }
394 }
395 }
396
397 //exit if an error occurred while loading
398 if(!filedata)
399 {
400 std::string msg = "Error loading building file";
401 if (insert == false)
403 else
404 ScriptError(msg);
405 return false;
406 }
407
408 Ogre::DataStreamPtr file(new Ogre::MemoryDataStream(Filename, filedata, true, true));
409
410 std::vector<std::string> insert_data;
411 insert_data.reserve(512);
412
413 while (file->eof() == false)
414 {
415 //push next line of data onto the tail end of the BuildingData array
416 std::string line = file->getLine(true);
417 if (insert == false)
418 {
419 //append data to building array
420 BuildingData.emplace_back(line);
421 BuildingDataOrig.emplace_back(line);
422 }
423 else
424 {
425 //otherwise add data to new array, and insert into buildings array later
426 insert_data.emplace_back(line);
427 }
428 }
429
430 if (insert == true)
431 {
432 //insert new building data into array
433 BuildingData.insert(BuildingData.begin() + location, insert_data.begin(), insert_data.end());
434 location += (int)insert_data.size();
435
436 int end = location - 1;
437 int lines = end - insert_line;
438 int parent = -1;
439
440 //adjust include end lines if included within another include
441 for (int i = 0; i < (int)includes.size(); i++)
442 {
443 if (includes[i].start_line < line && includes[i].end_line > line)
444 {
445 includes[i].end_line += lines;
446 parent = i;
447 }
448 }
449
450 //adjust function lines
451 for (size_t i = 0; i < functions.size(); i++)
452 {
453 if (functions[i].line > line)
454 functions[i].line += lines;
455 }
456
457 if (InFunction > 0)
458 {
459 for (size_t i = 0; i < FunctionStack.size(); i++)
460 {
461 if (FunctionStack[i].CallLine > line)
462 FunctionStack[i].CallLine += lines;
463 }
464 }
465
466 //store include info in array
467 IncludeInfo info;
468 info.filename = Filename;
469 info.start_line = line;
470 info.end_line = end;
471 info.parent = parent;
472 includes.emplace_back(info);
473 }
474
475 return true;
476}
477
478bool ScriptProcessor::LoadFromText(const std::string &text)
479{
480 //loads building commands from a string
481
482 if (text.size() == 0)
483 return false;
484
485 std::vector<std::string> textarray;
486 SplitString(textarray, text, '\n');
487
488 //feed each line of text into the script array
489
490 for (size_t i = 0; i < textarray.size(); i++)
491 {
492 //append data to building array
493 BuildingData.emplace_back(textarray[i]);
494 BuildingDataOrig.emplace_back(textarray[i]);
495 }
496 return true;
497}
498
499int ScriptProcessor::ScriptError(std::string message, bool warning)
500{
501 //report a script error, with line and context information.
502 //reports a warning message (without window popup) if 'warning' is true
503
504 std::string error;
505 int LineNumber, FunctionLine;
506 bool IsInclude, IsIncludeFunction;
507 std::string FunctionName, IncludeFile, IncludeFunctionFile;
508
509 GetLineInformation(true, LineNumber, FunctionName, FunctionLine, IsInclude, IncludeFile, IsIncludeFunction, IncludeFunctionFile);
510
511 if (warning == false)
512 error = "Script error ";
513 else
514 error = "Script warning ";
515
516 if (IsInclude == true)
517 error += "in included file " + IncludeFile + " ";
518
519 error += "on line " + ToString(LineNumber) + ": " + message + "\n\nFilename: " + engine->GetFilename() + "\nContext: " + config->Context;
520
521 if (config->RangeL != config->RangeH)
522 error += "\nIteration Number: " + ToString(config->Current);
523
524 if (InFunction == 0)
525 error += "\nLine Text: " + LineData;
526 else
527 {
528 error += "\nFunction: " + FunctionName;
529
530 if (IsIncludeFunction == true)
531 error += "\nFunction included in file: " + IncludeFunctionFile;
532
533 error += "\nFunction call line: " + ToString(FunctionLine) + "\nLine Text: " + LineData;
534 }
535
536 if (engine->GetVM()->GetEngineCount() > 1)
537 error += "\nEngine context: " + ToString(engine->GetNumber());
538
539 engine->ReportError(error);
540
541 //show error dialog
542 if (warning == false)
543 {
544#ifdef USING_WX
545 wxMessageDialog dialog (0, error, "Skyscraper", wxOK | wxICON_ERROR);
546 dialog.ShowModal();
547#endif
548 }
549 return sError;
550}
551
552void ScriptProcessor::GetLineInformation(bool CheckFunctionCall, int &LineNumber, std::string &FunctionName, int &FunctionLine, bool &IsInclude, std::string &IncludeFile, bool &IsIncludeFunction, std::string &IncludeFunctionFile)
553{
554 //calculate line information for current script line
555
556 //if IsInclude is true, LineNumber is the line of the included file IncludeFile
557 //if in a function, the function call line is returned as FunctionLine
558 //if function call line is in an included file, IsIncludeFunction is true, with IncludeFunctionFile as the file
559 //to skip the function call line check, set CheckFunctionCall to false
560
561 int linenum = line;
562 int newlinenum = line;
563 int linenum_start = 0;
564 int function_line = 0;
565 int newfunction_line = 0;
566 int function_line_start = 0;
567 int included_lines = 0;
568 int included_lines_f = 0;
569 int parent = -1;
570 int parent_f = -1;
571 FunctionLine = 0;
572 IsInclude = false;
573 IsIncludeFunction = false;
574 FunctionName = "";
575 IncludeFile = "";
576 IncludeFunctionFile = "";
577
578 if (InFunction > 0)
579 {
580 FunctionName = FunctionStack[InFunction - 1].Name;
581 function_line = FunctionStack[InFunction - 1].CallLine;
582 newfunction_line = function_line;
583 }
584
585 //first see if the current line is from an included file
586 for (int i = 0; i < (int)includes.size(); i++)
587 {
588 if (linenum < includes[i].start_line)
589 break;
590
591 //line is part of an included file
592 if (linenum >= includes[i].start_line && linenum <= includes[i].end_line)
593 {
594 IsInclude = true;
595 IncludeFile = includes[i].filename;
596 newlinenum = linenum - includes[i].start_line;
597 parent = i;
598 linenum_start = includes[i].start_line;
599 }
600
601 //function call line is part of an included file
602 if (CheckFunctionCall == true)
603 {
604 if (function_line >= includes[i].start_line && function_line <= includes[i].end_line)
605 {
606 IsIncludeFunction = true;
607 IncludeFunctionFile = includes[i].filename;
608 newfunction_line = function_line - includes[i].start_line;
609 parent_f = i;
610 function_line_start = includes[i].start_line;
611 }
612 }
613 }
614
615 linenum = newlinenum;
616 function_line = newfunction_line;
617
618 //calculate number of included lines before the current line
619 for (int i = 0; i < (int)includes.size(); i++)
620 {
621 if (includes[i].parent == parent)
622 {
623 if (linenum + linenum_start > includes[i].end_line)
624 included_lines += includes[i].end_line - includes[i].start_line;
625 }
626
627 if (CheckFunctionCall == true)
628 {
629 if (includes[i].parent == parent_f)
630 {
631 if (function_line + function_line_start > includes[i].end_line)
632 included_lines_f += includes[i].end_line - includes[i].start_line;
633 }
634 }
635 }
636
637 //have line numbers start from 1 instead of 0
638 linenum += 1;
639 function_line += 1;
640
641 //return values
642 LineNumber = linenum - included_lines;
643
644 if (InFunction > 0)
645 FunctionLine = function_line - included_lines_f;
646}
647
649{
650 //return automatic error message from contents of the simulator engine's LastError string
651
652 std::string message = Simcore->LastError;
653 int loc = message.find_last_of(":");
654
655 std::string result = message.substr(loc + 1);
656 TrimString(result);
657 return ScriptError(result);
658}
659
660int ScriptProcessor::ScriptWarning(std::string message)
661{
662 return ScriptError(message, true);
663}
664
665std::string ScriptProcessor::Calc(const std::string &expression)
666{
667 //performs a calculation operation on a string
668 //for example, the string "1 + 1" would output to "2"
669 //supports multiple and nested operations (within parenthesis)
670 //^ character is used as a 'power of' operator
671
672 int temp1;
673 std::string tmpcalc = expression;
674 std::string one;
675 std::string two;
676 int start, end;
677 CalcError = false;
678
679 //first remove all whitespace from the string
680 ReplaceAll(tmpcalc, " ", "");
681
682 //find parenthesis
683 do
684 {
685 start = tmpcalc.find("(", 0);
686 if (start >= 0)
687 {
688 //find matching parenthesis
689 int match = 1;
690 int end = -1;
691 for (int i = start + 1; i < (int)tmpcalc.length(); i++)
692 {
693 char &tmpchar = tmpcalc.at(i);
694 if (tmpchar == '(')
695 match++;
696 if (tmpchar == ')')
697 match--;
698 if (match == 0)
699 {
700 end = i;
701 break;
702 }
703 }
704 if (end != -1)
705 {
706 //call function recursively
707 std::string newdata;
708 newdata = Calc(tmpcalc.substr(start + 1, end - start - 1));
709
710 if (CalcError == true)
711 return tmpcalc;
712
713 //construct new string
714 one = tmpcalc.substr(0, start);
715 if (end < (int)tmpcalc.length() - 1)
716 two = tmpcalc.substr(end + 1);
717 else
718 two = "";
719 tmpcalc = one + newdata + two;
720 }
721 else
722 {
723 ScriptError("Syntax error in math operation: '" + tmpcalc + "' (might be nested)");
724 CalcError = true;
725 return tmpcalc;
726 }
727 }
728 else
729 break;
730 } while (1 == 1);
731
732 //find number of operators and recurse if multiple found
733 int operators;
734 do
735 {
736 operators = 0;
737 end = 0;
738 for (int i = 1; i < (int)tmpcalc.length(); i++)
739 {
740 char &tmpchar = tmpcalc.at(i);
741 char &tmpcharprev = tmpcalc.at(i - 1);
742 if (tmpchar == '+' || tmpchar == '/' || tmpchar == '*' || tmpchar == '^')
743 {
744 //ensure that numbers are around operator
745 if (i < (int)tmpcalc.length() - 1)
746 {
747 char &tmpcharnext = tmpcalc.at(i + 1);
748 if (IsNumeric(tmpcharprev) == true && (IsNumeric(tmpcharnext) == true || tmpcharnext == '-' || tmpcharnext == '.'))
749 {
750 //valid operator found
751 operators++;
752 if (operators == 2)
753 end = i;
754 }
755 }
756 }
757 if (tmpchar == '-' && tmpcharprev != '-' && tmpcharprev != '+' && tmpcharprev != '/' && tmpcharprev != '*' && tmpcharprev != '^')
758 {
759 //ensure that numbers are around operator
760 if (i < (int)tmpcalc.length() - 1)
761 {
762 char &tmpcharnext = tmpcalc.at(i + 1);
763 if (IsNumeric(tmpcharprev) == true && (IsNumeric(tmpcharnext) == true || tmpcharnext == '-' || tmpcharnext == '.'))
764 {
765 //valid operator found
766 operators++;
767 if (operators == 2)
768 end = i;
769 }
770 }
771 }
772 }
773 if (end >= (int)tmpcalc.length() - 1 && operators > 0)
774 {
775 ScriptError("Syntax error in math operation: '" + tmpcalc + "' (might be nested)");
776 CalcError = true;
777 return tmpcalc;
778 }
779 if (operators > 1)
780 {
781 std::string newdata;
782 newdata = Calc(tmpcalc.substr(0, end));
783
784 if (CalcError == true)
785 return tmpcalc;
786
787 //construct new string
788 two = tmpcalc.substr(end);
789 tmpcalc = newdata + two;
790 }
791 else
792 break;
793 } while (1 == 1);
794
795 //return value if none found
796 if (operators == 0)
797 {
798 TrimString(tmpcalc);
799 return tmpcalc;
800 }
801
802 //otherwise perform math
803 temp1 = tmpcalc.find("+", 1);
804 if (temp1 > 0)
805 {
806 one = tmpcalc.substr(0, temp1);
807 two = tmpcalc.substr(temp1 + 1);
808 if (IsNumeric(one) == true && IsNumeric(two) == true)
809 {
810 Real first = ToFloat(one);
811 Real second = ToFloat(two);
812 Real tmpnum = first + second;
813 tmpcalc = TruncateNumber(tmpnum, 6);
814 TrimString(tmpcalc);
815 return tmpcalc;
816 }
817 else
818 {
819 CalcError = true;
820 return tmpcalc;
821 }
822 }
823 temp1 = tmpcalc.find("/", 1);
824 if (temp1 > 0)
825 {
826 one = tmpcalc.substr(0, temp1);
827 two = tmpcalc.substr(temp1 + 1);
828 if (IsNumeric(one) == true && IsNumeric(two) == true)
829 {
830 Real first = ToFloat(one);
831 Real second = ToFloat(two);
832 if (second == 0)
833 {
834 ScriptError("Division by zero in math operation: '" + tmpcalc + "' (might be nested)");
835 CalcError = true;
836 return tmpcalc;
837 }
838 Real tmpnum = first / second;
839 tmpcalc = TruncateNumber(tmpnum, 6);
840 TrimString(tmpcalc);
841 return tmpcalc;
842 }
843 else
844 {
845 CalcError = true;
846 return tmpcalc;
847 }
848 }
849 temp1 = tmpcalc.find("*", 1);
850 if (temp1 > 0)
851 {
852 one = tmpcalc.substr(0, temp1);
853 two = tmpcalc.substr(temp1 + 1);
854 if (IsNumeric(one) == true && IsNumeric(two) == true)
855 {
856 Real first = ToFloat(one);
857 Real second = ToFloat(two);
858 Real tmpnum = first * second;
859 tmpcalc = TruncateNumber(tmpnum, 6);
860 TrimString(tmpcalc);
861 return tmpcalc;
862 }
863 else
864 {
865 CalcError = true;
866 return tmpcalc;
867 }
868 }
869 temp1 = tmpcalc.find("^", 1);
870 if (temp1 > 0)
871 {
872 one = tmpcalc.substr(0, temp1);
873 two = tmpcalc.substr(temp1 + 1);
874 if (IsNumeric(one) == true && IsNumeric(two) == true)
875 {
876 Real first = ToFloat(one);
877 Real second = ToFloat(two);
878 Real tmpnum = powf(first, second);
879 tmpcalc = TruncateNumber(tmpnum, 6);
880 TrimString(tmpcalc);
881 return tmpcalc;
882 }
883 else
884 {
885 CalcError = true;
886 return tmpcalc;
887 }
888 }
889 temp1 = tmpcalc.find("-", 1);
890 if (temp1 > 0)
891 {
892 one = tmpcalc.substr(0, temp1);
893 two = tmpcalc.substr(temp1 + 1);
894 if (IsNumeric(one) == true && IsNumeric(two) == true)
895 {
896 Real first = ToFloat(one);
897 Real second = ToFloat(two);
898 Real tmpnum = first - second;
899 tmpcalc = TruncateNumber(tmpnum, 6);
900 TrimString(tmpcalc);
901 return tmpcalc;
902 }
903 else
904 {
905 CalcError = true;
906 return tmpcalc;
907 }
908 }
909 return tmpcalc;
910}
911
913{
914 //store command and line info in object
915
916 if (!object)
917 return;
918
919 int LineNumber, FunctionLine;
920 bool IsInclude, IsIncludeFunction;
921 std::string FunctionName, IncludeFile, IncludeFunctionFile;
922
923 GetLineInformation(false, LineNumber, FunctionName, FunctionLine, IsInclude, IncludeFile, IsIncludeFunction, IncludeFunctionFile);
924 object->linenum = LineNumber;
925 object->includefile = IncludeFile;
926
928 object->command = BuildingData[line];
929 object->command_processed = LineData;
930 object->context = config->Context;
931 std::string current;
932 current = ToString(config->Current);
933 if (config->SectionNum == 2)
934 object->context = "Floor " + current;
935 if (config->SectionNum == 4)
936 object->context = "Elevator " + current;
937 if (config->SectionNum == 6)
938 object->context = "Elevator " + ToString(config->CurrentOld) + " Car " + current;
939 if (config->SectionNum == 7)
940 object->context = "Vehicle " + current;
941 if (config->SectionNum == 8)
942 object->context = "Controller " + current;
943 if (config->SectionNum == 9)
944 object->context = "Call Station " + current;
945}
946
948{
949 //process functions
950 for (size_t i = 0; i < functions.size(); i++)
951 {
952 if (functions[i].name == "runloop")
953 continue;
954
955 int location = LineData.find(functions[i].name + "(");
956 if (location >= 0)
957 {
958 //found a function
959
960 //store info
961 InFunction += 1;
962
963 FunctionData data;
964 data.CallLine = line;
965 data.Name = functions[i].name;
966
967 //get function parameters
968 int location2 = location + (int)data.Name.length() + 1;
969 int end_loc = LineData.find_last_of(")");
970 std::string newdata = LineData.substr(location2, end_loc - location2);
971 std::vector<std::string> tempdata;
972 SplitString(tempdata, newdata, ',');
973
974 //calculate inline math
975 data.Params.reserve(tempdata.size());
976 std::string buffer;
977 for (int j = 0; j < (int)tempdata.size(); j++)
978 {
979 buffer = Calc(tempdata[j]);
980 TrimString(buffer);
981 data.Params.emplace_back(buffer);
982 }
983
984 //remove function statement
985 LineData = LineData.substr(0, location) + LineData.substr(end_loc + 1);
986
987 //switch to function line
988 data.LineData = LineData;
989 line = functions[i].line;
990 FunctionStack.emplace_back(data);
991 return true;
992 }
993 }
994 return false;
995}
996
998{
999 //process runloop
1000
1001 //exit if already in a runloop
1002 if (in_runloop == true)
1003 return;
1004
1005 //add function to function stack, to run
1006 for (int i = 0; i < functions.size(); i++)
1007 {
1008 if (functions[i].name == "runloop")
1009 {
1010 //store info
1011 InFunction += 1;
1012
1013 FunctionData data;
1014 data.CallLine = BuildingData.size();
1015 data.Name = "runloop";
1016
1017 in_runloop = true;
1018 line = functions[i].line + 1;
1019 FunctionStack.emplace_back(data);
1020 }
1021 }
1022}
1023
1024void ScriptProcessor::CheckFile(const std::string &filename)
1025{
1026 //check to see if the specified file exists.
1027 //if not, add to nonexistent_files array
1028
1029 std::string file = filename;
1030
1031 if (file == "")
1032 return;
1033
1034 int loc = file.find_last_of("/");
1035 if (loc > 0)
1036 {
1037 if (file.length() == loc + 1)
1038 return;
1039 }
1040 loc = file.find_last_of("\\");
1041 if (loc > 0)
1042 {
1043 if (file.length() == loc + 1)
1044 return;
1045 }
1046
1047 ReplaceAll(file, "\\", "/");
1048
1049 //skip file if a wildcard character is found
1050 loc = file.find_last_of("*");
1051 if (loc > 0)
1052 return;
1053
1054 if (Simcore->FileExists(file) == false)
1055 {
1056 bool exists = false;
1057 for (size_t i = 0; i < nonexistent_files.size(); i++)
1058 {
1059 if (nonexistent_files[i] == file)
1060 {
1061 exists = true;
1062 break;
1063 }
1064 }
1065 if (exists == false)
1066 nonexistent_files.emplace_back(file);
1067 }
1068}
1069
1070std::vector<std::string> *ScriptProcessor::GetBuildingData()
1071{
1072 return &BuildingDataOrig;
1073}
1074
1075bool ScriptProcessor::IsFunctionDefined(const std::string &name)
1076{
1077 //return true if a function of the specified name has already been defined
1078
1079 for (size_t i = 0; i < functions.size(); i++)
1080 {
1081 if (functions[i].name == name)
1082 return true;
1083 }
1084 return false;
1085}
1086
1088{
1089 //dump the basic script interpreter state to a string
1090
1091 int LineNumber, FunctionLine;
1092 bool IsInclude, IsIncludeFunction;
1093 std::string FunctionName, IncludeFile, IncludeFunctionFile;
1094
1095 GetLineInformation(true, LineNumber, FunctionName, FunctionLine, IsInclude, IncludeFile, IsIncludeFunction, IncludeFunctionFile);
1096
1097 std::string output = "Line number: " + ToString(LineNumber) + "\n";
1098 if (IsInclude == true)
1099 output.append("In included file: " + IncludeFile + "\n");
1100 output.append("Context: " + config->Context + "\n");
1101
1102 if (config->RangeL != config->RangeH)
1103 output.append("Iteration Number: " + ToString(config->Current) + "\n");
1104
1105 if (InFunction != 0)
1106 {
1107 //report function information
1108 output.append("Function: " + FunctionName + "\n");
1109 if (IsIncludeFunction == true)
1110 output.append("Function include file: " + IncludeFunctionFile + "\n");
1111 output.append("Function call line: " + ToString(FunctionLine) + "\n");
1112 }
1113 output.append("Line text: " + LineData + "\n");
1114
1115 return output;
1116}
1117
1122
1127
1129{
1131 //Function parameter variables
1133 if (InFunction > 0)
1134 {
1135 startpos = 0;
1136 int loc1 = 0;
1137 int loc2 = 0;
1138 do
1139 {
1140 //Function parameter conversion
1141 loc1 = LineData.find("%param", startpos);
1142 loc2 = 0;
1143 if (loc1 >= startpos)
1144 {
1145 loc2 = LineData.find("%", loc1 + 6);
1146 if (loc2 >= (int)LineData.length() || loc2 < 0)
1147 {
1148 loc1 = 0;
1149 loc2 = 0;
1150 break;
1151 }
1152 }
1153 else
1154 {
1155 //none (or no more) variables found
1156 loc1 = 0;
1157 loc2 = 0;
1158 break;
1159 }
1160
1161 if (loc1 + loc2 > 0)
1162 {
1163 std::string str = LineData.substr(loc1 + 6, loc2 - (loc1 + 6));
1164 TrimString(str);
1165 if (IsNumeric(str) == true)
1166 {
1167 int index = ToInt(str);
1168
1169 //replace all occurrences of the variable with it's value
1170 std::string replacement;
1171 if (index > 0 && index <= (int)FunctionStack[InFunction - 1].Params.size())
1172 replacement = FunctionStack[InFunction - 1].Params[index - 1];
1173 else
1174 replacement = "";
1175
1176 ReplaceAll(LineData, "%param" + str + "%", replacement);
1177 startpos = loc1;
1178 }
1179 else
1180 startpos = loc2 + 1;
1181 }
1182 else
1183 startpos++;
1184 } while (true);
1185 }
1186 else if (show_percent == true)
1187 {
1188 int percent = ((Real)line / (Real)BuildingData.size()) * 100.0;
1189 std::string percent_s = ToString(percent);
1190 int marker = percent / 10;
1191 if (marker > progress_marker)
1192 {
1193 progress_marker = marker;
1194 engine->Report(percent_s + "%");
1195 return engine->UpdateProgress(percent);
1196 }
1197 }
1198 return true;
1199}
1200
1202{
1204 //User variables
1206 startpos = 0;
1207 do
1208 {
1209 //User variable conversion
1210 int loc1 = LineData.find("%", startpos);
1211 int loc2 = 0;
1212 std::string str;
1213 if (loc1 >= startpos)
1214 {
1215 loc2 = LineData.find("%", loc1 + 1);
1216 if (loc2 >= (int)LineData.length() || loc2 < 0)
1217 {
1218 loc1 = 0;
1219 loc2 = 0;
1220 break;
1221 }
1222 }
1223 else
1224 {
1225 //none (or no more) variables found
1226 loc1 = 0;
1227 loc2 = 0;
1228 break;
1229 }
1230
1231 if (loc1 + loc2 > 0)
1232 {
1233 str = LineData.substr(loc1 + 1, loc2 - loc1 - 1);
1234 TrimString(str);
1235
1236 bool found = false;
1237 for (size_t i = 0; i < variables.size(); i++)
1238 {
1239 if (variables[i].name == str)
1240 {
1241 found = true;
1242
1243 //replace all occurrences of the variable with it's value
1244 ReplaceAll(LineData, "%" + str + "%", variables[i].value);
1245 startpos = loc1;
1246 break;
1247 }
1248 }
1249
1250 if (found == false)
1251 startpos = loc2 + 1;
1252 }
1253 else
1254 startpos++;
1255 } while (true);
1256}
1257
1259{
1261 //Section information
1263 if (StartsWithNoCase(LineData, "<globals>"))
1264 {
1265 if (config->SectionNum > 0)
1266 {
1267 ScriptError("Already within a section");
1268 return sError;
1269 }
1270 config->SectionNum = 1;
1271 config->Context = "Globals";
1272 engine->Report("Processing globals...");
1273 return sNextLine;
1274 }
1275 if (StartsWithNoCase(LineData, "<end>"))
1276 {
1277 config->SectionNum = 0;
1278 config->Context = "None";
1279 engine->Report("Exiting building script");
1280 IsFinished = true;
1281 show_percent = false;
1282 line = (int)BuildingData.size(); //jump to end of script
1283 return sExit; //exit data file parser
1284 }
1285 if (StartsWithNoCase(LineData, "<break>"))
1286 {
1287 Breakpoint();
1288 return sNextLine;
1289 }
1290 if (StartsWithNoCase(LineData, "<include"))
1291 {
1292 //include another file at the current script location
1293
1294 int endloc = LineData.find(">");
1295
1296 if (endloc == -1)
1297 {
1298 ScriptError("Syntax error");
1299 return sError;
1300 }
1301
1302 std::string includefile = LineData.substr(9, endloc - 9);
1303 TrimString(includefile);
1304
1305 //delete current line
1306 BuildingData.erase(BuildingData.begin() + line);
1307
1308 //insert file at current line
1309 std::string filename = Simcore->VerifyFile(includefile);
1310 bool result = LoadDataFile(filename, true, line);
1311 if (result == false)
1312 return sError;
1313 engine->Report("Inserted file " + includefile);
1314
1315 //reset progress
1316 progress_marker = 0;
1317
1318 line--;
1319 return sNextLine;
1320 }
1321 if (StartsWithNoCase(LineData, "<function"))
1322 {
1323 //define a function
1324
1325 if (config->SectionNum != 0)
1326 {
1327 ScriptError("Cannot define a function within a section");
1328 return sError;
1329 }
1330
1331 int endloc = LineData.find(">");
1332
1333 if (endloc == -1)
1334 {
1335 ScriptError("Syntax error");
1336 return sError;
1337 }
1338
1339 std::string function = LineData.substr(10, endloc - 10);
1340 TrimString(function);
1341
1342 //skip the function definition and show a warning if it's already been defined
1343 bool defined = IsFunctionDefined(function);
1344
1345 if (defined == true)
1346 engine->Report("Function '" + function + "' already defined");
1347 else
1348 {
1349 //store function info in array
1350 FunctionInfo info;
1351 info.name = function;
1352 info.line = line;
1353 functions.emplace_back(info);
1354 }
1355
1356 //skip to end of function
1357 for (int i = line + 1; i < (int)BuildingData.size(); i++)
1358 {
1359 if (SetCaseCopy(BuildingData[i].substr(0, 13), false) == "<endfunction>")
1360 {
1361 line = i;
1362 break;
1363 }
1364 }
1365
1366 if (defined == false)
1367 engine->Report("Defined function " + function);
1368 return sNextLine;
1369 }
1370 if (StartsWithNoCase(LineData, "<textures>"))
1371 {
1372 if (config->SectionNum > 0)
1373 {
1374 ScriptError("Already within a section");
1375 return sError;
1376 }
1377 config->SectionNum = 5;
1378 config->Context = "Textures";
1379 engine->Report("Processing textures...");
1380 return sNextLine;
1381 }
1382 if (StartsWithNoCase(LineData, "<endfunction>") && InFunction > 0)
1383 {
1384 //end function and return to original line
1385 line = FunctionStack[InFunction - 1].CallLine - 1;
1388
1389 if (in_runloop == false)
1390 ReplaceLine = true;
1391
1392 if (data.Name == "runloop")
1393 {
1394 in_runloop = false;
1395 processed_runloop = true;
1396 }
1397 FunctionStack.erase(FunctionStack.begin() + InFunction - 1);
1398 InFunction -= 1;
1399 return sNextLine;
1400 }
1401 if (StartsWithNoCase(LineData, "<floors"))
1402 {
1403 if (config->SectionNum > 0)
1404 {
1405 ScriptError("Already within a section");
1406 return sError;
1407 }
1408 config->SectionNum = 2;
1409 std::string linecheck = SetCaseCopy(LineData, false);
1410 int loc = linecheck.find("to", 0);
1411 if (loc < 0)
1412 {
1413 ScriptError("Syntax error");
1414 return sError;
1415
1416 }
1417 //get low and high range markers
1418 std::string str1 = LineData.substr(8, loc - 9);
1419 std::string str2 = LineData.substr(loc + 2, LineData.length() - (loc + 2) - 1);
1420 TrimString(str1);
1421 TrimString(str2);
1422 if (!IsNumeric(str1, config->RangeL) || !IsNumeric(str2, config->RangeH))
1423 {
1424 ScriptError("Invalid range");
1425 return sError;
1426 }
1427 config->Context = "Floor range " + ToString(config->RangeL) + " to " + ToString(config->RangeH);
1430 engine->Report("Processing floors " + ToString(config->RangeL) + " to " + ToString(config->RangeH) + "...");
1431 return sNextLine;
1432 }
1433 if (StartsWithNoCase(LineData, "<floor "))
1434 {
1435 if (config->SectionNum > 0)
1436 {
1437 ScriptError("Already within a section");
1438 return sError;
1439 }
1440 config->SectionNum = 2;
1441 config->RangeL = 0;
1442 config->RangeH = 0;
1443 std::string str = LineData.substr(7, LineData.length() - 8);
1444 TrimString(str);
1445 if (!IsNumeric(str, config->Current))
1446 {
1447 ScriptError("Invalid floor");
1448 return sError;
1449 }
1450 config->Context = "Floor " + ToString(config->Current);
1451 engine->Report("Processing floor " + ToString(config->Current) + "...");
1452 return sNextLine;
1453 }
1454 if (StartsWithNoCase(LineData, "<elevators"))
1455 {
1456 if (config->SectionNum > 0)
1457 {
1458 ScriptError("Already within a section");
1459 return sError;
1460 }
1461 config->SectionNum = 4;
1462 std::string linecheck = SetCaseCopy(LineData, false);
1463 int loc = linecheck.find("to", 10);
1464 if (loc < 0)
1465 {
1466 ScriptError("Syntax error");
1467 return sError;
1468 }
1469 std::string str1 = LineData.substr(11, loc - 12);
1470 std::string str2 = LineData.substr(loc + 2, LineData.length() - (loc + 2) - 1);
1471 TrimString(str1);
1472 TrimString(str2);
1473 if (!IsNumeric(str1, config->RangeL) || !IsNumeric(str2, config->RangeH))
1474 {
1475 ScriptError("Invalid range");
1476 return sError;
1477 }
1478 config->Context = "Elevator range " + ToString(config->RangeL) + " to " + ToString(config->RangeH);
1481 engine->Report("Processing elevators " + ToString(config->RangeL) + " to " + ToString(config->RangeH) + "...");
1482 return sNextLine;
1483 }
1484 if (StartsWithNoCase(LineData, "<elevator "))
1485 {
1486 if (config->SectionNum > 0)
1487 {
1488 ScriptError("Already within a section");
1489 return sError;
1490 }
1491 config->SectionNum = 4;
1492 config->RangeL = 0;
1493 config->RangeH = 0;
1494 std::string str = LineData.substr(10, LineData.length() - 11);
1495 TrimString(str);
1496 if (!IsNumeric(str, config->Current))
1497 {
1498 ScriptError("Invalid elevator");
1499 return sError;
1500 }
1501 if (config->Current < 1 || config->Current > Simcore->GetElevatorCount() + 1)
1502 {
1503 ScriptError("Invalid elevator");
1504 return sError;
1505 }
1506 config->Context = "Elevator " + ToString(config->Current);
1507 engine->Report("Processing elevator " + ToString(config->Current) + "...");
1508 return sNextLine;
1509 }
1510 if (StartsWithNoCase(LineData, "<buildings>"))
1511 {
1512 //skip this section if reloading
1513 if (engine->IsReloading() == true)
1514 return sNextLine;
1515
1516 if (config->SectionNum > 0)
1517 {
1518 ScriptError("Already within a section");
1519 return sError;
1520 }
1521 config->SectionNum = 3;
1522 config->Context = "Buildings";
1523 engine->Report("Loading other buildings...");
1524 return sNextLine;
1525 }
1526 if (StartsWithNoCase(LineData, "<cars") && config->SectionNum == 4)
1527 {
1528 config->SectionNum = 6;
1529 std::string linecheck = SetCaseCopy(LineData, false);
1530 int loc = linecheck.find("to", 5);
1531 if (loc < 0)
1532 {
1533 ScriptError("Syntax error");
1534 return sError;
1535 }
1536
1537 //store previous elevator section values
1543
1544 std::string str1 = LineData.substr(6, loc - 7);
1545 std::string str2 = LineData.substr(loc + 2, LineData.length() - (loc + 2) - 1);
1546 TrimString(str1);
1547 TrimString(str2);
1548 if (!IsNumeric(str1, config->RangeL) || !IsNumeric(str2, config->RangeH))
1549 {
1550 ScriptError("Invalid range");
1551 return sError;
1552 }
1553
1554 //verify elevator
1556 {
1557 ScriptError("Invalid elevator");
1558 return sError;
1559 }
1560
1561 config->Context = "Elevator " + ToString(config->CurrentOld) + " Car range " + ToString(config->RangeL) + " to " + ToString(config->RangeH);
1564 engine->Report("Processing elevator " + ToString(config->CurrentOld) + " cars " + ToString(config->RangeL) + " to " + ToString(config->RangeH) + "...");
1565 return sNextLine;
1566 }
1567 if (StartsWithNoCase(LineData, "<car ") && config->SectionNum == 4)
1568 {
1569 config->SectionNum = 6;
1570
1571 //store previous elevator section values
1577
1578 config->RangeL = 0;
1579 config->RangeH = 0;
1580 std::string str = LineData.substr(5, LineData.length() - 6);
1581 TrimString(str);
1582 if (!IsNumeric(str, config->Current))
1583 {
1584 ScriptError("Invalid car number");
1585 return sError;
1586 }
1587
1588 //verify elevator
1590 {
1591 ScriptError("Invalid elevator");
1592 return sError;
1593 }
1594
1595 config->Context = "Elevator " + ToString(config->CurrentOld) + " Car " + ToString(config->Current);
1596 engine->Report("Processing elevator " + ToString(config->CurrentOld) + " car " + ToString(config->Current) + "...");
1597 return sNextLine;
1598 }
1599 if (StartsWithNoCase(LineData, "<vehicles"))
1600 {
1601 if (config->SectionNum > 0)
1602 {
1603 ScriptError("Already within a section");
1604 return sError;
1605 }
1606 config->SectionNum = 7;
1607 std::string linecheck = SetCaseCopy(LineData, false);
1608 int loc = linecheck.find("to", 9);
1609 if (loc < 0)
1610 {
1611 ScriptError("Syntax error");
1612 return sError;
1613 }
1614 std::string str1 = LineData.substr(10, loc - 11);
1615 std::string str2 = LineData.substr(loc + 2, LineData.length() - (loc + 2) - 1);
1616 TrimString(str1);
1617 TrimString(str2);
1618 if (!IsNumeric(str1, config->RangeL) || !IsNumeric(str2, config->RangeH))
1619 {
1620 ScriptError("Invalid range");
1621 return sError;
1622 }
1623 config->Context = "Vehicle range " + ToString(config->RangeL) + " to " + ToString(config->RangeH);
1626 engine->Report("Processing vehicles " + ToString(config->RangeL) + " to " + ToString(config->RangeH) + "...");
1627 return sNextLine;
1628 }
1629 if (StartsWithNoCase(LineData, "<vehicle "))
1630 {
1631 if (config->SectionNum > 0)
1632 {
1633 ScriptError("Already within a section");
1634 return sError;
1635 }
1636 config->SectionNum = 7;
1637 config->RangeL = 0;
1638 config->RangeH = 0;
1639 std::string str = LineData.substr(9, LineData.length() - 10);
1640 TrimString(str);
1641 if (!IsNumeric(str, config->Current))
1642 {
1643 ScriptError("Invalid vehicle");
1644 return sError;
1645 }
1646 if (config->Current < 1 || config->Current > Simcore->GetVehicleCount() + 1)
1647 {
1648 ScriptError("Invalid vehicle");
1649 return sError;
1650 }
1651 config->Context = "Vehicle " + ToString(config->Current);
1652 engine->Report("Processing vehicle " + ToString(config->Current) + "...");
1653 return sNextLine;
1654 }
1655 if (StartsWithNoCase(LineData, "<controllers"))
1656 {
1657 if (config->SectionNum > 0)
1658 {
1659 ScriptError("Already within a section");
1660 return sError;
1661 }
1662 config->SectionNum = 8;
1663 std::string linecheck = SetCaseCopy(LineData, false);
1664 int loc = linecheck.find("to", 12);
1665 if (loc < 0)
1666 {
1667 ScriptError("Syntax error");
1668 return sError;
1669 }
1670 std::string str1 = LineData.substr(13, loc - 14);
1671 std::string str2 = LineData.substr(loc + 2, LineData.length() - (loc + 2) - 1);
1672 TrimString(str1);
1673 TrimString(str2);
1674 if (!IsNumeric(str1, config->RangeL) || !IsNumeric(str2, config->RangeH))
1675 {
1676 ScriptError("Invalid range");
1677 return sError;
1678 }
1679 config->Context = "Controller range " + ToString(config->RangeL) + " to " + ToString(config->RangeH);
1682 engine->Report("Processing controllers " + ToString(config->RangeL) + " to " + ToString(config->RangeH) + "...");
1683 return sNextLine;
1684 }
1685 if (StartsWithNoCase(LineData, "<controller "))
1686 {
1687 if (config->SectionNum > 0)
1688 {
1689 ScriptError("Already within a section");
1690 return sError;
1691 }
1692 config->SectionNum = 8;
1693 config->RangeL = 0;
1694 config->RangeH = 0;
1695 std::string str = LineData.substr(12, LineData.length() - 13);
1696 TrimString(str);
1697 if (!IsNumeric(str, config->Current))
1698 {
1699 ScriptError("Invalid controller");
1700 return sError;
1701 }
1702 if (config->Current < 1 || config->Current > Simcore->GetControllerCount() + 1)
1703 {
1704 ScriptError("Invalid controller");
1705 return sError;
1706 }
1707 config->Context = "Controller " + ToString(config->Current);
1708 engine->Report("Processing controller " + ToString(config->Current) + "...");
1709 return sNextLine;
1710 }
1711 if (StartsWithNoCase(LineData, "<callstations") && config->SectionNum == 2)
1712 {
1713 config->SectionNum = 9;
1714 std::string linecheck = SetCaseCopy(LineData, false);
1715 int loc = linecheck.find("to", 13);
1716 if (loc < 0)
1717 {
1718 ScriptError("Syntax error");
1719 return sError;
1720 }
1721
1722 //store previous elevator section values
1728
1729 std::string str1 = LineData.substr(14, loc - 15);
1730 std::string str2 = LineData.substr(loc + 2, LineData.length() - (loc + 2) - 1);
1731 TrimString(str1);
1732 TrimString(str2);
1733 if (!IsNumeric(str1, config->RangeL) || !IsNumeric(str2, config->RangeH))
1734 {
1735 ScriptError("Invalid range");
1736 return sError;
1737 }
1738
1739 //verify floor
1741 {
1742 ScriptError("Invalid floor");
1743 return sError;
1744 }
1745
1746 config->Context = "Floor " + ToString(config->CurrentOld) + " Call Station range " + ToString(config->RangeL) + " to " + ToString(config->RangeH);
1749 if (Simcore->Verbose)
1750 engine->Report("Processing floor " + ToString(config->CurrentOld) + " call stations " + ToString(config->RangeL) + " to " + ToString(config->RangeH) + "...");
1751 return sNextLine;
1752 }
1753 if (StartsWithNoCase(LineData, "<callstation ") && config->SectionNum == 2)
1754 {
1755 config->SectionNum = 9;
1756
1757 //store previous elevator section values
1763
1764 config->RangeL = 0;
1765 config->RangeH = 0;
1766 std::string str = LineData.substr(13, LineData.length() - 14);
1767 TrimString(str);
1768 if (!IsNumeric(str, config->Current))
1769 {
1770 ScriptError("Invalid call station number");
1771 return sError;
1772 }
1773
1774 //verify floor
1776 {
1777 ScriptError("Invalid floor");
1778 return sError;
1779 }
1780
1781 config->Context = "Floor " + ToString(config->CurrentOld) + " Call Station " + ToString(config->Current);
1782 if (Simcore->Verbose)
1783 engine->Report("Processing floor " + ToString(config->CurrentOld) + " call station " + ToString(config->Current) + "...");
1784 return sNextLine;
1785 }
1786
1787 return sContinue;
1788}
1789
1791{
1793 //Floor object conversion
1795 int exists = SetCaseCopy(LineData, false).find("floor(", 0);
1796 while (exists > -1)
1797 {
1798 int loc1 = LineData.find("(", exists);
1799 int loc2 = LineData.find(")", exists);
1800 if (loc2 < 0)
1801 {
1802 ScriptError("Syntax error");
1803 return sError;
1804 }
1805 if (config->SectionNum == 2 && getfloordata == false)
1806 {
1807 //process floor-specific variables if in a floor section
1808 getfloordata = true;
1809 return sRecalc;
1810 }
1811 else
1812 getfloordata = false;
1813 std::string tempdata = Calc(LineData.substr(loc1 + 1, loc2 - loc1 - 1));
1814 LineData = LineData.substr(0, loc1 + 1) + tempdata + LineData.substr(loc2);
1815
1816 int floor = 0;
1817 if (!IsNumeric(tempdata, floor))
1818 {
1819 ScriptError("Invalid floor " + tempdata);
1820 return sError;
1821 }
1822 if (Simcore->IsValidFloor(floor) == false)
1823 {
1824 ScriptError("Invalid floor " + tempdata);
1825 return sError;
1826 }
1827
1828 //fullheight parameter
1829 std::string buffer = ToString(floor);
1830 TrimString(buffer);
1831 std::string name = "floor(" + buffer + ").fullheight";
1832 buffer = LineData;
1833 SetCase(buffer, false);
1834 loc1 = buffer.find(name, 0);
1835 if (loc1 > 0)
1836 {
1837 buffer = ToString(Simcore->GetFloor(floor)->FullHeight());
1838 TrimString(buffer);
1839 LineData = LineData.substr(0, loc1) + buffer + LineData.substr(loc1 + name.length());
1840 }
1841
1842 //height parameter
1843 buffer = ToString(floor);
1844 TrimString(buffer);
1845 name = "floor(" + buffer + ").height";
1846 buffer = LineData;
1847 SetCase(buffer, false);
1848 loc1 = buffer.find(name, 0);
1849 if (loc1 > 0)
1850 {
1851 buffer = ToString(Simcore->GetFloor(floor)->Height);
1852 TrimString(buffer);
1853 LineData = LineData.substr(0, loc1) + buffer + LineData.substr(loc1 + name.length());
1854 }
1855
1856 //altitude parameter
1857 buffer = ToString(floor);
1858 TrimString(buffer);
1859 name = "floor(" + buffer + ").altitude";
1860 buffer = LineData;
1861 SetCase(buffer, false);
1862 loc1 = buffer.find(name, 0);
1863 if (loc1 > 0)
1864 {
1865 buffer = ToString(Simcore->GetFloor(floor)->Altitude);
1866 TrimString(buffer);
1867 LineData = LineData.substr(0, loc1) + buffer + LineData.substr(loc1 + name.length());
1868 }
1869
1870 //interfloorheight parameter
1871 buffer = ToString(floor);
1872 TrimString(buffer);
1873 name = "floor(" + buffer + ").interfloorheight";
1874 buffer = LineData;
1875 SetCase(buffer, false);
1876 loc1 = buffer.find(name, 0);
1877 if (loc1 > 0)
1878 {
1879 buffer = ToString(Simcore->GetFloor(floor)->InterfloorHeight);
1880 TrimString(buffer);
1881 LineData = LineData.substr(0, loc1) + buffer + LineData.substr(loc1 + name.length());
1882 }
1883 exists = SetCaseCopy(LineData, false).find("floor(", 0);
1884
1885 //base parameter
1886 buffer = ToString(floor);
1887 TrimString(buffer);
1888 name = "floor(" + buffer + ").base";
1889 buffer = LineData;
1890 SetCase(buffer, false);
1891 loc1 = buffer.find(name, 0);
1892 if (loc1 > 0)
1893 {
1894 buffer = ToString(Simcore->GetFloor(floor)->GetBase());
1895 TrimString(buffer);
1896 LineData = LineData.substr(0, loc1) + buffer + LineData.substr(loc1 + name.length());
1897 }
1898 exists = SetCaseCopy(LineData, false).find("floor(", 0);
1899 }
1900
1901 return sContinue;
1902}
1903
1905{
1906 //breakpoint function for debugging scripts
1907 engine->Report("Script breakpoint reached");
1908}
1909
1911{
1912 //Extent variables
1917}
1918
1923
1925{
1926 if (StartsWithNoCase(LineData, "<for "))
1927 {
1928 //process a For loop
1929
1930 std::string linecheck = SetCaseCopy(LineData, false);
1931 int loc = linecheck.find("to", 0);
1932 if (loc < 0)
1933 {
1934 ScriptError("Syntax error");
1935 return sError;
1936 }
1937
1938 //get iterator
1939 int loc2 = linecheck.find(" ", 5);
1940 if (loc < 0)
1941 {
1942 ScriptError("Syntax error");
1943 return sError;
1944 }
1945 std::string it = LineData.substr(5, loc2 - 5);
1946 TrimString(it);
1947
1948 //check for existence of iterator variable
1949 for (int i = 0; i < variables.size(); i++)
1950 {
1951 if (variables[i].name == it)
1952 {
1953 ScriptError("Iterator variable in use");
1954 return sError;
1955 }
1956 }
1957
1958 //get low and high range markers
1959 std::string str1 = LineData.substr(loc2, loc - (loc2 + 1));
1960 std::string str2 = LineData.substr(loc + 2, LineData.length() - (loc + 2) - 1);
1961 TrimString(str1);
1962 TrimString(str2);
1963 int RangeL, RangeH;
1964 if (!IsNumeric(str1, RangeL) || !IsNumeric(str2, RangeH))
1965 {
1966 ScriptError("Invalid range");
1967 return sError;
1968 }
1969
1970 //set new iterator variable
1971 VariableMap var;
1972 var.name = it;
1973 var.value = ToString(RangeL);
1974 variables.emplace_back(var);
1975
1976 //set up for loop
1977 ForInfo info;
1978 info.iterator = it;
1979 info.line = line;
1980 info.i = RangeL;
1981 info.start = RangeL;
1982 info.end = RangeH;
1983
1984 ForLoops.emplace_back(info);
1985
1986 return sNextLine;
1987 }
1988 else if (StartsWithNoCase(LineData, "<endfor>"))
1989 {
1990 ForInfo &info = ForLoops.back();
1991
1992 bool end = false;
1993 if (info.start < info.end)
1994 {
1995 info.i++;
1996 if (info.i > info.end)
1997 end = true;
1998 }
1999 else
2000 {
2001 info.i--;
2002 if (info.i < info.end)
2003 end = true;
2004 }
2005
2006 for (int i = 0; i < variables.size(); i++)
2007 {
2008 if (variables[i].name == info.iterator)
2009 {
2010 if (end == false)
2011 {
2012 //put iterator into variable
2013 variables[i].value = ToString(info.i);
2014 break;
2015 }
2016 else
2017 {
2018 //remove iterator variable
2019 variables.erase(variables.begin() + i);
2020 break;
2021 }
2022 }
2023 }
2024
2025 if (end == true)
2026 {
2027 //process end of For loop
2028 ForLoops.pop_back();
2029 return sNextLine;
2030 }
2031 else
2032 return sLoopFor;
2033 }
2034
2035 return sContinue;
2036}
2037
2039{
2040 //return true if this building has a runloop function
2041
2042 for (int i = 0; i < functions.size(); i++)
2043 {
2044 if (functions[i].name == "runloop")
2045 {
2046 return true;
2047 }
2048 }
2049 return false;
2050}
2051
2053{
2054 Simcore->BuildingName = "Default";
2055 Simcore->BuildingDesigner = "Me";
2056 Simcore->SkyName = "noon";
2057 //Simcore->camera->EnableCollisions(false);
2058 //Simcore->camera->EnableGravity(false);
2059}
2060
2062{
2063 IsFinished = true;
2064 show_percent = false;
2065}
2066
2067}
Real FullHeight()
Definition floor.cpp:572
Real Altitude
Definition floor.h:43
Real GetBase(bool relative=false)
Definition floor.cpp:1140
Real Height
Definition floor.h:44
Real InterfloorHeight
Definition floor.h:45
virtual void Report(const std::string &message)
Definition object.cpp:78
Elevator * GetElevator(int number)
Definition sbs.cpp:1746
std::string BuildingName
Definition sbs.h:145
std::string SkyName
Definition sbs.h:183
int GetVehicleCount()
Definition sbs.cpp:1709
std::string GetMountPath(std::string filename, std::string &newfilename)
Definition sbs.cpp:3335
bool FileExists(const std::string &filename)
Definition sbs.cpp:3055
int GetElevatorCount()
Definition sbs.cpp:1703
int GetControllerCount()
Definition sbs.cpp:1733
bool IsValidFloor(int floor)
Definition sbs.cpp:2492
Floor * GetFloor(int number)
Definition sbs.cpp:1739
TextureManager * GetTextureManager()
Definition sbs.cpp:4558
std::string BuildingDesigner
Definition sbs.h:147
Floor * NewFloor(int number)
Definition sbs.cpp:1682
unsigned long GetRunTime()
Definition sbs.cpp:3290
std::string LastError
Definition sbs.h:189
std::string VerifyFile(const std::string &filename)
Definition sbs.cpp:2916
bool Verbose
Definition sbs.h:186
bool ReportError(const std::string &message)
bool UpdateProgress(int percent)
bool ReportFatalError(const std::string &message)
void Report(const std::string &message)
int Run(std::string &LineData)
Definition floors.cpp:76
VehicleSection * vehicle_section
Definition scriptproc.h:106
static const int sNextLine
Definition scriptproc.h:70
std::vector< FunctionInfo > functions
Definition scriptproc.h:155
std::vector< std::string > nonexistent_files
Definition scriptproc.h:86
static const int sContinue
Definition scriptproc.h:69
static const int sBreak
Definition scriptproc.h:73
std::vector< IncludeInfo > includes
Definition scriptproc.h:174
std::vector< ForInfo > ForLoops
Definition scriptproc.h:175
std::vector< VariableMap > variables
Definition scriptproc.h:85
ControllerSection * controller_section
Definition scriptproc.h:107
std::vector< FunctionData > FunctionStack
Definition scriptproc.h:123
static const int sError
Definition scriptproc.h:71
int ScriptWarning(std::string message)
bool LoadFromText(const std::string &text)
static const int sCheckFloors
Definition scriptproc.h:72
std::vector< std::string > BuildingDataOrig
Definition scriptproc.h:121
GlobalsSection * globals_section
Definition scriptproc.h:99
static const int sLoopFor
Definition scriptproc.h:77
void GetLineInformation(bool CheckFunctionCall, int &LineNumber, std::string &FunctionName, int &FunctionLine, bool &IsInclude, std::string &IncludeFile, bool &IsIncludeFunction, std::string &IncludeFunctionFile)
BuildingsSection * buildings_section
Definition scriptproc.h:100
ElevatorCarSection * GetElevatorCarSection()
std::vector< std::string > * GetBuildingData()
static const int sRecalc
Definition scriptproc.h:74
CallStationSection * callstation_section
Definition scriptproc.h:108
bool IsFunctionDefined(const std::string &name)
FloorSection * floor_section
Definition scriptproc.h:103
void StoreCommand(::SBS::Object *object)
ScriptProcessor(EngineContext *instance)
CommandsSection * commands_section
Definition scriptproc.h:102
ElevatorSection * elevator_section
Definition scriptproc.h:104
static const int sExit
Definition scriptproc.h:76
bool LoadDataFile(const std::string &filename, bool insert=false, int insert_line=0)
EngineContext * GetEngine()
std::string Calc(const std::string &expression)
TexturesSection * textures_section
Definition scriptproc.h:101
ConfigHandler * GetConfigHandler()
ElevatorCarSection * elevatorcar_section
Definition scriptproc.h:105
std::vector< std::string > BuildingData
Definition scriptproc.h:120
void CheckFile(const std::string &filename)
static const int sSkipReset
Definition scriptproc.h:75
void GetTime(int &hour, int &minute, int &second)
Definition sky.cpp:299
int GetEngineCount(bool loading_only=false)
Definition vm.cpp:442
SkySystem * GetSkySystem()
Definition vm.cpp:135
bool CheckScript
Definition vm.h:117
Ogre::Real Real
Definition globals.h:57
bool StartsWithNoCase(const std::string &string, const std::string &check_string)
Definition globals.cpp:237
void SplitString(std::vector< std::string > &dest_array, const std::string &original_string, char separator)
Definition globals.cpp:242
void ReplaceAll(std::string &string, const std::string &original, const std::string &replacement)
Definition globals.cpp:201
int ToInt(const std::string &string)
Definition globals.cpp:402
void SetCase(std::string &string, bool uppercase)
Definition globals.cpp:172
std::string ToString(int number)
Definition globals.cpp:279
std::string SetCaseCopy(std::string string, bool uppercase)
Definition globals.cpp:165
Real ToFloat(const std::string &string)
Definition globals.cpp:397
void TrimString(std::string &string)
Definition globals.cpp:188
bool IsNumeric(const wxString &string)
wxString TruncateNumber(float value, int decimals)
std::vector< std::string > Params
Definition scriptproc.h:115