Implemented NDK version of Box2D, written by the people behind libgdx - appears to...
[rokon:rokon.git] / src / com / stickycoding / rokon / Scene.java
1 package com.stickycoding.rokon;\r
2 \r
3 import java.lang.reflect.InvocationTargetException;\r
4 import java.lang.reflect.Method;\r
5 import java.util.Arrays;\r
6 \r
7 import javax.microedition.khronos.opengles.GL10;\r
8 \r
9 import android.view.MotionEvent;\r
10 \r
11 import com.badlogic.gdx.physics.box2d.World;\r
12 \r
13 /**\r
14  * Scene.java\r
15  * A Scene holds and prepares drawable objects or object groups\r
16  * \r
17  * @author Richard\r
18  */\r
19 public class Scene {\r
20         \r
21         public static final int SCENE_TEXTURE_COUNT = 32;\r
22         public static final int DEFAULT_LAYER_COUNT = 1;\r
23         public static final int DEFAULT_LAYER_OBJECT_COUNT = 32;\r
24 \r
25         protected Layer[] layer;\r
26         protected boolean loadedTextures;\r
27         protected int layerCount;\r
28         protected Window window = null;\r
29         protected Texture[] textures;\r
30         protected boolean useInvoke;\r
31         protected World world;\r
32         protected boolean usePhysics = false;\r
33 \r
34         public void onPreDraw(GL10 gl) { }\r
35         public void onPostDraw(GL10 gl) { }\r
36         public void onTouchDown(DrawableObject object, float x, float y, MotionEvent event) { }\r
37         public void onTouchUp(DrawableObject object, float x, float y, MotionEvent event) { }\r
38         public void onTouchMove(DrawableObject object, float x, float y, MotionEvent event) { }\r
39         public void onTouch(DrawableObject object, float x, float y, MotionEvent event) { }\r
40         public void onTouchDown(float x, float y, MotionEvent event) { }\r
41         public void onTouchMove(float x, float y, MotionEvent event) { }\r
42         public void onTouch(float x, float y, MotionEvent event) { }\r
43         public void onTouchUp(float x, float y, MotionEvent event) { }\r
44         \r
45         /**\r
46          * Sets a World for the physics in this Scene\r
47          * Automatically flags usePhysics\r
48          * \r
49          * @param world valid World object\r
50          */\r
51         public void setWorld(World world) {\r
52                 this.world = world;\r
53                 usePhysics = true;\r
54         }\r
55         \r
56         /**\r
57          * Returns the World associated with this Scene\r
58          * \r
59          * @return NULL if no World set\r
60          */\r
61         public World getWorld() {\r
62                 return world;\r
63         }\r
64         \r
65         /**\r
66          * Flags to use physics in this Scene\r
67          */\r
68         public void usePhysics() {\r
69                 usePhysics = true;\r
70         }\r
71         \r
72         /**\r
73          * Flags to not use physics\r
74          */\r
75         public void noPhysics() {\r
76                 usePhysics = false;\r
77         }\r
78         \r
79         /**\r
80          * Removes the World from this Scene\r
81          */\r
82         public void removeWorld() {\r
83                 this.world = null;\r
84                 usePhysics = false;\r
85         }\r
86         \r
87         /**\r
88          * Triggers the Scene to begin invoking methods on certain events, this is not set by default.\r
89          * If the methods that are to be invoked don't exist, no exceptions will be raised.\r
90          */\r
91         public void useInvoke() {\r
92                 useInvoke = true;\r
93         }\r
94         \r
95         /**\r
96          * Stops the Scene from invoking methods on events, this is the default state\r
97          */\r
98         public void stopInvoke() {\r
99                 useInvoke = false;\r
100         }\r
101         \r
102         /**\r
103          * Invokes a method inside the Scene class, defined by given parameters.\r
104          * If no parameters exist, use the alternative invoke method\r
105          * \r
106          * @param methodName String\r
107          * @param params Class[]\r
108          * @param paramValues Object[]\r
109          * \r
110          * @return TRUE if successful, FALSE otherwise\r
111          */\r
112         public boolean invoke(String methodName, Class<?>[] params, Object[] paramValues) {\r
113                 for(Method m : this.getClass().getDeclaredMethods()) {\r
114                         if(m.getName().equals(methodName)) {\r
115                                 if(Arrays.equals(params, m.getParameterTypes())) {\r
116                                         try {\r
117                                                 m.invoke(this, paramValues);\r
118                                                 return true;\r
119                                         } catch (IllegalArgumentException e) {\r
120                                                 Debug.error("Invoking, IllegalArgument");\r
121                                                 e.printStackTrace();\r
122                                                 return false;\r
123                                         } catch (IllegalAccessException e) {\r
124                                                 Debug.error("Invoking, IllegalAccess");\r
125                                                 e.printStackTrace();\r
126                                                 return false;\r
127                                         } catch (InvocationTargetException e) {\r
128                                                 Debug.error("Invoking, IllegalTarget");\r
129                                                 e.printStackTrace();\r
130                                                 return false;\r
131                                         }\r
132                                 }\r
133                         }\r
134                 }\r
135                 return false;\r
136         }\r
137         \r
138         /**\r
139          * Invokes a method by parameters inside a Callback object\r
140          * \r
141          * @param callback valid Callback object\r
142          * @return TRUE if successful, FALSE otherwise\r
143          */\r
144         public boolean invoke(Callback callback) {\r
145                 if(callback.parameters == null) {\r
146                         return invoke(callback.methodName);\r
147                 } \r
148                 if(callback.parameterTypes == null) {\r
149                         return invoke(callback.methodName, callback.parameters);\r
150                 }\r
151                 return invoke(callback.methodName, callback.parameterTypes, callback.parameters);\r
152         }\r
153         \r
154         /**\r
155          * USE AT YOUR OWN RISK\r
156          * Invokes a method inside the Scene class, it selects the first matching method name and tries to pass on given parameters\r
157          * An error will be raised if this isn't the correct method. This routine is simply for those too lazy (or wanting to save\r
158          * on a little processing time) and are 100% sure there are no name conflicts.\r
159          * \r
160          * IllegalArgumentException may be passed to the Debug class, logcat will be notified - but there is no way to test at your end.\r
161          * \r
162          * @param methodName String\r
163          * @param paramValues Object[]\r
164          * \r
165          * @return TRUE if successful, FALSE otherwise\r
166          */\r
167         public boolean invoke(String methodName, Object[] paramValues) {\r
168                 for(Method m : this.getClass().getDeclaredMethods()) {\r
169                         if(m.getName().equals(methodName)) {\r
170                                 try {\r
171                                         m.invoke(this, paramValues);\r
172                                         return true;\r
173                                 } catch (IllegalArgumentException e) {\r
174                                         Debug.error("Invoking, IllegalArgument");\r
175                                         e.printStackTrace();\r
176                                         return false;\r
177                                 } catch (IllegalAccessException e) {\r
178                                         Debug.error("Invoking, IllegalAccess");\r
179                                         e.printStackTrace();\r
180                                         return false;\r
181                                 } catch (InvocationTargetException e) {\r
182                                         Debug.error("Invoking, IllegalTarget");\r
183                                         e.printStackTrace();\r
184                                         return false;\r
185                                 }\r
186                         }\r
187                 }\r
188                 return false;\r
189         }\r
190         \r
191         /**\r
192          * Invokes a method inside the Scene class, assuming there are no parameters to pass\r
193          * \r
194          * @param methodName String\r
195          * \r
196          * @return TRUE if successful, FALSE otherwise\r
197          */\r
198         public boolean invoke(String methodName) {\r
199                 for(Method m : this.getClass().getDeclaredMethods()) {\r
200                         if(m.getName().equals(methodName)) {\r
201                                 if(m.getParameterTypes().length == 0) {\r
202                                         try {\r
203                                                 m.invoke(this);\r
204                                                 return true;\r
205                                         } catch (IllegalArgumentException e) {\r
206                                                 Debug.error("Invoking, IllegalArgument");\r
207                                                 e.printStackTrace();\r
208                                                 return false;\r
209                                         } catch (IllegalAccessException e) {\r
210                                                 Debug.error("Invoking, IllegalAccess");\r
211                                                 e.printStackTrace();\r
212                                                 return false;\r
213                                         } catch (InvocationTargetException e) {\r
214                                                 Debug.error("Invoking, IllegalTarget");\r
215                                                 e.printStackTrace();\r
216                                                 return false;\r
217                                         }\r
218                                 }\r
219                         }\r
220                 }\r
221                 return false;\r
222         }\r
223         \r
224         protected void handleTouch(MotionEvent event) {\r
225                 event.setLocation(event.getX() * (Device.widthPixels / RokonActivity.gameWidth), event.getY() * (Device.heightPixels  / RokonActivity.gameHeight));\r
226                 onTouch(event.getX(), event.getY(), event);\r
227                 switch(event.getAction()) {\r
228                         case MotionEvent.ACTION_DOWN:\r
229                                 onTouchDown(event.getX(), event.getY(), event);\r
230                                 break;\r
231                         case MotionEvent.ACTION_UP:\r
232                                 onTouchUp(event.getX(), event.getY(), event);\r
233                                 break;\r
234                         case MotionEvent.ACTION_MOVE:\r
235                                 onTouch(event.getX(), event.getY(), event);\r
236                                 break;\r
237                 }\r
238                 for(int i = 0; i < layerCount; i++) {\r
239                         for(int j = 0; j < layer[i].maximumDrawableObjects; j++) {\r
240                                 DrawableObject object = layer[i].drawableObjects.get(j);\r
241                                 if(object != null && object.isTouchable) {\r
242                                         if(MathHelper.pointInRect(event.getX(), event.getY(), object.x, object.y, object.width, object.height)) {\r
243                                                 onTouch(object, event.getX(), event.getY(), event);\r
244                                                 if(object.getName() != null) {\r
245                                                         invoke(object.getName() + "_onTouch", new Class[] { float.class, float.class, MotionEvent.class }, new Object[] { event.getX(), event.getY(), event });\r
246                                                 }\r
247                                                 switch(event.getAction()) {\r
248                                                         case MotionEvent.ACTION_DOWN:\r
249                                                                 onTouchDown(object, event.getX(), event.getY(), event);\r
250                                                                 if(object.getName() != null) {\r
251                                                                         invoke(object.getName() + "_onTouchDown", new Class[] { float.class, float.class, MotionEvent.class }, new Object[] { event.getX(), event.getY(), event });\r
252                                                                 }\r
253                                                                 break;\r
254                                                         case MotionEvent.ACTION_UP:\r
255                                                                 onTouchUp(object, event.getX(), event.getY(), event);\r
256                                                                 if(object.getName() != null) {\r
257                                                                         invoke(object.getName() + "_onTouchUp", new Class[] { float.class, float.class, MotionEvent.class }, new Object[] { event.getX(), event.getY(), event });\r
258                                                                 }\r
259                                                                 break;\r
260                                                         case MotionEvent.ACTION_MOVE:\r
261                                                                 onTouch(object, event.getX(), event.getY(), event);\r
262                                                                 if(object.getName() != null) {\r
263                                                                         invoke(object.getName() + "_onTouchMove", new Class[] { float.class, float.class, MotionEvent.class }, new Object[] { event.getX(), event.getY(), event });\r
264                                                                 }\r
265                                                                 break;\r
266                                                 }\r
267                                         }\r
268                                 }\r
269                         }\r
270                 }\r
271         }\r
272         \r
273         /**\r
274          * Creates a new Scene with given layer count, and a corresponding maximum DrawableObject count \r
275          * \r
276          * @param layerCount maximum number of layers\r
277          * @param layerObjectCount maximum number of DrawableObjects per layer, the array length must match layerCount\r
278          */\r
279         public Scene(int layerCount, int[] layerObjectCount) {\r
280                 this.layerCount = layerCount;\r
281                 layer = new Layer[layerCount];\r
282                 for(int i = 0; i < layerCount; i++) {\r
283                         layer[i] = new Layer(this, layerObjectCount[i]);\r
284                 }\r
285                 prepareNewScene();\r
286         }\r
287         \r
288         /**\r
289          * Creates a new Scene with given layer count, all layers will have the same maximum number of DrawableObjects\r
290          * \r
291          * @param layerCount maximum number of layers\r
292          * @param layerObjectCount maximum number of DrawableObjects per layer\r
293          */\r
294         public Scene(int layerCount, int layerObjectCount) {\r
295                 this.layerCount = layerCount;\r
296                 layer = new Layer[layerCount];\r
297                 for(int i = 0; i < layerCount; i++) {\r
298                         layer[i] = new Layer(this, layerObjectCount);\r
299                 }\r
300                 prepareNewScene();\r
301         }\r
302         \r
303         /**\r
304          * Creates a new Scene with given layer count, and a default maximum DrawableObject count of DEFAULT_LAYER_OBJECT_COUNT\r
305          * \r
306          * @param layerCount maximum number of layers\r
307          */\r
308         public Scene(int layerCount) {\r
309                 this(layerCount, DEFAULT_LAYER_OBJECT_COUNT);\r
310         }\r
311         \r
312         /**\r
313          * Creates a new Scene with defaults, DEFAULT_LAYER_COUNT and DEFAULT_LAYER_OBJECT_COUNT\r
314          */\r
315         public Scene() {\r
316                 this(DEFAULT_LAYER_COUNT, DEFAULT_LAYER_OBJECT_COUNT);\r
317                 \r
318         }\r
319         \r
320         private void prepareNewScene() {\r
321                 textures = new Texture[SCENE_TEXTURE_COUNT];\r
322         }\r
323         \r
324         /**\r
325          * Flags a Texture to be loaded into this Scene\r
326          * This must be called before RokonActivity.setScene\r
327          * \r
328          * @param texture valid Texture object\r
329          */\r
330         public void useTexture(Texture texture) {\r
331                 for(int i = 0; i < textures.length; i++) {\r
332                         if(textures[i] == null) {\r
333                                 textures[i] = texture;\r
334                                 return;\r
335                         }\r
336                 }\r
337                 Debug.warning("Scene.useTexture", "Tried loading too many Textures onto the Scene, max is " + textures.length);\r
338         }\r
339         \r
340         /**\r
341          * Defines the active Window for this Scene\r
342          * If no Window is given, a default static view will be rendered \r
343          * \r
344          * @param window\r
345          */\r
346         public void setWindow(Window window) {\r
347                 if(window == null) {\r
348                         Debug.warning("Scene.setWindow", "Tried setting a NULL Window");\r
349                         return;\r
350                 }\r
351                 this.window = window;\r
352         }\r
353         \r
354         /**\r
355          * Removes the current active Window, returning it to NULL\r
356          */\r
357         public void removeWindow() {\r
358                 window = null;\r
359         }\r
360         \r
361         /**\r
362          * @return NULL if there is no Window associated with this Scene\r
363          */\r
364         public Window getWindow() {\r
365                 if(window == null)\r
366                         return null;\r
367                 return window;\r
368         }\r
369         \r
370         /**\r
371          * Fetches the Layer object associated with the given index\r
372          * \r
373          * @param index the index of the Layer\r
374          * @return NULL if invalid index is given\r
375          */\r
376         public Layer getLayer(int index) {\r
377                 if(index < 0 || index > layerCount) {\r
378                         Debug.warning("Scene.getLayer", "Tried fetching invalid layer (" + index + "), maximum is " + layerCount);\r
379                         return null;\r
380                 }\r
381                 return layer[index];\r
382         }\r
383         \r
384         /**\r
385          * Clears the DrawableObjects from all Layers\r
386          */\r
387         public void clear() {\r
388                 for(int i = 0; i < layerCount; i++) {\r
389                         layer[i].clear();\r
390                 }\r
391         }\r
392         \r
393         /**\r
394          * Clears all the DrawableObjects from a specified Layer\r
395          * \r
396          * @param index the index of the Layer\r
397          */\r
398         public void clearLayer(int index) {\r
399                 if(index <= 0 || index > layerCount) {\r
400                         Debug.warning("Scene.clearLayer", "Tried clearing invalid layer (" + index + "), maximum is " + layerCount);\r
401                         return;\r
402                 }\r
403                 layer[index].clear();\r
404         }\r
405         \r
406         /**\r
407          * Moves a Layer from one index to another, and shuffles the others up or down to accomodate\r
408          * \r
409          * @param startIndex the current index of the Layer\r
410          * @param endIndex the desired final index of the Layer\r
411          */\r
412         public void moveLayer(int startIndex, int endIndex) {\r
413                 if(startIndex == endIndex) {\r
414                         Debug.warning("Scene.moveLayer", "Tried moving a Layer to its own position, stupid");\r
415                         return;\r
416                 }\r
417                 if(startIndex <= 0 || startIndex > layerCount) {\r
418                         Debug.warning("Scene.moveLayer", "Tried moving an invalid Layer, startIndex=" + startIndex + ", maximum is " + layerCount);\r
419                         return;\r
420                 }\r
421                 if(endIndex <= 0 || endIndex > layerCount) {\r
422                         Debug.warning("Scene.moveLayer", "Tried moving an invalid Layer, endIndex=" + endIndex + ", maximum is " + layerCount);\r
423                         return;\r
424                 }\r
425                 Layer temporaryLayer = layer[startIndex];\r
426                 if(endIndex < startIndex) {\r
427                         for(int i = endIndex; i < startIndex; i++) {\r
428                                 layer[i + 1] = layer[i];\r
429                         }\r
430                         layer[endIndex] = temporaryLayer;\r
431                 }\r
432                 if(endIndex > startIndex) { \r
433                         for(int i = startIndex; i < endIndex; i++) {\r
434                                 layer[i] = layer[i + 1];\r
435                         }\r
436                         layer[endIndex] = temporaryLayer;\r
437                 }\r
438         }\r
439         \r
440         /**\r
441          * Switches the position of one Layer with another\r
442          * \r
443          * @param layer1 the index of the first Layer\r
444          * @param layer2 the index of the second Layer\r
445          */\r
446         public void switchLayers(int layer1, int layer2) {\r
447                 if(layer1 == layer2) {\r
448                         Debug.warning("Scene.switchLayers", "Tried switching the same Layer");\r
449                         return;\r
450                 }\r
451                 if(layer1 < 0 || layer1 > layerCount) {\r
452                         Debug.warning("Scene.switchLayers", "Tried switch an invalid Layer, layer1=" + layer1 + ", maximum is " + layerCount);\r
453                         return;\r
454                 }\r
455                 if(layer2 < 0 || layer2 > layerCount) {\r
456                         Debug.warning("Scene.switchLayers", "Tried switch an invalid Layer, layer2=" + layer2 + ", maximum is " + layerCount);\r
457                         return;\r
458                 }\r
459                 Layer temporaryLayer = layer[layer1];\r
460                 layer[layer1] = layer[layer2];\r
461                 layer[layer2] = temporaryLayer;\r
462         }\r
463         \r
464         /**\r
465          * Replaces a Layer object in this Scene\r
466          * \r
467          * @param index a valid index for a Layer, less than getLayerCount\r
468          * @param layer a valid Layer object to replace the existing Layer\r
469          */\r
470         public void setLayer(int index, Layer layer) {\r
471                 if(layer == null) {\r
472                         Debug.warning("Scene.setLayer", "Tried setting to a null Layer");\r
473                         return;\r
474                 }\r
475                 if(index < 0 || index > layerCount) {\r
476                         Debug.warning("Scene.setLayer", "Tried setting an invalid Layer, index=" + index + ", maximum is " + layerCount);\r
477                         return;\r
478                 }\r
479                 this.layer[index] = layer;\r
480         }\r
481         \r
482         /**\r
483          * Adds a DrawableObject to the first (0th) Layer\r
484          * \r
485          * @param drawableObject a valid DrawableObject\r
486          */\r
487         public void add(DrawableObject drawableObject) {\r
488                 layer[0].add(drawableObject);\r
489         }\r
490         \r
491         /**\r
492          * Adds a DrawableObject to a given Layer\r
493          * \r
494          * @param layerIndex a valid index of a Layer\r
495          * @param drawableObject a valid DrawableObject\r
496          */\r
497         public void add(int layerIndex, DrawableObject drawableObject) {\r
498                 if(layerIndex < 0 || layerIndex > layerCount) {\r
499                         Debug.warning("Scene.add", "Tried adding to an invalid Layer, layerIndex=" + layerIndex + ", maximum is " + layerCount);\r
500                         return;\r
501                 }\r
502                 if(drawableObject == null) {\r
503                         Debug.warning("Scene.add", "Tried adding a NULL DrawableObject");\r
504                         return;\r
505                 }\r
506                 layer[layerIndex].add(drawableObject);\r
507         }\r
508         \r
509         /**\r
510          * Removes a DrawableObject from the Scene\r
511          * \r
512          * @param drawableObject a valid DrawableObject\r
513          */\r
514         public void remove(DrawableObject drawableObject) {\r
515                 drawableObject.remove();\r
516         }\r
517         \r
518         protected void onUpdate() {\r
519                 \r
520         }\r
521         \r
522         protected void onGameLoop() {\r
523                 \r
524         }\r
525         \r
526         protected void onSetScene() {\r
527                 loadedTextures = false;\r
528         }\r
529         \r
530         protected void onEndScene() {\r
531                 \r
532         }\r
533         \r
534         protected void onLoadTextures(GL10 gl) {\r
535                 Debug.print("Loading textures onto the Scene");\r
536                 for(int i = 0; i < textures.length; i++) {\r
537                         if(textures[i] != null) {\r
538                                 textures[i].onLoadTexture(gl);\r
539                                 textures[i] = null;\r
540                         }\r
541                 }\r
542                 loadedTextures = true;\r
543         }\r
544         \r
545         protected void onDraw(GL10 gl) {\r
546                 onPreDraw(gl);\r
547                 if(usePhysics) {\r
548                         world.step(Time.getTicksFraction(), 3, 3);\r
549                 }\r
550                 gl.glClear(GL10.GL_COLOR_BUFFER_BIT);\r
551         gl.glMatrixMode(GL10.GL_MODELVIEW);\r
552         gl.glLoadIdentity();\r
553                 for(int i = 0; i < layerCount; i++) {\r
554                         layer[i].onDraw(gl);\r
555                 }\r
556                 onPostDraw(gl);\r
557         }\r
558         \r
559 }\r