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