Created
November 1, 2015 04:59
-
-
Save WilliamBundy/ae62a2cad73a4504049e to your computer and use it in GitHub Desktop.
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
#!/usr/env python3 | |
game = """ | |
Quick--do you have an apple? | |
{switch} | |
{choice 1 "Yeah, I do"} | |
{choice 2 "*pats pockets* Nope"} | |
{case 1} {do "set({'apple'}, 'player', 'items')"} {goto "You run into John Appleseed"} | |
{case 2} {goto "You run into John Appleseed"} | |
{label "You run into John Appleseed"} | |
{addactor $playerName align=right} | |
{addactor John align=left} | |
Hello | |
Can you help? | |
{switch} | |
{choice 1 "I can help"} | |
{choice 2 "I can't help"} | |
{case 1} {goto "That's great!"} | |
{case 2} {goto "Are you sure?"} | |
if you see this, there's a problem | |
{label "Are you sure?"} | |
{switch} | |
{choice 1 "I'm sure"} | |
{choice 2 "I'm not sure"} | |
{case 1} Oh, sorry. I won't bother you further. {close} | |
{case 2} {goto "That's great!"} | |
{label "That's great!"} | |
I need an apple. | |
{if 'apple' in get('player', 'items')} | |
{actor $playerName} Here you go. | |
{actor John} Thanks! | |
{do scene['actor'] = None; get('player')['gold'] += 10} | |
He gives you 10 gold. | |
{close} | |
{end} | |
Can you get one for me? | |
{switch} | |
{choice 1 Yes} | |
{choice 2 No} | |
{case 1} Thanks; good luck! {close} | |
{case 2} It's okay, don't worry about it. {close} | |
""" | |
OpenTag = "{" | |
CloseTag = "}" | |
class DialogueTree: | |
def __init__(self, raw): | |
self.raw = raw | |
self.width = 40 | |
self.tags = {} | |
self.tagindex = 0 | |
self.parse() | |
self.actions = {} | |
def action_addactor(state, tag): | |
name = tag["contents"][0] | |
if name.startswith("$"): | |
name = state["variables"][name[1:]] | |
actor = {"name": name, "align": "left"} | |
for item in tag["contents"][1:]: | |
if "=" in item: | |
words = item.split("=") | |
lenw = len(words) | |
if lenw == 2: | |
actor[words[0]] = words[1] | |
elif lenw >= 3: | |
actor[words[0]] = words[1:] | |
state["actors"][name] = actor | |
state["actor"] = actor | |
self.actions["addactor"] = action_addactor | |
def action_setactor(state, tag): | |
name = tag["contents"][0] | |
if name.startswith("$"): | |
name = state["variables"][name[1:]] | |
state["actor"] = state["actors"][name] | |
self.actions["actor"] = action_setactor | |
def action_close(scene, tag): | |
scene["variables"]["quit"] = True | |
self.actions["close"] = action_close | |
def action_switch(scene, tag): | |
choices = [] | |
while True: | |
scene["i"] += 1 | |
token = self.get_token(scene) | |
if token.startswith("$"): | |
tag = self.tags[token[1:]] | |
if tag["action"] != "choice": | |
break | |
else: | |
choices.append(self.tags[self.parsed[scene["i"]][1:]]) | |
trying = True | |
value = -1 | |
while trying: | |
for choice in choices: | |
parts = choice["contents"] | |
print(" {0}. {1}".format(str(parts[0]), parts[1])) | |
line = input("Choose a number: ") | |
try: | |
value = int(line) | |
trying = False | |
except ValueError: | |
print("That's not a valid choice") | |
continue | |
# now, jump to [case value] | |
while True: | |
if self.get_token(scene).startswith("$case"): | |
if int(self.tags[self.get_token(scene)[1:]]["contents"][0]) == value: | |
scene["i"] -= 1 | |
#print(self.get_token(scene)) | |
return | |
scene["i"] += 1 | |
#this'll fail eventually if it can't find the case | |
self.actions["switch"] = action_switch | |
def action_label(scene, tag): | |
print(self.format_line(tag["contents"], scene)) | |
self.actions["label"] = action_label | |
def action_goto(scene, tag): | |
scene["i"] += 1 | |
looped = False | |
while True: | |
token = self.get_token(scene) | |
if token.startswith("$label"): | |
localtag = self.tags[token[1:]] | |
if localtag["contents"] == tag["contents"]: | |
scene["i"] -= 2 | |
return | |
scene["i"] += 1 | |
if scene["i"] > len(self.parsed): | |
if looped: | |
print("Error, could not find goto:", tag["contents"]) | |
return | |
scene["i"] = 0 | |
looped = True | |
self.actions["goto"] = action_goto | |
def action_if(scene, tag): | |
def get(*args): | |
last = scene["variables"] | |
for arg in args[:-1]: | |
last = last[arg] | |
return last[args[-1]] | |
def set(v, *args): | |
last = scene["variables"] | |
for arg in args[:-1]: | |
last = last[arg] | |
last[args[-1]] = v | |
val = eval(" ".join(tag["contents"])) | |
if val: | |
return | |
else: | |
while True: | |
token = self.get_token(scene) | |
if token.startswith("$else"): | |
return | |
elif token.startswith("$end"): | |
return | |
scene["i"] += 1 | |
self.actions["if"] = action_if | |
def action_do(scene, tag): | |
def get(*args): | |
last = scene["variables"] | |
for arg in args[:-1]: | |
last = last[arg] | |
return last[args[-1]] | |
def set(v, *args): | |
last = scene["variables"] | |
for arg in args[:-1]: | |
last = last[arg] | |
last[args[-1]] = v | |
exec(" ".join(tag["contents"])) | |
self.actions["do"] = action_do | |
def get_token(self, scene): | |
return self.parsed[scene["i"]] | |
def parse(self): | |
lines = self.raw.split("\n") | |
parsed = [] | |
mode = "text" | |
tag = None | |
string = None | |
for line in lines: | |
words = line.split(" ") | |
for word in words: | |
word = word.strip() | |
if tag == None: | |
if word.startswith(OpenTag): | |
tag = {} | |
tag["action"] = word[1:] if not word.endswith(CloseTag) else word[1:-1] | |
tag["action"] = tag["action"].lower() | |
tag["id"] = "{0}_{1}".format(tag["action"], self.tagindex) | |
parsed.append("$" +tag["id"]) | |
self.tagindex += 1 | |
if word.endswith(CloseTag): | |
self.tags[tag["id"]] = tag | |
tag = None | |
else: | |
tag["contents"] = [] | |
else: | |
parsed.append(word) | |
else: | |
if word.endswith(CloseTag): | |
if string != None: | |
if word[:-1].endswith("\""): | |
string = "{0} {1}".format(string, word[:-2]) | |
else: | |
#it's just part of the string | |
string = "{0} {1}".format(string, word) | |
continue | |
tag["contents"].append(string) | |
string = None | |
else: | |
tag["contents"].append(word[:-1]) | |
self.tags[tag["id"]] = tag | |
tag = None | |
elif word.startswith("\""): | |
#it's a string -- could be the start, or | |
#something like "My name ends with a space " | |
# | |
if string == None: | |
string = word[1:] if not word.endswith("\"") else word[1:-1] | |
if word.endswith("\""): | |
tag["contents"].append(string) | |
string = None | |
elif word.endswith("\""): | |
tag["contents"].append(string + word[:-1]) | |
string = None | |
elif string != None: | |
if word.endswith("\""): | |
tag["contents"].append("{0} {1}".format(string, word[:-1])) | |
string = None | |
else: | |
string = "{0} {1}".format(string, word) | |
else: | |
tag["contents"].append(word) | |
parsed.append("\n") | |
self.parsed = parsed | |
#print(" ".join(parsed)) | |
def format_line(self, line, scene): | |
chars = " ".join(line) | |
if "actor" in scene and scene["actor"]: | |
chars = "{0}: {1}".format(scene["actor"]["name"], chars) | |
if scene["actor"]["align"] == "right": | |
chars = "{0}{1}".format(" " * (self.width - len(chars)), chars) | |
elif scene["actor"]["align"] == "center": | |
chars = "{0}{1}".format(" " * ((self.width - len(chars)) / 2), chars) | |
return chars | |
def interpret(self, variables): | |
if self.parsed == None: self.parse() | |
scene = {} | |
scene["variables"] = variables | |
scene["actors"] = {} | |
line = [] | |
scene["i"] = 0 | |
while scene["i"] < len(self.parsed): | |
token = self.parsed[scene["i"]] | |
if "quit" in scene["variables"] and scene["variables"]["quit"]: | |
print(self.format_line(line, scene)) | |
break | |
if token == "": | |
scene["i"] += 1 | |
continue | |
elif token == "\n": | |
if len(line) > 0: | |
print(self.format_line(line, scene)) | |
line = [] | |
elif token.startswith("$"): | |
tag = self.tags[token[1:]] | |
if tag["action"] in self.actions.keys(): | |
self.actions[tag["action"]](scene, tag) | |
#print(tag) | |
else: | |
line.append(token) | |
scene["i"] += 1 | |
if __name__ == "__main__": | |
tree = DialogueTree(game) | |
#print(" ".join(tree.parsed)) | |
tree.interpret({"playerName": "You", "npc": "John", "player": {"gold": 0, "items": ["apple"]}}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment