KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > buchuki > ensmer > AreaManager


1 /*
2  * Copyright 2004 Dusty Phillips
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package com.buchuki.ensmer;
17
18 import java.awt.event.KeyEvent JavaDoc;
19 import static java.lang.Math.PI JavaDoc;
20 import java.util.*;
21 import javax.media.j3d.Transform3D;
22 import javax.swing.event.EventListenerList JavaDoc;
23 import javax.vecmath.*;
24 import com.buchuki.ensmer.builtin.*;
25 import com.buchuki.ensmer.input.InputManager;
26 import com.buchuki.ensmer.input.command.*;
27 import com.buchuki.ensmer.input.event.EnsmerInputEvent;
28 import com.buchuki.ensmer.input.event.KeyPressEvent;
29 import com.buchuki.ensmer.input.event.semantic.*;
30 import com.buchuki.ensmer.prevayler.queries.*;
31 import org.prevayler.*;
32
33
34 /**
35  * This class manages the available <code>Area</code>s that can be loaded in
36  * the world. Only one area can be visible at once, however, previous areas
37  * can still be accessed. This class interacts with the Prevayler in order to
38  * serialize data about available worlds. It also maintains a history of
39  * previously accessed worlds and provides methods to move through the
40  * history or access other areas in the history.
41  *
42  * @author Dusty Phillips [dusty@buchuki.com]
43  */

