1 /++
2 The module describing the unit of the object - Instance.
3 
4 An instance is an object in a scene with behavior only for itself with the
5 interaction of other instances, through collisions, any internal events.
6 Each instance has properties of execution conditions, rendering conditions,
7 rendering properties, conditions for the execution of some events. All of these
8 properties describe an instance, however, more behavior can be achieved using
9 inheritance (see `tida.localevent`). Instance functions are not inherited. but
10 they are directly written and marked with attributes that give execution
11 conditions (under what conditions it is necessary to execute, as if it is a
12 transfer of control between scenes, rendering of a frame, processing user input).
13 
14 Also, an instance has a hard mask, where, when it touches another mask,
15 a collision event can be generated and such instances can handle this event,
16 if, of course, the corresponding functions have been marked with attributes.
17 
18 ---
19 class MyObject : Instance
20 {
21     this() { ... }
22 
23     @Init
24     void onInitFunction()
25     {
26         firstProperty = 0.0f;
27         position = vecf(32f, 11.5f);
28         ...
29     }
30 
31     @Collision("OtherInstanceName")
32     void onCollsion(Instance other)
33     {
34         other.posiiton -= vecf(1, 0);
35     }
36 }
37 ---
38 
39 Macros:
40     LREF = <a href="#$1">$1</a>
41     HREF = <a href="$1">$2</a>
42 
43 Authors: $(HREF https://github.com/TodNaz,TodNaz)
44 Copyright: Copyright (c) 2020 - 2021, TodNaz.
45 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT)
46 +/
47 module tida.instance;
48 
49 /++
50 Checks if an object is an instance.
51 +/
52 template isInstance(T)
53 {
54     enum isInstance = is(T : Instance);
55 }
56 
57 struct InstanceEvents
58 {
59     import tida.event;
60     import tida.render;
61     import tida.localevent;
62 
63     struct FEEntry
64     {
65         void delegate() @safe func;
66         size_t argsLength;
67 
68         static FEEntry create(T...)(void delegate() @safe func) @trusted
69         {
70             FEEntry entry;
71             entry.func = func;
72             entry.appendArguments!T();
73 
74             return entry;
75         }
76 
77         void appendArguments(T...)() @trusted
78         {
79             static foreach (Arg; T)
80             {
81                 argsLength += Arg.sizeof;
82             }
83         }
84 
85         bool validArgs(T...)(T args) @trusted
86         {
87             size_t length;
88             foreach (arg; args)
89             {
90                 length += arg.sizeof;
91             }
92 
93             return length == argsLength;
94         }
95 
96         void opCall(T...)(T args) @trusted
97         in (validArgs(args))
98         {
99             void delegate(T) @safe callFunction = cast(void delegate(T) @safe) func;
100             callFunction(args);
101         }
102     }
103 
104     alias FEInit = FEEntry;
105     alias FERestart = FEEntry;
106 
107     alias FELeave = void delegate() @safe;
108     alias FEStep = void delegate() @safe;
109     alias FEGameStart = void delegate() @safe;
110     alias FEGameExit = void delegate() @safe;
111     alias FEGameRestart = void delegate() @safe;
112     alias FEEventHandle = void delegate(EventHandler) @safe;
113     alias FEDraw = void delegate(IRenderer) @safe;
114     alias FEOnError = void delegate() @safe;
115     alias FECollision = void delegate(Instance) @safe;
116     alias FETrigger = void delegate() @safe;
117     alias FEDestroy = void delegate(Instance) @safe;
118     alias FEATrigger = void delegate(string) @safe;
119 
120     struct SRCollider
121     {
122         Collision ev;
123         FECollision fun;
124     }
125 
126     struct SRTrigger
127     {
128         Trigger ev;
129         FETrigger fun;
130     }
131 
132     FEInit[] IInitFunctions;
133 
134     FEStep[] IStepFunctions;
135     FEStep[][size_t] IStepThreadFunctions;
136     FERestart[] IRestartFunctions;
137     FEEntry[] IEntryFunctions;
138     FELeave[] ILeaveFunctions;
139     FEGameStart[] IGameStartFunctions;
140     FEGameExit[] IGameExitFunctions;
141     FEGameRestart[] IGameRestartFunctions;
142     FEEventHandle[] IEventHandleFunctions;
143     FEDraw[] IDrawFunctions;
144     FEOnError[] IOnErrorFunctions;
145     SRCollider[] IColliderStructs;
146     FECollision[] ICollisionFunctions;
147     SRTrigger[] IOnTriggerFunctions;
148     FEDestroy[] IOnDestroyFunctions;
149     FEATrigger[] IOnAnyTriggerFunctions;
150 }
151 
152 /++
153 Instance object. Can be created for a render unit as well as for legacy
154 with a programmable model.
155 +/
156 class Instance
157 {
158     import tida.vector;
159     import tida.sprite;
160     import tida.shape;
161     import tida.component;
162 
163 protected:
164     /++
165     Instance sprite. Will be output at the position of the instance.
166     +/
167     Sprite sprite;
168 
169     /++
170     Components of an instance, complementing its functionality.
171     +/
172     Component[] components;
173 
174     /// only for call.
175     bool _destroy = false;
176 
177 public:
178     InstanceEvents events;
179 
180     @property auto colliders() @safe
181     {
182         return events.IColliderStructs;
183     }
184 
185     @property auto collisionFunctions() @safe
186     {
187         return events.ICollisionFunctions;
188     }
189 
190     /++
191     The name of the instance, by which you can later identify
192     the collided or other events.
193     +/
194     string name;
195 
196     /++
197     Instance tags. By this parameter, it is possible to distribute an instance
198     about groups, for example, instances that should not let the player in upon
199     collision will be marked with the "solid" tag, but not necessarily only
200     non-living creatures should be used, and others who should not squeeze
201     through are marked with such a tag.
202     (This is an example, there is no such implementation in the framework).
203     +/
204     string[] tags;
205 
206     /++
207     The position of the instance. Serves for collision, rendering, and
208     other instance services.
209     +/
210     Vector!float position = vec!float(0.0f, 0.0f);
211 
212     /++
213     An auxiliary variable that remembers the position in the previous
214     pass of the game loop.
215     +/
216     Vector!float previous = vec!float(0.0f, 0.0f);
217 
218     /++
219     Collision mask. A mask is a kind of geometric shape (or several shapes)
220     that sets the collision boundary between other instances.
221     +/
222     Shape!float mask;
223 
224     /++
225     A property that determines whether an instance can collide with
226     other instances.
227     +/
228     bool solid = false;
229 
230     /++
231     Instance identifier (often means storage location in an instances array).
232     +/
233     size_t id;
234 
235     /++
236     The identifier for the instance in the stream. Shows which thread the
237     instance is running on (changing the property does not affect thread selection).
238     +/
239     size_t threadid;
240 
241     /++
242     A property indicating whether to handle all events for such an instance.
243     If false, no event will be processed for this instance, however,
244     it will exist. It is necessary if you do not need to delete the instance,
245     but you also do not need to process its events.
246     +/
247     bool active = true;
248 
249     /++
250     A property that indicates whether to render the instance and perform
251     rendering functions.
252     +/
253     bool visible = true;
254 
255     /++
256     A property that indicates whether it is only necessary to draw the object
257     without handling other events in it.
258     +/
259     bool onlyDraw = false;
260 
261     /++
262     A property that indicates whether to transition to a new scene when
263     transferring control to another scene.
264     +/
265     bool persistent = false;
266 
267     /++
268     The identifier of the layer in which the instance is placed.
269     The render queue is affected, the larger the number, the later the
270     instance will be rendered.
271     +/
272     int depth = 0;
273 
274 @safe:
275     this()
276     {
277         sprite = new Sprite();
278     }
279 
280     /++
281     Removes an instance. However, not immediately, this will only happen on
282     the next iteration of the game loop for thread safety.
283     +/
284     final void destroy()
285     {
286         _destroy = true;
287     }
288 
289     /++
290     A method for adding an instance to an instance to expand functionality.
291 
292     Params:
293         component = Component object.
294     +/
295     final void add(T)(T component)
296     {
297         import tida.scenemanager;
298         
299         static if (is(T : Instance))
300             static assert(null, T.stringof ~ " is not a component! Maybe you meant `sceneManager.context.add`?");
301         else
302             static assert(isComponent!T, T.stringof ~ " is not a component!");
303 
304         components ~= component;
305         if (component.name == "")
306             component.name = T.stringof;
307 
308         sceneManager.componentExplore!T(this, component);
309     }
310 
311     /++
312     A method for adding an instance to an instance to expand functionality.
313 
314     Params:
315         T = Component type.
316     +/
317     final void add(T)()
318     {
319         add(new T());
320     }
321 
322     /++
323     A function that returns a component based on its class.
324 
325     Params:
326         T = Component type.
327     +/
328     final T cmp(T)()
329     {
330         static assert(isComponent!T, T.stringof ~ " is not a component!");
331 
332         foreach (e; components)
333         {
334             if ((cast(T) e) !is null)
335             {
336                 return cast(T) e;
337             }
338         }
339 
340         return null;
341     }
342 
343     /++
344     Finds a component by its name.
345 
346     Params:
347         name = Component name.
348     +/
349     final Component cmp(string name)
350     {
351         foreach (e; components)
352         {
353             if (e.name == name)
354                 return e;
355         }
356 
357         return null;
358     }
359 
360     /++
361     Detaches a component from an instance by finding it by class.
362 
363     Params:
364         T = Component type.
365     +/
366     final void dissconnect(T)()
367     {
368         import std.algorithm : remove;
369         import tida.scenemanager;
370         static assert(isComponent!T, "`" ~ T.stringof ~ "` is not a component!");
371 
372         Component cmp;
373 
374         foreach (i; 0 .. components.length)
375         {
376             if ((cast(T) components[i]) !is null)
377             {
378                 cmp = components[i];
379 
380                 foreach(fun; cmp.events.CLeaveFunctions) fun();
381 
382                 components = components.remove(i);
383                 break;
384             }
385         }
386     }
387 
388     /++
389     Detaches a component from an instance by finding it by name.
390 
391     Params:
392         name = Instance name.
393     +/
394     final void dissconnect(string name)
395     {
396         import std.algorithm : remove;
397         import tida.scenemanager;
398 
399         foreach (i; 0 .. components.length)
400         {
401             if (components[i].name == name)
402             {
403                 foreach(fun; components[i].events.CLeaveFunctions) fun();
404 
405                 components = components.remove(i);
406                 break;
407             }
408         }
409     }
410 
411     /++
412     Detaches absolutely all components in this instance.
413     +/
414     final void dissconnectAll() @trusted
415     {
416         import tida.scenemanager;
417         import std.algorithm : remove;
418 
419         foreach (i; 0 .. components.length)
420         {
421             if (sceneManager !is null)
422             {
423                 foreach(fun; components[i].events.CLeaveFunctions)
424                     fun();
425             }
426 
427             components = components.remove(i);
428         }
429     }
430 
431 package(tida):
432     Sprite spriteDraw()
433     {
434         return sprite;
435     }
436 
437     bool isDestroy()
438     {
439         return _destroy;
440     }
441 
442     Component[] getComponents()
443     {
444         return components;
445     }
446 }
447 
448 unittest
449 {
450     import tida.scenemanager;
451     import tida.component;
452 
453     initSceneManager();
454 
455     class CComponent : Component { }
456 
457     CComponent cmp = new CComponent();
458     cmp.name = "Cmp";
459 
460     Instance instance = new Instance();
461     instance.add(cmp);
462 
463     assert(instance.cmp("Cmp") is (cmp));
464     assert(instance.cmp!(CComponent) is (cmp));
465 }
466 
467 debug import tida.color;
468 
469 debug template debugCollisionMask(Color!ubyte color = Color!ubyte(255, 0, 0))
470 {
471     import tida.render;
472     import std.conv : to;
473 
474     void __drawShapeConture(Vecf releative, Shapef shape, IRenderer render) @safe
475     {
476         switch (shape.type)
477         {
478             case ShapeType.point:
479                 render.point(shape.begin + releative, color);
480             break;
481 
482             case ShapeType.line:
483                 render.line([   shape.begin + releative,
484                                 shape.end + releative], color);
485             break;
486 
487             case ShapeType.rectangle:
488                 render.rectangle(   shape.begin + releative, 
489                                     shape.width.to!uint, 
490                                     shape.height.to!uint,
491                                     color,
492                                     false);
493             break;
494 
495             case ShapeType.circle:
496                 render.circle(shape.begin + releative, shape.radius, color, false);
497             break;
498 
499             case ShapeType.triangle:
500                 render.triangle([   shape.vertex!0 + releative, 
501                                     shape.vertex!1 + releative, 
502                                     shape.vertex!2 + releative], color, false);
503             break;
504 
505             case ShapeType.polygon:
506                 render.polygon(shape.begin + releative, shape.data, color, false);
507             break;
508 
509             case ShapeType.multi:
510                 foreach (sh; shape.shapes)
511                 {
512                     __drawShapeConture(releative + shape.begin, sh, render);
513                 }
514             break;
515 
516             default:
517                 return;
518         }   
519     }
520 
521     @event(Draw)
522     void __debug_drawMask(IRenderer render) @safe
523     {
524         __drawShapeConture(position, mask, render);
525     }
526 }
527 
528 unittest
529 {
530     import tida.component;
531     import tida.scenemanager;
532     import tida.localevent;
533 
534     initSceneManager();
535 
536     static class A : Component
537     {
538         int trace = 0;
539 
540         @event(Init)
541         void onInit(Instance instance) @safe
542         {
543             trace++;
544         }
545     }
546 
547     Instance instance = new Instance();
548     A a;
549     instance.add(a = new A());
550 
551     assert(a.trace == 1);
552 }