| GameMap.java |
1 /*
2 * CVS: $Id: GameMap.java,v 1.12 2004/07/25 19:07:20 marcus Exp $
3 *
4 * This file is part of JZuul.
5 *
6 * JZuul is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * JZuul is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Zuul; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * Copyrigth 2004 by marcus, leh
21 *
22 */
23
24package org.jzuul.engine;
25
26import java.io.InputStream;
27import java.lang.reflect.Constructor;
28import java.lang.reflect.InvocationTargetException;
29import java.util.HashMap;
30import java.util.Iterator;
31import java.util.List;
32import java.util.Map;
33import java.util.Random;
34import java.util.Vector;
35
36import org.jdom.Element;
37import org.jzuul.engine.exceptions.ConnectAllRoomsFailed;
38import org.jzuul.engine.exceptions.NoSuchRoomException;
39import org.jzuul.engine.rooms.*;
40
41/**
42 * Diese Klasse bildet die Karte (das Raumgeflecht in dem sich der Spieler bewegen kann)
43 * in einem Spiel ab.
44 * Da durch die Struktur der Spieldateien es zulässt, das Wege ohne Room Objekte angelegt werden
45 * werden die Wege in einer Queue ähnlichen Struktur gehalten und erst zum schluss angelegt.
46 *
47 *
48 * @version $Revision: 1.12 $
49 */
50public class GameMap {
51 /**
52 * Diese Klasse implementiert einen in der Queue gehaltenen Weg.
53 *
54 *
55 */
56 private class QueueItem {
57 /**
58 * der Name des Raumes zu dem dieser Weg führt.
59 */
60 public String ofName;
61 /**
62 * die Richtung, ausgehend von dem Quellraum, in der der Raum liegt
63 */
64 int direction;
65
66 /**
67 * Erstellt einen neuen Weg für die Queue
68 *
69 * @param direction die Richtung in der dieser Raum liegt
70 * @param ofName der Name des Raumes
71 */
72 public QueueItem(int direction, String ofName) {
73 this.direction = direction;
74 this.ofName = ofName;
75 }
76 }
77
78 /**
79 * Der interne speicher für die Karte, enthällt Raumname auf Room Objekt mappings.
80 */
81 protected Map gameMap;
82 /**
83 * Der Name des Startraumes für diese Karte
84 */
85 protected String startRoom;
86 /**
87 * Die Queue der Wege die noch angelegt werden müssen. Enthällt Ausgangsraum auf QueueItem
88 * Mappings.
89 */
90 protected Map connectQueue;
91 /**
92 * Die Liste der NPCs die sich in den Räumen aufhalten.
93 */
94 protected List npcList;
95 /**
96 * Der Name dieser Karte
97 */
98 protected String name;
99
00 /**
01 * Erstell ein neues GameMap Objekt
02 *
03 * @param name der Name dieser Karte
04 */
05 public GameMap(String name) {
06 this.name = name;
07 gameMap = new HashMap();
08 connectQueue = new HashMap();
09 npcList = new Vector();
10 }
11
12 /**
13 * Fügt einen neuen Raum und einen Weg in die Karte ein.
14 *
15 * @param name der Name des neuen Raumes
16 * @param description die Beschreibung des neuen Raumes
17 * @param direction ein Richtung in der ein anderer Raum liegt
18 * @param ofRoom der Name des Raumes der in dieser Richtung liegt
19 * @see org.jzuul.engine.Directions
20 */
21 public void addRoom(String name, String description, int direction, String ofRoom) {
22 this.addRoom(name, description);
23 this.enqueueWay(name, direction, ofRoom);
24 }
25
26 /**
27 * Fügt ein neues Room Objekt der Karte hinzu.
28 * Für diesen Raum müssen später noch Wege angelegt werden!
29 *
30 * @param raum ein Room Objekt das der Karte hinzugefügt werden soll.
31 */
32 public void addRoom(Room raum) {
33 gameMap.put(raum.getName(), raum);
34 }
35
36 /**
37 * Fügt einen Room Objekt und einen Weg der Karte hinzu
38 *
39 * @param raum das Room Objekt das hinzugefügt werden soll
40 * @param direction die Richtung in der ein anderer Raum liegt
41 * @param ofRoom der Name des Raumes der in dieser Richtung liegt
42 * @see org.jzuul.engine.Directions
43 */
44 public void addRoom(Room raum, int direction, String ofRoom) {
45 this.addRoom(raum);
46 this.enqueueWay(raum.getName(), direction, ofRoom);
47 }
48
49 /**
50 * Fügt einen Raum der Karte hinzu.
51 * Für diesen Raum müssen später noch Wege angelegt werden!
52 *
53 * @param name der Name des neuen Raums
54 * @param description die Beschreibung des neuen Raums
55 */
56 public void addRoom(String name, String description) {
57 addRoom(new Room(name, description));
58 }
59
60 /**
61 * Fügt einen neuen typisierten Raum zu der Karte hinzu.
62 * Für diesen Raum müssen später noch Wege angelegt werden!
63 *
64 * @param name der Name des neuen Raumes
65 * @param description die Beschreibung des neuen Raumes
66 * @param typeOfRoom der Typ (die Klasse) des neuen Raumes
67 * @see org.jzuul.engine.rooms
68 */
69 public void addRoom(String name, String description, String typeOfRoom) {
70 try {
71 Class classDefinition = Class.forName(typeOfRoom);
72 Class[] types = new Class[] { String.class, String.class };
73 Constructor cons = classDefinition.getConstructor(types);
74 Object[] args = new Object[] { name, description };
75
76 gameMap.put(name, cons.newInstance(args));
77 } catch (InstantiationException e) {
78 System.out.println(e);
79 } catch (IllegalAccessException e) {
80 System.out.println(e);
81 } catch (ClassNotFoundException e) {
82 System.out.println(e);
83 } catch (IllegalArgumentException e) {
84 e.printStackTrace();
85 } catch (InvocationTargetException e) {
86 e.printStackTrace();
87 } catch (SecurityException e) {
88 e.printStackTrace();
89 } catch (NoSuchMethodException e) {
90 e.printStackTrace();
91 }
92 }
93
94 /**
95 * Zugriff auf einzelne Room Objekte
96 *
97 * @param name der Name eines Raumes
98 * @return das Room Objekt falls der Raum existiert, null sonst
99 */
00 public Room getRoom(String name) {
01 if (gameMap.containsKey(name)) {
02 return (Room) gameMap.get(name);
03 } else {
04 return null;
05 }
06 }
07
08 /**
09 * Überpüft ob schon ein Weg zwischen Raum 1 und Raum 2 in der Queue existiert.
10 *
11 * @param room1 der Name eines Raumes
12 * @param room2 der Name eines Raumes
13 * @return true wenn es einen Weg in der Queue von room1 nach room2 gibt, false sonst.
14 */
15 private boolean connectionExists(String room1, String room2) {
16 if (connectQueue.containsKey(room1)) {
17 Vector v = (Vector) connectQueue.get(room1);
18 for (Iterator i = v.iterator(); i.hasNext();) {
19 if (((QueueItem) i.next()).ofName.equals(room2)) {
20 return true;
21 }
22 }
23 }
24 return false;
25 }
26
27 /**
28 * Überprüft bidirektional ob schon ein Weg zwischen beiden Räumen in der Queue existiert.
29 *
30 * @param room1 der Name eines Raumes
31 * @param room2 der Name eines Raumes
32 * @return true wenn es einen Weg zwischen den beiden Räumen in der Queue gibt, false sonst
33 */
34 private boolean wayExists(String room1, String room2) {
35 return connectionExists(room1, room2) || connectionExists(room2, room1);
36 }
37
38 /**
39 * Fügt einen Weg in die Wegequeue ein falls dieser noch nicht existiert.
40 *
41 * @param name der Name des Ausgangsraumes
42 * @param direction die Richtung in der der Weg liegt
43 * @param ofRoomName der Name des Zielraumes.
44 */
45 public void enqueueWay(String name, int direction, String ofRoomName) {
46 if (wayExists(name, ofRoomName)) {
47 return;
48 }
49
50 if (connectQueue.containsKey(name)) {
51 ((Vector) connectQueue.get(name)).add(new QueueItem(direction, ofRoomName));
52 } else {
53 Vector v = new Vector();
54 v.add(new QueueItem(direction, ofRoomName));
55 connectQueue.put(name, v);
56 }
57 }
58
59 /**
60 * Erstellt alle Verbindungen für den gegebenen Raum.
61 * Die Räume für die Verbindungen erstellt wurden werden aus der Liste gelöscht.
62 *
63 * @param name der Name des Raumes bei dem begonnen werden soll
64 * @param seen die Liste der noch nicht besuchten Räume
65 */
66 protected void processQueue(String name, List seen) {
67 if (connectQueue.containsKey(name)) {
68 Vector queueItems = (Vector) connectQueue.get(name);
69 if (seen.contains(name)) {
70 seen.remove(name);
71 for (Iterator i = queueItems.iterator(); i.hasNext();) {
72 QueueItem qi = (QueueItem) i.next();
73 connect(name, qi.direction, qi.ofName);
74 processQueue(qi.ofName, seen);
75 }
76 }
77 connectQueue.remove(name);
78 }
79 }
80
81 /**
82 * Macht einen Verbindungsdurchlauf.
83 * Alle in der Wegequeue vorhandenen Wege werden angelegt.
84 *
85 */
86 public void processQueue() {
87 List seen = new Vector(gameMap.keySet());
88 for (Iterator i = this.gameMap.keySet().iterator(); i.hasNext();) {
89 this.processQueue((String) i.next(), seen);
90 }
91 }
92
93 /**
94 * Erstellt eine bidirektionale Verbindung zwischen zwei Räumen.
95 *
96 * @param name der Name des ersten Raumes
97 * @param direction die Richtung in der der Weg liegt
98 * @param ofName der Name des zweiten Raumes
99 * @see org.jzuul.engine.Directions
00 */
01 protected void connect(String name, int direction, String ofName) {
02 Room room = getRoom(name);
03 Room ofRoom = getRoom(ofName);
04
05 if (ofRoom == null) {
06 return;
07 }
08 if (room == null) {
09 return;
10 }
11
12 switch (direction) {
13 case Directions.NORTH_OF :
14 room.setExitByDirection(Directions.SOUTH, ofRoom);
15 ofRoom.setExitByDirection(Directions.NORTH, room);
16 break;
17 case Directions.EAST_OF :
18 room.setExitByDirection(Directions.WEST, ofRoom);
19 ofRoom.setExitByDirection(Directions.EAST, room);
20 break;
21 case Directions.SOUTH_OF :
22 room.setExitByDirection(Directions.NORTH, ofRoom);
23 ofRoom.setExitByDirection(Directions.SOUTH, room);
24 break;
25 case Directions.WEST_OF :
26 room.setExitByDirection(Directions.EAST, ofRoom);
27 ofRoom.setExitByDirection(Directions.WEST, room);
28 break;
29 case Directions.TOP_OF :
30 room.setExitByDirection(Directions.BELOW, ofRoom);
31 ofRoom.setExitByDirection(Directions.TOP, room);
32 break;
33 case Directions.BELOW_OF :
34 room.setExitByDirection(Directions.TOP, ofRoom);
35 ofRoom.setExitByDirection(Directions.BELOW, room);
36 break;
37 }
38 }
39
40 /**
41 * Fügt ein GameObject Objekt in einen Raum ein.
42 *
43 * @param name der Name des Raumes
44 * @param object das GameObject Objekt das in den Raum eingefügt werden soll.
45 */
46 public void addItemToRoom(String name, GameObject object) throws NoSuchRoomException {
47 if (gameMap.containsKey(name)) {
48 ((Room) gameMap.get(name)).getContent().addGameObject(object);
49 } else {
50 throw new NoSuchRoomException("Raum " + name + " existiert nicht!");
51 }
52
53 }
54
55 /**
56 * Fügt ein Item Objekt in einen Raum ein
57 *
58 * @param name der Name des Raumes
59 * @param itemName der Name des Item Objektes
60 * @deprecated Sollte nicht mehr funktionieren, da die Methode versucht das Objekt zu instanzieeren
61 */
62 public void addItemToRoom(String name, String itemName) throws NoSuchRoomException {
63 if (gameMap.containsKey(name)) {
64 Object object = null;
65 try {
66 Class classDefinition = Class.forName(itemName);
67 object = classDefinition.newInstance();
68 addItemToRoom(name, (GameObject) object);
69 } catch (InstantiationException e) {
70 System.out.println(e);
71 } catch (IllegalAccessException e) {
72 System.out.println(e);
73 } catch (ClassNotFoundException e) {
74 System.out.println(e);
75 }
76 } else {
77 throw new NoSuchRoomException("Raum " + name + " does not exist!");
78 }
79
80 }
81
82 /**
83 * Fügt eine Beschreibung für eine Himmelsrichtung einem Raum hinzu.
84 *
85 * @param name der Name des Raumes
86 * @param direction die Himmelrichtung
87 * @param description die Beschreibung
88 * @see org.jzuul.engine.Directions
89 */
90 public void setWayDescription(String name, int direction, String description) {
91 Room r = getRoom(name);
92
93 r.setWayDescription(direction, description);
94
95 }
96
97 /**
98 * Setzt die Beschreibung für alle Himmelrichtungen in einem bestimmten Raum
99 *
00 * @param name der Name des Raumes
01 * @param north die Beschreibung für Directions.NORTH
02 * @param east die Beschreibung für Directions.EAST
03 * @param south die Beschreibung für Directions.SOUTH
04 * @param west die Beschreibung für Directions.WEST
05 * @deprecated Benutzen wir das irgendwo?
06 */
07 public void setWayDescription(String name, String north, String east, String south, String west) {
08 Room r = getRoom(name);
09 r.setWayDescription(Directions.NORTH, north);
10 r.setWayDescription(Directions.EAST, east);
11 r.setWayDescription(Directions.SOUTH, south);
12 r.setWayDescription(Directions.WEST, west);
13
14 }
15
16 /**
17 * Testet übergänge von einem Raum in andere.
18 * Die Räume für die Übergange existieren werden aus der Liste gelöscht.
19 *
20 * @param raum der Raum von dem aus die Übergange getestet werden sollen
21 * @param seen eine Liste von noch nicht besuchten Räumen.
22 */
23 public void verifyMap(Room raum, List seen) {
24 List ways = raum.getWays();
25 seen.remove(raum);
26 for (Iterator i = ways.iterator(); i.hasNext();) {
27 Room r = (Room) i.next();
28 if (seen.contains(r)) {
29 verifyMap(r, seen);
30 }
31 }
32 }
33
34 /**
35 * Versucht einen kompletten Durchlauf durch die Karte und überpüft ob alle
36 * Räume erreicht werden können.
37 *
38 * @return True falls alle Räume erreicht werden können, false sonst
39 * @throws NoSuchRoomException falls ein Raum besucht werden soll der nicht existiert
40 */
41 public boolean verifyMap() throws NoSuchRoomException, ConnectAllRoomsFailed{
42 Vector seen = new Vector(gameMap.values());
43 if (this.startRoom == null) {
44 Room roomObj = getRandomRoom();
45 if (roomObj == null) {
46 throw new NoSuchRoomException("Can not get any Room for map " + name + ". Does this Map have rooms?");
47 }
48 startRoom = roomObj.getName();
49 }
50
51 this.processQueue();
52 Room sraum = this.getRoom(startRoom);
53 if (sraum == null)
54 throw new NoSuchRoomException("Raum " + startRoom + " does not exist!");
55 verifyMap(sraum, seen);
56 if (!this.connectQueue.isEmpty()) {
57 String roomNames = "";
58 for (Iterator i = connectQueue.keySet().iterator(); i.hasNext();) {
59 roomNames += ((String) i.next()) + "\n";
60 }
61 throw new NoSuchRoomException("Missing Rooms in Map " + name + ":\n" + roomNames);
62 }
63
64 if (seen.isEmpty()) {
65 return true;
66 } else {
67 String error = "Non reachable rooms in map " + name + ":\n";
68 for (Iterator i = seen.iterator(); i.hasNext();) {
69 error += (((Room) i.next()).getName()) + "\n";
70 }
71 throw new ConnectAllRoomsFailed(error);
72
73 }
74 }
75
76 /**
77 * Setzt den Startraum für diese Karte
78 *
79 * @param name der Name des Startraumes
80 */
81 public void setStartRoom(String name) {
82 this.startRoom = name;
83 }
84
85 /**
86 * Wandelt die Karte in ein JDOM XML Element um.
87 *
88 * @return ein JDOM XML Element das den aktuellen Stand der Karte wiederspiegelt
89 */
90 public Element toElement() {
91 Element map = new Element("map");
92 map.setAttribute("name", getName());
93 map.setAttribute("startroom", getStartRoom());
94
95 for (Iterator i = this.gameMap.values().iterator(); i.hasNext();) {
96 map.addContent(((Room) i.next()).toElement());
97 }
98
99 return map;
00 }
01
02 /**
03 * Gibt einen beliebigen Raum aus der Karte zurück
04 *
05 * @return ein beliebiger Raum aus der Karte
06 */
07 public Room getRandomRoom() {
08 Vector v = new Vector(this.gameMap.values());
09 if (this.gameMap.size()>0) {
10 return (Room) v.get(new Random().nextInt(this.gameMap.size()));
11 } else {
12 return null;
13 }
14 }
15
16 /**
17 * Weist einem Raum eine Inventory Objekt zu.
18 *
19 * @param raum der Name des Raumes
20 * @param inv das Inventory Objekt das dem Raum gesetzt werden soll
21 */
22 public void addInvToRoom(String raum, Inventory inv) throws NoSuchRoomException {
23 this.npcList.addAll(inv.getCharacterObjects());
24 if (getRoom(raum) == null) {
25 throw new NoSuchRoomException("The room " + raum + " does not exist");
26 } else {
27 getRoom(raum).setInv(inv);
28 }
29 }
30
31 /**
32 * Gibt eine Liste der Character Objekte die sich in den Räumen befinden zurück
33 *
34 * @return Liste von Character Objekten
35 */
36 public List getNpcList() {
37 return this.npcList;
38 }
39
40 /**
41 * Fügt einen {@link org.jzuul.engine.rooms.TransitionRoom} in die Karte ein.
42 *
43 * @param name der Name des Raumes
44 * @param preconditions die Namen von GameObject Objekten die Vorbedingungen für einen Übergang sind
45 * @param isFinal ist das das Ende des Spieles?
46 * @param target der Name der Zielkarte
47 */
48 public void addTransRoom(String name, String[] preconditions, boolean isFinal, String target) {
49 TransitionRoom newRoom = new TransitionRoom(name, preconditions, isFinal, target);
50 this.addRoom(newRoom);
51 }
52
53 /**
54 * Zugriff auf den Namen der Karte.
55 *
56 * @return der Name der Karte
57 */
58 public String getName() {
59 return this.name;
60 }
61
62 /**
63 * Zugriff auf den Namen des Startraumes der Karte
64 *
65 * @return der Name des Startraumes.
66 */
67 public String getStartRoom() {
68 return this.startRoom;
69 }
70
71 /**
72 * Zugriff auf das Room Objekt des Startraumes
73 *
74 * @return das Room Objekt des Startraume, null wenn kein Startraum gesetzt ist
75 */
76 public Room getStartRoomObj() {
77 return this.getRoom(this.startRoom);
78 }
79
80 /**
81 * Teilt allen Räumen in der Karte ein Event mit
82 *
83 * @param eventId eine Id eines Events
84 * @see org.jzuul.engine.Event
85 */
86 public void notifyRooms(int eventId) {
87 for (Iterator roomIter = gameMap.values().iterator(); roomIter.hasNext();) {
88 EventListener element = (EventListener) roomIter.next();
89 element.doEvent(eventId);
90 }
91 }
92
93 /**
94 * Setzt einem Raum einen EventHandler
95 *
96 * @param room der Name des Raumes für den EventHandler
97 * @param eventName der Name des Events
98 * @param eventHandler das EventHandler Objekt das das Event behandelt
99 * @see org.jzuul.engine.Event
00 */
01 public void setRoomEventHandler(String room, String eventName, EventHandler eventHandler) {
02 Room r = getRoom(room);
03 r.setHandler(eventName, eventHandler);
04 }
05
06 /**
07 * Weist einem Raum einen auf ein Bild verweisenden Stream zu
08 *
09 * @param roomName der Name des Raumes dem der Stream zugewiesen werden soll
10 * @param imageStream der Stream der auf das Bild verweist.
11 */
12 public void setRoomImageStream(String roomName, InputStream imageStream) {
13 getRoom(roomName).setImageStream(imageStream);
14 }
15
16}
17