44 public class AreaManager {
45
46     /**
47      * Move to the previous area in the history. This method retains the current
48      * area so it can be processed by accessing the nextArea method. If there
49      * are no previous areas, then nothing happens, no error is thrown.
50      */

51     public void previousArea() {
52         Prevayler prevayler = EnsmerManager.instance().getPrevayler();
53         try {
54             Long JavaDoc prevID = (Long JavaDoc) prevayler.execute(new PreviousAreaQuery());
55             if (prevID != null) {
56                 loadArea(prevID);
57             }
58         } catch (Exception JavaDoc e) {
59             e.printStackTrace();
60         }
61     }
62
63     /**
64      * Move to the next area in the history. This method retains the current
65      * area so it can be processed by accessing the proviousArea method. If
66      * there are no nextAreas, then nothing happens, no error is thrown. Note
67      * that the history of 'next' areas is reset every time a new area is set
68      * (ie: with setCurrentArea, and not with previousArea or nextArea), so it
69      * is more likely to be empty than previousArea.
70      */

71     public void nextArea() {
72         Prevayler prevayler = EnsmerManager.instance().getPrevayler();
73
74         try {
75             Long JavaDoc nextID = (Long JavaDoc) prevayler.execute(new NextAreaQuery());
76             if (nextID != null) {
77                 loadArea(nextID);
78             }
79         } catch (Exception JavaDoc e) {
80             e.printStackTrace();
81         }
82     }
83
84     /**
85      * Add an area to the prevayler and the map and return the ID of the new
86      * Area. The Area will be empty and the user will be positioned at the
87      * origin. It is up to the calling class to ensure that objects are
88      * positioned in the area.
89      *
90      * @return the new Area that was added
91      */

92     public Area newArea() {
93         try {
94             Prevayler prevayler = EnsmerManager.instance().getPrevayler();
95             Long JavaDoc areaID = (Long JavaDoc) prevayler.execute(new GetNextAreaIDQuery());
96             prevayler.execute(new AddAreaTransaction(areaID));
97             Area newArea = new Area(areaID);
98             areaMap.put(areaID, newArea);
99             return newArea;
100         } catch (Exception JavaDoc e) {
101             e.printStackTrace();
102             return null;
103         }
104     }
105
106     /**
107      * Remove an area from the prevayler and the map. All objects in the area
108      * are also removed and destroyed. The area is also removed from the history
109      *
110      * @param areaID the identifier of the area to remove
111      * @throws IllegalStateException if the area to be removed is the current
112      * area
113      */

114     public void removeArea(Long JavaDoc areaID) {
115         if (currentArea.getAreaID().equals(areaID)) {
116             throw new IllegalStateException JavaDoc("Cannot destroy the currently visible area");
117         }
118         Area area = areaMap.remove(areaID);
119         if (area == null) {
120             return;
121         }
122         area.destroyAll();
123         InputManager.getCommandMap().removeAllCommands(area);
124         EnsmerManager.instance().getPrevayler().execute(new RemoveAreaTransaction(areaID));
125     }
126
127     /**
128      * Add an ObjectListener to the list of objects listening for object change
129      * events on the areas
130      *
131      * @param listener the objectListener to be added
132      */

133     public void addObjectListener(ObjectListener listener) {
134         listenerList.add(ObjectListener.class, listener);
135     }
136
137     /**
138      * Remove an ObjectListener from the list of objects listening for object
139      * change events on the areas
140      *
141      * @param listener the objectListener to be removed
142      */

143     public void removeObjectListener(ObjectListener listener) {
144         listenerList.remove(ObjectListener.class, listener);
145     }
146
147     /**
148      * Access the current area of the AreaManager.
149      *
150      * @return the currently displayed Area
151      */

152     public Area getCurrentArea() {
153         return currentArea;
154     }
155
156     /**
157      * Get an Area identifier from the history. The parameter tells you how many
158      * areas to go back in the history. 0 returns the identifier of the curent
159      * area, 1 returns the identifier of the previous area, 2 returns the
160      * identifier of the area accessed before that, etc. Further, -1 returns the
161      * identifier of the next area, -2 returns the identifier of the area
162      * accessed after that, etc. This may seem unintuitive, but because there
163      * are generally more previous areas and they are generally accessed more
164      * often, it makes sense to make their accessors positive. Think that
165      * history gets bigger if you go back in time.
166      *
167      * @param index the index in the history to retrieve an Area identifier
168      * for. 0 returns the current area, positive numbers return a previous
169      * area, negative numbers return a next area.
170      * @return a Long area identifier indicating the area at that position
171      * in the history. If the index is outside the Area's history, then
172      * null is returned.
173      */

174     public Long JavaDoc getHistoricalAreaID(int index) {
175         Prevayler prevayler = EnsmerManager.instance().getPrevayler();
176         try {
177             return (Long JavaDoc) prevayler.execute(new GetHistoricalIDQuery(index));
178         } catch (Exception JavaDoc e) {
179             e.printStackTrace();
180             return null;
181         }
182     }
183
184     /**
185      * Get the area identifier of the area that contains the object with the
186      * given ID
187      *
188      * @param objectID the identifier of an object backend to determine which
189      * area it is in
190      * @return the identifier of the area containing that object, or
191      * null if the object doesn't exist
192      */

193     public Long JavaDoc getAreaIDForObject(Long JavaDoc objectID) {
194         Prevayler prevayler = EnsmerManager.instance().getPrevayler();
195         try {
196             return (Long JavaDoc) prevayler.execute(new GetAreaForObjectQuery(objectID));
197         } catch (Exception JavaDoc e) {
198             e.printStackTrace();
199             return null;
200         }
201     }
202
203     /**
204      * Get a reference to an Area based on its identifier
205      *
206      * @param id the id of the area to retrieve
207      * @return teh area associated with that ID or null if no such area
208      * exists
209      */

210     public Area getArea(Long JavaDoc id) {
211         return areaMap.get(id);
212     }
213
214     /**
215      * Set the current area based on the Area identifier. Change the current
216      * area, clear the selection, instruct the InterfaceManager to display the
217      * new Area, reset the view position to the Area's view position. Calling
218      * this method puts a new entry for the previous current Area into the
219      * history, and clears any history entries in front of it.
220      *
221      * @param id the identifier of the new current area
222      * @throws IllegalStateException if the area is not available
223      */

224     public void setCurrentArea(Long JavaDoc id) {
225         Prevayler prevayler = EnsmerManager.instance().getPrevayler();
226         prevayler.execute(new SetCurrentAreaTransaction(id));
227         loadArea(id);
228     }
229
230     /**
231      * Fire an ObjectEvent to all listening objects
232      *
233      * @param event Description of the Parameter
234      */

235     protected void fireObjectEvent(ObjectEvent event) {
236         Object JavaDoc[] listeners = listenerList.getListenerList();
237         for (int i = listeners.length - 2; i >= 0; i -= 2) {
238             if (listeners[i] == ObjectListener.class) {
239                 ((ObjectListener) listeners[i + 1]).objectStateChanged(event);
240             }
241         }
242     }
243
244     /**
245      * Construct this class. Determine if this is an initial run, or if previous
246      * areas have been prevayled and either load a default area or the
247      * previously prevayled ones. Ensure that loadAreas is called after
248      * construction to completely reload any previously prevayled areas.
249      *
250      * @throws Exception if the prevayler fails
251      */

252     AreaManager() throws Exception JavaDoc {
253         Prevayler prevayler = EnsmerManager.instance().getPrevayler();
254         long[] prevayledAreas = (long[]) prevayler.execute(new GetAreasQuery());
255
256         if (prevayledAreas.length != 0) {
257             for (long id : prevayledAreas) {
258                 areaMap.put(id, new Area(id));
259             }
260             long currentAreaID = (Long JavaDoc) prevayler.execute(new GetCurrentAreaQuery());
261             currentArea = areaMap.get(currentAreaID);
262         }
263     }
264
265     /**
266      * Completely load previously prevayled objects into the new areas. Should
267      * always be called after constructing the AreaManager.
268      *
269      * @throws Exception if the areas cannot be loaded
270      */

271     void loadAreas() throws Exception JavaDoc {
272         if (areaMap.isEmpty()) {
273             setupDefaultAreas();
274         }
275
276         /* I do not like having these command added here; its going to be impossible
277          * to find in future maintenance and easy to overlook. I can't figure out
278          * where else to put it though. It would fit naturally in the Navigator
279          * class, but when that is being constructed, it is not possible to get
280          * access to the areaManager class, as it has not been constructed yet.
281          */

282         final SpecialAreaManager spec = EnsmerManager.instance().getSpecialAreaManager();
283         Command emptyStorageCommand =
284             new Command() {
285                 public boolean execute(EnsmerInputEvent evt) {
286                     getArea(spec.getAreaID(spec.STORAGE)).destroyAll();
287                     return true;
288                 }
289             };
290         Area inventory = getArea(spec.getAreaID(spec.INVENTORY));
291         CommandMap map = InputManager.getCommandMap();
292         map.addCommand(inventory, new RemoveFromAreaCommand(inventory.getAreaID()), new KeyPressEvent(KeyEvent.VK_R));
293         Area storage = getArea(spec.getAreaID(spec.STORAGE));
294         map.addCommand(storage, new RemoveFromAreaCommand(storage.getAreaID()), new KeyPressEvent(KeyEvent.VK_R));
295         map.addCommand(storage, emptyStorageCommand, new KeyPressEvent(KeyEvent.VK_DELETE));
296         loadArea(currentArea.getAreaID());
297     }
298
299     /**
300      * Load a specific Area based on its ID. This method is called be the
301      * setCurrentArea, previousArea, and nextArea methods. If the area has not
302      * previously been loaded, it is done so now.
303      *
304      * @param id the areaID of the area to load
305      * @throws IllegalStateException if the area does not exist
306      */

307     private void loadArea(Long JavaDoc id) {
308         if (areaMap.containsKey(id)) {
309             currentArea = areaMap.get(id);
310             try {
311                 currentArea.loadObjects(); //Area ignores if they have already been loaded
312
} catch (Exception JavaDoc e) {
313                 e.printStackTrace();
314             }
315             InterfaceManager intf = EnsmerManager.instance().getInterfaceManager();
316             intf.setViewPosition(currentArea.getUserPosition());
317             intf.displayArea(currentArea);
318             EnsmerManager.instance().getSelectionManager().clearSelection();
319             EnsmerManager.instance().getUserManager().getInputManager().systemMode();
320         }
321         else {
322             throw new IllegalStateException JavaDoc("Area " + id + " does not exist.");
323         }
324     }
325
326     /**
327      * If no areas have been prevayled, create some default areas, including a
328      * main area, inventory, object loading area, etc. This is a rather ugly,
329      * large method. It could be refactored into a different class; it is only
330      * called the first time Ensmer is run.
331      *
332      * @exception Exception Description of the Exception
333      */

334     private void setupDefaultAreas() throws Exception JavaDoc {
335         Prevayler prevayler = EnsmerManager.instance().getPrevayler();
336
337         //set up the main area
338
currentArea = newArea();
339         prevayler.execute(new SetCurrentAreaTransaction(currentArea.getAreaID()));
340         currentArea.loadObjects(); //no objects to load, but indicates it has been loaded
341

342         //Add some static objects to the default world
343
Transform3D trans = new Transform3D();
344         Matrix4f mat = new Matrix4f();
345         trans.rotY(PI / 2); //90 degrees
346
trans.setTranslation(new Vector3f(0f, -1.25f, -2f));
347         trans.get(mat);
348         Static obj = new Static();
349         obj.setFilename("floor.wrl");
350         currentArea.newObject(obj, mat);
351
352         trans.setIdentity();
353         trans.setTranslation(new Vector3f(-2.99f, -.673f, -7.24f));
354         trans.get(mat);
355         obj = new Static();
356         obj.setFilename("bookshelf.wrl");
357         currentArea.newObject(obj, mat);
358
359         trans.setIdentity();
360         trans.setTranslation(new Vector3f(2.75f, -1.02f, -7.25f));
361         trans.get(mat);
362         obj = new Static();
363         obj.setFilename("desk.wrl");
364         currentArea.newObject(obj, mat);
365
366         trans.setIdentity();
367         trans.rotY(PI / 2);
368         trans.setTranslation(new Vector3f(-3.25f, -1.4f, -1f));
369         trans.get(mat);
370         obj = new Static();
371         obj.setFilename("table.wrl");
372         currentArea.newObject(obj, mat);
373
374         trans.setIdentity();
375         trans.setTranslation(new Vector3f(0f, -1.6f, -2f));
376         trans.get(mat);
377         Background bg = new Background();
378         currentArea.newObject(bg, mat);
379         bg.setConfigProperty("filename", "windows.gif");
380
381         SpecialAreaManager spec = EnsmerManager.instance().getSpecialAreaManager();
382         Area inventory = newArea();
383         spec.setSpecialID(spec.INVENTORY, inventory.getAreaID());
384         Area storage = newArea();
385         spec.setSpecialID(spec.STORAGE, storage.getAreaID());
386     }
387
388     /**
389      * A map of currently available Areas. The map maps long identifiers to Area
390      * objects. Each mapping in this map should have a corresponding entry in
391      * the EnsmerPrevayler.
392      */

393     private Map<Long JavaDoc, Area> areaMap = new HashMap<Long JavaDoc, Area>();
394
395     /**
396      * Locally cached reference to the current Area. This Area MUST have the
397      * same identifier as the Area returned by the prevayler's
398      * GetCurrentAreaCommand.
399      */

400     private Area currentArea;
401
402     /**
403      * List of ObjectListeners for the Area Manager
404      */

405     private EventListenerList JavaDoc listenerList = new EventListenerList JavaDoc();
406 }
407
408
Popular Tags