# NPC Gespräche?



## Hercules (2. Feb 2013)

Hi!
Habe bei meinen alten RPG sowas schon eingebaut, aber total aufwendig und umständlich.
Wollte mal fragen wie ihr das machen würdet?

Ich habe halt meine NPCs mit X/Y und Image.
Jetzt hab ich gemacht das abgefragt wird ob der Spieler neben einen NPC steht und ENTER drückt.

Aber wie regel ich das mit dem Gesprächen.
Da es ein RPG ist wird es 500+ Gespräche geben.

Wie sollte ich die erstellen?
Bei meinen ersten Spiel hatte ich das z.B. so gemacht:

String[][] msg = new String[2][3];
msg[0][0] = "Hallo!";
msg[0][1] = "Was kann ich für dich tun?";
msg[0][2] = "";

msg[1][0] = "Ich suche einen Weg hier raus.";
msg[1][1] = "Können sie mir dabei helfen?";
msg[1][2] = "Ich bezahle auch!";

Das waren dann zwei Textboxen. Das hab ich dann als Objekt dem NPC gegeben.
Aber da gibt es bestimmt doch eine bessere Lösung oder?

Habe schon überlegt für jede Textbox eine txt Datei zu machen und die dann auszulesen aber da habe ich ja nen Ordner mit keine Ahnung 3000+ Textdateien.....


----------



## Cola_Colin (2. Feb 2013)

Schon alleine zur einfachen Anpassung sollte das ganze auf jeden Fall in irgendwelche Dateien ausgelagert werden.
Statt für jeden String eine eigene Datei zu machen würde ich aber eher eine Datei verwenden, in der die einzelnen Textfetzen alle unter je einer ID stehen. Dem NPC in deiner Spiellogik weist du dann einfach den Text anhand einer ID (hier würde ich nen kurzen Textzug verwenden, z.B.: "InnKeeperGreeting_1")
Eventuell ein einfachen XML-Format oder ähnliches würde sich anbieten um die Zuordnung von ID zu text zu machen und das ganze leicht ins Programm zu lesen.
Dort wäre das ganze dann eine einfache HashMap von ID auf den entsprechenden Text.


----------



## Skanky (4. Feb 2013)

Hi ho, ich habe vor langer zeit vor dem selben Problem gestanden 

Ich habe das ganze wie folgt gelöscht:

Klassen:
DialogBox (für die Grafische anzeige)
DialogControl (für das auswählen der Texte und bestätigen)
Dialog (das ding was die texte usw enthält)

(( Wer RS-Fehler in den Kommentaren findet, darf sie mir gerne via PN berichtigt zukommen lassen  ))


