1 module demos.snake;
2 
3 import app;
4 
5 import bindbc.sdl;
6 
7 import bubel.ecs.attributes;
8 import bubel.ecs.core;
9 import bubel.ecs.entity;
10 import bubel.ecs.manager;
11 import bubel.ecs.std;
12 import bubel.ecs.vector;
13 
14 import cimgui.cimgui;
15 
16 import ecs_utils.gfx.texture;
17 import ecs_utils.math.vector;
18 import ecs_utils.utils;
19 
20 import game_core.basic;
21 
22 import gui.attributes;
23 //import std.array : staticArray;
24 
25 enum float px = 1.0/512.0;
26 
27 extern(C):
28 
29 //Map is simple grid. Every cell has type and id to entity.
30 struct MapElement
31 {
32     enum Type
33     {
34         empty = 0,
35         apple = 1,
36         wall = 2,
37         snake = 3,
38     }
39     Type type;
40     EntityID id;
41 }
42 
43 //snake part is corresponding to graphical representation of snake
44 enum SnakePart : ubyte
45 {
46     head_up = 0,
47     head_down = 1,
48     head_left = 2,
49     head_right = 3,
50     tail_up = 4,
51     tail_down = 5,
52     tail_left = 6,
53     tail_right = 7,
54     turn_ld = 8,
55     turn_lu = 9,
56     turn_rd = 10,
57     turn_ru = 11,
58     vertical = 12,
59     horizontal = 13
60 }
61 
62 struct Snake
63 {
64     __gshared const (char)* tips = "Use \"WASD\" keys to move. If you loose you can always spawn new snake... or several snakes.
65 This demo is an example that in ECS you can make very \"non-ECS\" game";
66 
67     EntityTemplate* apple_tmpl;
68     EntityTemplate* snake_tmpl;
69     EntityTemplate* snake_destroy_particle;
70     Texture texture;
71 
72     vec4[] snake_destroy_particle_frames;
73     vec4[] smoke_frames;
74 
75 
76     bool move_system = true;
77     bool draw_system = true;
78 
79     enum int map_size = 18;
80 
81     MapElement[map_size * map_size] map;
82 
83     ~this() @nogc nothrow
84     {
85         if(snake_destroy_particle_frames)Mallocator.dispose(snake_destroy_particle_frames);
86         if(smoke_frames)Mallocator.dispose(smoke_frames);
87         if(apple_tmpl)gEntityManager.freeTemplate(apple_tmpl);
88         if(snake_tmpl)gEntityManager.freeTemplate(snake_tmpl);
89         if(snake_destroy_particle)gEntityManager.freeTemplate(snake_destroy_particle);
90         texture.destroy();
91     }
92 
93     MapElement element(ivec2 pos)
94     {
95         uint index = pos.x + pos.y * map_size;
96         if(index >= map.length)index = map.length - 1;
97         return map[index];
98     }
99 
100     void element(MapElement el, ivec2 pos)
101     {
102         uint index = pos.x + pos.y * map_size;
103         if(index >= map.length)index = map.length - 1;
104         map[index] = el;
105     }
106 
107     void addApple()
108     {
109         ivec2 random_pos = ivec2(rand()%map_size,rand()%map_size);
110         ivec2 base_pos = random_pos;
111         while(element(random_pos).type != MapElement.Type.empty)
112         {
113             random_pos.x += 1;
114             if(random_pos.x > map_size)
115             {
116                 random_pos.x = 0;
117                 random_pos.y += 1;
118                 if(random_pos.y > map_size)random_pos.y = 0;
119             }
120             if(base_pos.x == random_pos.x && base_pos.y == random_pos.y)return;
121         }
122         gEntityManager.addEntity(apple_tmpl,[CLocation(cast(vec2)(random_pos)*16).ref_].staticArray);
123     }
124 }
125 
126 struct Animation
127 {
128 
129 }
130 
131 //component has array of frames (texture coordinates) and time used for selection frames
132 struct CAnimation
133 {
134     mixin ECS.Component;
135     
136     vec4[] frames;
137     @GUIRangeF(0,float.max)float time = 0;
138 }
139 
140 //CIlocation is integer location used as grid cell coordination
141 struct CILocation
142 {
143     mixin ECS.Component;
144 
145     alias location this;
146 
147     ivec2 location;
148 }
149 
150 // struct CLocation
151 // {
152 //     mixin ECS.Component;
153 
154 //     alias location this;
155 
156 //     vec2 location = vec2(0,0);
157 // }
158 
159 struct CSnake
160 {
161     void onCreate()
162     {
163         parts.array = Mallocator.makeArray!ivec2(100);
164     }
165 
166     void onDestroy()
167     {
168         Mallocator.dispose(parts.array);
169     }
170 
171     mixin ECS.Component;
172 
173 
174     struct Parts
175     {
176         uint length = 0;
177         ivec2[] array;
178 
179         ivec2 opIndex(size_t ind) const
180         {
181             return array[ind];
182         }
183 
184         ivec2[] opSlice() 
185         {
186             return array[0 .. length];
187         }
188 
189         void opIndexAssign(ivec2 vec, size_t ind) 
190         {
191             array[ind] = vec;
192         }
193 
194         size_t opDollar() const 
195         {
196             return length;
197         }
198 
199         void add(ivec2 v)
200         {
201             length++;
202             array[length-1] = v;
203         }
204     }
205 
206     Parts parts;
207     @GUIRange(0,3)CMovement.Direction direction;
208 }
209 
210 //flag for apple
211 struct CApple
212 {
213     mixin ECS.Component;
214 }
215 
216 //particle is removed when life drops below 0
217 struct CParticle
218 {
219     mixin ECS.Component;
220 
221     float life = 0;
222 }
223 
224 //vector for particle movement
225 struct CParticleVector
226 {
227     mixin ECS.Component;
228 
229     vec2 velocity = vec2(0,0);
230 }
231 
232 //contains current movement direction for snake
233 struct CMovement
234 {
235     mixin ECS.Component;
236 
237     enum Direction : byte
238     {
239         up,
240         down,
241         left,
242         right
243     }   
244 
245     @GUIRange(0,3)Direction direction;
246 }
247 
248 // struct CInput
249 // {
250 //     mixin ECS.Component;
251 // }
252 
253 //this system has no onUpdate and only processing events. It responsible for adding and removing applce from grid.
254 struct AppleSystem
255 {
256     mixin ECS.System!1;
257 
258     struct EntitiesData
259     {
260         uint length;
261         @readonly Entity[] entity;
262         @readonly CApple[] apple;
263         @readonly CILocation[] location;
264     }
265 
266     //called when entity was created
267     void onAddEntity(EntitiesData data)
268     {
269         foreach(i;0..data.length)
270         {
271             if(snake.element(data.location[i]).id == EntityID())snake.element(MapElement(MapElement.Type.apple,data.entity[i].id),data.location[i]);
272             else gEntityManager.removeEntity(data.entity[i].id);
273         }
274     }
275 
276     //called when entity was removed
277     void onRemoveEntity(EntitiesData data)
278     {
279         foreach(i;0..data.length)
280         {
281             if(snake.element(data.location[i].location).id == data.entity[i].id)
282                 snake.element(MapElement(MapElement.Type.empty, EntityID()),data.location[i].location);
283         }
284     }
285 }
286 
287 //system is responsible for killing particles when their life drops below 0
288 struct ParticleSystem
289 {
290     mixin ECS.System!1;
291 
292     struct EntitiesData
293     {
294         uint length;
295         @readonly Entity[] entities;
296         CParticle[] particle;
297     }
298 
299     void onUpdate(EntitiesData data)
300     {
301         foreach(i;0..data.length)
302         {
303             data.particle[i].life -= launcher.deltaTime;
304             if(data.particle[i].life < 0)gEntityManager.removeEntity(data.entities[i].id);
305         }
306     }
307 }
308 
309 //system responsible for moving particles
310 struct ParticleMovementSystem
311 {
312     mixin ECS.System!1;
313 
314     struct EntitiesData
315     {
316         uint length;
317         @readonly Entity[] entities;
318         @readonly CParticleVector[] movement;
319         CLocation[] location;
320     }
321 
322     void onUpdate(EntitiesData data)
323     {
324         foreach(i;0..data.length)
325         {
326             data.location[i] -= data.movement[i].velocity;
327         }
328     }
329 }
330 
331 
332 struct AnimationSystem
333 {
334     mixin ECS.System!1;
335 
336     struct EntitiesData
337     {
338         uint length;
339         CAnimation[] animation;
340     }
341 
342     void onUpdate(EntitiesData data)
343     {
344         foreach(i;0..data.length)
345         {
346             data.animation[i].time += launcher.deltaTime * 0.01;
347             while(data.animation[i].time >= data.animation[i].frames.length)data.animation[i].time -= cast(float)data.animation[i].frames.length;
348         }
349     }
350 }
351 
352 
353 struct AnimationRenderSystem
354 {
355     mixin ECS.System!1;
356 
357     struct EntitiesData
358     {
359         uint length;
360         @readonly CAnimation[] animation;
361         @readonly CLocation[] location;
362     }
363 
364     void onUpdate(EntitiesData data)
365     {
366         import ecs_utils.gfx.renderer;
367         Renderer.DrawData draw_data;
368         draw_data.size = vec2(16,16);
369         //draw_data.coords = vec4(0,0,1,1)*px;
370         draw_data.color = 0x80808080;
371         draw_data.material_id = 0;
372         draw_data.thread_id = 0;
373         draw_data.texture = snake.texture;
374         draw_data.depth = -1;
375         foreach(i;0..data.length)
376         {
377             uint frame = cast(uint)(data.animation[i].time);
378             if(frame >= data.animation[i].frames.length)frame = cast(uint)data.animation[i].frames.length - 1;
379             draw_data.position = data.location[i];
380             draw_data.coords = data.animation[i].frames[frame];
381             launcher.renderer.draw(draw_data);
382         }
383     }
384 }
385 
386 struct MoveSystem
387 {
388     mixin ECS.System!64;
389 
390     EntityTemplate* destroy_template;
391 
392     struct EntitiesData
393     {
394         uint length;
395         @readonly Entity[] entities;
396         @readonly CMovement[] movement;
397         @optional CSnake[] snakes;
398         CILocation[] location;
399     }
400 
401     void setTemplates()
402     {
403         //template is used for adding particles when snake will collide with himself
404         destroy_template = snake.snake_destroy_particle;
405     }
406 
407     void moveLocation(ref CILocation location, CMovement.Direction direction)
408     {
409         final switch(direction)
410         {
411             case CMovement.Direction.down:
412                 location.y -= 1;
413                 if(location.y < 0)location.y = snake.map_size - 1;
414                 break;
415             case CMovement.Direction.up:
416                 location.y += 1;
417                 if(location.y >= snake.map_size)location.y = 0;
418                 break;
419             case CMovement.Direction.left:
420                 location.x -= 1;
421                 if(location.x < 0)location.x = snake.map_size - 1;
422                 break;
423             case CMovement.Direction.right:
424                 location.x += 1;
425                 if(location.x >= snake.map_size)location.x = 0;
426                 break;
427         }
428     }
429 
430     void moveSnake(ref CSnake snake, ivec2 location)
431     {
432         if(snake.parts.length)
433         {
434             .snake.element(MapElement(),snake.parts[0]);
435             foreach(j; 0 .. snake.parts.length - 1)
436             {
437                 snake.parts[j] = snake.parts[j + 1];
438             }
439             snake.parts[$-1] = location;
440         }
441         else .snake.element(MapElement(),location);
442     }
443 
444     void onUpdate(EntitiesData data)
445     {
446         if(data.snakes)
447         {
448             foreach(i; 0..data.length)
449             {
450                 data.snakes[i].direction = data.movement[i].direction;
451                 ivec2 new_location = data.location[i];
452                 moveLocation(data.location[i], data.movement[i].direction);
453                 final switch(snake.element(data.location[i].location).type)
454                 {
455                     case MapElement.Type.snake:
456                         foreach(loc; data.snakes[i].parts)
457                         {
458                             //destroy_location.x = loc.x * 16;
459                             //destroy_location.y = loc.y * 16;
460                             snake.element(MapElement(MapElement.Type.empty, EntityID()),loc);
461                             gEntityManager.addEntity(snake.snake_destroy_particle,[CLocation(cast(vec2)(loc * 16)).ref_].staticArray);
462 
463                             CLocation destroy_location;
464                             foreach(j;0..10)
465                             {
466                                 destroy_location.x = loc.x * 16 + randomf() * 8 - 4;
467                                 destroy_location.y = loc.y * 16 + randomf() * 8 - 4;
468                                 //destroy_vector.velocity = vec2(randomf(),randomf())*0.4-0.2;
469                                 snake.element(MapElement(MapElement.Type.empty, EntityID()),loc);
470                                 gEntityManager.addEntity(snake.snake_destroy_particle, [destroy_location.ref_, CParticleVector(vec2(randomf(),randomf())*0.4-0.2).ref_].staticArray);
471                             }
472                             
473                         }
474                         //destroy_location.x = new_location.x * 16;
475                         //destroy_location.y = new_location.y * 16;
476                         snake.element(MapElement(MapElement.Type.empty, EntityID()),new_location);
477                         gEntityManager.addEntity(snake.snake_destroy_particle,[CLocation(cast(vec2)(new_location * 16)).ref_].staticArray);
478                         gEntityManager.removeEntity(data.entities[i].id);
479                         break;
480 
481                     case MapElement.Type.wall:break;
482 
483                     case MapElement.Type.empty:
484                         moveSnake(data.snakes[i], new_location);
485                         snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.location[i].location);
486                         if(data.snakes[i].parts.length > 1)
487                         {
488                             snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[$-1]);
489                             snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[0]);
490                         }
491                         else if(data.snakes[i].parts.length == 1)
492                         {
493                             snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[0]);
494                         }
495                         break;
496                     case MapElement.Type.apple:
497                         gEntityManager.removeEntity(snake.element(data.location[i].location).id);
498                         if(data.snakes[i].parts.length >= 99)
499                         {
500                             snake.addApple();
501                             goto case(MapElement.Type.empty);
502                         }
503                         data.snakes[i].parts.add(new_location);
504 
505                         if(data.snakes[i].parts.length > 1)
506                         {
507                             snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.snakes[i].parts[$-1]);
508                         }
509                         else if(data.snakes[i].parts.length == 1)
510                         {
511                             snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),new_location);
512                         }
513                         snake.element(MapElement(MapElement.Type.snake, data.entities[i].id),data.location[i].location);
514                         snake.addApple();
515                         break;
516                 }
517             }
518         }
519         else
520         {
521             foreach(i; 0..data.length)
522             {
523                 final switch(data.movement[i].direction)
524                 {
525                     case CMovement.Direction.down:data.location[i].y -= 1;break;
526                     case CMovement.Direction.up:data.location[i].y += 1;break;
527                     case CMovement.Direction.left:data.location[i].x -= 1;break;
528                     case CMovement.Direction.right:data.location[i].x += 1;break;
529                 }
530             }
531         }
532     }
533 }
534 
535 struct SnakeSystem
536 {
537     mixin ECS.System!1;
538 
539     struct EntitiesData
540     {
541         uint length;
542         Entity[] entity;
543         @readonly CSnake[] snake;
544         @readonly CILocation[] location;
545     }
546 
547     void onAddSystem(EntitiesData data)
548     {
549         foreach(i;0..data.length)
550         {
551             if(snake.element(data.location[i]).id == EntityID())snake.element(MapElement(MapElement.Type.snake,data.entity[i].id),data.location[i]);
552             else gEntityManager.removeEntity(data.entity[i].id);
553         }
554     }
555 
556     void onRemoveEntity(EntitiesData data)
557     {
558         foreach(i;0..data.length)
559         {
560             if(snake.element(data.location[i].location).id == data.entity[i].id)
561                 snake.element(MapElement(MapElement.Type.empty, EntityID()),data.location[i].location);
562             foreach(part; data.snake[i].parts.array)
563                 if(snake.element(part).id == data.entity[i].id)
564                     snake.element(MapElement(MapElement.Type.empty, EntityID()),part);
565         }
566     }
567 }
568 
569 struct InputSystem
570 {
571     mixin ECS.System!64;
572 
573     struct EntitiesData
574     {
575         uint length;
576         CMovement[] movement;
577         @readonly CInput[] input;
578     }
579 
580     void onUpdate(EntitiesData data)
581     {
582         foreach(i; 0..data.length)
583         {
584             if(launcher.getKeyState(SDL_SCANCODE_W))
585             {
586                 data.movement[i].direction = CMovement.Direction.up;
587             }
588             else if(launcher.getKeyState(SDL_SCANCODE_S))
589             {
590                 data.movement[i].direction = CMovement.Direction.down;
591             }
592             else if(launcher.getKeyState(SDL_SCANCODE_A))
593             {
594                 data.movement[i].direction = CMovement.Direction.left;
595             }
596             else if(launcher.getKeyState(SDL_SCANCODE_D))
597             {
598                 data.movement[i].direction = CMovement.Direction.right;
599             }
600         }
601     }
602 }
603 
604 struct FixSnakeDirectionSystem
605 {
606     mixin ECS.System!64;
607 
608     struct EntitiesData
609     {
610         uint length;
611         CMovement[] movement;
612         @readonly CILocation[] location;
613         const (CSnake)[] snake;
614     }
615 
616     void onUpdate(EntitiesData data)
617     {
618         foreach(i; 0..data.length)
619         {
620             ivec2 last_location;
621             if(data.snake[i].parts.length)last_location = data.snake[i].parts[$ - 1];
622             else continue;
623             ivec2 next_location = data.location[i];
624             
625             final switch(data.movement[i].direction)
626             {
627                 case CMovement.Direction.up:
628                     next_location.y += 1;
629                     if(next_location.y >= snake.map_size)next_location.y = 0;
630                     if(next_location.x == last_location.x && next_location.y == last_location.y)
631                     {
632                         data.movement[i].direction = CMovement.Direction.down;
633                     }
634                     break;
635                 case CMovement.Direction.down:
636                     next_location.y -= 1;
637                     if(next_location.y < 0)next_location.y = snake.map_size - 1;
638                     if(next_location.x == last_location.x && next_location.y == last_location.y)
639                     {
640                         data.movement[i].direction = CMovement.Direction.up;
641                     }
642                     break;
643                 case CMovement.Direction.left:
644                     next_location.x -= 1;
645                     if(next_location.x < 0)next_location.x = snake.map_size - 1;
646                     if(next_location.x == last_location.x && next_location.y == last_location.y)
647                     {
648                         data.movement[i].direction = CMovement.Direction.right;
649                     }
650                     break;
651                 case CMovement.Direction.right:
652                     next_location.x += 1;
653                     if(next_location.x >= snake.map_size)next_location.x = 0;
654                     if(next_location.x == last_location.x && next_location.y == last_location.y)
655                     {
656                         data.movement[i].direction = CMovement.Direction.left;
657                     }
658                     break;
659             }
660         }
661     }
662 }
663 
664 struct DrawAppleSystem
665 {
666     mixin ECS.System!1;
667 
668     struct EntitiesData
669     {
670         uint length;
671         @readonly CILocation[] location;
672         const (CApple)[] apple;
673     }
674 
675     void onUpdate(EntitiesData data)
676     {
677         import ecs_utils.gfx.renderer;
678         Renderer.DrawData draw_data;
679         draw_data.size = vec2(16,16);
680         draw_data.coords = vec4(0,32*px,16*px,16*px);
681         draw_data.color = 0x80808080;
682         draw_data.material_id = 0;
683         draw_data.thread_id = 0;
684         draw_data.texture = snake.texture;
685         foreach(i; 0..data.location.length)
686         {
687             draw_data.position = vec2(data.location[i].x*16,data.location[i].y*16);
688             launcher.renderer.draw(draw_data);
689             //launcher.renderer.draw(snake.texture, vec2(data.location[i].x*16,data.location[i].y*16), vec2(16,16), vec4(0,32*px,16*px,16*px), 0, 0x80808080, 0);
690         }
691     }
692 }
693 
694 struct DrawSnakeSystem
695 {
696     mixin ECS.System!1;
697 
698     struct EntitiesData
699     {
700         uint length;
701         @readonly CILocation[] location;
702         const (CSnake)[] snake;
703     }
704 
705     static CMovement.Direction getDirection(ivec2 p1, ivec2 p2)
706     {
707         if(p1.x - p2.x == -1)return CMovement.direction.right;
708         else if(p1.x - p2.x == 1)return CMovement.direction.left;
709         else if(p1.y - p2.y == -1)return CMovement.direction.up;
710         else if(p1.y - p2.y == 1)return CMovement.direction.down;
711         else if(p1.x - p2.x > 1)return CMovement.direction.right;
712         else if(p1.x - p2.x < -1)return CMovement.direction.left;
713         else if(p1.y - p2.y > 1)return CMovement.direction.up;
714         else return CMovement.direction.down;
715     }
716 
717     static SnakePart snakePart(ivec2 p1, ivec2 p2, ivec2 p3)
718     {
719         CMovement.Direction direction = getDirection(p1, p2);
720         CMovement.Direction direction2 = getDirection(p1, p3);
721         uint case_ = direction*4 + direction2;
722         final switch(case_)
723         {
724             case 0:return SnakePart.horizontal;
725             case 1:return SnakePart.horizontal;
726             case 2:return SnakePart.turn_lu;
727             case 3:return SnakePart.turn_ru;
728             case 4:return SnakePart.horizontal;
729             case 5:return SnakePart.horizontal;
730             case 6:return SnakePart.turn_ld;
731             case 7:return SnakePart.turn_rd;
732             case 8:return SnakePart.turn_lu;
733             case 9:return SnakePart.turn_ld;
734             case 10:return SnakePart.vertical;
735             case 11:return SnakePart.vertical;
736             case 12:return SnakePart.turn_ru;
737             case 13:return SnakePart.turn_rd;
738             case 14:return SnakePart.vertical;
739             case 15:return SnakePart.vertical;
740         }
741     }
742 
743     static SnakePart snakeTail(ivec2 p1, ivec2 p2)
744     {
745         CMovement.Direction direction = getDirection(p1, p2);
746         final switch(direction)
747         {
748             case CMovement.Direction.up:return SnakePart.tail_up;
749             case CMovement.Direction.down:return SnakePart.tail_down;
750             case CMovement.Direction.left:return SnakePart.tail_left;
751             case CMovement.Direction.right:return SnakePart.tail_right;
752         }
753     }
754 
755     static void drawElement(ivec2 loc, SnakePart part)
756     {
757         import ecs_utils.gfx.renderer;
758         Renderer.DrawData draw_data;
759         draw_data.size = vec2(16,16);
760         draw_data.color = 0x80808080;
761         draw_data.texture = snake.texture;
762         draw_data.position = cast(vec2)loc;
763         final switch(cast(ubyte)part)
764         {
765             case SnakePart.tail_up:draw_data.coords = vec4(16,112,16,16)*px;break;
766             case SnakePart.tail_down:draw_data.coords = vec4(0,112,16,16)*px;break;
767             case SnakePart.tail_left:draw_data.coords = vec4(32,112,16,16)*px;break;
768             case SnakePart.tail_right:draw_data.coords = vec4(0,144,16,16)*px;break;
769             case SnakePart.turn_ld:draw_data.coords = vec4(64,128,16,16)*px;break;
770             case SnakePart.turn_lu:draw_data.coords = vec4(32,144,16,16)*px;break;
771             case SnakePart.turn_rd:draw_data.coords = vec4(16,144,16,16)*px;break;
772             case SnakePart.turn_ru:draw_data.coords = vec4(64,112,16,16)*px;break;
773             case SnakePart.vertical:draw_data.coords = vec4(16,128,16,16)*px;break;
774             case SnakePart.horizontal:draw_data.coords = vec4(48,128,16,16)*px;break;
775         }
776         launcher.renderer.draw(draw_data);
777     }
778 
779     void onUpdate(EntitiesData data)
780     {
781         import ecs_utils.gfx.renderer;
782         Renderer.DrawData draw_data;
783         draw_data.size = vec2(16,16);
784         draw_data.color = 0x80808080;
785         draw_data.texture = snake.texture;
786         
787         foreach(i; 0..data.length)
788         {
789             const (CSnake)* snake = &data.snake[i];
790             scope vec2 loc = cast(vec2)(data.location[i].location * 16);
791             draw_data.position = loc;
792             final switch(snake.direction)
793             {
794                 case CMovement.Direction.up:draw_data.coords = vec4(48,112,16,16)*px;break;
795                 case CMovement.Direction.down:draw_data.coords = vec4(48,144,16,16)*px;break;
796                 case CMovement.Direction.left:draw_data.coords = vec4(0,128,16,16)*px;break;
797                 case CMovement.Direction.right:draw_data.coords = vec4(32,128,16,16)*px;break;
798             }
799             launcher.renderer.draw(draw_data);
800             if(snake.parts.length >1)
801             {
802                 foreach(j;1..snake.parts.length - 1)drawElement(snake.parts[j]*16, snakePart(snake.parts[j], snake.parts[j+1], snake.parts[j-1]));
803                 drawElement(snake.parts[$-1]*16, snakePart(snake.parts[$-1], data.location[i], snake.parts[$-2]));
804                 drawElement(snake.parts[0]*16, snakeTail(snake.parts[1], snake.parts[0]));
805             }
806             else if(snake.parts.length == 1)
807             {
808                 drawElement(snake.parts[0]*16, snakeTail(data.location[i], snake.parts[0]));
809             }
810             
811         }
812     }
813 }
814 
815 struct CleanSystem
816 {
817     mixin ECS.System!64;
818 
819     struct EntitiesData
820     {
821         uint length;
822         Entity[] entities;
823     }
824 
825     void onUpdate(EntitiesData data)
826     {
827         foreach(i; 0..data.length)
828         {
829             gEntityManager.removeEntity(data.entities[i].id);
830         }
831     }
832 }
833 
834 struct CopyLocationSystem
835 {
836     mixin ECS.System!32;
837 
838     struct EntitiesData
839     {
840         uint length;
841         const (Entity)[] entity;
842         CLocation[] location;
843         @readonly CILocation[] ilocation;
844     }
845 
846     void onAddEntity(EntitiesData data)
847     {
848         foreach(i;0..data.length)
849         {
850             data.ilocation[i] = cast(ivec2)(data.location[i] / 16);
851         }
852     }
853 
854     void onUpdate(EntitiesData data)
855     {
856         foreach(i;0..data.length)
857         {
858             data.location[i] = cast(vec2)(data.ilocation[i] * 16);
859         }
860     }
861 }
862 
863 __gshared Snake* snake;
864 
865 void snakeRegister()
866 {
867     import game_core.rendering;
868 
869     snake = Mallocator.make!Snake;
870 
871     snake.texture.create();
872     snake.texture.load("assets/textures/atlas.png");
873 
874     gEntityManager.beginRegister();
875 
876     gEntityManager.registerPass("fixed");
877 
878     registerRenderingModule(gEntityManager);
879 
880     gEntityManager.registerComponent!CLocation;
881     gEntityManager.registerComponent!CILocation;
882     gEntityManager.registerComponent!CSnake;
883     gEntityManager.registerComponent!CApple;
884     gEntityManager.registerComponent!CParticle;
885     gEntityManager.registerComponent!CParticleVector;
886     gEntityManager.registerComponent!CMovement;
887     gEntityManager.registerComponent!CInput;
888     gEntityManager.registerComponent!CAnimation;
889 
890     gEntityManager.registerSystem!MoveSystem(0,"fixed");
891     gEntityManager.registerSystem!InputSystem(-100);
892     gEntityManager.registerSystem!FixSnakeDirectionSystem(-1,"fixed");
893     gEntityManager.registerSystem!AnimationRenderSystem(100);
894     gEntityManager.registerSystem!AnimationSystem(-1);
895     gEntityManager.registerSystem!ParticleSystem(-1);
896     gEntityManager.registerSystem!ParticleMovementSystem(-1);
897     gEntityManager.registerSystem!DrawAppleSystem(99);
898     gEntityManager.registerSystem!DrawSnakeSystem(101);
899 
900     gEntityManager.registerSystem!CopyLocationSystem(100);
901     //gEntityManager.registerSystem!AppleRemoveSystem(100);
902     gEntityManager.registerSystem!AppleSystem(101);
903     gEntityManager.registerSystem!SnakeSystem(101);
904 
905     gEntityManager.endRegister();
906 }
907 
908 void snakeStart()
909 {
910     launcher.gui_manager.addComponent(CApple(),"Apple");
911     launcher.gui_manager.addComponent(CSnake(),"Snake");
912     launcher.gui_manager.addComponent(CParticle(1000),"Particle");
913     launcher.gui_manager.addComponent(CParticleVector(vec2(0,1)),"Particle Vector");
914     launcher.gui_manager.addComponent(CInput(),"Input");
915     launcher.gui_manager.addComponent(CMovement(CMovement.Direction.up),"Movement");
916     launcher.gui_manager.addComponent(CAnimation(),"Animation");
917     launcher.gui_manager.addComponent(CILocation(),"Int Location");
918     launcher.gui_manager.addComponent(CLocation(),"Location");
919 
920     launcher.gui_manager.addSystem(becsID!MoveSystem,"Move System");
921     launcher.gui_manager.addSystem(becsID!InputSystem,"Input System");
922     launcher.gui_manager.addSystem(becsID!FixSnakeDirectionSystem,"Fix Direction System");
923     launcher.gui_manager.addSystem(becsID!AnimationRenderSystem,"Animation Render System");
924     launcher.gui_manager.addSystem(becsID!AnimationSystem,"Animation System");
925     launcher.gui_manager.addSystem(becsID!ParticleSystem,"Particle Life System");
926     launcher.gui_manager.addSystem(becsID!ParticleMovementSystem,"Particle Movement System");
927     launcher.gui_manager.addSystem(becsID!DrawAppleSystem,"Draw Apple System");
928     launcher.gui_manager.addSystem(becsID!DrawSnakeSystem,"Draw Snake System");
929     launcher.gui_manager.addSystem(becsID!CopyLocationSystem,"Copy Location System");
930     //launcher.gui_manager.addSystem(becsID!AppleSystem,"Apple System");
931     //launcher.gui_manager.addSystem(becsID!SnakeSystem,"Snake System");
932 
933     snake.snake_destroy_particle_frames = Mallocator.makeArray([vec4(64,144,16,16)*px,vec4(80,144,16,16)*px,vec4(96,144,16,16)*px,vec4(112,144,16,16)*px].staticArray);
934 
935     {
936         ushort[5] components = [becsID!CILocation, becsID!CSnake, becsID!CMovement, becsID!CInput, becsID!CLocation];
937         snake.snake_tmpl = gEntityManager.allocateTemplate(components);
938         gEntityManager.addEntity(snake.snake_tmpl,[CILocation(ivec2(2,2)).ref_].staticArray);
939     }
940 
941     {
942         snake.snake_destroy_particle = gEntityManager.allocateTemplate([becsID!CLocation, becsID!CParticle, becsID!CParticleVector, becsID!CAnimation, becsID!CLocation].staticArray);
943         CAnimation* canim = snake.snake_destroy_particle.getComponent!CAnimation;
944         canim.frames = snake.snake_destroy_particle_frames;
945         CParticle* particle = snake.snake_destroy_particle.getComponent!CParticle;
946         particle.life = 400;
947     }
948 
949     {
950         ushort[3] components = [becsID!CILocation, becsID!CApple, becsID!CLocation];
951         snake.apple_tmpl = gEntityManager.allocateTemplate(components);
952         snake.addApple();
953     }
954     
955     launcher.gui_manager.addTemplate(gEntityManager.allocateTemplate(snake.snake_tmpl), "Snake");
956     launcher.gui_manager.addTemplate(gEntityManager.allocateTemplate(snake.apple_tmpl), "Apple");
957     launcher.gui_manager.addTemplate(gEntityManager.allocateTemplate(snake.snake_destroy_particle), "Particle");
958 
959     MoveSystem* move_system = gEntityManager.getSystem!MoveSystem();
960     move_system.setTemplates();
961 }
962 
963 void snakeEnd()
964 {
965     //gEntityManager.freeTemplate(simple.tmpl);
966     Mallocator.dispose(snake);
967 }
968 
969 void snakeEvent(SDL_Event* event)
970 {
971 
972 }
973 
974 bool snakeLoop()
975 {
976     launcher.render_position = (vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling - vec2(288,288)) * 0.5;
977 
978     // if(launcher.show_demo_wnd)
979     // {
980     //     igSetNextWindowPos(ImVec2(800 - 260, 30), ImGuiCond_Once, ImVec2(0,0));
981     //     igSetNextWindowSize(ImVec2(250, 0), ImGuiCond_Once);
982     //     if(igBegin("Snake",&launcher.show_demo_wnd,0))
983     //     {
984             
985     //     }
986     //     igEnd();
987     // }
988 
989     gEntityManager.begin();
990 
991     float delta_time = launcher.deltaTime;
992     if(delta_time > 2000)delta_time = 2000;
993     __gshared float time = 0;
994 
995     if(launcher.getKeyState(SDL_SCANCODE_SPACE))time += delta_time * 3;
996     else time += delta_time;
997     
998     while(time > 200)
999     {
1000         time -= 200;
1001         
1002         gEntityManager.update("fixed");
1003     }
1004 
1005     gEntityManager.update();
1006 
1007     gEntityManager.end();
1008 
1009     return true;
1010 }
1011 
1012 DemoCallbacks getSnakeDemo()
1013 {
1014     DemoCallbacks demo;
1015     demo.register = &snakeRegister;
1016     demo.initialize = &snakeStart;
1017     demo.deinitialize = &snakeEnd;
1018     demo.loop = &snakeLoop;
1019     demo.tips = snake.tips;
1020     return demo;
1021 }