# gameshell.py from shell import Shell from gamebase import GameBase import sys as _sys #import re import heapq #import gamemap import gameevents import textwrap as _tw from shutil import get_terminal_size as _gts #import random TERM_SIZE = _gts()[0] class GameShell(Shell): UP = 1 RIGHT = 2 DOWN = 4 LEFT = 8 WALLS = ('++', '++', ' +', '++', '++', '||', '++', '|+', '+ ', '++', '==', '++', '++', '+|', '++', '++') def __init__(self, gameBase): super(GameShell, self).__init__() self.outstream = _sys.stdout self.gameBase = gameBase self.colorMode = 0 self.ps2 = '?> ' # register functions self.registerCommand('map', self.showMap) self.registerCommand('ls', self.showMap) self.registerCommand('go', self.gameBase.go) self.registerCommand('move', self.gameBase.go) self.registerCommand('walk', self.gameBase.go) self.registerCommand('look', self.gameBase.look) self.registerCommand('talk', self.gameBase.talk) self.registerCommand('use', self.gameBase.use) self.registerCommand('loadMap', self.gameBase.loadMap) self.registerCommand('man', self.man) self.registerCommand('help', self.man) self.registerCommand('save', self.gameBase.saveGame) self.registerCommand('load', self.gameBase.loadGame) self.registerCommand('take', self.gameBase.take) self.registerCommand('get', self.gameBase.take) self.registerCommand('drop', self.gameBase.drop) self.registerCommand('inv', self.inv) self.registerCommand('bag', self.inv) self.registerCommand('items', self.inv) self.registerCommand('status', self.status) self.registerCommand('options', self.options) self.registerCommand('colorTest', self.colorTest) self.registerAlias('run', ['go', '-r']) self.gameBase.registerIO('container', self.container) self.gameBase.registerIO('dialog', self.dialog) self.gameBase.registerIO('info', self.info) # Helper functions def man(self, args): super(GameShell, self).man(args) #heapq.heappush(self.gameBase.eventQueue, (self.gameBase.gameTime, gameevents.NoOpEvent())) def options(self, args): i = 0 while i < len(args): if args[i] == 'color': i += 1 if args[i] in ('0', '3', '4', '8', '24'): self.colorMode = int(args[i]) i += 1 def __colorTestRoutine(self, lower, upper): colors = [] step = int((upper - lower) / 8) #print(step) for g in range(lower, upper, step): colors.append(self.color(upper-1, g, lower, False)) #print(len(colors)) colors.append(self.color(upper-1, upper-1, lower, False)) for r in range(upper-step, lower-1, -step): colors.append(self.color(r, upper-1, lower, False)) #print(len(colors)) for b in range(lower+step, upper, step): colors.append(self.color(lower, upper-1, b, False)) #print(len(colors)) colors.append(self.color(lower, upper-1, upper-1, False)) for g in range(upper-step, lower-1, -step): colors.append(self.color(lower, g, upper-1, False)) #print(len(colors)) for r in range(lower+step, upper, step): colors.append(self.color(r, lower, upper-1, False)) colors.append(self.color(upper-1, lower, upper-1, False)) #print(len(colors)) for b in range(upper-step, lower, -step): colors.append(self.color(upper-1, lower, b, False)) #print(len(colors)) colors.append('\x1b[0m') print(' '.join(colors)) def colorTest(self, args): for i in (3, 4, 8, 24): print(i) self.colorMode = i self.__colorTestRoutine(0, 128) # dark self.__colorTestRoutine(0, 256) # medium self.__colorTestRoutine(128, 256) # light def showMap(self, args): """map [-l] See a map of the local area. "ls" is an alias of map. "l" and "legend" are aliases of -l. If -l is given, a map legend will be printed under the map.""" xAxis = ' ' + ''.join([self.gameBase.numberToLetter(i).ljust(2) for i in range(self.gameBase.level.dimensions[0])]) + '\n' rows = [] index = 0 exits = {} characters = {} doors = {} useables = {} items = {} level = self.gameBase.level textColor = level.wallColors[0] floorColor = level.floorColors[0] for y in range(level.dimensions[1]): rows.append(['{0}{1:2} {2}{3}'.format(self.clearColor(), y, self.color(textColor[1:]), self.color(floorColor[1:], fg = False))]) rows[-1].append(self.color(textColor[1:])) for x in range(level.dimensions[0]): pos = level.mapMatrix[y][x] thing = level.getThingAtPos(index) if x == self.gameBase.playerx and y == self.gameBase.playery: if '#0000FF' != textColor: textColor = '#0000FF' rows[-1].append(self.color(textColor[1:])) rows[-1].append('()') elif thing: if thing.graphic[1] != textColor: textColor = thing.graphic[1] rows[-1].append(self.color(textColor[1:])) if thing.thingType == 'x': # exit rows[-1].append('X{0}'.format(thing.exitid)) exits[thing.exitid] = (thing.name, thing.graphic[1]) elif thing.thingType == 'c': # useable characters[len(characters)+1] = (thing.name, thing.graphic[1]) rows[-1].append('C{0}'.format(len(characters))) elif thing.thingType == 'd': # door doors[len(doors)+1] = (thing.name, thing.graphic[1]) rows[-1].append('D{0}'.format(len(doors))) elif thing.thingType == 'u': # useable useables[len(useables)+1] = (thing.name, thing.graphic[1]) rows[-1].append('U{0}'.format(len(useables))) elif thing.thingType == 'i': # item items[len(items)+1] = (thing.name, thing.graphic[1]) rows[-1].append('I{0}'.format(len(items))) elif pos[0] == 'w': if level.wallColors[level.mapMatrix[y][x][1]] != textColor: textColor = level.wallColors[level.mapMatrix[y][x][1]] rows[-1].append(self.color(textColor[1:])) sides = 0 if y > 0 and level.mapMatrix[y-1][x][0] == 'w': sides += GameShell.UP if x < level.dimensions[0]-1 and level.mapMatrix[y][x+1][0] == 'w': sides += GameShell.RIGHT if y < level.dimensions[1]-1 and level.mapMatrix[y+1][x][0] == 'w': sides += GameShell.DOWN if x > 0 and level.mapMatrix[y][x-1][0] == 'w': sides += GameShell.LEFT rows[-1].append(GameShell.WALLS[sides]) else: rows[-1].append(' ') index += 1 rows[-1] = ''.join(rows[-1]) print(xAxis) print('{0}\n'.format(self.clearColor(False)).join(rows) + self.clearColor()) self.setClearColor() if len(args) > 0: if args[0] == '-l' or args[0] == 'l' or args[0] == 'legend': legend = ["\n---Legend---\n", "{0}(){1} - {2}".format(self.color('0000FF'), self.clearColor(), self.gameBase.playerName), "Xn - Exit to another area"] for i in exits: legend.append(' {0}X{1}{2} - {3}'.format(self.color(exits[i][1][1:]), i, self.clearColor(), exits[i][0])) legend.append("Cn - Character") for i in characters: legend.append(' {0}U{1}{2} - {3}'.format(self.color(characters[i][1][1:]), i, self.clearColor(), characters[i][0])) legend.append("Un - Useable object") for i in useables: legend.append(' {0}U{1}{2} - {3}'.format(self.color(useables[i][1][1:]), i, self.clearColor(), useables[i][0])) legend.append("In - Item") for i in items: legend.append(' {0}I{1}{2} - {3}'.format(self.color(items[i][1][1:]), i, self.clearColor(), items[i][0])) legend.append("Dn - Door") for i in doors: legend.append(' {0}D{1}{2} - {3}'.format(self.color(doors[i][1][1:]), i, self.clearColor(), doors[i][0])) print('\n'.join(legend)) #heapq.heappush(self.gameBase.eventQueue, (self.gameBase.gameTime, gameevents.NoOpEvent())) return def status(self, args): ret = [] if self.gameBase.level != None: ret.append("Level:{0:.>74}".format(self.gameBase.level.name)) else: ret.append("Level:{0:.>74}".format("None")) ret.append("Gametime:{0:.>71.3}".format(self.gameBase.gameTime)) ret.append("Event queue:") for i in sorted(self.gameBase.eventQueue): ret.append("{0:.<8.3}:{1:.>72}".format(i[0], str(i[1]))) ret.append("Player name:{0:.>68}".format(self.gameBase.playerName)) ret.append("Player position:{0:.>64}".format("{0}{1}".format(self.gameBase.numberToLetter(self.gameBase.playerx), self.gameBase.playery))) ret.append("Prev. position:{0:.>65}".format("{0}{1}".format(self.gameBase.numberToLetter(self.gameBase.prevx), self.gameBase.prevy))) ret.append("Inventory:") for i in self.gameBase.playerInv: ret.append("{0:<8}: {1}".format(i, self.gameBase.playerInv[i].name)) ret.append("Things:\nID Name X Y") for i in self.gameBase.level.things: j = self.gameBase.level.things[i] ret.append("{0:<7} {1:<31} {2:<3} {3:<3}".format(i, j.name, j.x, j.y)) ret.append("Persistent:") for i in self.gameBase.level.persistent: ret.append(str(i)) print('\n'.join(ret)) #heapq.heappush(self.gameBase.eventQueue, (self.gameBase.gameTime, gameevents.NoOpEvent())) return def inv(self, args): print('\n'.join([self.gameBase.playerInv[i].name for i in self.gameBase.playerInv])) def container(self, inv, cont): """container IO""" # Pretty print: get length of the longest inventory item's name longestLen = 0 longestStr = "" for i in inv: if len(i) > longestLen: longestLen = len(i) longestStr = i if longestLen > 0: print('{{0:<{0}}}{1}'.format(max(6, longestLen+2), "Container:").format("Inv:")) i = 0 invKeys = tuple(inv.keys()) while i < len(invKeys) and i < len(cont): print('{{0:<{0}}}{1}'.format(longestLen+2, cont[i].name).format(invKeys[i])) i += 1 while i < len(invKeys): print(invKeys[i]) i += 1 while i < len(cont): print(' '*(longestLen+2) + cont[i].name) i += 1 else: print('Container:') for i in cont: print(i.name) # Now, actually interacting with the container timeSpent = 0.5 # using a container always takes at least 1/2 second, even just opening and closing it again. instr = input("Take, store, or exit: ") while instr != "exit": instr = instr.split() if instr[0] == "take": # take something out of the container if instr[1] == "the": del instr[1] thing = ' '.join(instr[1:]) for i in range(len(cont)): if thing == cont[i].name: inv[cont[i].thingID] = cont[i] del cont[i] timeSpent += 0.5 print("{0} taken.".format(thing)) break elif instr[0] == "store": # store something in the container if instr[1] == "the": del instr[1] thingName = ' '.join(instr[1:]) for i in inv: thing = inv[i].name if thing == thingName: cont.append(inv[i]) del inv[i] print("{0} stored.".format(thing)) timeSpent += 0.5 break # so that all things with the same name don't get stored instr = input("Take, store, or exit: ") return inv, cont, timeSpent def info(self, items): """IO for collections of information""" charsRead = 0 for i in items: print(' ', i) instr = input("Choose an item to view, or exit: ") while instr != 'exit': if instr in items: print(_tw.fill(items[instr], width = TERM_SIZE)) charsRead += len(items[instr]) else: print('{} not here.'.format(instr)) input('') for i in items: print(' ', i) instr = input("Choose an item to view, or exit: ") return charsRead / 27 # based on average 250 words per minute, and word length of 5.5 + 1 for space. def dialog(self, dialogObj): if 'opener' in dialogObj: print(_tw.fill(dialogObj['opener'], width = TERM_SIZE)) while isinstance(dialogObj, dict): if 'action' in dialogObj: action = dialogObj['action'] if action == 'answer': answer = 0 if 'answers' in dialogObj and isinstance(dialogObj['answers'], list): for i in range(len(dialogObj['answers'])): print(_tw.fill('{}: {}'.format(i+1, dialogObj['answers'][i]), width = TERM_SIZE)) answer = int(input(self.ps2)) - 1 if 'replies' in dialogObj and isinstance(dialogObj['replies'], list): ret = self.dialog(dialogObj['replies'][answer]) if ret > 1: return ret - 1 elif ret == 0: return 0 # if ret == 1, then do this dialog again elif len(action) >= 4 and action[:4] == 'back': if len(action) == 4: return 1 return int(action[4:]) elif action == 'exit': return 0 def update(self): self.gameBase.gameEventLoop() if __name__ == '__main__': sh = GameShell(GameBase()) sh.run()