Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# python-text-adventure

A story program.
2 changes: 1 addition & 1 deletion story.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"progress": [{"id": 2, "player": "umbrella", "lastPlayed": "2021-01-02 22:09:06.027832", "position": 2, "state": {}}, {"id": 3, "player": "jntowell", "lastPlayed": "2021-01-02 22:42:36.478003", "position": 0, "state": {"inventory": ["potion"], "health": -20}}, {"id": 4, "player": "rex", "lastPlayed": "2021-01-02 22:22:25.713141", "position": 4, "state": {"inventory": ["potion"]}}, {"id": 5, "player": "frog", "lastPlayed": "2021-01-02 22:52:08.063381", "position": 0, "state": {"inventory": ["potion"], "health": -20}}, {"id": 6, "player": "rudolph", "lastPlayed": "2021-01-02 22:54:23.407919", "position": 0, "state": {"inventory": ["potion"], "health": -20}}, {"id": 7, "player": "jj", "lastPlayed": "2021-01-02 22:55:13.186008", "position": 0, "state": {"inventory": ["potion"], "health": -20}}, {"id": 8, "player": "rr", "lastPlayed": "2021-01-02 22:55:51.925230", "position": 0, "state": {"inventory": ["potion"]}}, {"id": 9, "player": "gerdrude", "lastPlayed": "2021-01-02 22:56:23.939869", "position": 0, "state": {"inventory": ["potion"], "health": -20}}, {"id": 10, "player": "fr", "lastPlayed": "2021-01-02 22:59:39.238532", "position": 0, "state": {"health": -20}}, {"id": 11, "player": "efer", "lastPlayed": "2021-01-02 23:01:10.638514", "position": 4, "state": {"inventory": ["potion"]}}, {"id": 12, "player": "fg", "lastPlayed": "2021-01-02 23:02:18.107944", "position": 5, "state": {"inventory": ["potion"], "health": -20}}, {"id": 13, "player": "ert", "lastPlayed": "2021-01-02 23:03:31.083821", "position": 5, "state": {"inventory": ["potion"], "health": -20}}, {"id": 14, "player": "dfs", "lastPlayed": "2021-01-02 23:04:34.797636", "position": 5, "state": {"inventory": ["potion"], "health": -20}}], "content": [{"id": 1, "text": "You are in the woods.", "options": [{"id": 1, "text": "Go east", "nextText": 2}, {"id": 2, "text": "Go west", "nextText": 3}]}, {"id": 2, "text": "You went east. There is a potion!", "options": [{"id": 1, "text": "Collect potion", "setState": {"newInventory": "potion"}, "nextText": 4}, {"id": 2, "text": "Leave potion", "nextText": 4}]}, {"id": 3, "text": "You went west. There is a goblin!", "options": [{"id": 1, "text": "Fight the goblin", "nextText": 4}, {"id": 2, "text": "Run away", "nextText": 4}]}, {"id": 4, "text": "There's a wizard. He wants something from you!", "options": [{"id": 1, "text": "Give potion", "setState": {"removeInventory": "potion"}, "requiredState": {"inventory": "potion"}, "nextText": 5}, {"id": 2, "text": "Fight him", "setState": {"health": -20}, "nextText": 5}, {"id": 3, "text": "Run away", "nextText": 5}]}, {"id": 5, "text": "You find a haunted house.", "options": [{"id": 1, "text": "Go straight in"}, {"id": 2, "text": "Ask if anyone is home"}, {"id": 3, "text": "Continue on path", "setState": {"endGame": "true"}}]}]}
{"progress": [{"id": 2, "player": "umbrella", "lastPlayed": "2021-01-02 22:09:06.027832", "position": 2, "state": {}}, {"id": 3, "player": "jntowell", "lastPlayed": "2021-01-02 22:42:36.478003", "position": 0, "state": {"inventory": ["potion"], "health": -20}}, {"id": 4, "player": "rex", "lastPlayed": "2021-01-02 22:22:25.713141", "position": 4, "state": {"inventory": ["potion"]}}, {"id": 5, "player": "frog", "lastPlayed": "2021-01-02 22:52:08.063381", "position": 0, "state": {"inventory": ["potion"], "health": -20}}, {"id": 6, "player": "rudolph", "lastPlayed": "2021-01-02 22:54:23.407919", "position": 0, "state": {"inventory": ["potion"], "health": -20}}, {"id": 7, "player": "jj", "lastPlayed": "2021-01-02 22:55:13.186008", "position": 0, "state": {"inventory": ["potion"], "health": -20}}, {"id": 8, "player": "rr", "lastPlayed": "2021-01-02 22:55:51.925230", "position": 0, "state": {"inventory": ["potion"]}}, {"id": 9, "player": "gerdrude", "lastPlayed": "2021-01-02 22:56:23.939869", "position": 0, "state": {"inventory": ["potion"], "health": -20}}, {"id": 10, "player": "fr", "lastPlayed": "2021-01-02 22:59:39.238532", "position": 0, "state": {"health": -20}}, {"id": 11, "player": "efer", "lastPlayed": "2021-01-02 23:01:10.638514", "position": 4, "state": {"inventory": ["potion"]}}, {"id": 12, "player": "fg", "lastPlayed": "2021-01-02 23:02:18.107944", "position": 5, "state": {"inventory": ["potion"], "health": -20}}, {"id": 13, "player": "ert", "lastPlayed": "2021-01-02 23:03:31.083821", "position": 5, "state": {"inventory": ["potion"], "health": -20}}, {"id": 14, "player": "dfs", "lastPlayed": "2021-01-02 23:04:34.797636", "position": 5, "state": {"inventory": ["potion"], "health": -20}}, {"id": 15, "player": "freddy", "lastPlayed": "2021-01-14 23:10:56.713022", "position": 0, "state": {"inventory": ["potion"], "health": -20}}, {"id": 16, "player": "ready", "lastPlayed": "2021-03-08 00:02:23.069947", "position": 0, "state": {}}, {"id": 17, "player": "georgr", "lastPlayed": "2021-03-08 00:03:15.170369", "position": 0, "state": {"inventory": []}}, {"id": 18, "player": "george", "lastPlayed": "2022-01-04 18:44:45.397536", "position": 0, "state": {"inventory": ["potion"], "health": -20}}], "content": [{"id": 1, "text": "You are in the woods.", "options": [{"id": 1, "text": "Go east", "nextText": 2}, {"id": 2, "text": "Go west", "nextText": 3}]}, {"id": 2, "text": "You went east. There is a potion!", "options": [{"id": 1, "text": "Collect potion", "setState": {"newInventory": "potion"}, "nextText": 4}, {"id": 2, "text": "Leave potion", "nextText": 4}]}, {"id": 3, "text": "You went west. There is a goblin!", "options": [{"id": 1, "text": "Fight the goblin", "nextText": 4}, {"id": 2, "text": "Run away", "nextText": 4}]}, {"id": 4, "text": "There's a wizard. He wants something from you!", "options": [{"id": 1, "text": "Give potion", "setState": {"removeInventory": "potion"}, "requiredState": {"inventory": "potion"}, "nextText": 5}, {"id": 2, "text": "Fight him", "setState": {"health": -20}, "nextText": 5}, {"id": 3, "text": "Run away", "nextText": 5}]}, {"id": 5, "text": "You find a haunted house.", "options": [{"id": 1, "text": "Go straight in"}, {"id": 2, "text": "Ask if anyone is home"}, {"id": 3, "text": "Continue on path", "setState": {"endGame": "true"}}]}]}
271 changes: 214 additions & 57 deletions textAdventure.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import datetime as dt
import traceback ## Allows visual of errors -> traceback.print_exc()

