Skip to content

Instantly share code, notes, and snippets.

Created February 10, 2016 09:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/3c2dc519c7245a892789 to your computer and use it in GitHub Desktop.
Save anonymous/3c2dc519c7245a892789 to your computer and use it in GitHub Desktop.
Interactive Fiction, erster Versuch
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.function.Function;
public abstract class Event<S extends State> {
List<String> questions = new ArrayList<>();
List<Function<S, String>> methods = new ArrayList<>();
protected int numTriggers;
private static Scanner intScanner = new Scanner(System.in);
protected Event() {
this.addOptions();
}
/** prüft, ob sich dieser Event in Zustand s ereignen kann */
public abstract boolean isTriggered(S s);
/** beschreibt, wie Zustand s bei Ausführen des Events verändert wird. */
public abstract void effect(S s);
/** Textrepräsentation des Events, d.h. wie wird über dieses Ereignis erzählt. */
public abstract String reportEvent();
/** führt die Effekte eines Events aus, insb. wird effect() aufgerufen und Verändert den Zustand s. */
public final void applyEffects(S s) {
numTriggers += 1;
effect(s);
}
/** fügt Interaktionsmöglichkeiten an diesem Punkt der Geschichte ein */
public void addOptions() {
// default: keine Eingriffsmöglichkeit in die Geschichte an diesem Punkt
}
public final boolean userActionPossible() {
return questions.size() > 0;
}
public String userAction(S s) {
for (int i = 0; i < questions.size(); i++) {
String optionStr = String.format("(%d) %s", i+1, questions.get(i));
Story.pprintln(optionStr);
}
int choice;
do {
choice = intScanner.nextInt();
} while (choice < 1 || choice > questions.size());
System.out.println();
Function<S, String> func = methods.get(choice-1);
return func.apply(s);
}
protected void addOption(String optionStr, Function<S, String> affectFunc) {
questions.add(optionStr);
methods.add(affectFunc);
}
}
public class MainGame {
public static void main(String[] args) {
Story story = new SchwesternStory();
story.tellStory();
}
}
public class SchwesternStory extends Story {
class SchwesternState extends State {
String locationMilly;
String locationEleanor;
boolean vaterHatGetrunken;
protected String chapter = "Heimkehr";
}
protected SchwesternState currentState() {
return (SchwesternState) currentState;
}
protected void initializeState() {
currentState = new SchwesternState();
}
protected void initializeEvents() {
DEFAULT_EVENT = new Event<SchwesternState>() {
@Override
public boolean isTriggered(SchwesternState s) {
return true;
}
@Override
public String reportEvent() {
return String.format("Die Glocken von St. Paul's schlugen in der Ferne %d Uhr.", time);
}
@Override
public void effect(SchwesternState s) {
// Da time automatisch erhöht wird, passiert hier sonst erstmal nichts
}
};
addEvent(new Event<SchwesternState>() {
@Override
public boolean isTriggered(SchwesternState s) {
return time == 0;
}
@Override
public String reportEvent() {
return "Die Kutsche erreichte London in der Morgendämmerung. "
+ "Milly, die zusammengekauert in einer Ecke des Wagens geschlafen hatte, schreckte auf, als Eleanor ihre Wange berührte.\n"
+ "Vor dem Tor ihres Elternhauses wartete George, der Butler.\n"
+ "'Es ist gut, dass die jungen Damen so schnell gekommen sind', begrüßte er sie ernst. "
+ "'Ihrem Vater geht es sehr schlecht.'\n\n" + "Die Mädchen blickten sich kurz an, dann... ";
}
@Override
public void addOptions() {
addOption("...fassten sie sich an der Hand und folgten George ins Innere des Hauses.",
this::beideInsHaus);
addOption(
"...eilte Eleanor ans Bett ihres Vaters. Milly verharrte noch einige Augenblicke reglos, dann stieg sie wieder in die Kutsche.",
this::nurEleanorInsHaus);
addOption(
"...wies Eleanor den Kutscher an, sie schnellstens zu Dr. Hargreaves zu bringen. Milly betrat zögernd das Haus.",
this::eleanorZuHargreaves);
}
public String nurEleanorInsHaus(SchwesternState s) {
s.locationMilly = "chiswick";
s.locationEleanor = "vater";
return "Während Milly in der Kutsche ihren Gedanken nachhing, stieg Eleanor die steilen Treppen zum Schlafzimmer ihres Vaters hinauf. "
+ "Seit sie ein kleines Mädchen gewesen war, hatte sie dieses Zimmer nicht mehr betreten, und schon damals hatte sie, wenn ein "
+ "Besuch in dem engen, verdunkelten Raum unvermeidlich geworden war, stets eine unbeschreibliche "
+ "Furcht erfüllt, .";
}
public String eleanorZuHargreaves(SchwesternState s) {
s.locationMilly = "vater";
s.locationEleanor = "hargreaves";
return "Milly stieg die steilen Treppen zum Schlafzimmer ihres Vaters hinauf. "
+ "Seit sie ein kleines Mädchen gewesen war, hatte sie dieses Zimmer nicht mehr betreten. "
+ "Ihre Eltern hatten es ihr streng untersagt - wenn auch beide aus höchst unterschiedlichen Gründen, wie sie heute wusste.\n\n ***\n\n"
+ "Unterdessen hing Eleanor in der Kutsche ihren Gedanken nach. Seit ihrer Verlobung mit Edward Chiswick "
+ "hatte sie Dr. Hargreaves nicht wiedergesehen. Es würde eine unangenehme, ja peinliche Begegnung werden.";
}
public String beideInsHaus(SchwesternState s) {
s.locationMilly = "vater";
s.locationEleanor = "vater";
return "'Wir müssen jetzt beide stark sein', wisperte Eleanor. Milly nickte nur und ballte die Faust um den Gegenstand in ihrer Manteltasche.";
}
@Override
public void effect(SchwesternState s) {
s.id = "s1";
}
});
addEvent(new Event<SchwesternState>() {
@Override
public boolean isTriggered(SchwesternState s) {
return s.id == "s1" && s.locationEleanor == "vater" && s.locationMilly != "vater";
}
@Override
public void effect(SchwesternState s) {
// nur individuelle Effekte nach der Spielerentscheidung
}
@Override
public String reportEvent() {
return "Eleanor blieb auf der Schwelle des Krankenzimmers stehen. Der Geruch, der ihr vom Bett entgegenschlug, war unerträglich."
+ "\n...\n 'Die Flasche', krächzte er und deutete zum Schreibtisch. Dort stand ein Flacon aus dunklem Glas, dessen Etikett "
+ " mit, wie Eleanor zu erkennen glaubte, kyrillischen Schriftzeichen beschriftet war.";
}
@Override
public void addOptions() {
addOption(
"Sie brachte das Fläschchen zum Bett, wo ihr Vater den Korken mit dem Mund herausriss und die dunkelrote Flüssigkeit gierig hinunterstürzte.",
this::vaterTrinkt);
addOption(
"Sie war im Begriff, das Flacon zu ergreifen, als ihr Blick auf einen Brief fiel, der daneben lag. 'Geliebte E.', stand dort.",
this::eleanorFindetBrief);
addOption(
"'Vater, ich denke nicht, dass du jetzt Alkohol trinken solltest', sagte Eleanor. 'Ich werde George bitten, uns stattdessen eine Tasse Tee zu kochen.'",
this::vaterTrinktNicht);
}
public String vaterTrinkt(SchwesternState s) {
s.vaterHatGetrunken = true;
return "";
}
public String eleanorFindetBrief(SchwesternState s) {
s.chapter = "Betrug";
return "";
}
public String vaterTrinktNicht(SchwesternState s) {
s.vaterHatGetrunken = false;
return "";
}
});
addEvent(new Event<SchwesternState>() {
@Override
public boolean isTriggered(SchwesternState s) {
return time > 5 && !s.vaterHatGetrunken;
}
@Override
public void effect(SchwesternState s) {
s.id = "end";
}
@Override
public String reportEvent() {
return "...\n Zärtlich zupfte sie das blutverschmierte Nachthemd ihrer Schwester zurecht.\n 'Nun bin nur noch ich übrig', flüsterte sie."
+ "'George, bitte bringen Sie mir eine Tasse Tee.'\n\nENDE";
}
});
addEvent(new Event<SchwesternState>() {
@Override
public boolean isTriggered(SchwesternState s) {
return time > 5 && s.vaterHatGetrunken;
}
@Override
public void effect(SchwesternState s) {
s.id = "end";
}
@Override
public String reportEvent() {
return "'Du bleibst?' Seine Augen füllten sich mit Tränen.\n "
+ "'Selbstverständlich bleibe ich, Vater', antwortete sie. 'Wir haben jetzt nur noch uns.'"
+ "'George, bringen Sie uns bitte eine Tasse Tee.'\n\nENDE";
}
});
}
}
public class State {
protected String id;
public State() {
id = "start";
}
public boolean isEndState() {
return id.toLowerCase().startsWith("end");
}
}
import java.util.ArrayList;
import java.util.List;
public abstract class Story {
private static final int MAX_TRIGGERS_SAME_EVENT = 1;
private static final int MAX_COL = 80;
protected List<Event> events = new ArrayList<>();
protected State currentState = new State();
protected int time;
public Event DEFAULT_EVENT;
protected final void addEvent(Event event) {
events.add(event);
}
private final void initialize() {
initializeState();
initializeEvents();
}
abstract protected void initializeState();
abstract protected void initializeEvents();
public final void tellStory() {
initialize();
do {
Event e = findTriggeredEvent();
e.applyEffects(currentState);
pprintAbsatz(e.reportEvent());
if (e.userActionPossible()) {
pprintAbsatz(e.userAction(currentState));
}
timePasses();
} while (!currentState().isEndState());
}
private State currentState() {
return currentState;
}
protected static void pprint(String s) {
if (s == null || s.isEmpty())
return;
String[] words = s.split(" ");
int col = 0;
for (String word : words) {
if (col + word.length() > MAX_COL) {
System.out.println();
col = 0;
}
System.out.print(word + " ");
col += word.length() + 1;
int cr = word.indexOf("\n");
if (cr >= 0)
col = word.length() - cr;
}
}
protected static void pprintln(String s) {
pprint(s);
System.out.println();
}
protected static void pprintAbsatz(String s) {
pprintln(s);
System.out.println();
}
private Event findTriggeredEvent() {
for (Event event : events) {
if (event.isTriggered(currentState) && event.numTriggers < MAX_TRIGGERS_SAME_EVENT)
return event;
}
return DEFAULT_EVENT;
}
private void timePasses() {
time += 1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment