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