diff --git a/gamebase.py b/gamebase.py index 28d63d3..a849dd3 100644 --- a/gamebase.py +++ b/gamebase.py @@ -25,6 +25,7 @@ class GameBase(object): self.ps2 = '? ' self.eventQueue = [] self.gameTime = 0.0 + self.skipLoop = True # player info self.playerx = -1 @@ -187,18 +188,18 @@ The letter is not case-sensitive.""" # Now we have a heading! Let's see if we can get there... if (x, y) == (self.playerx, self.playery): - _hq.heappush(self.eventQueue, (self.gameTime, _ge.ArriveEvent(self.playerName, x, y, 0.0))) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.ArriveEvent(self.playerName, x, y, 0.0))) return dist, path = self.level.path(x, y, self.playerx, self.playery) if dist == -1: print('{0} cannot reach {1}{2}.'.format(self.playerName, self.numberToLetter(x), y), file = self.outstream) - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) return else: pos = self.level.coordsToInt(self.playerx, self.playery) space = path[pos] if space == -1: - _hq.heappush(self.eventQueue, (self.gameTime, _ge.ArriveEvent(self.playerName, self.playerx, self.playery, 0.0))) + self.setEvent(0.0, _ge.ArriveEvent(self.playerName, self.playerx, self.playery, 0.0)) return #target = self.level.coordsToInt(x, y) t = 1 @@ -207,12 +208,11 @@ The letter is not case-sensitive.""" newx, newy = self.level.intToCoords(space) space = path[space] if space != -1: - _hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy))) + self.setEvent(t * speed, _ge.GoEvent(self.playerName, newx, newy)) else: - _hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.ArriveEvent(self.playerName, newx, newy, t * speed))) + self.setEvent(t * speed, _ge.ArriveEvent(self.playerName, newx, newy, t * speed)) break t += 1 - #newx, newy = self.level.intToCoords(space) #_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.ArriveEvent(self.playerName, newx, newy, t * speed))) return @@ -236,7 +236,7 @@ Object can be the name of the object, or its coordinates.""" print(self.justifyText(str(thing)), file = self.outstream) else: print("There is nothing to see here.\n", file = self.outstream) - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) def use(self, args): """use [-r] [the] object [on [the] object2] @@ -261,21 +261,21 @@ the name of an item in the player's inventory.""" thing, x, y = self.parseCoords(args) if thing == None: print("There is nothing to use.", file = self.outstream) - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) return if thing.thingType != 'u' and thing.name not in self.playerInv: print("The {0} cannot be used.".format(thing.name), file = self.outstream) - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) return # Similar to go, but not quite the same. if (x, y) == (self.playerx, self.playery) or thing.name in self.playerInv: - _hq.heappush(self.eventQueue, (self.gameTime + 0.125, _ge.UseEvent(thing))) + self.setEvent(0.125, _ge.UseEvent(thing)) return dist, path = self.level.path(x, y, self.playerx, self.playery) if dist == -1: print('{0} cannot reach the {1}.'.format(self.playerName, thing.name), file = self.outstream) - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) return else: pos = self.level.coordsToInt(self.playerx, self.playery) @@ -286,11 +286,11 @@ the name of an item in the player's inventory.""" while space != -1: newx, newy = self.level.intToCoords(space) space = path[space] - _hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy))) + self.setEvent(t * speed, _ge.GoEvent(self.playerName, newx, newy)) t += 1 #newx, newy = self.level.intToCoords(space) #_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy))) - _hq.heappush(self.eventQueue, (self.gameTime + t * speed + 0.125, _ge.UseEvent(thing))) + self.setEvent(t * speed + 0.125, _ge.UseEvent(thing)) return def useOn(self, args, speed): @@ -302,16 +302,16 @@ the name of an item in the player's inventory.""" thing, x, y = self.parseCoords(args[onIndex+1:]) if item == None or item.name not in self.playerInv: print("There is no {0} in the inventory.".format(item.name), file = self.outstream) - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) return if thing == None and x < 0 and y < 0: print("Argument contains 'to' but with no real predicate.", file = self.outstream) - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) return # Similar to go, but not quite the same. if (x, y) == (self.playerx, self.playery): - _hq.heappush(self.eventQueue, (self.gameTime + 0.125, _ge.UseOnEvent(item, thing))) + self.setEvent(0.125, _ge.UseOnEvent(item, thing)) return dist, path = self.level.path(x, y, self.playerx, self.playery) if dist == -1: @@ -319,7 +319,7 @@ the name of an item in the player's inventory.""" print('{0} cannot reach the {1}.'.format(self.playerName, thing.name), file = self.outstream) else: print('{0} cannot reach {1}{2}.'.format(self.playerName, self.numberToLetter(x), y), file = self.outstream) - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) return else: pos = self.level.coordsToInt(self.playerx, self.playery) @@ -330,11 +330,11 @@ the name of an item in the player's inventory.""" while space != -1: newx, newy = self.level.intToCoords(space) space = path[space] - _hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy))) + self.setEvent(t * speed, _ge.GoEvent(self.playerName, newx, newy)) t += 1 #newx, newy = self.level.intToCoords(space) #_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy))) - _hq.heappush(self.eventQueue, (self.gameTime + t * speed + 0.125, _ge.UseOnEvent(item, thing))) + self.setEvent(t * speed + 0.125, _ge.UseOnEvent(item, thing)) return def take(self, args): @@ -357,17 +357,17 @@ Object can be the name of the object, or its coordinates.""" return if thing.thingType != 'i': print("The {0} cannot be taken.".format(thing.name), file = self.outstream) - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) return # Similar to go, but not quite the same. if (x, y) == (self.playerx, self.playery): - _hq.heappush(self.eventQueue, (self.gameTime + 0.125, _ge.TakeEvent(thing))) + self.setEvent(0.125, _ge.TakeEvent(thing)) return dist, path = self.level.path(x, y, self.playerx, self.playery) if dist == -1: print('{0} cannot reach the {1}.'.format(self.playerName, thing.name), file = self.outstream) - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) return else: pos = self.level.coordsToInt(self.playerx, self.playery) @@ -378,11 +378,11 @@ Object can be the name of the object, or its coordinates.""" while space != -1: newx, newy = self.level.intToCoords(space) space = path[space] - _hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy))) + self.setEvent(t * speed, _ge.GoEvent(self.playerName, newx, newy)) t += 1 #newx, newy = self.level.intToCoords(space) #_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy))) - _hq.heappush(self.eventQueue, (self.gameTime + t * speed + 0.125, _ge.TakeEvent(thing))) + self.setEvent(t * speed + 0.125, _ge.TakeEvent(thing)) return def drop(self, args): @@ -390,7 +390,7 @@ Object can be the name of the object, or its coordinates.""" if args[0] == 'the': args.pop(0) if args[0] in self.playerInv: - _hq.heappush(self.eventQueue, (self.gameTime, _ge.DropEvent(self.playerInv[args[0]]))) + self.setEvent(0.0, _ge.DropEvent(self.playerInv[args[0]])) else: print('{0} do not have a {1}.'.format(self.playerName, args[0]), file = self.outstream) @@ -464,7 +464,7 @@ Object can be the name of the object, or its coordinates.""" del self.persist[self.level.name][i] # push a no-op event so that saving doesn't cost player characters time - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) return def loadGame(self, args): @@ -483,19 +483,29 @@ Object can be the name of the object, or its coordinates.""" self.playerName, x, y, self.playerInv, levelname, self.persist, self.eventQueue, self.gameTime = _pi.load(f) #print(levelname, x, y, file = self.outstream) self.loadMap((levelname, x, y)) - _hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) + #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) return def gameEventLoop(self): + #print(self.skipLoop) + if self.skipLoop: + return + #print(self.skipLoop) while len(self.eventQueue) > 0: ev = _hq.heappop(self.eventQueue) self.gameTime = ev[0] e = ev[1] if self.__gameEvents[e.eventType](e): + #print('break loop') break if len(self.eventQueue) == 0: self.gameTime = 0.0 _ge.resetEventNum() + self.skipLoop = True + + def setEvent(self, t, e, skip = False): + _hq.heappush(self.eventQueue, (self.gameTime + t, e)) + self.skipLoop = skip # default event handlers @@ -540,14 +550,14 @@ Object can be the name of the object, or its coordinates.""" if e.thing.useFunc == '': print('The {0} cannot be used by itself.'.format(e.thing.name), file = self.outstream) return True - _hq.heappush(self.eventQueue, (self.gameTime + self.__useFuncs[e.thing.useFunc](e.thing), _ge.NoOpEvent())) + self.setEvent(self.__useFuncs[e.thing.useFunc](e.thing), _ge.NoOpEvent()) return False def handleUseOn(self, e): if e.item.useOnFunc == '': print('The {0} cannot be used on other objects.'.format(e.item.name), file = self.outstream) return True - _hq.heappush(self.eventQueue, (self.gameTime + self.__useFuncs[e.item.useOnFunc](e.item, e.thing), _ge.NoOpEvent())) + self.setEvent(self.__useFuncs[e.item.useOnFunc](e.item, e.thing), _ge.NoOpEvent()) return False def handleTake(self, e): diff --git a/gameshell.py b/gameshell.py index 9f4aab5..d9dad2a 100644 --- a/gameshell.py +++ b/gameshell.py @@ -54,7 +54,7 @@ class GameShell(Shell): def man(self, args): super(GameShell, self).man(args) - heapq.heappush(self.gameBase.eventQueue, (self.gameBase.gameTime, gameevents.NoOpEvent())) + #heapq.heappush(self.gameBase.eventQueue, (self.gameBase.gameTime, gameevents.NoOpEvent())) def options(self, args): i = 0 @@ -182,7 +182,7 @@ If -l is given, a map legend will be printed under the map.""" 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())) + #heapq.heappush(self.gameBase.eventQueue, (self.gameBase.gameTime, gameevents.NoOpEvent())) return def status(self, args): @@ -199,7 +199,7 @@ If -l is given, a map legend will be printed under the map.""" 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))) print('\n'.join(ret)) - heapq.heappush(self.gameBase.eventQueue, (self.gameBase.gameTime, gameevents.NoOpEvent())) + #heapq.heappush(self.gameBase.eventQueue, (self.gameBase.gameTime, gameevents.NoOpEvent())) return def inv(self, args): diff --git a/shell.py b/shell.py index 1603b31..4ea1cc6 100644 --- a/shell.py +++ b/shell.py @@ -20,22 +20,24 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # -# Ver. 0.1.0029 +# Ver. 0.1.0032 import types as _types import traceback as _tb import math as _math +import re as _re _CONV24TO8 = 6 / 256 class Shell(object): def __init__(self): - self.__commands = {'exit': self.exitShell} + self.__commands = {'exit': self.exitShell, 'batch': self.batch, 'alias': self.makeAlias, 'def': self.defineBatch} self.__aliases = {} + self.__batches = {} self.ps1 = '> ' - self.ps2 = '> ' + self.ps2 = '. ' self.colorMode = 0 # bits of color depth. Supports: 0, 3, 4, 8, 24 self.prevColor = '\x1b[0m' self.__exit = False @@ -126,16 +128,7 @@ class Shell(object): print(self.ps1, end = '') command = self.scanInput() # we have to handle shell built-ins first (when we get some) - if len(command) == 0: - continue - if command[0] in self.__commands: - try: - self.__commands[command[0]](command[1:]) - except Exception as e: - _tb.print_exc() - print(e) - else: - self.handleUnknownCommand(command) + self.handleCommand(command) self.update() self.__exit = False @@ -152,20 +145,23 @@ conventionally called args or argv""" 'a' must be one token, but original can be multiple.""" self.__aliases[a] = original + def registerBatch(self, name: str, commands: list): + self.__batches[name] = commands + def getAlias(self, a: str): if a in self.__aliases: return self.__aliases[a] else: return None + + def getBatch(self, name: str): + if name in self.__batches: + return ['batch'] + else: + return None - def exitShell(self, args): - """The default exit command.""" - self.__exit = True - - # Beyond this point are functions that are called within the main loop. - def scanInput(self): - """Parses input. Override this for custom input parsing, or input source.""" - instr = input() + def scanLine(self, instr): + """Take a line of text and turn it into an argument list""" if instr == '': return [] inQuotes = False @@ -193,8 +189,78 @@ conventionally called args or argv""" ret.append(instr[argStart:c]) a = self.getAlias(ret[0]) if a: - ret = a[:] + ret[1:] + ret = a + ret[1:] + b = self.getBatch(ret[0]) + if b: + ret = b + ret return ret + + # Default functions + + def exitShell(self, args): + """The default exit command.""" + self.__exit = True + + def makeAlias(self, args): + """Make an alias.""" + self.registerAlias(args[0], args[1:]) + + def defineBatch(self, args): + """Define a batch file.""" + if len(args) != 1: + raise ValueError('def takes only one argument') + ret = [] + command = input(self.ps2) + while command != 'end': + ret.append(command) + command = input(self.ps2) + self.registerBatch(args[0], ret) + + def batch(self, args): + """Run commands in batch mode +$0 is the name of the batch +$1 - $n are individual arguments +$* is all the arguments except 0 +$>0 - $>n is all arguments after the nth +$>=0 - $>=n is the nth and later arguments""" + script = self.__batches[args[0]] + var = _re.compile(r'\$((?:[>][=]?)?[0-9]+)') + u = False + for line in script: + if u: + self.update() + else: + u = True + newLine = line.replace(r'$*', ' '.join(args[1:])) + matches = var.finditer(newLine) + for m in matches: + n = m.group(1) + if n[0] == '>': + if n[1] == '=': + num = int(n[2:]) + else: + num = int(n[1:])+1 + newLine = newLine.replace(m.group(), ' '.join(args[num:])) + else: + newLine = newLine.replace(m.group(), args[int(n)]) + self.handleCommand(self.scanLine(newLine)) + + # Beyond this point are functions that are called within the main loop. + def scanInput(self): + """Parses input. Override this for custom input parsing, or input source.""" + return self.scanLine(input()) + + def handleCommand(self, command): + if len(command) == 0: + return + if command[0] in self.__commands: + try: + self.__commands[command[0]](command[1:]) + except Exception as e: + _tb.print_exc() + print(e) + else: + self.handleUnknownCommand(command) def handleUnknownCommand(self, command): """Handle commands that aren't registered. Override this if you want to do