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 /++
58 Instance object. Can be created for a render unit as well as for legacy
59 with a programmable model.
60 +/
61 class Instance
62 {
63     import tida.vector;
64     import tida.sprite;
65     import tida.shape;
66     import tida.component;
67 
68 protected:
69     /++
70     Instance sprite. Will be output at the position of the instance.
71     +/
72     Sprite sprite;
73 
74     /++
75     Components of an instance, complementing its functionality.
76     +/
77     Component[] components;
78 
79     /// only for call.
80     bool _destroy = false;
81 
82 public:
83     /++
84     The name of the instance, by which you can later identify
85     the collided or other events.
86     +/
87     string name;
88 
89     /++
90     Instance tags. By this parameter, it is possible to distribute an instance
91     about groups, for example, instances that should not let the player in upon
92     collision will be marked with the "solid" tag, but not necessarily only
93     non-living creatures should be used, and others who should not squeeze
94     through are marked with such a tag.
95     (This is an example, there is no such implementation in the framework).
96     +/
97     string[] tags;
98 
99     /++
100     The position of the instance. Serves for collision, rendering, and
101     other instance services.
102     +/
103     Vector!float position = vec!float(0.0f, 0.0f);
104 
105     /++
106     An auxiliary variable that remembers the position in the previous
107     pass of the game loop.
108     +/
109     Vector!float previous = vec!float(0.0f, 0.0f);
110 
111     /++
112     Collision mask. A mask is a kind of geometric shape (or several shapes)
113     that sets the collision boundary between other instances.
114     +/
115     Shape!float mask;
116 
117     /++
118     A property that determines whether an instance can collide with
119     other instances.
120     +/
121     bool solid = false;
122 
123     /++
124     Instance identifier (often means storage location in an instances array).
125     +/
126     size_t id;
127 
128     /++
129     The identifier for the instance in the stream. Shows which thread the
130     instance is running on (changing the property does not affect thread selection).
131     +/
132     size_t threadid;
133 
134     /++
135     A property indicating whether to handle all events for such an instance.
136     If false, no event will be processed for this instance, however,
137     it will exist. It is necessary if you do not need to delete the instance,
138     but you also do not need to process its events.
139     +/
140     bool active = true;
141 
142     /++
143     A property that indicates whether to render the instance and perform
144     rendering functions.
145     +/
146     bool visible = true;
147 
148     /++
149     A property that indicates whether it is only necessary to draw the object
150     without handling other events in it.
151     +/
152     bool onlyDraw = false;
153 
154     /++
155     A property that indicates whether to transition to a new scene when
156     transferring control to another scene.
157     +/
158     bool persistent = false;
159 
160     /++
161     The identifier of the layer in which the instance is placed.
162     The render queue is affected, the larger the number, the later the
163     instance will be rendered.
164     +/
165     int depth = 0;
166 
167 @safe:
168     this()
169     {
170         sprite = new Sprite();
171 
172         name = typeid(this).stringof;
173     }
174 
175     /++
176     Removes an instance. However, not immediately, this will only happen on
177     the next iteration of the game loop for thread safety.
178     +/
179     final void destroy()
180     {
181         _destroy = true;
182     }
183 
184     /++
185     A method for adding an instance to an instance to expand functionality.
186 
187     Params:
188         component = Component object.
189     +/
190     void add(T)(T component)
191     {
192         import tida.scenemanager;
193         static assert(isComponent!T, T.stringof ~ " is not a component!");
194 
195         components ~= component;
196         if (component.name == "")
197             component.name = T.stringof;
198 
199         sceneManager.componentExplore!T(this, component);
200     }
201 
202     /++
203     A method for adding an instance to an instance to expand functionality.
204 
205     Params:
206         T = Component type.
207     +/
208     void add(T)()
209     {
210         add(new T());
211     }
212 
213     /++
214     A function that returns a component based on its class.
215 
216     Params:
217         T = Component type.
218     +/
219     T cmp(T)()
220     {
221         static assert(isComponent!T, T.stringof ~ " is not a component!");
222 
223         foreach (e; components)
224         {
225             if ((cast(T) e) !is null)
226             {
227                 return cast(T) e;
228             }
229         }
230 
231         return null;
232     }
233 
234     /++
235     Finds a component by its name.
236 
237     Params:
238         name = Component name.
239     +/
240     Component cmp(string name)
241     {
242         foreach (e; components)
243         {
244             if (e.name == name)
245                 return e;
246         }
247 
248         return null;
249     }
250 
251     /++
252     Detaches a component from an instance by finding it by class.
253 
254     Params:
255         T = Component type.
256     +/
257     void dissconnect(T)()
258     {
259         import std.algorithm : remove;
260         import tida.scenemanager;
261         static assert(isComponent!T, "`" ~ T.stringof ~ "` is not a component!");
262 
263         Component cmp;
264 
265         foreach (i; 0 .. components.length)
266         {
267             if ((cast(T) components[i]) !is null)
268             {
269                 cmp = components[i];
270 
271                 foreach(fun; sceneManager.leaveComponents[cmp]) fun();
272 
273                 components = components.remove(i);
274                 break;
275             }
276         }
277     }
278 
279     /++
280     Detaches a component from an instance by finding it by name.
281 
282     Params:
283         T = Component type.
284     +/
285     void dissconnect(string name)
286     {
287         import std.algorithm : remove;
288         import tida.scenemanager;
289 
290         foreach (i; 0 .. components.length)
291         {
292             if (components[i].name == name)
293             {
294                 foreach(fun; sceneManager.leaveComponents[components[i]]) fun();
295 
296                 components = components.remove(i);
297                 break;
298             }
299         }
300     }
301 
302     /++
303     Detaches absolutely all components in this instance.
304     +/
305     void dissconnectAll() @trusted
306     {
307         import tida.scenemanager;
308         import std.algorithm : remove;
309 
310         foreach (i; 0 .. components.length)
311         {
312             if (sceneManager !is null)
313             {
314                 foreach(fun; sceneManager.leaveComponents[components[i]]) fun();
315             }
316 
317             components = components.remove(i);
318         }
319     }
320 
321 package(tida):
322     Sprite spriteDraw()
323     {
324         return sprite;
325     }
326 
327     bool isDestroy()
328     {
329         return _destroy;
330     }
331 
332     Component[] getComponents()
333     {
334         return components;
335     }
336 }
337 
338 unittest
339 {
340     import tida.scenemanager;
341     import tida.component;
342 
343     initSceneManager();
344 
345     class CComponent : Component { }
346 
347     CComponent cmp = new CComponent();
348     cmp.name = "Cmp";
349 
350     Instance instance = new Instance();
351     instance.add(cmp);
352 
353     assert(instance.cmp("Cmp") is (cmp));
354     assert(instance.cmp!(CComponent) is (cmp));
355 }
356 
357 debug import tida.color;
358 
359 debug template debugCollisionMask(Color!ubyte color = Color!ubyte(255, 0, 0))
360 {
361     import tida.render;
362     import std.conv : to;
363 
364     void __drawShapeConture(Vecf releative, Shapef shape, IRenderer render) @safe
365     {
366         switch (shape.type)
367         {
368             case ShapeType.point:
369                 render.point(shape.begin + releative, color);
370             break;
371 
372             case ShapeType.line:
373                 render.line([   shape.begin + releative,
374                                 shape.end + releative], color);
375             break;
376 
377             case ShapeType.rectangle:
378                 render.rectangle(   shape.begin + releative, 
379                                     shape.width.to!uint, 
380                                     shape.height.to!uint,
381                                     color,
382                                     false);
383             break;
384 
385             case ShapeType.circle:
386                 render.circle(shape.begin + releative, shape.radius, color, false);
387             break;
388 
389             case ShapeType.triangle:
390                 render.triangle([   shape.vertex!0 + releative, 
391                                     shape.vertex!1 + releative, 
392                                     shape.vertex!2 + releative], color, false);
393             break;
394 
395             case ShapeType.polygon:
396                 render.polygon(shape.begin + releative, shape.data, color, false);
397             break;
398 
399             case ShapeType.multi:
400                 foreach (sh; shape.shapes)
401                 {
402                     __drawShapeConture(releative + shape.begin, sh, render);
403                 }
404             break;
405 
406             default:
407                 return;
408         }   
409     }
410 
411     @Event!Draw
412     void __debug_drawMask(IRenderer render) @safe
413     {
414         __drawShapeConture(position, mask, render);
415     }
416 }