1 /**
2  * This module defines the Scene class, TODO
3  *
4  */
5 module dash.core.scene;
6 import dash.core, dash.components, dash.graphics, dash.utility;
7 
8 import std.path;
9 
10 enum SceneName = "[scene]";
11 
12 /**
13  * The Scene contains a list of all objects that should be drawn at a given time.
14  */
15 final class Scene
16 {
17 private:
18     GameObject _root;
19 
20 package:
21     GameObject[uint] objectById;
22     uint[string] idByName;
23 
24 public:
25     /// The camera to render with.
26     Camera camera;
27     Listener listener;
28 	UserInterface ui;
29 
30     /// The root object of the scene.
31     mixin( Getter!_root );
32 
33     this()
34     {
35         _root = new GameObject;
36         _root.name = SceneName;
37         _root.scene = this;
38     }
39 
40     /**
41      * Load all objects inside the specified folder in FilePath.Objects.
42      *
43      * Params:
44      *  objectPath =            The folder location inside of /Objects to look for objects in.
45      */
46     final void loadObjects( string objectPath = "" )
47     {
48         foreach( file; buildNormalizedPath( Resources.Objects, objectPath ).scanDirectory() )
49         {
50             // Create the objects
51             foreach( desc; file.deserializeMultiFile!( GameObject.Description )() )
52             {
53                 auto newObj = GameObject.create( desc );
54                 _root.addChild( newObj );
55                 objectsByResource[ file ] ~= GameObjectResource( file, newObj );
56             }
57         }
58     }
59 
60     /**
61      * Remove all objects from the collection.
62      */
63     final void clear()
64     {
65         _root.shutdown();
66         destroy( _root );
67         _root = new GameObject;
68 
69         if( ui )
70         {
71             ui.shutdown();
72             destroy( ui );
73             // TODO: Can be built automatically by config
74             ui = null;
75         }
76     }
77 
78     /**
79      * Updates all objects in the scene.
80      */
81     final void update()
82     {
83         if( ui )
84             ui.update();
85         _root.update();
86     }
87 
88     /**
89      * Draws all objects in the scene.
90      */
91     final void draw()
92     {
93         _root.draw();
94     }
95 
96     /**
97      * Refreshes all objects in the scene that need refreshing.
98      */
99     final void refresh()
100     {
101         // Iterate over each file, and it's objects
102         foreach_reverse( file; objectsByResource.keys )
103         {
104             auto objReses = objectsByResource[ file ];
105 
106             if( file.exists() )
107             {
108                 if( file.needsRefresh() )
109                 {
110                     auto descs = deserializeMultiFile!( GameObject.Description )( file );
111                     assert( descs.length == objReses.length, "Adding or removing objects from a file is currently unsupported." );
112 
113                     foreach( i, objRes; objReses )
114                     {
115                         objRes.object.refresh( descs[ i ] );
116                     }
117                 }
118             }
119             else
120             {
121                 foreach( objRes; objReses )
122                 {
123                     objRes.object.shutdown();
124                     removeChild( objRes.object );
125                 }
126 
127                 objectsByResource.remove( file );
128             }
129         }
130     }
131 
132     /**
133      * Gets the object in the scene with the given name.
134      *
135      * Params:
136      *  name =            The name of the object to look for.
137      *
138      * Returns: The object with the given name.
139      */
140     final GameObject opIndex( string name )
141     {
142         if( auto id = name in idByName )
143             return this[ *id ];
144         else
145             return null;
146     }
147 
148     /**
149      * Gets the object in the scene with the given id.
150      *
151      * Params:
152      *  index =           The id of the object to look for.
153      *
154      * Returns: The object with the given id.
155      */
156     final GameObject opIndex( uint index )
157     {
158         if( auto obj = index in objectById )
159             return *obj;
160         else
161             return null;
162     }
163 
164     /**
165      * Adds object to the children, adds it to the scene graph.
166      *
167      * Params:
168      *  newChild =            The object to add.
169      */
170     final void addChild( GameObject newChild )
171     {
172         _root.addChild( newChild );
173     }
174 
175     /**
176      * Removes the given object as a child from this scene.
177      *
178      * Params:
179      *  oldChild =            The object to remove.
180      */
181     final void removeChild( GameObject oldChild )
182     {
183         _root.removeChild( oldChild );
184     }
185 
186     /**
187      * Gets all objects in the scene.
188      *
189      * Returns: All objects belonging to this scene.
190      */
191     final @property GameObject[] objects()
192     {
193         return objectById.values;
194     }
195 }