1 /*
2  *  CVS: $Id: Character.java,v 1.22 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.text.MessageFormat;
27import java.util.Iterator;
28import java.util.List;
29import java.util.Random;
30
31import org.jdom.Element;
32import org.jzuul.engine.gui.GuiInterface;
33import org.jzuul.engine.rooms.*;
34
35/**
36 * Ein Character ist ein interaktives GameObject im Sinne eines Lebewesens
37 * 
38 * 
39 * @version $Revision: 1.22 $
40 */
41public class Character extends GameObject {
42
43    /**
44     * Enthält die Dialoge die ein Charakter führen kann
45     */
46    private List dialogs;
47
48    /**
49     * Die Nummer des aktuellen Dialogs
50     */
51    private int currentDialog = 0;
52
53    /**
54     * Das Inventar des Charakters
55     */
56    private Inventory inv;
57
58    /**
59     * enthält den aktuellen Room in dem sich der Character aufhält
60     */
61    protected Room currentRoom;
62
63    /**
64     * enthält das aktuell aktive Dialog Objekt
65     */
66    protected Dialog dialog;
67
68    /**
69     * für diverse Zwecke ein Random Object
70     */
71    private Random r;
72
73    /**
74     * Instanzieiert einen character
75     * 
76     * @param name
77     *            Der Name des Charakters
78     */
79    public Character(String name) {
80        super(name);
81        r = new Random();
82        inv = new Inventory(Inventory.UNLIMITED_INVENTORY);
83        this.takeable = false;
84        this.useable = false;
85    }
86
87    /**
88     * Führt eine Aktion aus (z.B moveRandom, leaveRoom) Die Namen für die
89     * Aktionen sind im gamefile.dtd definiert
90     * 
91     * @param actionName
92     *            der Name der Aktion
93     */
94    public void doAction(String actionName) {
95        Engine.debug(this.getName() + ": " + actionName, 2); //$NON-NLS-1$
96        if (actionName.equals("moveRandom")) { //$NON-NLS-1$
97            moveRandom();
98        }
99        if (actionName.equals("leaveRoom")) { //$NON-NLS-1$
00            this.leaveRoom();
01        }
02    }
03
04    /**
05     * Führt eine Bewegung eines Characters in eine mögliche Himmelsrichtung in
06     * Abhängigkeit von einer Zufallsvariable aus.
07     */
08    protected void moveRandom() {
09        if (r.nextBoolean()) { return; }
10        this.leaveRoom();
11    }
12
13    /**
14     * Sorgt dafür das der Character den Raum verlässt.
15     *  
16     */
17    protected void leaveRoom() {
18        if (this.currentRoom == null) { return; }
19
20        List rooms = currentRoom.getWays();
21        Object[] formatArgs = { Helpers.firstToUpper(this.getName()) };
22        if (inRoomWithPlayer()) {
23            Engine.gui.printlnI(MessageFormat.format(Messages.getString("CHARACTER_LEAVES_ROOM"),formatArgs)); //$NON-NLS-1$
24        }
25        currentRoom.getContent().deleteGameObject(this.getName());
26        this.currentRoom = (Room) rooms.get(new Random().nextInt(rooms.size()));
27        currentRoom.getContent().addGameObject(this);
28        if (inRoomWithPlayer()) {
29            Engine.gui.printlnI(MessageFormat.format(Messages.getString("CHARACTER_ENTERS_ROOM"), formatArgs)); //$NON-NLS-1$
30        }
31    }
32
33    /**
34     * Sagt etwas im Namen des Characters wenn der Charakter sich im selben Raum
35     * wie der Spieler befindet.
36     * 
37     * @param something
38     *            Das was gesagt werden soll
39     */
40    public void say(String something) {
41        if ((inRoomWithPlayer() || (this.currentRoom == null)) && (something != null)& (!something.equals("")) ) { //$NON-NLS-1$
42            Engine.gui.println(Helpers.firstToUpper(this.getName()) + ": " + something, GuiInterface.BLUE); //$NON-NLS-1$
43        } else {
44            Engine.debug(this.getName() + " won't say something", 1); //$NON-NLS-1$
45        }
46    }
47
48    /**
49     * Stellt fest ob der Character sich im selben Room wie der Spieler befindet
50     * 
51     * @return true wenn der Character im selben Room wie der Spieler ist, sonst
52     *         false
53     */
54    public boolean inRoomWithPlayer() {
55        if (this.currentRoom == null) {
56            // I'm taken!
57            return false;
58        }
59        return this.currentRoom.getName().equals(Engine.player.getCurrentRoom().getName());
60    }
61
62    /**
63     * Setzt den aktiven Room für den Character
64     * 
65     * @param newRoom
66     *            der Room in dem sich der Player ist
67     */
68    public void setCurrentRoom(Room newRoom) {
69        this.currentRoom = newRoom;
70    }
71
72    /**
73     * Wird von dem Befehl "talk" aufgerufen uns sollte zu einem Dialog führen
74     * 
75     * @return true bei erfolg des Dialoges, false otherwise
76     */
77    public boolean talkTo() {
78        if (this.dialog == null) {
79            if ((this.dialogs != null) && (this.dialogs.get(currentDialog) != null)
80                    && checkPreconditions(currentDialog)) {
81                this.dialog = (Dialog) dialogs.get(currentDialog);
82                if (this.dialog.talk()) {
83                    return true;
84                } else {
85                    this.say(Messages.getString("CHARACTER_WONT_TALK")); //$NON-NLS-1$
86                    return false;
87                }
88            } else {
89                this.say(Messages.getString("CHARACTER_WONT_TALK")); //$NON-NLS-1$
90                return false;
91            }
92        } else {
93            return this.dialog.talk();
94        }
95    }
96
97    /**
98     * Fordert den Character auf etwas zu nehmen. Die aufrufende Methode ist
99     * dafür zuständig den Gegenstand zu entfernen
00     * 
01     * @param obj
02     *            Das GameObject was genommen werden soll
03     * @return true falls der Gegenstand genommen wurde, false sonst
04     */
05    public boolean take(GameObject obj) {
06        if (isPrecondition(obj.getName()) 
07                || Engine.player.getTargetList().isGiveTarget(this.getName(), obj.getName())) {
08            this.say(Messages.getString("CHARACTER_THANKS")); //$NON-NLS-1$
09            inv.addGameObject(obj);
10            Engine.player.findAndDeleteGameObject(obj.getName());
11            return true;
12        } else {
13            return false;
14        }
15    }
16
17    /**
18     * Gibt das Objekt als JDOM Element XML zurück.
19     * 
20     * @return den Character als JDOM XML Element
21     */
22    public Element toElement() {
23        Element characterElement = new Element("character"); //$NON-NLS-1$
24        characterElement.setAttribute("name", this.getName()); //$NON-NLS-1$
25        characterElement.setAttribute("dialog", String.valueOf(this.currentDialog + 1)); //$NON-NLS-1$
26        return characterElement;
27    }
28
29    /**
30     * Weist dem Charakter seine Dialoge zu
31     * 
32     * @param dialogs
33     *            eine Liste von Dialog Objekten
34     */
35    public void addDialogs(List dialogs) {
36        for (Iterator i = dialogs.iterator(); i.hasNext();) {
37            Dialog element = (Dialog) i.next();
38            element.setCharacter(this);
39        }
40        this.dialogs = dialogs;
41    }
42
43    /**
44     * Wird von dem AuswahlListener aufgerufen nachdem der Spieler eine
45     * DialogObject ausgewählt hat
46     * 
47     * @param type
48     *            Ein Event.DIALOG_ wert
49     */
50    public void pushAnswer(int type) {
51        this.doEvent(type);
52        Object[] formatArgs = { Helpers.firstToUpper(this.name) };
53        switch (type) {
54        case Event.DIALOG_CONTINUE:
55            talkTo();
56            break;
57        case Event.DIALOG_END_FAILURE:
58            Engine.gui.setDefaultActionListener();
59            Engine.gui.println(MessageFormat.format(Messages.getString("CHARACTER_ENDS_DIALOG"),formatArgs)); //$NON-NLS-1$
60            this.dialog.resetPhase();
61            break;
62        // das ist absicht:
63        case Event.DIALOG_END_SUCCESS:
64        case Event.DIALOG_CUSTOM_RESULT_1:
65        case Event.DIALOG_CUSTOM_RESULT_2:
66        case Event.DIALOG_CUSTOM_RESULT_3:
67            Engine.gui.setDefaultActionListener();
68            Engine.gui.println(MessageFormat.format(Messages.getString("CHARACTER_ENDS_DIALOG"),formatArgs)); //$NON-NLS-1$
69            this.dialog = null;
70            this.currentDialog++;
71            break;
72        default:
73            Engine.gui.setDefaultActionListener();
74            Engine.gui.println(MessageFormat.format(Messages.getString("CHARACTER_ENDS_DIALOG"),formatArgs)); //$NON-NLS-1$
75        }
76    }
77
78    /**
79     * Überprüft die Vorbedingungen für einen Dialog
80     * 
81     * @param dialogNr
82     *            die Dialognummer für den die Vorbedingungen überpüft werden
83     *            sollen
84     * @return true wenn die Vorbedingungen erfüllt sind, false otherwise
85     */
86    protected boolean checkPreconditions(int dialogNr) {
87        if (dialogs.size() >= dialogNr) {
88            Dialog d = (Dialog) dialogs.get(dialogNr);
89            List precons = d.getPreconditions();
90            if (precons == null) { return true; }
91            for (Iterator iter = precons.iterator(); iter.hasNext();) {
92                String element = (String) iter.next();
93                if (!inv.containsGameObject(element)) { return false; }
94            }
95        }
96        return true;
97    }
98
99    /**
00     * Überprüft ob das GameObject mit dem Namen objName eine Dialogvorbedingung
01     * ist.
02     * 
03     * @param objName
04     *            der Name des Objekts das überpüft werden
05     * @return true wenn das spezifizierte Objekt eine Vorbedingung darstellt,
06     *         false otherwise
07     */
08    protected boolean isPrecondition(String objName) {
09        if ((dialogs != null) && (dialogs.size() > currentDialog)) {
10            Dialog d = (Dialog) dialogs.get(currentDialog);
11            List precons = d.getPreconditions();
12            if (precons == null) { return true; }
13            return precons.contains(objName);
14        } else {
15            return false;
16        }
17
18    }
19
20    /**
21     * Setzt die aktuelle Dialognummer
22     * 
23     * @param currentDialog
24     *            die neue Dialognummer
25     */
26    public void setDialog(int currentDialog) {
27        this.currentDialog = currentDialog;
28    }
29
30    /**
31     * Führt das event mit der Id eventId aus. Spezifiziert folgende Defaults:
32     * Event.TAKEUP: setzt den aktullen Raum auf null Event.DROP: setzt den
33     * aktuellen Raum auf den aktuellen Raum des Spielers
34     * 
35     * @param eventId
36     *            eine Event Id
37     */
38    public void doEvent(int eventId) {
39        super.doEvent(eventId);
40        switch (eventId) {
41        case Event.TAKEUP:
42            this.setCurrentRoom(null);
43            break;
44        case Event.DROP:
45            this.setCurrentRoom(Engine.player.getCurrentRoom());
46            break;
47        }
48
49    }
50
51    /**
52     * Erstellt eine 1 zu 1 Kopie des Character Objektes
53     * 
54     * @return eine Kopie des Character Objekt
55     */
56    public GameObject copy() {
57        Character newChar = new Character(this.getName());
58        super.cloneInto(newChar);
59        newChar.inv = this.inv.copy();
60        newChar.currentDialog = this.currentDialog;
61        // NOTE Again, not really save:
62        newChar.dialogs = this.dialogs;
63        newChar.currentRoom = this.currentRoom;
64        newChar.dialog = this.dialog;
65        return newChar;
66    }
67    
68    public boolean isCharacter() {
69        return true;
70    }
71
72}