| # Pass this program the Holy Grail script on stdin. |
| import sys |
| import string |
| import stdwin |
| from stdwinevents import * |
| |
| try: |
| import macspeech |
| except ImportError: |
| macspeech = None |
| |
| WINWIDTH = 1000 |
| scrw, scrh = stdwin.getscrsize() |
| if WINWIDTH > 0.8*scrw: |
| WINWIDTH = int(0.8*scrw) |
| BLACK = stdwin.fetchcolor('black') |
| RED = stdwin.fetchcolor('red') |
| BLUE = stdwin.fetchcolor('blue') |
| |
| done='done' |
| |
| class MacSpeaker: |
| def __init__(self): |
| self.voices = [] |
| self.nvoices = macspeech.CountVoices() |
| self.curvoice = 1 |
| self.rate = 1.0 |
| |
| def _newvoice(self): |
| vd = macspeech.GetIndVoice(self.curvoice) |
| sc = vd.NewChannel() |
| self.curvoice = self.curvoice + 1 |
| if self.curvoice > self.nvoices: |
| self.curvoice = 1 |
| return sc |
| |
| def newvoices(self, n): |
| self.voices = [] |
| for i in range(n): |
| self.voices.append(self._newvoice()) |
| if self.rate <> 1.0: |
| self.setrate(1.0) |
| |
| def setrate(self, factor): |
| self.rate = self.rate*factor |
| for v in self.voices: |
| r = v.GetRate() |
| v.SetRate(r*factor) |
| |
| def speak(self, i, text): |
| self.voices[i-1].SpeakText(text) |
| |
| def busy(self): |
| return macspeech.Busy() |
| |
| [NOTHING, NEWSCENE, ACT, TEXT, MORETEXT] = range(5) |
| def parseline(line): |
| stripline = string.strip(line) |
| if not stripline: |
| return NOTHING, '' |
| if stripline[:5] == 'Scene': |
| return NEWSCENE, stripline |
| if line[0] == '[': |
| return ACT, stripline |
| if line[0] == ' ' and ':' in line: |
| splitline = string.splitfields(stripline, ':') |
| stripline = string.joinfields(splitline[1:], ':') |
| return TEXT, (splitline[0], string.strip(stripline)) |
| return MORETEXT, stripline |
| |
| def readscript(file): |
| lines = file.readlines() |
| acts = [] |
| actor_dict = {} |
| longest = 0 |
| prev_act = 0 |
| for i in range(len(lines)): |
| tp, data = parseline(lines[i]) |
| if tp == NEWSCENE: |
| acts.append((actor_dict.keys(), lines[prev_act:i])) |
| prev_act = i |
| actor_dict = {} |
| elif tp == TEXT: |
| actor_dict[data[0]] = 1 |
| lines[i] = tp, data |
| return acts[1:] |
| |
| class Main: |
| def __init__(self): |
| if macspeech: |
| self.speaker = MacSpeaker() |
| else: |
| self.speaker = None |
| sys.stdin = open('SCRIPT', 'r') |
| self.acts = readscript(sys.stdin) |
| maxactor = 0 |
| for actorlist, actdata in self.acts: |
| if len(actorlist) > maxactor: |
| maxactor = len(actorlist) |
| if not self.loadnextact(): |
| print 'No acts to play!' |
| raise done |
| self.lh = stdwin.lineheight() |
| self.winheight = (maxactor+2)*self.lh |
| stdwin.setdefwinsize(WINWIDTH, self.winheight) |
| self.win = stdwin.open('The Play') |
| self.win.setdocsize(WINWIDTH, self.winheight) |
| self.win.change(((0,0),(WINWIDTH, self.winheight))) |
| self.menu = self.win.menucreate('Play') |
| self.menu.additem('Faster', '+') |
| self.menu.additem('Slower', '-') |
| self.menu.additem('Quit', 'Q') |
| self.speed = 4 |
| |
| def done(self): |
| del self.win |
| del self.menu |
| |
| def loadnextact(self): |
| if not self.acts: return 0 |
| actors, lines = self.acts[0] |
| del self.acts[0] |
| prevactor = 0 |
| for i in range(len(lines)): |
| tp, data = lines[i] |
| if tp == NOTHING: |
| continue |
| elif tp in (NEWSCENE, ACT): |
| lines[i] = 0, data |
| elif tp == TEXT: |
| prevactor = actors.index(data[0]) |
| lines[i] = prevactor+1, data[1] |
| else: |
| lines[i] = prevactor+1, data |
| self.lines = lines |
| self.actors = [''] + actors |
| self.actorlines = [''] * len(self.actors) |
| if self.speaker: |
| self.speaker.newvoices(len(self.actors)-1) |
| self.prevline = 0 |
| self.actwidth = 0 |
| for a in self.actors: |
| w = stdwin.textwidth(a) |
| if w > self.actwidth: |
| self.actwidth = w |
| return 1 |
| |
| def loadnextline(self): |
| if not self.lines: return 0 |
| self.actorlines[self.prevline] = '' |
| top = self.lh*self.prevline |
| self.win.change(((0, top), (WINWIDTH, top+self.lh))) |
| line, data = self.lines[0] |
| del self.lines[0] |
| self.actorlines[line] = data |
| self.prevline = line |
| top = self.lh*self.prevline |
| self.win.change(((0, top), (WINWIDTH, top+self.lh))) |
| if line == 0: |
| self.win.settimer(5*self.speed) |
| else: |
| if self.speaker: |
| self.speaker.speak(line, data) |
| tv = 1 |
| else: |
| nwords = len(string.split(data)) |
| tv = self.speed*(nwords+1) |
| self.win.settimer(tv) |
| return 1 |
| |
| def timerevent(self): |
| if self.speaker and self.speaker.busy(): |
| self.win.settimer(1) |
| return |
| while 1: |
| if self.loadnextline(): return |
| if not self.loadnextact(): |
| stdwin.message('The END') |
| self.win.close() |
| raise done |
| self.win.change(((0,0), (WINWIDTH, self.winheight))) |
| |
| def redraw(self, top, bottom, draw): |
| for i in range(len(self.actors)): |
| tpos = i*self.lh |
| bpos = (i+1)*self.lh-1 |
| if tpos < bottom and bpos > top: |
| draw.setfgcolor(BLUE) |
| draw.text((0, tpos), self.actors[i]) |
| if i == 0: |
| draw.setfgcolor(RED) |
| else: |
| draw.setfgcolor(BLACK) |
| draw.text((self.actwidth+5, tpos), self.actorlines[i]) |
| |
| def run(self): |
| self.win.settimer(10) |
| while 1: |
| ev, win, arg = stdwin.getevent() |
| if ev == WE_DRAW: |
| ((left, top), (right, bot)) = arg |
| self.redraw(top, bot, self.win.begindrawing()) |
| elif ev == WE_TIMER: |
| self.timerevent() |
| elif ev == WE_CLOSE: |
| self.win.close() |
| raise done |
| elif ev == WE_MENU and arg[0] == self.menu: |
| if arg[1] == 0: |
| if self.speed > 1: |
| self.speed = self.speed/2 |
| if self.speaker: |
| self.speaker.setrate(1.4) |
| elif arg[1] == 1: |
| self.speed = self.speed * 2 |
| if self.speaker: |
| self.speaker.setrate(0.7) |
| elif arg[1] == 2: |
| self.win.close() |
| raise done |
| |
| if 1: |
| main = Main() |
| try: |
| main.run() |
| except done: |
| pass |
| del main |