##=========##
## SETUP ##
##=========##

## Name of file containing progress and content
FILENAME = ""

Expand All @@ -26,12 +30,16 @@

## Menu option lists
MAIN_LIST = ["Play a story", "Write a story", "Manage players", "Quit"]
WRITE_LIST = ["Create a new story", "Load an existing story", "Back"]
WRITE_LIST = ["Create a new story", "Create a story from an existing text", "Load an existing story", "Back"]
LOAD_LIST = ["Load default story", "Load from a known file", "Back"]
PROGRESS_LIST = ["Load progress using player name", "Start a new game", "Back"]
MULTISAVE_LIST = ["Overwrite existing progress", "Create a new progress save", "Re-enter player name"]
MANAGE_LIST = ["View player saves", "Delete a player save", "Delete all player saves", "Back"]

##====================##
## GLOBAL FUNCTIONS ##
##====================##

def camelCaseSplit(string):
## Seperates camel case words into sentence case string
return re.findall(r'[A-Z](?:[a-z]+|[A-Z]*(?=[A-Z]|$))', string)
Expand All @@ -58,6 +66,51 @@ def sPrint(text):
time.sleep(duration)
print("")

def createBlankLong(length, char = ' '):
blank = ""
for i in range(length):
blank += char
return blank

def showMenu(menuName, menuList, subText = "", subList = []):
global PLAYER
nPrint(2)
sPrint("===| " + menuName + " |===")
if (len(PLAYER) > 0):
sPrint("Player: " + PLAYER)
if (len(subText) > 0):
sPrint(subText)
if (len(subList) > 0):
nPrint(1)
for i in subList:
if (i == "Player Progression"):
sPrint(i + ":")
longest = 0
for p in subList.get(i):
if (len(p[0]) > longest):
longest = len(p[0])
for p in subList.get(i):
sPrint(" " + p[0] + createBlankLong(longest + 3 - len(p[0])) + p[1])
else:
sPrint(i + ": " + str(subList[i]))
nPrint(1)
if (len(menuList) > 0):
sPrint("Please select an option:")
ordinal = 1
for o in menuList:
if (o == "Quit"):
prefix = "q"
elif (o == "Back"):
prefix = "b"
else:
prefix = str(ordinal)
sPrint("[{}] {}".format(prefix, o))
ordinal += 1

