1 /** 2 * Port of Ash Entity system into D 3 * 4 */ 5 6 module ashd.core.entity; 7 8 import std.conv : to; 9 import std.signals; // for Signal template. 10 11 import ashd.core.component : Component; 12 13 /** 14 * An entity is composed of components. As such, it is essentially a collection object for components. 15 * Sometimes, the entities in a game will mirror the actual characters and objects in the game, but this 16 * is not necessary. 17 * 18 * <p>Components are simple value objects that contain data relevant to the entity. Entities 19 * with similar functionality will have instances of the same components. So we might have 20 * a position component</p> 21 * 22 * <p><code>public class PositionComponent 23 * { 24 * public var x : Number; 25 * public var y : Number; 26 * }</code></p> 27 * 28 * <p>All entities that have a position in the game world, will have an instance of the 29 * position component. Systems operate on entities based on the components they have.</p> 30 */ 31 public class Entity 32 { 33 /** 34 * The constructor 35 * 36 * Params: 37 * name = The name for the entity. If left blank, a default name is assigned 38 * with the form _entityN where N is an integer. 39 */ 40 public this( string name = string.init ) nothrow @safe 41 { 42 if ( name != string.init ) 43 mName = name; 44 else 45 mName = "_entity"~to!string(++mNameCount); 46 } 47 48 49 @property 50 { 51 Entity next() nothrow @safe { return mNext; } 52 Entity previous() nothrow @safe { return mPrevious; } 53 void next( Entity entity ) nothrow @safe { mNext = entity; } 54 void previous( Entity entity ) nothrow @safe { mPrevious = entity; } 55 56 /** 57 * All entities have a name. If no name is set, a default name is used. Names are used to 58 * fetch specific entities from the engine, and can also help to identify an entity when debugging. 59 */ 60 public string name() nothrow @safe { return mName; } 61 public void name( string value ) 62 { 63 if ( value == string.init ) 64 throw new Exception("Setting Entity name to empty string"); 65 66 if ( mName != value ) 67 { 68 string previous = mName; 69 mName = value; 70 nameChanged.emit( this, previous ); 71 } 72 } 73 } 74 75 /// This signal is dispatched when a component is added to the entity. 76 mixin Signal!( Entity, ClassInfo ) componentAdded; 77 78 /// This signal is dispatched when a component is removed from the entity. 79 mixin Signal!( Entity, ClassInfo ) componentRemoved; 80 81 /// Dispatched when the name of the entity changes. 82 /// Used internally by the engine to track entities based on their names. 83 mixin Signal!( Entity, string ) nameChanged; 84 85 86 /** 87 * Add a component to the entity. 88 * 89 * If a component of the same type is already attached, it will be removed 90 * and replaced by the supplied component. 91 * 92 * Params: 93 * component_a = The component object to add. 94 * T = the type of component it is to be stored as 95 * 96 * Returns: 97 * A reference to the entity. This enables the chaining of calls to add, to make 98 * creating and configuring entities cleaner. e.g. 99 * 100 * Entity entity = new Entity() 101 * entity.add( new Position( 100, 200 ) ) 102 * entity.add( new Display( new PlayerClip() ) ); 103 */ 104 public Entity add(T)(T component) 105 { 106 if (T.classinfo in mComponents) 107 this.removeCpt(T.classinfo); 108 109 mComponents[T.classinfo] = component; 110 componentAdded.emit(this, T.classinfo); 111 112 return this; 113 } 114 115 public Entity add(Object component, ClassInfo class_info) 116 { 117 118 if (class_info in mComponents) 119 this.removeCpt(class_info); 120 121 mComponents[class_info] = component; 122 componentAdded.emit(this, class_info); 123 124 return this; 125 } 126 127 // Quicker internal removal method 128 private void removeCpt(ClassInfo class_info) 129 { 130 mComponents.remove(class_info); 131 componentRemoved.emit(this, class_info); 132 } 133 134 135 /** 136 * Remove a component from the entity. 137 * 138 * Params: 139 * T = The class of the component to be removed. 140 * 141 * Returns: 142 * the component, or null if the component doesn't exist in the entity 143 */ 144 public Object remove(T)() 145 { 146 T c; 147 148 if ( T.classinfo in mComponents ) 149 { 150 c = to!T(mComponents[T.classinfo]); 151 this.removeCpt( T.classinfo ); 152 return c; 153 } 154 return null; 155 } 156 157 public Object remove( ClassInfo class_a ) 158 { 159 Object c; 160 161 if ( class_a in mComponents ) 162 { 163 c = cast(Object)(mComponents[class_a]); 164 this.removeCpt( class_a ); 165 return c; 166 } 167 return null; 168 } 169 170 171 /** 172 * Get a component from the entity. 173 * 174 * @param componentClass The class of the component requested. 175 * @return The component, or null if none was found. 176 */ 177 public T get(T)() 178 { 179 if ( T.classinfo in mComponents ) 180 { 181 return to!T(mComponents[ T.classinfo ]); 182 } 183 else 184 { 185 return null; 186 } 187 } 188 /** 189 * Get a component from the entity. 190 * 191 * @param componentClass The class of the component requested. 192 * @return The component, or null if none was found. 193 */ 194 public Object get( ClassInfo class_a ) 195 { 196 if ( class_a in mComponents ) 197 return mComponents[ class_a ]; 198 else 199 return null; 200 } 201 202 /** 203 * Get all components from the entity. 204 * 205 * Returns: 206 * An array containing all the components that are on the entity. 207 */ 208 public Object[] getAll() 209 { 210 Object[] componentArray; 211 ulong i; 212 213 componentArray.length = mComponents.length; 214 foreach( component; mComponents ) 215 { 216 componentArray[i++] = component; 217 } 218 return componentArray; 219 } 220 221 /** 222 * Does the entity have a component of a particular type. 223 * 224 * @param componentClass The class of the component sought. 225 * @return true if the entity has a component of the type, false if not. 226 */ 227 public bool has(T)() 228 { 229 return ( T.classinfo in mComponents ) !is null; 230 } 231 232 public bool has( ClassInfo class_a ) 233 { 234 return ( class_a in mComponents ) !is null; 235 } 236 237 private: 238 string mName; // Optional, give the entity a name. 239 // This can help with debugging and with serialising the entity. 240 static int mNameCount; // Used if name not specified 241 Object[ClassInfo] mComponents; // List of components attached to entity 242 243 // Used by the EntityList class to form linked list 244 Entity mPrevious; 245 Entity mNext; 246 247 248 } // class Entity 249 250