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