##==================##
## GAME FUNCTIONS ##
##==================##

def showPlayerState():
global PLAYER
global STATE
Expand Down Expand Up @@ -158,7 +211,7 @@ def updateState(opt):
STATE[req] = optState.get(req)
return ''

def getOption(options, answer):
def getChosenOption(options, answer):
ordinal = 0
for opt in options:
optReq = opt.get('requiredState')
Expand All @@ -168,7 +221,7 @@ def getOption(options, answer):
if (answer == str(ordinal)):
return opt

def loadPos(pos):
def getPos(pos):
global CONTENT
for obj in CONTENT:
if (pos == obj.get('id')):
Expand All @@ -188,7 +241,7 @@ def startGame():
showPlayerState()

## Load current object
obj = loadPos(HEAD_POS)
obj = getPos(HEAD_POS)
objText = obj.get('text')
objOpt = obj.get('options')

Expand All @@ -199,7 +252,7 @@ def startGame():
while (nextText < 1 and answer != 'q'):
showOptions(objOpt)
answer = getInput()
chosenOption = getOption(objOpt, answer)
chosenOption = getChosenOption(objOpt, answer)
if (chosenOption and answer != 'q'):
nextText = getNextText(chosenOption)
HEAD_POS = nextText
Expand All @@ -216,14 +269,6 @@ def startGame():
sPrint("Error loading position. Restarting sequence.")
nPrint(1)
sPrint("|>>> END OF GAME <<<|")

def createNewPlayer(players):
global PLAYER
newId = 1
for player in players:
if (player.get('id') > newId):
newId = player.get('id')
return {"id": newId + 1, "player": PLAYER, "lastPlayed": str(dt.datetime.now()), "position": HEAD_POS, "state": STATE}

def saveProgress():
global FILENAME
Expand Down Expand Up @@ -313,6 +358,18 @@ def saveProgress():
return False
return False

##=========================##
## MANAGE GAME FUNCTIONS ##
##=========================##

def createNewPlayer(players):
global PLAYER
newId = 1
for player in players:
if (player.get('id') > newId):
newId = player.get('id')
return {"id": newId + 1, "player": PLAYER, "lastPlayed": str(dt.datetime.now()), "position": HEAD_POS, "state": STATE}

def importAllData(existFilename = ""):
global FILENAME
global LOAD_LIST
Expand Down Expand Up @@ -578,8 +635,14 @@ def managePlayers():
sPrint("Your input was not recognised.")
sPrint("Error detail (MANAGE1): " + str(e))

##==========================##
## WRITING GAME FUNCTIONS ##
##==========================##

def saveStory(new = False):
global FILENAME
global PROGRESS
global CONTENT
try:
if (not new):
with open(FILENAME + '.json', 'r') as readFile:
Expand All @@ -595,16 +658,143 @@ def saveStory(new = False):
return False
with open(FILENAME + '.json', 'w') as writeFile:
if (new):
json.dump({"progress": [], "content": []}, writeFile)
else:
json.dump({"progress": PROGRESS, "content": CONTENT}, writeFile)
PROGRESS = []
CONTENT = []
json.dump({"progress": PROGRESS, "content": CONTENT}, writeFile)
return True
except (json.decoder.JSONDecodeError, FileNotFoundError) as e:
nPrint(1)
sPrint("The save file could not be edited.")
sPrint("Error detail (STORY1): " + str(e))
return False

def highestId(listObj = CONTENT):
global CONTENT
highestId = 0
for i in listObj:
if (i.get('id') and i.get('id') > highestId):
highestId = i.get('id')
return highestId

def addToPosition(newId = 0, newText = "", newOption = {}):
global CONTENT
position = {}
for i in CONTENT:
if (i.get('id') and newId > 0 and i.get('id') == newId):
position = i
break
if (len(position) < 1 and newId > 0):
position = {'id': newId, 'text':"", 'options':{}}
if (len(newText) < 1):
saveObj(position)
return True
else:
sPrint("Unable to save position ID.")
return False
if (len(newText) > 0):
position['text'] = newText
saveObj(position)
return True
else:
sPrint("Unable to save position text.")
return False
if (len(options) > 0):
position['options'].append(newOption)
saveObj(position)
return True
else:
sPrint("Unable to save position text.")
return False
saveObj(position)
return True