```
import java.util.ArrayList;
import java.util.List;

import core.game.structures.environment.Resource;
import core.game.unit.NonPlayerCharacter;
import core.game.unit.actions.DoCollect;
import core.game.unit.actions.DoNothing;
import core.game.unit.actions.Doable;
import core.helper.Translator;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

/**
 * 
 * @author RICO
 * TODO implement reading an xml file with dialogs
 */
public class Dialog {

    private static final Logger LOGGER = LogManager.getLogger(Dialog.class);

    /**
     * All actions that the player can select in a dialog
     */
   public enum Action {
       QUIT, RETURN, DO_NPC_ACTION, DO_PLAYER_ACTION, TALK
   }

   private Dialog parent;

   private Class ofClass;

   private Doable doable;

   private String dialog;

   private String option;

   private Action action;

   private List<Dialog> options;

	/**
	 * Constructor for dialog
	 * 
	 * @param dialog String
	 */
	public Dialog(final String dialog) {
		this.dialog = dialog;
        parent = null;
        option = "";

        options = new ArrayList<Dialog>();
	}

    /**
     *
     * @param dialog String
     * @param option String
     * @param parent Dialog
     */
    private Dialog(final String dialog, final String option, final Dialog parent) {
        this(dialog);
        this.option = option;
        this.parent = parent;
    }

	/**
	 * Constructor for dialog as option
	 * 
	 * @param dialog String
	 * @param option String
	 * @param parent Dialog
	 * @param action Action
	 */
	public Dialog(final String dialog, final String option, final Dialog parent, final Action action) {
		this(dialog, option, parent);
		this.action = action;
	}

    /**
     * Constructor for dialog as option
     *
     * @param dialog String
     * @param option String
     * @param parent Dialog
     * @param action Action
     * @param ofClass Class
     */
    public <T> Dialog(final String dialog, final String option, final Dialog parent, final Action action, final Class<T> ofClass) {
        this(dialog, option, parent, action);
        this.ofClass = ofClass;
    }

    /**
     *
     * @param dialog String
     * @param option String
     * @param parent Dialog
     * @param action Action
     * @param doable Doable
     */
    public Dialog(final String dialog, final String option, final Dialog parent, final Action action, final Doable doable) {
        this(dialog, option, parent, action);
        this.doable = doable;
    }

	/**
	 * Return a default return option for the dialog to go back to the parent
	 * 
	 * @param parent Dialog
	 * @return Dialog with return action
	 */
	public static Dialog returnOption(final Dialog parent) {
		final String option = Translator.translate("dialog.default.option.return");
		return new Dialog("", option, parent, Action.RETURN);
	}

	/**
	 * Return a default quit option for the dialog
	 * 
	 * @param parent Dialog
	 * @return Dialog with quit action
	 */
	public static Dialog quitOption(final Dialog parent) {
		final String option = Translator.translate("dialog.default.option.quit");
		return Dialog.quitOption(parent, option);
	}

	/**
	 * Return a quit option with the given sentence
     *
	 * @param parent Dialog
	 * @param option String
	 * @return Dialog with quit action
	 */
	private static Dialog quitOption(final Dialog parent, final String option) {
		return new Dialog("", option, parent, Action.QUIT);
	}
	
	/**
	 * Add a option to this dialog
	 * 
	 * @param dialog Dialog
	 */
	public void addOption(final Dialog dialog) {
		options.add(dialog);
	}

	/**
	 * Return a List with all options from this dialog
	 * 
	 * @return List(Dialog) options
	 */
	public List<Dialog> getOptions() {
		return options;
	}

	/**
	 * Return the main dialog from this dialog
	 * 
	 * @return String dialog
	 */
	public String getDialog() {
		return dialog;
	}

	/**
	 * Return the option string from this dialog
	 * 
	 * @return String option
	 */
	public String getOption() {
		return option;
	}

	/**
	 * Return the parent dialog from this dialog
	 * 
	 * @return Dialog parent
	 */
	public Dialog getParent() {
		return parent;
	}

	/**
	 * Return the action key
	 * 
	 * @return Action action
	 */
	public Action getAction() {
		return action;
	}

    /**
     * Return a Class that implements doable
     *
     * @return doable
     */
    @SuppressWarnings("unchecked")
    public Doable getDoable() {
        if (ofClass != null) {
            if (Resource.class.isAssignableFrom(ofClass)) {
                return new DoCollect(ofClass);
            }

            if (NonPlayerCharacter.class.isAssignableFrom(ofClass)) {
                return new DoNothing(); // TODO Replace with a doable for fight
            }

        } else if (doable != null) {
            return doable;
        }

        return new DoNothing();
    }
}
```

Und so sieht das ganze im Spiel aus:







Das ganze ist halt so aufgebaut, das es immer ein Haupttext (dialog), ein Option (mit einen kurzen Satz) und eine Aktion gibt (irgendwas, was der NPC dann tun soll), Hinter einer Option kann sich ein weitere Dialog verbergen oder halt eine Aktion. Und natürlich kann man damit elendig lange und komplexe Dialoge aufbauen.

Die Texte kommen aus einer .properties Datei und sehen so aus:

```
#citizen
citizen.name.male.0 = Johann
citizen.name.male.1 = Hinrich
citizen.name.male.2 = Hans
citizen.name.male.3 = Peter
citizen.name.male.4 = Velten
citizen.name.male.5 = Jakob
citizen.dialog.opening.0 = Seit gegrüßt mein Lord, was kann ich für Sie tun?
citizen.dialog.opening.1 = Oh Herr, habt Ihr etwas das ich erledigen soll?
citizen.dialog.collect.question = Welche Rohstoffe soll ich Sammeln mein Lord?
citizen.dialog.option.collect = Folgende Ressourcen sammeln...
citizen.dialog.option.goHome = Geht nach Hause.
citizen.dialog.option.resourceCollect = %s sammeln.
citizen.dialog.collect.confirmation = Sehr gern mein Lord.
```

Das ist auch gleich so aufgezogen, dass man den ganzen Spaß auch in mehren Sprachen nutzen kann. Es gibt also eine Datei für DE eine für EN usw....

Ich hoffe ich konnte dir einen Weg zu Ziel zeigen.  

EDIT: 
Ach und erstellt wird ein Dialog dann wie folgt:

```
dialog = new Dialog(getRandomOpeningDialog());

String textCollect = Translator.translate("citizen.dialog.collect.question");
String optionCollect = Translator.translate("citizen.dialog.option.collect");
Dialog collect = new Dialog(textCollect, optionCollect, dialog, Dialog.Action.TALK);

String textGoHome = Translator.translate("citizen.dialog.option.goHome");
Dialog goHome = new Dialog("", textGoHome, dialog, Dialog.Action.DO_NPC_ACTION, new DoGoHome());
String optionCollectResource = Translator.translate("citizen.dialog.option.resourceCollect");

String optionWood = String.format(optionCollectResource, Translator.translate("resource.wood.name"));
collect.addOption(new Dialog("", optionWood, collect, Dialog.Action.DO_NPC_ACTION, Tree.class));

String optionGold = String.format(optionCollectResource, Translator.translate("resource.gold.name"));
collect.addOption(new Dialog("", optionGold, collect, Dialog.Action.DO_NPC_ACTION, GoldVein.class));

collect.addOption(Dialog.returnOption(dialog));

Dialog quit = Dialog.quitOption(dialog);

dialog.addOption(collect);
dialog.addOption(goHome);
dialog.addOption(quit);
```

Später, will ich dann die Dialoge in einer XML Datei haben. Bis jetzt war ich aber zu faul um das zu Implementieren.

grüße Skanky (rico)


----------



## DerFeivel (5. Feb 2013)

Also mein naiver Ansatz wäre:

Jedes NPC-Objekt hat seinen eigenen Satz von Textbausteinen 
da das Text(gedankengut) Eigenschaft des NPCs ist (so hast du es ja bereits gemacht gehabt, wenn ich dich richtig verstanden habe).

Die Pflege der Textbausteine würde ich ähnlich machen, wie es skanky beschrieben hat.
Allerdings würde ich die Addressierung nicht über Ziffern/Arrayindizes vornehmen.

Eher würde ich die Properties-Datei wie folgt aufbauen:


```
npcs = Hans, Friedrich, Bert

Hans.gender = male
Hans.type = citizen
Hans.dialog.opening = Blablablub
Hans.dialog.opening.option.0 = Ich will zu meiner Mami!
Hans.dialog.opening.option.1 = Deine Mudder!! 

Friedrich.gender = female
Friedrich.type = doxy
Friedrich.dialog.opening = Hallo süßer
```

Wäre für mich zumindest angenehmer mit zu arbeiten. 
Über PropertiesConfiguration aus dem apache.commons-Packet könntest du dann recht angenehm zunächst die Liste der zu verwendenden NPCs aus npcs (oberste Zeile) auslesen und dann 
für jeden NPC die Konfiguration durchführen bzw. den Dialog konfigurieren.


Um nicht zuviele oder eine zu große Datei zu haben, könntest du die Properties-Datei auch nach Regionen in deinem Spiel aufteilen (also eine properties-Datei für alle NPCs in Region) oder nach
NPC-Typ (also eine properties-Datei für alle NPCs vom Typ Citizen und eine für alle NPCs vom Typ doxy)


----------

