1 /++ 2 Scene and instance control module. 3 4 Macros: 5 LREF = <a href="#$1">$1</a> 6 HREF = <a href="$1">$2</a> 7 8 Authors: $(HREF https://github.com/TodNaz,TodNaz) 9 Copyright: Copyright (c) 2020 - 2021, TodNaz. 10 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT) 11 +/ 12 module tida.scenemanager; 13 14 import tida.instance; 15 import tida.localevent; 16 import tida.scene; 17 import tida.render; 18 import tida.instance; 19 import tida.event; 20 import tida.fps; 21 import core.thread; 22 23 /++ 24 Mistakes of using communication between the manager and the game cycle. 25 +/ 26 enum APIError : uint 27 { 28 succes, /// Errors are not detected. 29 ThreadIsNotExists /// The stream with which it was necessary to interact - does not exist. 30 } 31 32 /++ 33 Commands that should execute the game cycle. 34 +/ 35 enum APIType : uint 36 { 37 None, /// None 38 ThreadCreate, /// Create the specified number of threads. 39 ThreadPause, 40 ThreadResume, 41 ThreadClose, 42 GameClose 43 } 44 45 /++ 46 Container to send a message to the game cycle. 47 +/ 48 struct APIResponse 49 { 50 uint code; /// Command thah should execute the game cycle. 51 uint value; /// Value response 52 } 53 54 /++ 55 Thread for execution of steps for scenes and instances. 56 +/ 57 class InstanceThread : Thread 58 { 59 private: 60 bool isJob = true; 61 bool isPause = false; 62 FPSManager fps; 63 Instance[] list; 64 size_t thread; 65 IRenderer rend; 66 67 void run() 68 { 69 while (isJob) 70 { 71 if (isPause) continue; 72 fps.countDown(); 73 74 sceneManager.callStep(thread, rend); 75 76 fps.control(); 77 } 78 } 79 80 public @safe: 81 82 /++ 83 Params: 84 thread = Unique Identificator for Flow, namely, a place in the array for which it can 85 contact such arrays for copies of the compliant ideal. 86 rend = Renderer instance. 87 +/ 88 this(size_t thread,IRenderer rend) 89 { 90 fps = new FPSManager(); 91 92 this.thread = thread; 93 this.rend = rend; 94 95 super(&run); 96 } 97 98 /// Replaces the idle identifier. 99 void rebindThreadID(size_t newID) 100 { 101 thread = newID; 102 } 103 104 /// Pause the work of the thread. 105 void pause() 106 { 107 isPause = true; 108 } 109 110 /// Continues thread work. 111 void resume() 112 { 113 isPause = false; 114 } 115 116 /// Completes the flow of the thread. 117 void exit() 118 { 119 isJob = false; 120 } 121 } 122 123 __gshared SceneManager _sceneManager; 124 125 /// Scene manager instance. 126 SceneManager sceneManager() @trusted 127 { 128 return _sceneManager; 129 } 130 131 /// Allocates memory under the scene manager. 132 void initSceneManager() @trusted 133 { 134 _sceneManager = new SceneManager(); 135 } 136 137 /++ 138 Class describing scene manager. 139 140 Performs the functions of switching the context of the scenes, memorize 141 the list for subsequent circulation, the ability to execute elementary 142 events, give an instance access to the current scene or scene, which is 143 involved in the event. 144 145 To transfer the context, use the `gotoin`. Learn the current scene - `current`. 146 previous - `previous` Contact precisely to the global object - `scenemanager`. 147 +/ 148 final class SceneManager 149 { 150 private: 151 Scene[string] _scenes; 152 Scene _current; 153 Scene _previous; 154 Scene _ofbegin; 155 Scene _ofend; 156 Scene _initable; 157 Scene _restarted; 158 159 public @safe: 160 /// List scenes 161 @property Scene[string] scenes() nothrow 162 { 163 return _scenes; 164 } 165 166 /++ 167 The first added scene. 168 169 It can be overridden so that when the game 170 is restarted, the manager will jump to the 171 scene from this line: 172 --- 173 sceneManager.ofbegin = myScene; 174 sceneManager.gameRestart(); 175 --- 176 +/ 177 @property Scene ofbegin() nothrow 178 { 179 return _ofbegin; 180 } 181 182 /++ 183 The last added scene. 184 +/ 185 @property Scene ofend() nothrow 186 { 187 return _ofend; 188 } 189 190 /++ 191 The previous scene that was active. 192 +/ 193 @property Scene previous() nothrow 194 { 195 return _previous; 196 } 197 198 /++ 199 A scene that restarts at the moment. 200 +/ 201 @property Scene restarted() nothrow 202 { 203 return _restarted; 204 } 205 206 /++ 207 Restarting the game. 208 209 Please note that this does not affect memory, 210 the state of variables, etc., however, gives such a simulation, 211 therefore, create a corresponding event for resetting the state 212 when the game is restarted, if this is provided. 213 +/ 214 void gameRestart() @trusted 215 { 216 foreach (scene; scenes) 217 { 218 if (!scene.isInit) continue; 219 220 _restarted = scene; 221 222 foreach (fun; GameRestartFunctions[scene]) fun(); 223 foreach (instance; scene.list()) 224 foreach (fun; IGameRestartFunctions[instance]) fun(); 225 226 scene.isInit = false; 227 228 _restarted = null; 229 } 230 231 gotoin(ofbegin); 232 } 233 234 /++ 235 Link to the current scene. 236 237 Please note that such a pointer is correct only in those events that 238 differ from `init`,` restart`, `leave`, they can not go at all on the 239 current one that you hoped. Example: In the initialization event, you 240 want access to the scene, which is initialized, but here you can make 241 a mistake - the pointer leads to the previous scene. You can access 242 the current through `sceneManager.initable`. 243 244 See_Also: 245 tida.scene.manager.SceneManager.initable 246 +/ 247 @property Scene current() nothrow 248 { 249 return _current; 250 } 251 252 /++ 253 The reference to the scene, which is undergoing context change 254 processing. 255 256 The use of such a link is permissible only in context transmission 257 events, otherwise, it is possible to detect the scene leading nowhere. 258 +/ 259 @property Scene initable() nothrow 260 { 261 return _initable; 262 } 263 264 /++ 265 The reference to the current stage, as if it is under initialization, 266 whether it is during a restart or without them. 267 268 This link is selected depending on what is happening. If this is caused 269 during the change of context, it will lead exactly the scene that 270 receives the context. If the manager restarts the game, the link leads 271 to the scene, which is now restarting if there are no such events, then 272 the scene leads to the current working scene. 273 274 Examples: 275 --- 276 @FunEvent!Init 277 void Initialization() @safe 278 { 279 assert(sceneManager.initable is sceneManager.context); // ok 280 } 281 282 @FunEvent!Step 283 void Move() @safe 284 { 285 assert(sceneManager.current is sceneManager.context); // ok 286 } 287 288 @FunEvent!GameRestart 289 void onGameRestart() @safe 290 { 291 assert(sceneManager.restarted is sceneManager.context); // ok 292 } 293 --- 294 +/ 295 @property Scene context() 296 { 297 if (_initable is null) 298 { 299 if (_restarted is null) 300 { 301 return _current; 302 } else 303 return _restarted; 304 } else 305 return _initable; 306 307 } 308 309 /++ 310 Calls a trigger for the current scene, as well as its instances. 311 312 Triggers are required for custom signal and events. By calling, you can 313 force to pull functions with special attributes, for example: 314 --- 315 alias SpecEvent = Trigger("SpecialEvent"); 316 317 @SpecEvent 318 void onSpec() @safe { ... } 319 ... 320 sceneManager.trigger("SpecialEvent"); 321 // Will cause the exact event to be called by calling the function, 322 // only for the scene that is being held in the context. 323 --- 324 325 Params: 326 name = Trigger name. 327 +/ 328 void trigger(string name) @trusted 329 { 330 auto scene = this.context(); 331 332 if (scene in OnTriggerFunctions) 333 { 334 foreach (fun; OnTriggerFunctions[scene]) 335 { 336 if (fun.ev.name == name) 337 { 338 fun.fun(); 339 } 340 } 341 } 342 343 foreach (instance; scene.list()) 344 { 345 if (instance in IOnTriggerFunctions) 346 { 347 foreach (fun; IOnTriggerFunctions[instance]) 348 { 349 if (fun.ev.name == name) 350 { 351 fun.fun(); 352 } 353 } 354 } 355 } 356 } 357 358 /++ 359 Checks if the scene is in the scene list. 360 361 Params: 362 scene = Scene. 363 +/ 364 bool hasScene(Scene scene) 365 { 366 if (scene is null) 367 return false; 368 369 foreach (inscene; scenes) 370 { 371 if (scene is inscene) 372 return true; 373 } 374 375 return false; 376 } 377 378 /++ 379 Checks for the existence of a scene by its original class. 380 381 Params: 382 Name = Class name. 383 +/ 384 bool hasScene(Name)() 385 { 386 foreach (scene; scenes) 387 { 388 if ((cast(Name) scene) !is null) 389 return true; 390 } 391 392 return false; 393 } 394 395 /++ 396 Checks if there is a scene with the specified name. 397 398 Params: 399 name = Scene name. 400 +/ 401 bool hasScene(string name) 402 { 403 foreach (scene; scenes) 404 { 405 if (scene.name == name) return true; 406 } 407 408 return false; 409 } 410 411 /++ 412 Adds a scene to the list. 413 414 Params: 415 scene = Scene. 416 +/ 417 void add(T)(T scene) 418 { 419 exploreScene!T(scene); 420 421 if (_ofbegin is null) 422 _ofbegin = scene; 423 424 _scenes[scene.name] = scene; 425 } 426 427 protected 428 { 429 import std.container, std.range, std.traits; 430 import tida.component : Component; 431 432 alias FEInit = void delegate() @safe; 433 alias FEStep = void delegate() @safe; 434 alias FERestart = void delegate() @safe; 435 alias FEEntry = void delegate() @safe; 436 alias FELeave = void delegate() @safe; 437 alias FEGameStart = void delegate() @safe; 438 alias FEGameExit = void delegate() @safe; 439 alias FEGameRestart = void delegate() @safe; 440 alias FEEventHandle = void delegate(EventHandler) @safe; 441 alias FEDraw = void delegate(IRenderer) @safe; 442 alias FEOnError = void delegate() @safe; 443 alias FECollision = void delegate(Instance) @safe; 444 alias FETrigger = void delegate() @safe; 445 alias FEDestroy = void delegate(Instance) @safe; 446 447 alias FECInit = void delegate(Instance) @safe; 448 449 struct SRCollider 450 { 451 Collision ev; 452 FECollision fun; 453 } 454 455 struct SRTrigger 456 { 457 Trigger ev; 458 FETrigger fun; 459 } 460 461 FEInit[][Scene] InitFunctions; 462 FEStep[][Scene] StepFunctions; 463 FEStep[][size_t][Scene] StepThreadFunctions; 464 FERestart[][Scene] RestartFunctions; 465 FEEntry[][Scene] EntryFunctions; 466 FELeave[][Scene] LeaveFunctions; 467 FEGameStart[][Scene] GameStartFunctions; 468 FEGameExit[][Scene] GameExitFunctions; 469 FEGameRestart[][Scene] GameRestartFunctions; 470 FEEventHandle[][Scene] EventHandleFunctions; 471 FEDraw[][Scene] DrawFunctions; 472 FEOnError[][Scene] OnErrorFunctions; 473 SRTrigger[][Scene] OnTriggerFunctions; 474 FEDestroy[][Scene] OnDestroyFunctions; 475 476 FEInit[][Instance] IInitFunctions; 477 FEStep[][Instance] IStepFunctions; 478 FEStep[][size_t][Instance] IStepThreadFunctions; 479 FERestart[][Instance] IRestartFunctions; 480 FEEntry[][Instance] IEntryFunctions; 481 FELeave[][Instance] ILeaveFunctions; 482 FEGameStart[][Instance] IGameStartFunctions; 483 FEGameExit[][Instance] IGameExitFunctions; 484 FEGameRestart[][Instance] IGameRestartFunctions; 485 FEEventHandle[][Instance] IEventHandleFunctions; 486 FEDraw[][Instance] IDrawFunctions; 487 FEOnError[][Instance] IOnErrorFunctions; 488 SRCollider[][Instance] IColliderStructs; 489 FECollision[][Instance] ICollisionFunctions; 490 SRTrigger[][Instance] IOnTriggerFunctions; 491 FEDestroy[][Instance] IOnDestroyFunctions; 492 493 FEStep[][Component] CStepFunctions; 494 FEStep[][size_t][Component] CStepThreadFunctions; 495 FELeave[][Component] CLeaveFunctions; 496 FEEventHandle[][Component] CEventHandleFunctions; 497 FEDraw[][Component] CDrawFunctions; 498 FEOnError[][Component] COnErrorFunctions; 499 SRTrigger[][Component] COnTriggerFunctions; 500 } 501 502 /++ 503 Raise the event of destruction of the instance. (@FunEvent!Destroy) 504 505 Params: 506 instance = Instance. 507 +/ 508 void destroyEventCall(T)(T instance) @trusted 509 { 510 static assert(isInstance!T, "`" ~ T.stringof ~ "` is not a instance!"); 511 foreach(func; IOnDestroyFunctions[instance]) func(instance); 512 } 513 514 /++ 515 Reise the event of destruction in current scene. (@FunEvent!Destroy) 516 517 Params: 518 scene = Current scene. 519 instance = Instance. 520 +/ 521 void destroyEventSceneCall(T, R)(T scene, R instance) @trusted 522 { 523 foreach(func; OnDestroyFunctions[scene]) func(instance); 524 } 525 526 package(tida) void componentExplore(T)(Instance instance, T component) @trusted 527 { 528 CStepFunctions[component] = []; 529 CLeaveFunctions[component] = []; 530 CEventHandleFunctions[component] = []; 531 CDrawFunctions[component] = []; 532 COnErrorFunctions[component] = []; 533 COnTriggerFunctions[component] = []; 534 535 static foreach (member; __traits(allMembers, T)) 536 { 537 static foreach (attrib; __traits(getAttributes, __traits(getMember, component, member))) 538 { 539 static if (is(attrib : FunEvent!Init)) 540 { 541 auto fun = cast(FECInit) &__traits(getMember, component, member); 542 fun(instance); 543 } else 544 static if (is(attrib : FunEvent!Step)) 545 { 546 CStepFunctions[component] ~= &__traits(getMember, component, member); 547 } else 548 static if (is(attrib : FunEvent!Leave)) 549 { 550 CLeaveFunctions[component] ~= &__traits(getMember, component, member); 551 } else 552 static if (is(attrib : FunEvent!Input)) 553 { 554 CEventHandleFunctions[component] ~= cast(FEEventHandle) &__traits(getMember, component, member); 555 } else 556 static if (is(attrib : FunEvent!Draw)) 557 { 558 CDrawFunctions[component] ~= cast(FEDraw) &__traits(getMember, component, member); 559 } else 560 static if (is(attrib : FunEvent!GameError)) 561 { 562 COnErrorFunctions[component] ~= &__traits(getMember, component, member); 563 } else 564 static if (attrib.stringof[0 .. 8] == "InThread") 565 { 566 CStepThreadFunctions[instance][attrib.id] ~= &__traits(getMember, instance, member); 567 }else 568 static if (attrig.stringof[0 .. 7] == "Trigger") 569 { 570 COnTriggerFunctions[component] ~= SRTrigger(attrib, 571 cast(FETrigger) &__traits(getMember, component, member)); 572 } 573 } 574 } 575 } 576 577 package(tida) @property FEStep[][size_t][Instance] threadSteps() 578 { 579 return IStepThreadFunctions; 580 } 581 582 package(tida) @property SRCollider[][Instance] colliders() 583 { 584 return IColliderStructs; 585 } 586 587 package(tida) @property FECollision[][Instance] collisionFunctions() 588 { 589 return ICollisionFunctions; 590 } 591 592 package(tida) @property FELeave[][Component] leaveComponents() 593 { 594 return CLeaveFunctions; 595 } 596 597 package(tida) void removeHandle(Scene scene, Instance instance) @trusted 598 { 599 IInitFunctions.remove(instance); 600 IStepFunctions.remove(instance); 601 IEntryFunctions.remove(instance); 602 IRestartFunctions.remove(instance); 603 ILeaveFunctions.remove(instance); 604 IGameStartFunctions.remove(instance); 605 IGameExitFunctions.remove(instance); 606 IGameRestartFunctions.remove(instance); 607 IEventHandleFunctions.remove(instance); 608 IDrawFunctions.remove(instance); 609 IOnErrorFunctions.remove(instance); 610 IColliderStructs.remove(instance); 611 ICollisionFunctions.remove(instance); 612 IOnTriggerFunctions.remove(instance); 613 IOnDestroyFunctions.remove(instance); 614 IStepThreadFunctions.remove(instance); 615 } 616 617 package(tida) void instanceExplore(T)(Scene scene, T instance) @trusted 618 { 619 import std.algorithm : canFind, remove; 620 static assert(isInstance!T, "`" ~ T.stringof ~ "` is not a instance!"); 621 if (instance in IInitFunctions) return; 622 623 IInitFunctions[instance] = []; 624 IStepFunctions[instance] = []; 625 IEntryFunctions[instance] = []; 626 IRestartFunctions[instance] = []; 627 ILeaveFunctions[instance] = []; 628 IGameStartFunctions[instance] = []; 629 IGameExitFunctions[instance] = []; 630 IGameRestartFunctions[instance] = []; 631 IEventHandleFunctions[instance] = []; 632 IDrawFunctions[instance] = []; 633 IOnErrorFunctions[instance] = []; 634 IColliderStructs[instance] = []; 635 IOnTriggerFunctions[instance] = []; 636 IOnDestroyFunctions[instance] = []; 637 ICollisionFunctions[instance] = []; 638 639 static foreach (member; __traits(allMembers, T)) 640 { 641 static foreach (attrib; __traits(getAttributes, __traits(getMember, instance, member))) 642 { 643 static if (is(attrib : FunEvent!Init)) 644 { 645 IInitFunctions[instance] ~= &__traits(getMember, instance, member); 646 }else 647 static if (is(attrib : FunEvent!Step)) 648 { 649 IStepFunctions[instance] ~= &__traits(getMember, instance, member); 650 }else 651 static if (is(attrib : FunEvent!Entry)) 652 { 653 IEntryFunctions[instance] ~= &__traits(getMember, instance, member); 654 }else 655 static if (is(attrib : FunEvent!Restart)) 656 { 657 IRestartFunctions[instance] ~= &__traits(getMember, instance, member); 658 }else 659 static if (is(attrib : FunEvent!Leave)) 660 { 661 ILeaveFunctions[instance] ~= &__traits(getMember, instance, member); 662 }else 663 static if (is(attrib : FunEvent!GameStart)) 664 { 665 IGameStartFunctions[instance] ~= &__traits(getMember, instance, member); 666 }else 667 static if (is(attrib : FunEvent!GameExit)) 668 { 669 IGameExitFunctions[instance] ~= &__traits(getMember, instance, member); 670 }else 671 static if (is(attrib : FunEvent!GameRestart)) 672 { 673 IGameRestartFunctions[instance] ~= &__traits(getMember, instance, member); 674 }else 675 static if (is(attrib : FunEvent!Input)) 676 { 677 IEventHandleFunctions[instance] ~= cast(FEEventHandle) &__traits(getMember, instance, member); 678 }else 679 static if (is(attrib : FunEvent!Draw)) 680 { 681 IDrawFunctions[instance] ~= cast(FEDraw) &__traits(getMember, instance, member); 682 }else 683 static if (is(attrib : FunEvent!GameError)) 684 { 685 IOnErrorFunctions[instance] ~= &__traits(getMember, instance, member); 686 }else 687 static if (is(attrib : FunEvent!Destroy)) 688 { 689 IOnDestroyFunctions[instance] ~= &__traits(getMember, instance, member); 690 }else 691 static if (is(attrib : FunEvent!AnyCollision)) 692 { 693 ICollisionFunctions[instance] ~= &__traits(getMember, instance, member); 694 }else 695 static if (attrib.stringof[0 .. 8] == "InThread") 696 { 697 IStepThreadFunctions[instance][attrib.id] ~= &__traits(getMember, instance, member); 698 }else 699 static if (attrib.stringof[0 .. 7] == "Trigger") 700 { 701 IOnTriggerFunctions[instance] ~= SRTrigger(attrib, 702 cast(FETrigger) &__traits(getMember, instance, member)); 703 }else 704 static if (attrib.stringof[0 .. 9] == "Collision") 705 { 706 IColliderStructs[instance] ~= SRCollider(attrib, 707 cast(FECollision) &__traits(getMember, instance, member)); 708 } 709 } 710 } 711 } 712 713 /++ 714 Creates and adds a scene to the list. 715 716 Params: 717 T = Scene name. 718 719 Example: 720 --- 721 sceneManager.add!MyScene; 722 --- 723 +/ 724 void add(T)() @trusted 725 { 726 auto scene = new T(); 727 add!T(scene); 728 } 729 730 void remove(T)(T scene) @trusted 731 { 732 scenes.remove(scene.name); 733 destroy(scene); 734 } 735 736 void remove(T)() @trusted 737 { 738 static assert(isScene!T, "`" ~ T.stringof ~ "` is not a scene!"); 739 740 foreach(scene; scenes) 741 { 742 if((cast(T) scene) !is null) { 743 remove(scene); 744 return; 745 } 746 } 747 } 748 749 void remove(string name) @trusted 750 { 751 foreach(scene; scenes) { 752 if(scene.name == name) { 753 remove(scene); 754 return; 755 } 756 } 757 } 758 759 private void exploreScene(T)(T scene) @trusted 760 { 761 InitFunctions[scene] = []; 762 StepFunctions[scene] = []; 763 EntryFunctions[scene] = []; 764 RestartFunctions[scene] = []; 765 LeaveFunctions[scene] = []; 766 GameStartFunctions[scene] = []; 767 GameExitFunctions[scene] = []; 768 GameRestartFunctions[scene] = []; 769 EventHandleFunctions[scene] = []; 770 DrawFunctions[scene] = []; 771 OnErrorFunctions[scene] = []; 772 OnTriggerFunctions[scene] = []; 773 OnDestroyFunctions[scene] = []; 774 775 static foreach(member; __traits(allMembers, T)) 776 { 777 static foreach(attrib; __traits(getAttributes, __traits(getMember, scene, member))) 778 { 779 static if (is(attrib : FunEvent!Init)) 780 { 781 InitFunctions[scene] ~= &__traits(getMember, scene, member); 782 }else 783 static if (is(attrib : FunEvent!Step)) 784 { 785 StepFunctions[scene] ~= &__traits(getMember, scene, member); 786 }else 787 static if (is(attrib : FunEvent!Entry)) 788 { 789 EntryFunctions[scene] ~= &__traits(getMember, scene, member); 790 }else 791 static if (is(attrib : FunEvent!Restart)) 792 { 793 RestartFunctions[scene] ~= &__traits(getMember, scene, member); 794 }else 795 static if (is(attrib : FunEvent!Leave)) 796 { 797 LeaveFunctions[scene] ~= &__traits(getMember, scene, member); 798 }else 799 static if (is(attrib : FunEvent!GameStart)) 800 { 801 GameStartFunctions[scene] ~= &__traits(getMember, scene, member); 802 }else 803 static if (is(attrib : FunEvent!GameExit)) 804 { 805 GameExitFunctions[scene] ~= &__traits(getMember, scene, member); 806 }else 807 static if (is(attrib : FunEvent!GameRestart)) 808 { 809 GameRestartFunctions[scene] ~= &__traits(getMember, scene, member); 810 }else 811 static if (is(attrib : FunEvent!Input)) 812 { 813 EventHandleFunctions[scene] ~= cast(FEEventHandle) &__traits(getMember, scene, member); 814 }else 815 static if (is(attrib : FunEvent!Draw)) 816 { 817 DrawFunctions[scene] ~= cast(FEDraw) &__traits(getMember, scene, member); 818 }else 819 static if (is(attrib : FunEvent!GameError)) 820 { 821 OnErrorFunctions[scene] ~= &__traits(getMember, scene, member); 822 }else 823 static if (is(attrib : FunEvent!Destroy)) 824 { 825 OnDestroyFunctions[scene] ~= &__traits(getMember, scene, member); 826 }else 827 static if (attrib.stringof[0 .. 8] == "InThread") 828 { 829 StepThreadFunctions[scene][attrib.id] ~= &__traits(getMember, scene, member); 830 }else 831 static if (attrib.stringof[0 .. 7] == "Trigger") 832 { 833 OnTriggerFunctions[scene] ~= SRTrigger(attrib, 834 cast(FETrigger) &__traits(getMember, scene, member)); 835 } 836 } 837 } 838 } 839 840 public 841 { 842 /++ 843 Array of requests. At each stroke of the cycle, it is checked, 844 processed and cleaned. If an error occurs during the request, 845 they are added to `apiError`. 846 +/ 847 APIResponse[] api; 848 849 /++ 850 An array of request errors associated with the request type. 851 +/ 852 uint[uint] apiError; 853 } 854 855 /++ 856 Exits the game with a successful error code. 857 +/ 858 void close(int code = 0) @safe 859 { 860 api ~= APIResponse(APIType.GameClose, code); 861 } 862 863 /++ 864 Creates the specified count of anonymous threads. 865 866 Params: 867 count = Count anonymous threads. 868 +/ 869 void initThread(uint count = 1) @safe 870 { 871 api ~= APIResponse(APIType.ThreadCreate, count); 872 } 873 874 /++ 875 Pauses said thread. 876 877 Params: 878 value = Thread identificator. 879 +/ 880 void pauseThread(uint value) @safe 881 { 882 api ~= APIResponse(APIType.ThreadPause, value); 883 } 884 885 /++ 886 Resumes said thread. 887 888 Params: 889 value = Thread identificator. 890 +/ 891 void resumeThread(uint value) @safe 892 { 893 api ~= APIResponse(APIType.ThreadResume, value); 894 } 895 896 void stopThread(uint value) @safe 897 { 898 api ~= APIResponse(APIType.ThreadClose, value); 899 } 900 901 /++ 902 Goes to the first scene added. 903 +/ 904 void inbegin() @safe 905 { 906 gotoin(_ofbegin); 907 } 908 909 /++ 910 Goes to the scene by its string name. 911 912 Params: 913 name = Scene name. 914 +/ 915 void gotoin(string name) 916 { 917 foreach (inscene; scenes) 918 { 919 if(inscene.name == name) 920 { 921 gotoin(inscene); 922 break; 923 } 924 } 925 } 926 927 /++ 928 Goes to the scene by its class. 929 930 Params: 931 Name = Scene. 932 +/ 933 void gotoin(Name)() 934 { 935 foreach (s; scenes) 936 { 937 if ((cast(Name) s) !is null) 938 { 939 gotoin(s); 940 return; 941 } 942 } 943 944 throw new Exception("Not find this scene!"); 945 } 946 947 /++ 948 Moves to the scene at the pointer. 949 950 It is such a function that generates initialization events, entry, 951 transferring the context to the scene and causing the corresponding 952 events to lose the context. 953 954 Params: 955 scene = Scene heir. 956 +/ 957 void gotoin(Scene scene) @trusted 958 in(hasScene(scene)) 959 do 960 { 961 _previous = current; 962 963 if (current !is null) 964 { 965 if (current in LeaveFunctions) 966 { 967 foreach (fun; LeaveFunctions[current]) 968 { 969 fun(); 970 } 971 } 972 973 foreach (instance; current.list()) 974 { 975 if (instance in ILeaveFunctions) 976 { 977 foreach (fun; ILeaveFunctions[instance]) 978 { 979 fun(); 980 } 981 } 982 } 983 } 984 985 if (current !is null) 986 { 987 Instance[] persistents; 988 989 foreach (instance; _previous.list()) 990 { 991 if (instance.persistent) 992 persistents ~= instance; 993 } 994 995 foreach (e; persistents) 996 { 997 auto threadID = e.threadid; 998 current.instanceDestroy!InScene(e, false); 999 1000 if (scene.isThreadExists(threadID)) 1001 scene.add(e,threadID); 1002 else 1003 scene.add(e); 1004 } 1005 } 1006 1007 _initable = scene; 1008 1009 if (!scene.isInit) 1010 { 1011 if (scene in InitFunctions) 1012 { 1013 foreach (fun; InitFunctions[scene]) 1014 { 1015 fun(); 1016 } 1017 } 1018 1019 foreach (instance; scene.list()) 1020 { 1021 if (instance in IInitFunctions) 1022 { 1023 foreach (fun; IInitFunctions[instance]) 1024 { 1025 fun(); 1026 } 1027 } 1028 } 1029 1030 scene.isInit = true; 1031 }else 1032 { 1033 if (scene in RestartFunctions) 1034 { 1035 foreach (fun; RestartFunctions[scene]) 1036 { 1037 fun(); 1038 } 1039 } 1040 1041 foreach (instance; scene.list()) 1042 { 1043 if (instance in IRestartFunctions) 1044 { 1045 foreach (fun; IRestartFunctions[instance]) 1046 { 1047 fun(); 1048 } 1049 } 1050 } 1051 } 1052 1053 if (scene in EntryFunctions) 1054 { 1055 foreach(fun; EntryFunctions[scene]) 1056 { 1057 fun(); 1058 } 1059 } 1060 1061 foreach (instance; scene.list()) 1062 { 1063 if (instance in IEntryFunctions) 1064 { 1065 foreach (fun; IEntryFunctions[instance]) 1066 { 1067 fun(); 1068 } 1069 } 1070 } 1071 1072 _initable = null; 1073 1074 _current = scene; 1075 } 1076 1077 /++ 1078 Calling the game launch event. 1079 1080 Should be called before all events, before the beginning of the 1081 cycle of life. 1082 +/ 1083 void callGameStart() @trusted 1084 { 1085 foreach (scene; scenes) 1086 { 1087 if (scene in GameStartFunctions) 1088 { 1089 foreach (fun; GameStartFunctions[scene]) 1090 { 1091 fun(); 1092 } 1093 } 1094 1095 foreach (instance; scene.list()) 1096 { 1097 if (instance.active && !instance.onlyDraw) 1098 { 1099 if (instance in IGameStartFunctions) 1100 { 1101 foreach (fun; IGameStartFunctions[instance]) 1102 { 1103 fun(); 1104 } 1105 } 1106 } 1107 } 1108 } 1109 } 1110 1111 /++ 1112 Game completion call events (successful). 1113 The unsuccessful event should raise the `onError` event. 1114 +/ 1115 void callGameExit() @trusted 1116 { 1117 foreach (scene; scenes) 1118 { 1119 if (scene in GameExitFunctions) 1120 { 1121 foreach (fun; GameExitFunctions[scene]) 1122 { 1123 fun(); 1124 } 1125 } 1126 1127 foreach (instance; scene.list()) 1128 { 1129 if (instance.active && !instance.onlyDraw) 1130 { 1131 if (instance in IGameExitFunctions) 1132 { 1133 foreach (fun; IGameExitFunctions[instance]) 1134 { 1135 fun(); 1136 } 1137 } 1138 } 1139 } 1140 } 1141 } 1142 1143 /++ 1144 Triggering an emergency event. 1145 1146 Does not terminate the game, should be called on exceptions. After that, 1147 the programmer himself decides what to do next (if he implements his own 1148 life cycle). Called usually on `scope (failure)`, however, it will not 1149 throw a specific exception. 1150 +/ 1151 void callOnError() @trusted 1152 { 1153 if (current !is null) 1154 { 1155 if (current in OnErrorFunctions) 1156 { 1157 foreach (fun; OnErrorFunctions[current]) 1158 { 1159 fun(); 1160 } 1161 } 1162 1163 foreach (instance; current.list()) 1164 { 1165 if (instance.active && !instance.onlyDraw) 1166 { 1167 if (instance in IOnErrorFunctions) 1168 { 1169 foreach (fun; IOnErrorFunctions[instance]) 1170 { 1171 fun(); 1172 } 1173 } 1174 } 1175 } 1176 } 1177 } 1178 1179 /++ 1180 Calling a game step. Should always be called during a loop step in an 1181 exception when the thread is suspended. 1182 1183 Params: 1184 thread = Thread identificator. 1185 rend = Renderer instance. 1186 +/ 1187 void callStep(size_t thread, IRenderer rend) @trusted 1188 { 1189 if (current !is null) 1190 { 1191 current.worldCollision(); 1192 1193 if (thread == 0) 1194 if (current in StepFunctions) 1195 { 1196 foreach (fun; StepFunctions[current]) 1197 { 1198 fun(); 1199 } 1200 } 1201 1202 if (current in StepThreadFunctions) 1203 if (thread in StepThreadFunctions[current]) 1204 { 1205 foreach (fun; StepThreadFunctions[current][thread]) 1206 fun(); 1207 } 1208 1209 foreach (instance; current.getThreadList(thread)) 1210 { 1211 if (instance.isDestroy) 1212 { 1213 current.instanceDestroy!InMemory(instance); 1214 current.sort(); 1215 continue; 1216 } 1217 1218 if (!instance.active || instance.onlyDraw) continue; 1219 1220 if (instance in IStepFunctions) 1221 { 1222 foreach (fun; IStepFunctions[instance]) 1223 { 1224 fun(); 1225 } 1226 } 1227 1228 foreach (component; instance.getComponents()) 1229 { 1230 if (component in CStepFunctions) 1231 { 1232 foreach (fun; CStepFunctions[component]) 1233 { 1234 fun(); 1235 } 1236 } 1237 } 1238 } 1239 1240 foreach(instance; current.list()) 1241 { 1242 if (!instance.active || instance.onlyDraw) continue; 1243 1244 if (instance in IStepThreadFunctions) 1245 if (thread in IStepThreadFunctions[instance]) 1246 { 1247 foreach (fun; IStepThreadFunctions[instance][thread]) 1248 fun(); 1249 } 1250 1251 foreach (component; instance.getComponents()) 1252 { 1253 if (component in CStepThreadFunctions) 1254 if (thread in CStepThreadFunctions[component]) 1255 { 1256 foreach(fun; CStepThreadFunctions[component][thread]) 1257 { 1258 fun(); 1259 } 1260 } 1261 } 1262 } 1263 } 1264 } 1265 1266 /++ 1267 System event event for scenes and instances of the current context. 1268 1269 Params: 1270 event = System event handler instance. 1271 +/ 1272 void callEvent(EventHandler event) @trusted 1273 { 1274 if (current !is null) 1275 { 1276 if (current in EventHandleFunctions) 1277 { 1278 foreach (fun; EventHandleFunctions[current]) 1279 { 1280 fun(event); 1281 } 1282 } 1283 1284 foreach (instance; current.list()) 1285 { 1286 if (instance.active && !instance.onlyDraw) 1287 { 1288 if (instance in IEventHandleFunctions) 1289 { 1290 foreach(fun; IEventHandleFunctions[instance]) 1291 { 1292 fun(event); 1293 } 1294 } 1295 1296 foreach (component; instance.getComponents()) 1297 { 1298 if (component in CEventHandleFunctions) 1299 { 1300 foreach(fun; CEventHandleFunctions[component]) 1301 { 1302 fun(event); 1303 } 1304 } 1305 } 1306 } 1307 } 1308 } 1309 } 1310 1311 /++ 1312 Calling an event to render scenes and instances of the current context. 1313 1314 Params: 1315 render = Render instance. 1316 +/ 1317 void callDraw(IRenderer render) @trusted 1318 { 1319 import tida.vector; 1320 1321 if (current !is null) 1322 { 1323 if (current in DrawFunctions) 1324 { 1325 foreach (fun; DrawFunctions[current]) 1326 { 1327 fun(render); 1328 } 1329 } 1330 1331 foreach (instance; current.getAssortedInstances()) 1332 { 1333 if (instance.active && instance.visible) 1334 { 1335 render.draw(instance.spriteDraw(), instance.position); 1336 1337 if (instance in IDrawFunctions) 1338 { 1339 foreach (fun; IDrawFunctions[instance]) 1340 { 1341 fun(render); 1342 } 1343 } 1344 1345 foreach (component; instance.getComponents()) 1346 { 1347 if (component in CDrawFunctions) 1348 { 1349 foreach (fun; CDrawFunctions[component]) 1350 { 1351 fun(render); 1352 } 1353 } 1354 } 1355 } 1356 } 1357 } 1358 } 1359 1360 /// Free memory. 1361 void free() @safe 1362 { 1363 _scenes = null; 1364 } 1365 1366 ~this() @safe 1367 { 1368 free(); 1369 } 1370 }