1 module artemisd.entitysystem;
2 
3 import std.bitmanip;
4 import std.algorithm;
5 import core.bitop;
6 import artemisd.utils.bag;
7 import artemisd.aspect;
8 import artemisd.entityobserver;
9 import artemisd.world;
10 import artemisd.entity;
11 import artemisd.utils.ext;
12 import artemisd.utils.type;
13 
14 abstract class EntitySystem : EntityObserver 
15 {
16     mixin TypeDecl;
17 
18     private const int systemIndex;
19 
20     protected World world;
21 
22     private Bag!Entity actives;
23 
24     private Aspect aspect;
25 
26     private BitArray allSet;
27     private BitArray exclusionSet;
28     private BitArray oneSet;
29 
30     private bool passive;
31 
32     private bool dummy;
33 
34     this(Aspect aspect) 
35     {
36         actives = new Bag!Entity();
37         this.aspect = aspect;
38         allSet = aspect.getAllSet();
39         exclusionSet = aspect.getExclusionSet();
40         oneSet = aspect.getOneSet();
41         systemIndex = GetTypeId();
42         dummy = allSet.isEmpty() && oneSet.isEmpty(); // This system can't possibly be interested in any entity, so it must be "dummy"
43     }
44     
45     protected void begin() {}
46 
47     final void process() 
48     {
49         if(checkProcessing()) 
50         {
51             begin();
52             processEntities(actives);
53             end();
54         }
55     }
56     
57     protected void end() {}
58     
59     protected void processEntities(Bag!Entity entities);
60     protected bool checkProcessing();
61 
62     void initialize() {};
63     protected void inserted(Entity e) {};
64     protected void removed(Entity e) {};
65 
66     protected final void check(Entity e) 
67     {
68         if(dummy) return;
69 
70         bool contains = e.getSystemBits()[systemIndex];
71         bool interested = true; // possibly interested, let's try to prove it wrong.
72         
73         BitArray componentBits = e.getComponentBits();
74 
75         // Check if the entity possesses ALL of the components defined in the aspect.
76         if(!allSet.isEmpty()) 
77         {
78             for (auto i = allSet.nextSetBit(0); i >= 0; i = allSet.nextSetBit(i+1)) 
79             {
80                 if(!componentBits[i]) 
81                 {
82                     interested = false;
83                     break;
84                 }
85             }
86         }
87         
88         // Check if the entity possesses ANY of the exclusion components, if it does then the system is not interested.
89         if(!exclusionSet.isEmpty() && interested) 
90         {
91             interested = !exclusionSet.intersects(componentBits);
92         }
93         
94         // Check if the entity possesses ANY of the components in the oneSet. If so, the system is interested.
95         if(!oneSet.isEmpty()) 
96         {
97             interested = oneSet.intersects(componentBits);
98         }
99 
100         if (interested && !contains) 
101         {
102             insertToSystem(e);
103         } 
104         else if (!interested && contains) 
105         {
106             removeFromSystem(e);
107         }
108     }
109 
110     private final void removeFromSystem(Entity e) 
111     {
112         actives.remove(e);
113         e.getSystemBits()[systemIndex]=0;
114         removed(e);
115     }
116 
117     private final void insertToSystem(Entity e) 
118     {
119         actives.add(e);
120         e.getSystemBits()[systemIndex] = 1;
121         inserted(e);
122     }
123     
124     final override void added(Entity e) 
125     {
126         check(e);
127     }
128     
129     final override void changed(Entity e) 
130     {
131         check(e);
132     }
133 
134     final override void deleted(Entity e) 
135     {
136         if(e.getSystemBits()[systemIndex]) 
137         {
138             removeFromSystem(e);
139         }
140     }
141     
142     final override void disabled(Entity e) 
143     {
144         if(e.getSystemBits()[systemIndex]) 
145         {
146             removeFromSystem(e);
147         }
148     }
149     
150     final override void enabled(Entity e) 
151     {
152         check(e);
153     }
154     
155     final void setWorld(World world) 
156     {
157         this.world = world;
158     }
159     
160     final bool isPassive() 
161     {
162         return passive;
163     }
164 
165     final void setPassive(bool passive) 
166     {
167         this.passive = passive;
168     }
169     
170     final Bag!Entity getActives() 
171     {
172         return actives;
173     }
174 }