1 /**
2  * Defines the DGame class, the base class for all game logic.
3  */
4 module dash.core.dgame;
5 import dash;
6 import core.memory;
7 
8 /**
9  * The states the game can be in.
10  */
11 enum EngineState
12 {
13     /// The main game state.
14     Run,
15     /// In edit mode
16     Editor,
17     /// Refresh changed assets, but don't reset state.
18     Refresh,
19     /// Reload all assets at the beginning of the next cycle.
20     Reset,
21     /// Quit the game and the end of this cycle.
22     Quit
23 }
24 
25 /**
26  * Contains flags for all things that could be disabled.
27  */
28 struct GameStateFlags
29 {
30     bool updateScene;
31     bool updateUI;
32     bool updateTasks;
33     bool autoRefresh;
34     //bool updatePhysics;
35 
36     /**
37      * Set each member to false.
38      */
39     void pauseAll()
40     {
41         foreach( member; __traits(allMembers, GameStateFlags) )
42             static if( __traits(compiles, __traits(getMember, GameStateFlags, member) = false) )
43                 __traits(getMember, GameStateFlags, member) = false;
44     }
45 
46     /**
47      * Set each member to true.
48      */
49     void resumeAll()
50     {
51         foreach( member; __traits(allMembers, GameStateFlags) )
52             static if( __traits(compiles, __traits(getMember, GameStateFlags, member) = true) )
53                 __traits(getMember, GameStateFlags, member) = true;
54     }
55 }
56 
57 /**
58  * The main game loop manager. Meant to be overridden.
59  */
60 class DGame
61 {
62 public:
63     /// The instance to be running from
64     static DGame instance;
65 
66     /// The editor controller, resolved by reflection.d
67     Editor editor;
68 
69     /// Current state of the game
70     EngineState currentState;
71 
72     /// The current update settings
73     GameStateFlags* stateFlags;
74 
75     /// The currently active scene
76     Scene activeScene;
77 
78     /**
79      * Overrideable. Returns the name of the window.
80      */
81     @property string title()
82     {
83         return "Dash";
84     }
85 
86     /**
87      * Main Game loop.
88      */
89     final void run()
90     {
91         start();
92 
93         GC.collect();
94 
95         // Loop until there is a quit message from the window or the user.
96         while( currentState != EngineState.Quit )
97         {
98             if( currentState == EngineState.Reset )
99             {
100                 stop();
101                 start();
102                 GC.collect();
103             }
104             else if( currentState == EngineState.Refresh )
105             {
106                 refresh();
107             }
108 
109             //////////////////////////////////////////////////////////////////////////
110             // Update
111             //////////////////////////////////////////////////////////////////////////
112 
113             // Platform specific program stuff
114             Graphics.messageLoop();
115 
116             // Update time
117             Time.update();
118 
119             // Update input
120             Input.update();
121 
122             // Update webcore
123             if ( stateFlags.updateUI )
124             {
125                 UserInterface.updateAwesomium();
126             }
127 
128             // Update physics
129             //if( stateFlags.updatePhysics )
130             //  PhysicsController.stepPhysics( Time.deltaTime );
131 
132             if ( stateFlags.updateTasks )
133             {
134                 executeTasks();
135             }
136 
137             if ( stateFlags.updateScene )
138             {
139                 activeScene.update();
140             }
141 
142             // Do the updating of the child class.
143             onUpdate();
144 
145             // Update the editor.
146             //if( currentState == EngineState.Editor )
147             editor.update();
148 
149             //////////////////////////////////////////////////////////////////////////
150             // Draw
151             //////////////////////////////////////////////////////////////////////////
152 
153             activeScene.draw();
154 
155             // Draw in child class
156             onDraw();
157 
158             // End drawing
159             Graphics.endDraw();
160 
161             //break;
162         }
163 
164         stop();
165     }
166 
167 protected:
168     /**
169      * To be overridden, logic for when the game is being initalized.
170      */
171     void onInitialize() { }
172     /**
173      * To be overridden, called once per frame during the update cycle.
174      */
175     void onUpdate() { }
176     /**
177      * To be overridden, called once per frame during the draw cycle.
178      */
179     void onDraw() { }
180     /**
181      * To be overridden, called when the came is closing.
182      */
183     void onShutdown() { }
184     /**
185      * To be overridden, called when refreshing content.
186      */
187     void onRefresh() { }
188     /**
189      * To be overridden, called when resetting and the state must be saved.
190      */
191     void onSaveState() { }
192 
193 private:
194     /**
195      * Function called to initialize controllers.
196      */
197     final void start()
198     {
199         currentState = EngineState.Run;
200 
201         stateFlags = new GameStateFlags;
202         stateFlags.resumeAll();
203 
204         // This is so that the bench marks will log properly,
205         // and config options will be update upon second call.
206         DashLogger.setDefaults();
207 
208         bench!( { Config.initialize(); } )( "Config init" );
209         bench!( { DashLogger.initialize(); } )( "Logger init" );
210         bench!( { Input.initialize(); } )( "Input init" );
211         bench!( { Graphics.initialize(); } )( "Graphics init" );
212         bench!( { Assets.initialize(); } )( "Assets init" );
213         bench!( { Audio.initialize(); } )( "Audio init" );
214         bench!( { Prefabs.initialize(); } )( "Prefabs init" );
215         bench!( { UserInterface.initializeAwesomium(); } )( "UI init" );
216         bench!( { editor.initialize( this ); } )( "Editor init" );
217         bench!( { DGame.instance.onInitialize(); } )( "Game init" );
218 
219         debug scheduleIntervaledTask( 1.seconds, { if( stateFlags.autoRefresh ) currentState = EngineState.Refresh; return false; } );
220     }
221 
222     /**
223      * Function called to shutdown controllers.
224      */
225     final void stop()
226     {
227         onShutdown();
228         editor.shutdown();
229         resetTasks();
230         UserInterface.shutdownAwesomium();
231         Assets.shutdown();
232         Graphics.shutdown();
233         Audio.shutdown();
234     }
235 
236     /**
237      * Reloads content and yaml.
238      */
239     final void refresh()
240     {
241         // Refresh
242         Config.refresh();
243         Assets.refresh();
244         Graphics.refresh();
245         Prefabs.refresh();
246         activeScene.refresh();
247         // Need to refresh key events.
248         //Input.initialize();
249 
250         // Restart
251         currentState = EngineState.Run;
252 
253         // Refresh game.
254         onRefresh();
255     }
256 
257     /**
258      * Called when engine is resetting.
259      */
260     final void saveState()
261     {
262         onSaveState();
263     }
264 }