1 /++ 2 Scene description module. 3 4 A stage is an object that manages instances, collisions between them and serves 5 as a place for them (reserves them in a separate array). Such an object can 6 also be programmable using events (see tida.localevent). It does not have 7 properties that define behavior without functions, it can only contain 8 instances that, through it, can refer to other instances. 9 10 Macros: 11 LREF = <a href="#$1">$1</a> 12 HREF = <a href="$1">$2</a> 13 PHOBREF = <a href="https://dlang.org/phobos/$1.html#$2">$2</a> 14 15 Authors: $(HREF https://github.com/TodNaz,TodNaz) 16 Copyright: Copyright (c) 2020 - 2021, TodNaz. 17 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT) 18 +/ 19 module tida.scene; 20 21 enum 22 { 23 InMemory, /// Delete with memory 24 InScene /// Delete with scene 25 } 26 27 /++ 28 Scene object. 29 +/ 30 class Scene 31 { 32 import tida.scenemanager; 33 import tida.instance; 34 import tida.component; 35 import tida.render; 36 import tida.event; 37 38 package(tida): 39 bool isInit = false; 40 41 protected: 42 Instance[] instances; 43 Instance[] erentInstances; 44 Instance[][] bufferThread; 45 46 public: 47 Camera camera; /// Camera scene 48 string name = ""; /// Scene name 49 50 @safe: 51 this() nothrow 52 { 53 bufferThread = [[]]; 54 } 55 56 /++ 57 Returns a list of instances. 58 +/ 59 @property final Instance[] list() nothrow 60 { 61 return instances; 62 } 63 64 /++ 65 Returns a buffer of instances from the thread. 66 67 Params: 68 index = Thread id. 69 +/ 70 final Instance[] getThreadList(size_t index) nothrow 71 { 72 return bufferThread[index]; 73 } 74 75 /++ 76 Is there such a thread buffer. 77 78 Params: 79 index = Thread id. 80 +/ 81 final bool isThreadExists(size_t index) nothrow 82 { 83 return index < bufferThread.length; 84 } 85 86 /++ 87 Creates an instance buffer for the thread. 88 +/ 89 final void initThread(size_t count = 1) nothrow 90 { 91 foreach (_; 0 .. count) 92 { 93 bufferThread ~= [[]]; 94 } 95 } 96 97 /++ 98 Adds an instance to the scene for interpreting its actions. 99 100 Params: 101 instance = Instance. 102 threadID = In which thread to add execution. 103 +/ 104 final void add(T)(T instance,size_t threadID = 0) 105 in(instance, "Instance is not a create!") 106 do 107 { 108 static assert(isInstance!T, T.stringof ~ " is not a instance!"); 109 if (threadID >= bufferThread.length) threadID = 0; 110 111 this.instances ~= instance; 112 instance.id = this.instances.length - 1; 113 instance.threadid = threadID; 114 115 bufferThread[threadID] ~= instance; 116 117 sceneManager.instanceExplore!T(this, instance); 118 119 this.sort(); 120 } 121 122 /++ 123 Creates and adds an instance to the scene. 124 125 Params: 126 Name = Name class. 127 threadID = In which thread to add execution. 128 129 Note: 130 There must be no arguments in the constructor. 131 +/ 132 final void add(Name)(size_t threadID = 0) 133 { 134 static assert(isInstance!T, T.stringof ~ " is not a instance!"); 135 auto instance = new Name(); 136 137 add(instance, threadID); 138 } 139 140 /++ 141 Adds multiple instances at a time. 142 143 Params: 144 instances = Instances. 145 threadID = In which thread to add execution. 146 +/ 147 final void add(Instance[] instances,size_t threadID = 0) 148 { 149 foreach(instance; instances) { 150 add(instance,threadID); 151 } 152 } 153 154 /++ 155 Adds multiple instances at a time. 156 157 Params: 158 Names = Names instances. 159 threadID = In which thread to add execution. 160 +/ 161 final void add(Names...)(size_t threadID = 0) 162 { 163 static foreach (Name; Names) { 164 add!Name(threadID); 165 } 166 } 167 168 /++ 169 Returns a assorted list of instances. 170 +/ 171 final Instance[] getAssortedInstances() 172 { 173 return erentInstances; 174 } 175 176 /++ 177 Whether this instance is on the list. 178 179 Params: 180 instance = Instance. 181 +/ 182 final bool hasInstance(Instance instance) nothrow 183 { 184 foreach(ins; instances) { 185 if(instance is ins) 186 return true; 187 } 188 189 return false; 190 } 191 192 /++ 193 Checks instances for collisions. 194 +/ 195 void worldCollision() @trusted 196 { 197 import tida.collision; 198 199 auto collstragt = sceneManager.collisionFunctions; 200 auto coll = sceneManager.colliders; 201 202 foreach(first; list()) 203 { 204 foreach(second; list()) 205 { 206 if(first !is second && first.solid && second.solid) { 207 if(first.active && second.active) 208 { 209 import std.algorithm : canFind, each; 210 211 if ( 212 isCollide( first.mask, 213 second.mask, 214 first.position, 215 second.position) 216 ) { 217 if (first in collstragt) 218 collstragt[first].each!(fun => fun(second)); 219 220 if (second in collstragt) 221 collstragt[second].each!(fun => fun(first)); 222 223 if (first in coll) 224 { 225 foreach (cls; coll[first]) 226 { 227 if (cls.ev.name != "") 228 { 229 if (cls.ev.name == second.name) 230 { 231 if (cls.ev.tag != "") 232 { 233 if (second.tags.canFind(cls.ev.tag)) 234 { 235 cls.fun(second); 236 } 237 } else 238 cls.fun(second); 239 } 240 } else 241 { 242 if (cls.ev.tag != "") 243 { 244 if (second.tags.canFind(cls.ev.tag)) 245 { 246 cls.fun(second); 247 } 248 } else 249 { 250 cls.fun(second); 251 } 252 } 253 } 254 } 255 256 if (second in coll) 257 { 258 foreach (cls; coll[second]) 259 { 260 if (cls.ev.name != "") 261 { 262 if (cls.ev.name == first.name) 263 { 264 if (cls.ev.tag != "") 265 { 266 if (first.tags.canFind(cls.ev.tag)) 267 { 268 cls.fun(first); 269 } 270 } else 271 cls.fun(first); 272 } 273 } else 274 { 275 if (first.tags.canFind(cls.ev.tag)) 276 { 277 cls.fun(first); 278 } 279 } 280 } 281 } 282 } else 283 { 284 if (first in collstragt) 285 collstragt[first].each!(fun => fun(null)); 286 287 if (second in collstragt) 288 collstragt[second].each!(fun => fun(null)); 289 } 290 } 291 } 292 } 293 } 294 } 295 296 /++ 297 Removes an instance from the list and, if a delete method is 298 specified in the template, from memory. 299 300 Params: 301 type = Type destroy. 302 instance = Instance. 303 304 Type: 305 `InScene` - Removes only from the scene, does not free memory. 306 `InMemory` - Removes permanently, from the scene and from memory 307 (by the garbage collector). 308 +/ 309 final void instanceDestroy(ubyte type)(Instance instance, bool isRemoveHandle = true) @trusted 310 in(hasInstance(instance)) 311 do 312 { 313 import std.algorithm : each; 314 315 // dont remove, it's succes work. 316 void remove(T)(ref T[] obj, size_t index) @trusted nothrow 317 { 318 auto dump = obj.dup; 319 foreach (i; index .. dump.length) 320 { 321 import core.exception : RangeError; 322 try 323 { 324 dump[i] = dump[i + 1]; 325 } 326 catch (RangeError e) 327 { 328 continue; 329 } 330 } 331 obj = dump[0 .. $-1]; 332 } 333 334 remove(instances, instance.id); 335 foreach (size_t i; 0 .. bufferThread[instance.threadid].length) 336 { 337 if (bufferThread[instance.threadid][i] is instance) 338 { 339 remove(bufferThread[instance.threadid],i); 340 break; 341 } 342 } 343 344 if (this.instances.length != 0) 345 { 346 this.instances[instance.id .. $].each!((ref e) => e.id--); 347 } 348 349 if (sceneManager !is null) 350 { 351 sceneManager.destroyEventCall(instance); 352 sceneManager.destroyEventSceneCall(this, instance); 353 } 354 355 if (sceneManager !is null && isRemoveHandle) 356 sceneManager.removeHandle(this, instance); 357 358 static if(type == InMemory) 359 { 360 instance.dissconnectAll(); 361 destroy(instance); 362 } 363 } 364 365 /++ 366 Destroys an instance from the scene or from memory, depending on the template argument, by its class. 367 368 Params: 369 type = Type destroy. 370 Name = Instance class. 371 372 Type: 373 `InScene` - Removes only from the scene, does not free memory. 374 `InMemory` - Removes permanently, from the scene and from memory 375 (by the garbage collector). 376 +/ 377 final void instanceDestroy(ubyte type, Name)() @trusted 378 in(isInstance!Name) 379 do 380 { 381 instanceDestroy!type(getInstanceByClass!Name); 382 } 383 384 /++ 385 Returns an instance by name. 386 387 Params: 388 name = Instance name. 389 +/ 390 final Instance getInstanceByName(string name) nothrow 391 { 392 foreach (instance; list()) 393 { 394 if (instance.name == name) 395 return instance; 396 } 397 398 return null; 399 } 400 401 /++ 402 Returns an instance by name and tag. 403 404 Params: 405 name = Instance name. 406 tag = Instance tag. 407 +/ 408 final Instance getInstanceByNameTag(string name, string tag) nothrow 409 { 410 foreach (instance; list()) 411 { 412 if (instance.name == name) 413 { 414 foreach (tage; instance.tags) 415 if (tag == tage) 416 return instance; 417 } 418 } 419 420 return null; 421 } 422 423 /++ 424 Returns an object by its instance inheritor. 425 426 Params: 427 T = Class name. 428 +/ 429 final T getInstanceByClass(T)() nothrow 430 in(isInstance!T) 431 do 432 { 433 foreach (instance; list) 434 { 435 if ((cast(T) instance) !is null) 436 return cast(T) instance; 437 } 438 439 return null; 440 } 441 442 import tida.shape, tida.vector; 443 444 /++ 445 Returns instance(-s) by its mask. 446 447 Params: 448 shape = Shape mask. 449 position = Instance position. 450 +/ 451 final Instance getInstanceByMask(Shapef shape, Vecf position) 452 { 453 import tida.collision; 454 455 foreach (instance; list()) 456 { 457 if (instance.solid) 458 if (isCollide(shape,instance.mask,position,instance.position)) 459 { 460 return instance; 461 } 462 } 463 464 return null; 465 } 466 467 /// ditto 468 final Instance[] getInstancesByMask(Shapef shape,Vecf position) @safe 469 { 470 import tida.collision; 471 472 Instance[] result; 473 474 foreach(instance; list()) 475 { 476 if(instance.solid) 477 if(isCollide(shape,instance.mask,position,instance.position)) { 478 result ~= instance; 479 continue; 480 } 481 } 482 483 return result; 484 } 485 486 /// Clear sorted list of instances. 487 void sortClear() @safe 488 { 489 this.erentInstances = null; 490 } 491 492 /// Sort list of instances. 493 void sort() @trusted 494 { 495 void sortErent(T)(ref T[] data, bool delegate(T a, T b) @safe nothrow func) @trusted nothrow 496 { 497 T tmp; 498 for (size_t i = 0; i < data.length; i++) 499 { 500 for (size_t j = (data.length-1); j >= (i + 1); j--) 501 { 502 if (func(data[j],data[j-1])) 503 { 504 tmp = data[j]; 505 data[j] = data[j-1]; 506 data[j-1] = tmp; 507 } 508 } 509 } 510 } 511 512 sortClear(); 513 514 erentInstances = instances.dup; 515 sortErent!Instance(erentInstances,(a, b) @safe nothrow => a.depth > b.depth); 516 } 517 } 518 519 unittest 520 { 521 import tida.instance; 522 import tida.scenemanager; 523 524 initSceneManager(); 525 526 class A : Instance { this() @safe { name = "A"; tags = ["A"]; }} 527 class B : Instance { this() @safe { name = "B"; tags = ["B"]; }} 528 529 Scene scene = new Scene(); 530 531 auto a = new A(); 532 auto b = new B(); 533 scene.add([a,b]); 534 535 assert(scene.getInstanceByClass!A == a); 536 assert(scene.getInstanceByClass!B == b); 537 538 assert(scene.getInstanceByName("A") == a); 539 assert(scene.getInstanceByName("B") == b); 540 541 assert(scene.getInstanceByNameTag("A", "A") == a); 542 assert(scene.getInstanceByNameTag("B", "B") == b); 543 } 544 545 unittest 546 { 547 import tida.instance; 548 import tida.scenemanager; 549 550 initSceneManager(); 551 552 Scene scene = new Scene(); 553 554 Instance a = new Instance(), 555 b = new Instance(); 556 557 a.depth = 3; 558 b.depth = 7; 559 560 scene.add([a,b]); 561 562 assert(scene.getAssortedInstances == [b,a]); 563 }