Created
February 10, 2016 09:40
-
-
Save anonymous/3c2dc519c7245a892789 to your computer and use it in GitHub Desktop.
Interactive Fiction, erster Versuch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MainGame { | |
public static void main(String[] args) { | |
Story story = new SchwesternStory(); | |
story.tellStory(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | |
} | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class State { | |
protected String id; | |
public State() { | |
id = "start"; | |
} | |
public boolean isEndState() { | |
return id.toLowerCase().startsWith("end"); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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