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 static assert(isScene!Name, "`" ~ Name.stringof ~ "` is not a scene!"); 387 388 foreach (scene; scenes) 389 { 390 if ((cast(Name) scene) !is null) 391 return true; 392 } 393 394 return false; 395 } 396 397 /++ 398 Checks if there is a scene with the specified name. 399 400 Params: 401 name = Scene name. 402 +/ 403 bool hasScene(string name) 404 { 405 foreach (scene; scenes) 406 { 407 if (scene.name == name) return true; 408 } 409 410 return false; 411 } 412 413 /++ 414 Adds a scene to the list. 415 416 Params: 417 scene = Scene. 418 +/ 419 void add(T)(T scene) 420 { 421 static assert(isScene!T, "`" ~ T.stringof ~ "` is not a scene!"); 422 exploreScene!T(scene); 423 424 if (_ofbegin is null) 425 _ofbegin = scene; 426 427 _scenes[scene.name] = scene; 428 } 429 430 package(tida) 431 { 432 import std.container, std.range, std.traits; 433 import tida.component : Component; 434 435 alias FEInit = void delegate() @safe; 436 alias FEStep = void delegate() @safe; 437 alias FERestart = void delegate() @safe; 438 alias FEEntry = void delegate() @safe; 439 alias FELeave = void delegate() @safe; 440 alias FEGameStart = void delegate() @safe; 441 alias FEGameExit = void delegate() @safe; 442 alias FEGameRestart = void delegate() @safe; 443 alias FEEventHandle = void delegate(EventHandler) @safe; 444 alias FEDraw = void delegate(IRenderer) @safe; 445 alias FEOnError = void delegate() @safe; 446 alias FECollision = void delegate(Instance) @safe; 447 alias FETrigger = void delegate() @safe; 448 alias FEDestroy = void delegate(Instance) @safe; 449 alias FEATrigger = void delegate(string) @safe; 450 451 alias FECInit = void delegate(Instance) @safe; 452 453 struct SRCollider 454 { 455 Collision ev; 456 FECollision fun; 457 } 458 459 struct SRTrigger 460 { 461 Trigger ev; 462 FETrigger fun; 463 } 464 465 FEInit[][Scene] InitFunctions; 466 FEStep[][Scene] StepFunctions; 467 FEStep[][size_t][Scene] StepThreadFunctions; 468 FERestart[][Scene] RestartFunctions; 469 FEEntry[][Scene] EntryFunctions; 470 FELeave[][Scene] LeaveFunctions; 471 FEGameStart[][Scene] GameStartFunctions; 472 FEGameExit[][Scene] GameExitFunctions; 473 FEGameRestart[][Scene] GameRestartFunctions; 474 FEEventHandle[][Scene] EventHandleFunctions; 475 FEDraw[][Scene] DrawFunctions; 476 FEOnError[][Scene] OnErrorFunctions; 477 SRTrigger[][Scene] OnTriggerFunctions; 478 FEDestroy[][Scene] OnDestroyFunctions; 479 FEATrigger[][Scene] OnAnyTriggerFunctions; 480 FECollision[][Scene] OnAnyCollisionFunctions; 481 482 FEInit[][Instance] IInitFunctions; 483 FEStep[][Instance] IStepFunctions; 484 FEStep[][size_t][Instance] IStepThreadFunctions; 485 FERestart[][Instance] IRestartFunctions; 486 FEEntry[][Instance] IEntryFunctions; 487 FELeave[][Instance] ILeaveFunctions; 488 FEGameStart[][Instance] IGameStartFunctions; 489 FEGameExit[][Instance] IGameExitFunctions; 490 FEGameRestart[][Instance] IGameRestartFunctions; 491 FEEventHandle[][Instance] IEventHandleFunctions; 492 FEDraw[][Instance] IDrawFunctions; 493 FEOnError[][Instance] IOnErrorFunctions; 494 SRCollider[][Instance] IColliderStructs; 495 FECollision[][Instance] ICollisionFunctions; 496 SRTrigger[][Instance] IOnTriggerFunctions; 497 FEDestroy[][Instance] IOnDestroyFunctions; 498 FEATrigger[][Instance] IOnAnyTriggerFunctions; 499 500 FEStep[][Component] CStepFunctions; 501 FEStep[][size_t][Component] CStepThreadFunctions; 502 FELeave[][Component] CLeaveFunctions; 503 FEEventHandle[][Component] CEventHandleFunctions; 504 FEDraw[][Component] CDrawFunctions; 505 FEOnError[][Component] COnErrorFunctions; 506 SRTrigger[][Component] COnTriggerFunctions; 507 } 508 509 /++ 510 Raise the event of destruction of the instance. (@FunEvent!Destroy) 511 512 Params: 513 instance = Instance. 514 +/ 515 void destroyEventCall(T)(T instance) @trusted 516 { 517 static assert(isInstance!T, "`" ~ T.stringof ~ "` is not a instance!"); 518 foreach(func; IOnDestroyFunctions[instance]) func(instance); 519 } 520 521 /++ 522 Reise the event of destruction in current scene. (@FunEvent!Destroy) 523 524 Params: 525 scene = Current scene. 526 instance = Instance. 527 +/ 528 void destroyEventSceneCall(T, R)(T scene, R instance) @trusted 529 { 530 static assert(isScene!T, "`" ~ T.stringof ~ "` is not a scene!"); 531 static assert(isInstance!R, "`" ~ R.stringof ~ "` is not a instance!"); 532 533 foreach(func; OnDestroyFunctions[scene]) func(instance); 534 } 535 536 package(tida) void componentExplore(T)(Instance instance, T component) @trusted 537 { 538 import tida.component : isComponent; 539 540 static assert(isComponent!T, "`" ~ T.stringof ~ "` is not a component!"); 541 542 CStepFunctions[component] = []; 543 CLeaveFunctions[component] = []; 544 CEventHandleFunctions[component] = []; 545 CDrawFunctions[component] = []; 546 COnErrorFunctions[component] = []; 547 COnTriggerFunctions[component] = []; 548 549 static foreach (member; __traits(allMembers, T)) 550 { 551 static foreach (attrib; __traits(getAttributes, __traits(getMember, component, member))) 552 { 553 static if (is(attrib : FunEvent!Init)) 554 { 555 auto fun = cast(FECInit) &__traits(getMember, component, member); 556 fun(instance); 557 } else 558 static if (is(attrib : FunEvent!Step)) 559 { 560 CStepFunctions[component] ~= &__traits(getMember, component, member); 561 } else 562 static if (is(attrib : FunEvent!Leave)) 563 { 564 CLeaveFunctions[component] ~= &__traits(getMember, component, member); 565 } else 566 static if (is(attrib : FunEvent!Input)) 567 { 568 CEventHandleFunctions[component] ~= cast(FEEventHandle) &__traits(getMember, component, member); 569 } else 570 static if (is(attrib : FunEvent!Draw)) 571 { 572 CDrawFunctions[component] ~= cast(FEDraw) &__traits(getMember, component, member); 573 } else 574 static if (is(attrib : FunEvent!GameError)) 575 { 576 COnErrorFunctions[component] ~= &__traits(getMember, component, member); 577 } else 578 static if (attrib.stringof[0 .. 8] == "InThread") 579 { 580 CStepThreadFunctions[instance][attrib.id] ~= &__traits(getMember, instance, member); 581 }else 582 static if (attrig.stringof[0 .. 7] == "Trigger") 583 { 584 COnTriggerFunctions[component] ~= SRTrigger(attrib, 585 cast(FETrigger) &__traits(getMember, component, member)); 586 } 587 } 588 } 589 } 590 591 package(tida) @property FEStep[][size_t][Instance] threadSteps() 592 { 593 return IStepThreadFunctions; 594 } 595 596 package(tida) @property SRCollider[][Instance] colliders() 597 { 598 return IColliderStructs; 599 } 600 601 package(tida) @property FECollision[][Instance] collisionFunctions() 602 { 603 return ICollisionFunctions; 604 } 605 606 package(tida) @property FELeave[][Component] leaveComponents() 607 { 608 return CLeaveFunctions; 609 } 610 611 package(tida) void removeHandle(Scene scene, Instance instance) @trusted 612 { 613 IInitFunctions.remove(instance); 614 IStepFunctions.remove(instance); 615 IEntryFunctions.remove(instance); 616 IRestartFunctions.remove(instance); 617 ILeaveFunctions.remove(instance); 618 IGameStartFunctions.remove(instance); 619 IGameExitFunctions.remove(instance); 620 IGameRestartFunctions.remove(instance); 621 IEventHandleFunctions.remove(instance); 622 IDrawFunctions.remove(instance); 623 IOnErrorFunctions.remove(instance); 624 IColliderStructs.remove(instance); 625 ICollisionFunctions.remove(instance); 626 IOnTriggerFunctions.remove(instance); 627 IOnDestroyFunctions.remove(instance); 628 IStepThreadFunctions.remove(instance); 629 IOnAnyTriggerFunctions.remove(instance); 630 } 631 632 template hasMatch(alias attrib, alias AttribType) 633 { 634 enum hasMatch = is(typeof(attrib) == AttribType) || is(attrib == AttribType) || 635 is(typeof(attrib) : AttribType) || is(attrib : AttribType); 636 } 637 638 template hasAttrib(T, AttribType, string member) 639 { 640 alias same = __traits(getMember, T, member); 641 642 static if (isFunction!(same)) 643 { 644 alias attributes = __traits(getAttributes, same); 645 646 static if (attributes.length != 0) 647 { 648 static foreach (attrib; attributes) 649 { 650 static if (hasMatch!(attrib, AttribType)) 651 { 652 static assert(isSafe!(same), 653 "The function `" ~ member ~"` does not guarantee safe execution."); 654 655 enum hasAttrib = true; 656 }else 657 { 658 enum hasAttrib = false; 659 } 660 } 661 } else 662 { 663 enum hasAttrib = false; 664 } 665 } else 666 { 667 enum hasAttrib = false; 668 } 669 } 670 671 template attributeIn(T, AttribType, string member) 672 { 673 alias same = __traits(getMember, T, member); 674 alias attributes = __traits(getAttributes, same); 675 676 static foreach (attrib; attributes) 677 { 678 static if (hasMatch!(attrib, AttribType)) 679 { 680 enum attributeIn = attrib; 681 } 682 } 683 } 684 685 package(tida) void instanceExplore(T)(Scene scene, T instance) @trusted 686 { 687 import std.algorithm : canFind, remove; 688 static assert(isInstance!T, "`" ~ T.stringof ~ "` is not a instance!"); 689 if (instance in IInitFunctions) return; 690 691 IInitFunctions[instance] = []; 692 IStepFunctions[instance] = []; 693 IEntryFunctions[instance] = []; 694 IRestartFunctions[instance] = []; 695 ILeaveFunctions[instance] = []; 696 IGameStartFunctions[instance] = []; 697 IGameExitFunctions[instance] = []; 698 IGameRestartFunctions[instance] = []; 699 IEventHandleFunctions[instance] = []; 700 IDrawFunctions[instance] = []; 701 IOnErrorFunctions[instance] = []; 702 IColliderStructs[instance] = []; 703 IOnTriggerFunctions[instance] = []; 704 IOnDestroyFunctions[instance] = []; 705 ICollisionFunctions[instance] = []; 706 IOnAnyTriggerFunctions[instance] = []; 707 708 static if (T.stringof != Instance.stringof) 709 static foreach (member; __traits(allMembers, T)) 710 { 711 static if (hasAttrib!(T, FunEvent!Init, member)) 712 { 713 IInitFunctions[instance] ~= &__traits(getMember, instance, member); 714 } else 715 static if (hasAttrib!(T, FunEvent!Step, member)) 716 { 717 IStepFunctions[instance] ~= &__traits(getMember, instance, member); 718 } else 719 static if (hasAttrib!(T, FunEvent!Entry, member)) 720 { 721 IEntryFunctions[instance] ~= &__traits(getMember, instance, member); 722 } else 723 static if (hasAttrib!(T, FunEvent!Restart, member)) 724 { 725 IRestartFunctions[instance] ~= &__traits(getMember, instance, member); 726 } else 727 static if (hasAttrib!(T, FunEvent!Leave, member)) 728 { 729 ILeaveFunctions[instance] ~= &__traits(getMember, instance, member); 730 } else 731 static if (hasAttrib!(T, FunEvent!GameStart, member)) 732 { 733 IGameStartFunctions[instance] ~= &__traits(getMember, instance, member); 734 } else 735 static if (hasAttrib!(T, FunEvent!GameExit, member)) 736 { 737 IGameExitFunctions[instance] ~= &__traits(getMember, instance, member); 738 } else 739 static if (hasAttrib!(T, FunEvent!GameRestart, member)) 740 { 741 IGameRestartFunctions[instance] ~= &__traits(getMember, instance, member); 742 } else 743 static if (hasAttrib!(T, FunEvent!Input, member)) 744 { 745 IEventHandleFunctions[instance] ~= &__traits(getMember, instance, member); 746 } else 747 static if (hasAttrib!(T, FunEvent!Draw, member)) 748 { 749 IDrawFunctions[instance] ~= &__traits(getMember, instance, member); 750 } else 751 static if (hasAttrib!(T, FunEvent!GameError, member)) 752 { 753 IOnErrorFunctions[instance] ~= &__traits(getMember, instance, member); 754 } else 755 static if (hasAttrib!(T, Collision, member)) 756 { 757 IColliderStructs[instance] ~= SRCollider(attributeIn!(T, Collision, member), 758 &__traits(getMember, instance, member)); 759 } else 760 static if (hasAttrib!(T, Trigger, member)) 761 { 762 IOnTriggerFunctions[instance] ~= SRTrigger( attributeIn!(T, Trigger, member), 763 &__traits(getMember, instance, member)); 764 } else 765 static if (hasAttrib!(T, FunEvent!Destroy, member)) 766 { 767 IOnDestroyFunctions[instance] ~= &__traits(getMember, instance, member); 768 } else 769 static if (hasAttrib!(T, FunEvent!AnyCollision, member)) 770 { 771 ICollisionFunctions[instance] ~= &__traits(getMember, instance, member); 772 } else 773 static if (hasAttrib!(T, FunEvent!AnyTrigger, member)) 774 { 775 IOnAnyTriggerFunctions[instance] ~= &__traits(getMember, instance, member); 776 } 777 } 778 } 779 780 /++ 781 Creates and adds a scene to the list. 782 783 Params: 784 T = Scene name. 785 786 Example: 787 --- 788 sceneManager.add!MyScene; 789 --- 790 +/ 791 void add(T)() @trusted 792 { 793 auto scene = new T(); 794 add!T(scene); 795 } 796 797 void remove(T)(T scene) @trusted 798 { 799 scenes.remove(scene.name); 800 destroy(scene); 801 } 802 803 void remove(T)() @trusted 804 { 805 static assert(isScene!T, "`" ~ T.stringof ~ "` is not a scene!"); 806 807 foreach(scene; scenes) 808 { 809 if((cast(T) scene) !is null) { 810 remove(scene); 811 return; 812 } 813 } 814 } 815 816 void remove(string name) @trusted 817 { 818 foreach(scene; scenes) { 819 if(scene.name == name) { 820 remove(scene); 821 return; 822 } 823 } 824 } 825 826 private void exploreScene(T)(T scene) @trusted 827 { 828 InitFunctions[scene] = []; 829 StepFunctions[scene] = []; 830 EntryFunctions[scene] = []; 831 RestartFunctions[scene] = []; 832 LeaveFunctions[scene] = []; 833 GameStartFunctions[scene] = []; 834 GameExitFunctions[scene] = []; 835 GameRestartFunctions[scene] = []; 836 EventHandleFunctions[scene] = []; 837 DrawFunctions[scene] = []; 838 OnErrorFunctions[scene] = []; 839 OnTriggerFunctions[scene] = []; 840 OnDestroyFunctions[scene] = []; 841 842 static if (T.stringof != Instance.stringof) 843 static foreach (member; __traits(allMembers, T)) 844 { 845 static if (hasAttrib!(T, FunEvent!Init, member)) 846 { 847 InitFunctions[scene] ~= &__traits(getMember, scene, member); 848 } else 849 static if (hasAttrib!(T, FunEvent!Step, member)) 850 { 851 StepFunctions[scene] ~= &__traits(getMember, scene, member); 852 } else 853 static if (hasAttrib!(T, FunEvent!Entry, member)) 854 { 855 EntryFunctions[scene] ~= &__traits(getMember, scene, member); 856 } else 857 static if (hasAttrib!(T, FunEvent!Restart, member)) 858 { 859 RestartFunctions[scene] ~= &__traits(getMember, scene, member); 860 } else 861 static if (hasAttrib!(T, FunEvent!Leave, member)) 862 { 863 LeaveFunctions[scene] ~= &__traits(getMember, scene, member); 864 } else 865 static if (hasAttrib!(T, FunEvent!GameStart, member)) 866 { 867 GameStartFunctions[scene] ~= &__traits(getMember, scene, member); 868 } else 869 static if (hasAttrib!(T, FunEvent!GameExit, member)) 870 { 871 GameExitFunctions[scene] ~= &__traits(getMember, scene, member); 872 } else 873 static if (hasAttrib!(T, FunEvent!GameRestart, member)) 874 { 875 GameRestartFunctions[scene] ~= &__traits(getMember, scene, member); 876 } else 877 static if (hasAttrib!(T, FunEvent!Input, member)) 878 { 879 EventHandleFunctions[scene] ~= &__traits(getMember, scene, member); 880 } else 881 static if (hasAttrib!(T, FunEvent!Draw, member)) 882 { 883 DrawFunctions[scene] ~= &__traits(getMember, scene, member); 884 } else 885 static if (hasAttrib!(T, FunEvent!GameError, member)) 886 { 887 OnErrorFunctions[scene] ~= &__traits(getMember, scene, member); 888 } else 889 static if (hasAttrib!(T, Trigger, member)) 890 { 891 IOnTriggerFunctions[scene] ~= SRTrigger( attributeIn!(T, Collision, member), 892 &__traits(getMember, scene, member)); 893 } else 894 static if (hasAttrib!(T, FunEvent!AnyCollision, member)) 895 { 896 ICollisionFunctions[scene] ~= &__traits(getMember, scene, member); 897 } else 898 static if (hasAttrib!(T, FunEvent!AnyTrigger, member)) 899 { 900 IOnAnyTriggerFunctions[scene] ~= &__traits(getMember, scene, member); 901 } 902 } 903 //static foreach(member; __traits(allMembers, T)) 904 //{ 905 // static foreach(attrib; __traits(getAttributes, __traits(getMember, scene, member))) 906 // { 907 // static if (is(attrib : FunEvent!Init)) 908 // { 909 // InitFunctions[scene] ~= &__traits(getMember, scene, member); 910 // }else 911 // static if (is(attrib : FunEvent!Step)) 912 // { 913 // StepFunctions[scene] ~= &__traits(getMember, scene, member); 914 // }else 915 // static if (is(attrib : FunEvent!Entry)) 916 // { 917 // EntryFunctions[scene] ~= &__traits(getMember, scene, member); 918 // }else 919 // static if (is(attrib : FunEvent!Restart)) 920 // { 921 // RestartFunctions[scene] ~= &__traits(getMember, scene, member); 922 // }else 923 // static if (is(attrib : FunEvent!Leave)) 924 // { 925 // LeaveFunctions[scene] ~= &__traits(getMember, scene, member); 926 // }else 927 // static if (is(attrib : FunEvent!GameStart)) 928 // { 929 // GameStartFunctions[scene] ~= &__traits(getMember, scene, member); 930 // }else 931 // static if (is(attrib : FunEvent!GameExit)) 932 // { 933 // GameExitFunctions[scene] ~= &__traits(getMember, scene, member); 934 // }else 935 // static if (is(attrib : FunEvent!GameRestart)) 936 // { 937 // GameRestartFunctions[scene] ~= &__traits(getMember, scene, member); 938 // }else 939 // static if (is(attrib : FunEvent!Input)) 940 // { 941 // EventHandleFunctions[scene] ~= cast(FEEventHandle) &__traits(getMember, scene, member); 942 // }else 943 // static if (is(attrib : FunEvent!Draw)) 944 // { 945 // DrawFunctions[scene] ~= cast(FEDraw) &__traits(getMember, scene, member); 946 // }else 947 // static if (is(attrib : FunEvent!GameError)) 948 // { 949 // OnErrorFunctions[scene] ~= &__traits(getMember, scene, member); 950 // }else 951 // static if (is(attrib : FunEvent!Destroy)) 952 // { 953 // OnDestroyFunctions[scene] ~= &__traits(getMember, scene, member); 954 // }else 955 // static if (attrib.stringof[0 .. 8] == "InThread") 956 // { 957 // StepThreadFunctions[scene][attrib.id] ~= &__traits(getMember, scene, member); 958 // }else 959 // static if (attrib.stringof[0 .. 7] == "Trigger") 960 // { 961 // OnTriggerFunctions[scene] ~= SRTrigger(attrib, 962 // cast(FETrigger) &__traits(getMember, scene, member)); 963 // } 964 // } 965 //} 966 } 967 968 public 969 { 970 /++ 971 Array of requests. At each stroke of the cycle, it is checked, 972 processed and cleaned. If an error occurs during the request, 973 they are added to `apiError`. 974 +/ 975 APIResponse[] api; 976 977 /++ 978 An array of request errors associated with the request type. 979 +/ 980 uint[uint] apiError; 981 } 982 983 /++ 984 Exits the game with a successful error code. 985 +/ 986 void close(int code = 0) @safe 987 { 988 api ~= APIResponse(APIType.GameClose, code); 989 } 990 991 /++ 992 Creates the specified count of anonymous threads. 993 994 Params: 995 count = Count anonymous threads. 996 +/ 997 void initThread(uint count = 1) @safe 998 { 999 api ~= APIResponse(APIType.ThreadCreate, count); 1000 } 1001 1002 /++ 1003 Pauses said thread. 1004 1005 Params: 1006 value = Thread identificator. 1007 +/ 1008 void pauseThread(uint value) @safe 1009 { 1010 api ~= APIResponse(APIType.ThreadPause, value); 1011 } 1012 1013 /++ 1014 Resumes said thread. 1015 1016 Params: 1017 value = Thread identificator. 1018 +/ 1019 void resumeThread(uint value) @safe 1020 { 1021 api ~= APIResponse(APIType.ThreadResume, value); 1022 } 1023 1024 void stopThread(uint value) @safe 1025 { 1026 api ~= APIResponse(APIType.ThreadClose, value); 1027 } 1028 1029 /++ 1030 Goes to the first scene added. 1031 +/ 1032 void inbegin() @safe 1033 { 1034 gotoin(_ofbegin); 1035 } 1036 1037 /++ 1038 Goes to the scene by its string name. 1039 1040 Params: 1041 name = Scene name. 1042 +/ 1043 void gotoin(string name) 1044 { 1045 foreach (inscene; scenes) 1046 { 1047 if(inscene.name == name) 1048 { 1049 gotoin(inscene); 1050 break; 1051 } 1052 } 1053 } 1054 1055 /++ 1056 Goes to the scene by its class. 1057 1058 Params: 1059 Name = Scene. 1060 +/ 1061 void gotoin(Name)() 1062 { 1063 foreach (s; scenes) 1064 { 1065 if ((cast(Name) s) !is null) 1066 { 1067 gotoin(s); 1068 return; 1069 } 1070 } 1071 1072 throw new Exception("Not find this scene!"); 1073 } 1074 1075 /++ 1076 Moves to the scene at the pointer. 1077 1078 It is such a function that generates initialization events, entry, 1079 transferring the context to the scene and causing the corresponding 1080 events to lose the context. 1081 1082 Params: 1083 scene = Scene heir. 1084 +/ 1085 void gotoin(Scene scene) @trusted 1086 in(hasScene(scene)) 1087 do 1088 { 1089 _previous = current; 1090 1091 if (current !is null) 1092 { 1093 if (current in LeaveFunctions) 1094 { 1095 foreach (fun; LeaveFunctions[current]) 1096 { 1097 fun(); 1098 } 1099 } 1100 1101 foreach (instance; current.list()) 1102 { 1103 if (instance in ILeaveFunctions) 1104 { 1105 foreach (fun; ILeaveFunctions[instance]) 1106 { 1107 fun(); 1108 } 1109 } 1110 } 1111 } 1112 1113 if (current !is null) 1114 { 1115 Instance[] persistents; 1116 1117 foreach (instance; _previous.list()) 1118 { 1119 if (instance.persistent) 1120 persistents ~= instance; 1121 } 1122 1123 foreach (e; persistents) 1124 { 1125 auto threadID = e.threadid; 1126 current.instanceDestroy!InScene(e, false); 1127 1128 if (scene.isThreadExists(threadID)) 1129 scene.add(e,threadID); 1130 else 1131 scene.add(e); 1132 } 1133 } 1134 1135 _initable = scene; 1136 1137 if (!scene.isInit) 1138 { 1139 if (scene in InitFunctions) 1140 { 1141 foreach (fun; InitFunctions[scene]) 1142 { 1143 fun(); 1144 } 1145 } 1146 1147 foreach (instance; scene.list()) 1148 { 1149 if (instance in IInitFunctions) 1150 { 1151 foreach (fun; IInitFunctions[instance]) 1152 { 1153 fun(); 1154 } 1155 } 1156 } 1157 1158 scene.isInit = true; 1159 }else 1160 { 1161 if (scene in RestartFunctions) 1162 { 1163 foreach (fun; RestartFunctions[scene]) 1164 { 1165 fun(); 1166 } 1167 } 1168 1169 foreach (instance; scene.list()) 1170 { 1171 if (instance in IRestartFunctions) 1172 { 1173 foreach (fun; IRestartFunctions[instance]) 1174 { 1175 fun(); 1176 } 1177 } 1178 } 1179 } 1180 1181 if (scene in EntryFunctions) 1182 { 1183 foreach(fun; EntryFunctions[scene]) 1184 { 1185 fun(); 1186 } 1187 } 1188 1189 foreach (instance; scene.list()) 1190 { 1191 if (instance in IEntryFunctions) 1192 { 1193 foreach (fun; IEntryFunctions[instance]) 1194 { 1195 fun(); 1196 } 1197 } 1198 } 1199 1200 _initable = null; 1201 1202 _current = scene; 1203 } 1204 1205 /++ 1206 Calling the game launch event. 1207 1208 Should be called before all events, before the beginning of the 1209 cycle of life. 1210 +/ 1211 void callGameStart() @trusted 1212 { 1213 foreach (scene; scenes) 1214 { 1215 if (scene in GameStartFunctions) 1216 { 1217 foreach (fun; GameStartFunctions[scene]) 1218 { 1219 fun(); 1220 } 1221 } 1222 1223 foreach (instance; scene.list()) 1224 { 1225 if (instance.active && !instance.onlyDraw) 1226 { 1227 if (instance in IGameStartFunctions) 1228 { 1229 foreach (fun; IGameStartFunctions[instance]) 1230 { 1231 fun(); 1232 } 1233 } 1234 } 1235 } 1236 } 1237 } 1238 1239 /++ 1240 Game completion call events (successful). 1241 The unsuccessful event should raise the `onError` event. 1242 +/ 1243 void callGameExit() @trusted 1244 { 1245 foreach (scene; scenes) 1246 { 1247 if (scene in GameExitFunctions) 1248 { 1249 foreach (fun; GameExitFunctions[scene]) 1250 { 1251 fun(); 1252 } 1253 } 1254 1255 foreach (instance; scene.list()) 1256 { 1257 if (instance.active && !instance.onlyDraw) 1258 { 1259 if (instance in IGameExitFunctions) 1260 { 1261 foreach (fun; IGameExitFunctions[instance]) 1262 { 1263 fun(); 1264 } 1265 } 1266 } 1267 } 1268 } 1269 } 1270 1271 /++ 1272 Triggering an emergency event. 1273 1274 Does not terminate the game, should be called on exceptions. After that, 1275 the programmer himself decides what to do next (if he implements his own 1276 life cycle). Called usually on `scope (failure)`, however, it will not 1277 throw a specific exception. 1278 +/ 1279 void callOnError() @trusted 1280 { 1281 if (current !is null) 1282 { 1283 if (current in OnErrorFunctions) 1284 { 1285 foreach (fun; OnErrorFunctions[current]) 1286 { 1287 fun(); 1288 } 1289 } 1290 1291 foreach (instance; current.list()) 1292 { 1293 if (instance.active && !instance.onlyDraw) 1294 { 1295 if (instance in IOnErrorFunctions) 1296 { 1297 foreach (fun; IOnErrorFunctions[instance]) 1298 { 1299 fun(); 1300 } 1301 } 1302 } 1303 } 1304 } 1305 } 1306 1307 /++ 1308 Calling a game step. Should always be called during a loop step in an 1309 exception when the thread is suspended. 1310 1311 Params: 1312 thread = Thread identificator. 1313 rend = Renderer instance. 1314 +/ 1315 void callStep(size_t thread, IRenderer rend) @trusted 1316 { 1317 if (current !is null) 1318 { 1319 current.worldCollision(); 1320 1321 if (thread == 0) 1322 if (current in StepFunctions) 1323 { 1324 foreach (fun; StepFunctions[current]) 1325 { 1326 fun(); 1327 } 1328 } 1329 1330 if (current in StepThreadFunctions) 1331 if (thread in StepThreadFunctions[current]) 1332 { 1333 foreach (fun; StepThreadFunctions[current][thread]) 1334 fun(); 1335 } 1336 1337 foreach (instance; current.getThreadList(thread)) 1338 { 1339 if (instance.isDestroy) 1340 { 1341 current.instanceDestroy!InMemory(instance); 1342 current.sort(); 1343 continue; 1344 } 1345 1346 if (!instance.active || instance.onlyDraw) continue; 1347 1348 if (instance in IStepFunctions) 1349 { 1350 foreach (fun; IStepFunctions[instance]) 1351 { 1352 fun(); 1353 } 1354 } 1355 1356 foreach (component; instance.getComponents()) 1357 { 1358 if (component in CStepFunctions) 1359 { 1360 foreach (fun; CStepFunctions[component]) 1361 { 1362 fun(); 1363 } 1364 } 1365 } 1366 } 1367 1368 foreach(instance; current.list()) 1369 { 1370 if (!instance.active || instance.onlyDraw) continue; 1371 1372 if (instance in IStepThreadFunctions) 1373 if (thread in IStepThreadFunctions[instance]) 1374 { 1375 foreach (fun; IStepThreadFunctions[instance][thread]) 1376 fun(); 1377 } 1378 1379 foreach (component; instance.getComponents()) 1380 { 1381 if (component in CStepThreadFunctions) 1382 if (thread in CStepThreadFunctions[component]) 1383 { 1384 foreach(fun; CStepThreadFunctions[component][thread]) 1385 { 1386 fun(); 1387 } 1388 } 1389 } 1390 } 1391 } 1392 } 1393 1394 /++ 1395 System event event for scenes and instances of the current context. 1396 1397 Params: 1398 event = System event handler instance. 1399 +/ 1400 void callEvent(EventHandler event) @trusted 1401 { 1402 if (current !is null) 1403 { 1404 if (current in EventHandleFunctions) 1405 { 1406 foreach (fun; EventHandleFunctions[current]) 1407 { 1408 fun(event); 1409 } 1410 } 1411 1412 foreach (instance; current.list()) 1413 { 1414 if (instance.active && !instance.onlyDraw) 1415 { 1416 if (instance in IEventHandleFunctions) 1417 { 1418 foreach(fun; IEventHandleFunctions[instance]) 1419 { 1420 fun(event); 1421 } 1422 } 1423 1424 foreach (component; instance.getComponents()) 1425 { 1426 if (component in CEventHandleFunctions) 1427 { 1428 foreach(fun; CEventHandleFunctions[component]) 1429 { 1430 fun(event); 1431 } 1432 } 1433 } 1434 } 1435 } 1436 } 1437 } 1438 1439 /++ 1440 Calling an event to render scenes and instances of the current context. 1441 1442 Params: 1443 render = Render instance. 1444 +/ 1445 void callDraw(IRenderer render) @trusted 1446 { 1447 import tida.vector; 1448 1449 if (current !is null) 1450 { 1451 if (current in DrawFunctions) 1452 { 1453 foreach (fun; DrawFunctions[current]) 1454 { 1455 fun(render); 1456 } 1457 } 1458 1459 foreach (instance; current.getAssortedInstances()) 1460 { 1461 if (instance.active && instance.visible) 1462 { 1463 render.draw(instance.spriteDraw(), instance.position); 1464 1465 if (instance in IDrawFunctions) 1466 { 1467 foreach (fun; IDrawFunctions[instance]) 1468 { 1469 fun(render); 1470 } 1471 } 1472 1473 foreach (component; instance.getComponents()) 1474 { 1475 if (component in CDrawFunctions) 1476 { 1477 foreach (fun; CDrawFunctions[component]) 1478 { 1479 fun(render); 1480 } 1481 } 1482 } 1483 } 1484 } 1485 } 1486 } 1487 1488 /// Free memory. 1489 void free() @safe 1490 { 1491 _scenes = null; 1492 } 1493 1494 ~this() @safe 1495 { 1496 free(); 1497 } 1498 } 1499 1500 unittest 1501 { 1502 initSceneManager(); 1503 1504 class A : Scene 1505 { 1506 this() @safe 1507 { 1508 name = "Test"; 1509 } 1510 } 1511 1512 sceneManager.add(new A()); 1513 assert(("Test" in sceneManager.scenes) !is null); 1514 } 1515 1516 unittest 1517 { 1518 initSceneManager(); 1519 1520 class A : Scene 1521 { 1522 @Event!Init 1523 void onInit() @safe { } 1524 } 1525 1526 A obj = new A(); 1527 sceneManager.add(obj); 1528 1529 assert((sceneManager.InitFunctions[obj][0].ptr) == ((&obj.onInit).ptr)); 1530 } 1531 1532 unittest 1533 { 1534 initSceneManager(); 1535 1536 class A : Scene 1537 { 1538 this() @safe 1539 { 1540 name = "Test"; 1541 } 1542 } 1543 1544 sceneManager.add(new A()); 1545 assert(sceneManager.hasScene("Test")); 1546 }