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