def addToOption(posId, newId = 0, newText = "", newList = []):
option = {}
if (newId > 0):
option = {'id': newId, 'text':""}
if (len(newText) < 1):
saveObj(option, listObj = getPosObj(posId).get('options'))
return True
else:
sPrint("Unable to save option ID.")
return False
if (len(newText) > 0):
option['text'] = newText
saveObj(option, listObj = getPosObj(posId).get('options'))
return True
else:
sPrint("Unable to save option text.")
return False
saveObj(option, listObj = getPosObj(posId).get('options'))
return True

def getPosObj(posId):
global CONTENT
for i in CONTENT:
if (i.get('id') and i.get('id') == posID):
return i
sPrint("Position could not be loaded.")

def saveObj(obj, listObj = CONTENT):
global CONTENT
for i in listObj:
if (i.get('id') and i.get('id') == obj['id']):
listObj[i] = obj
return True
sPrint("Object could not be saved.")
return False

def writePosition(newId = 0, newText = "", newOption = {}):
answer = ''
allValid = False
while (not allValid):
showMenu("Edit Menu", [])
try:
sPrint("Please enter the position ID (current highest ID is '{}')...".format(highestId()))
newId = int(getInput())
if (addToPosition(newId = newId)):
sPrint("Please enter the position text (story and question)...")
newText = str(getInput())
if (addToPosition(newId = newId, newText = newText)):
sPrint("Please enter the number of options you wish to add for this position...".format(highestId()))
optionCount = int(getInput())
while (optionCount > 0):
posObj = getPosObj(newId)
if (posObj):
optId = highestId(listObj = len(posObj.get('options')) + 1)
if (addToOption(newId, newId = optId)):
sPrint("Please enter the option text...")
optText = str(getInput())
if (addToOption(newId, newId = optId)):
if (addToPosition(newId = newId, newOption = newOption)):
optionCount -= 1
except (TypeError, ValueError, IndexError) as e:
sPrint("Your input was not recognised.")
sPrint("Error detail (EDIT1): " + str(e))

##def positionMenu():
## answer = ''
## while (not answer == 'b'):
## showMenu("Navigation Menu", WRITE_LIST)
## answer = getInput()
## try:
## answer = int(answer)
## if (WRITE_LIST[answer - 1] == "Create a new story"):
## sPrint("Please enter the file name for your story (no spaces)...")
## filename = getInput()
## FILENAME = filename
## if (not saveStory(True)):
## nPrint(1)
## sPrint("File was unable to be created.")
## elif (WRITE_LIST[answer - 1] == "Create a story from an existing text"):
## sPrint("This feature is still in development.")
## elif (WRITE_LIST[answer - 1] == "Load an existing story"):
## sPrint("This feature is still in development.")
## except (TypeError, ValueError, IndexError) as e:
## if (answer != 'b'):
## sPrint("Your input was not recognised.")
## sPrint("Error detail (WRITE1): " + str(e))

def writeStory():
global FILENAME
answer = ''
Expand All @@ -620,53 +810,20 @@ def writeStory():
if (not saveStory(True)):
nPrint(1)
sPrint("File was unable to be created.")
## while (): ## HELP
writePosition()
elif (WRITE_LIST[answer - 1] == "Create a story from an existing text"):
sPrint("This feature is still in development.")
elif (WRITE_LIST[answer - 1] == "Load an existing story"):
sPrint("This feature is still in development.")
except (TypeError, ValueError, IndexError) as e:
if (answer != 'b'):
sPrint("Your input was not recognised.")
sPrint("Error detail (WRITE1): " + str(e))

def createBlankLong(length, char = ' '):
blank = ""
for i in range(length):
blank += char
return blank

def showMenu(menuName, menuList, subText = "", subList = []):
global PLAYER
nPrint(2)
sPrint("===| " + menuName + " |===")
if (len(PLAYER) > 0):
sPrint("Player: " + PLAYER)
if (len(subText) > 0):
sPrint(subText)
if (len(subList) > 0):
nPrint(1)
for i in subList:
if (i == "Player Progression"):
sPrint(i + ":")
longest = 0
for p in subList.get(i):
if (len(p[0]) > longest):
longest = len(p[0])
for p in subList.get(i):
sPrint(" " + p[0] + createBlankLong(longest + 3 - len(p[0])) + p[1])
else:
sPrint(i + ": " + str(subList[i]))
nPrint(1)
if (len(menuList) > 0):
sPrint("Please select an option:")
ordinal = 1
for o in menuList:
if (o == "Quit"):
prefix = "q"
elif (o == "Back"):
prefix = "b"
else:
prefix = str(ordinal)
sPrint("[{}] {}".format(prefix, o))
ordinal += 1

##==================##
## MAIN FUNCTIONS ##
##==================##

def mainMenu():
global MAIN_LIST
Expand Down