def __init__(self): """ Initializes the Script """ self.actors= []; self.backdrop= None; self.actions= []; self.frames= [frame.Frame()]; self.metadata= ScriptMetadata(); self.subtitler= subtitler.Subtitler(); self.director= Director(self, self.subtitler); self.theater= None; #This is a Spyral Group to hold the actors, backdrop self.controls= gui.Form(); #Records all the PGU controls self.disabledControls= None; #Used to store the previous state of the controls when the're disabled self.skipControls= []; #Used to choose certain controls to be skipped when disabling. self.filepath= None; #Where this script is currently saved self.journal= False #Whether we're saving this script to the journal or not self.unsaved= False; #Whether changes have been made to this script since it's been saved.
def decodeJSON(self, input): """ Decodes a JSON-created object and modifies the script based on it. """ self.metadata= ScriptMetadata.decodeJSON(input['Meta']); self.setBackdrop(backdrop.Backdrop.decodeJSON(input['Backdrop'])); self.killActors(); self.actors= []; actorMap= {}; for encodedActor in input['Actors']: decodedActor, oldActorId= actor.Actor.decodeJSON(encodedActor); oldActorId= int(oldActorId); actorMap[oldActorId]= decodedActor; self.addActor(decodedActor); self.frames= [frame.Frame.decodeJSON(input['Initial Frame'], actorMap)]; i= 0; self.actions= []; for encodedAction in input['Actions']: decodedAction= action.Action.decodeJSON(encodedAction, actorMap); self.insertAction(decodedAction, i); i+= 1;
class Script(object): """ A *Script* is the essiential object of Broadway. It holds all the *Action*s, *Frame*s, *Actor*s, *ScriptMetadata*, and the *Backdrop*. It also holds references to the *Director* and *Subtitler*. The custom PGU widget *ScriptArea* manipulates the Script quite extensively. HACK: The Script is also aware of all the named PGU widgets, so that it can handle the disabling and enabling of them. This should be delegated to someone else. Two very important data structures are *actions* and *frames*, which are lists of *Action*s and *Frame*s respectively. There is always one more frame than action. An empty script still has at least one frame, indicating the initial state. Applying an Action to a frame produces a new frame; all frames represent the state after each action is applied. For a script of length N, you can see the transition as follows. Frame 0 -> Action 0 -> Frame 1 -> Action 1 -> ... -> Action N -> Frame N+1 TODO: Subtitler should probably belong to the Director directly. In fact, the Director should probably be more responsible for some of the things that the Script does with the Theater in general. """ def __init__(self): """ Initializes the Script """ self.actors= []; self.backdrop= None; self.actions= []; self.frames= [frame.Frame()]; self.metadata= ScriptMetadata(); self.subtitler= subtitler.Subtitler(); self.director= Director(self, self.subtitler); self.theater= None; #This is a Spyral Group to hold the actors, backdrop self.controls= gui.Form(); #Records all the PGU controls self.disabledControls= None; #Used to store the previous state of the controls when the're disabled self.skipControls= []; #Used to choose certain controls to be skipped when disabling. self.filepath= None; #Where this script is currently saved self.journal= False #Whether we're saving this script to the journal or not self.unsaved= False; #Whether changes have been made to this script since it's been saved. def refreshTheater(self): """ Forces the theater to redraw itself by calling spyrals redraw function. This can be used to clear up artifacts created by PGU that Spyral doesn't know about it. Those two just can't play nicely with each other. """ spyral.director.get_camera().redraw(); def scriptChanged(self): """ This is called to explictly mention that the script is changed, i.e. by changing Script Metadata, adding an actor, etc. """ self.unsaved= True; def setTheater(self, theater): """ Used to change the theater (at which point the subtitler is automatically added to it). """ self.theater= theater; theater.add(self.subtitler); def setRecorder(self, recorder): self.recorder= recorder def disableControls(self, skip= []): """ Disable all controls, except for those in the skip list. Keeps a record of the original state of the controls so that they can be restored correctly later. """ self.skipControls= skip; if self.disabledControls is None: self.disabledControls= {}; for aWidget in self.controls.results(): if aWidget not in self.skipControls: self.disabledControls[aWidget]= self.controls[aWidget].disabled; self.controls[aWidget].disabled= True; def enableControls(self, skip= []): """ Reset all controls to their previous state, as previously recorded before they were disabled. Skips any in the skip list. """ if self.disabledControls is not None: for aWidget in self.controls.results(): if aWidget in self.disabledControls and aWidget not in self.skipControls: self.controls[aWidget].disabled= self.disabledControls[aWidget]; self.disabledControls= None; self.skipControls= []; def setBackdrop(self, backdrop): ''' Change the current backdrop ''' if self.backdrop is not None: self.backdrop.kill(); self.backdrop= backdrop; self.theater.add(self.backdrop); def getRandomTwist(self): """ Uses the actors and backdrop to get a random twist """ return self.backdrop.getRandomTwist(self.actors); def getLongestTwist(self): """ Finds the longest twist possible, for sizing purposes. """ return self.backdrop.getLongestTwist(); def default(self): ''' Set the Script to it's defaults ''' #Backdrop self.setBackdrop(backdrop.Backdrop(defaults['backdrop'])); #Frames self.frames= [frame.Frame()]; #Actors self.killActors(); self.actors= []; narrator= actor.Actor('narrator',_('Narrator'), narrator= True); #otherActor= actor.Actor('cheerleader (blue)', 'Becky', narrator= False); self.addActor(narrator); #self.addActor(otherActor); #Actions self.actions= []; focusNarrator= action.Action(narrator, verbs.FOCUS, None); self.insertAction(focusNarrator, 0); #Metadata self.metadata.reset(); #Internal self.filepath= None self.unsaved= False def setState(self, time, character, state): ''' Change the state of a character at a time ''' actions= state.makeActions(character); for anAction in actions: self.applyAction(time, anAction); def applyAction(self, frame, newAction): """ Advance throught the script, starting at the *frame*, and apply the *newAction* until you come across the same type of action associated with that subject. """ if frame == 0: self.frames[frame][newAction.subject].applyAction(newAction); frame+= 1; while ((frame < len(self.frames)) and not (self.actions[frame-1].isSameType(newAction))): self.frames[frame][newAction.subject].applyAction(newAction); frame+= 1; def applyActionRecursively(self, time, newAction): ''' Recursively apply the action through the list of frames until either the end or a similar action is reached. This function is now deprecated. Probably doesn't work anymore either. ''' if time == 0: self.frames[time][newAction.subject].applyAction(newAction); self.applyAction(time+1, newAction); elif time-1 >= len(self.actions): # Equal to (time >= len(self.frames)) return; elif self.actions[time-1].isSameType(newAction): return; else: self.frames[time][newAction.subject].applyAction(newAction); self.applyAction(time+1, newAction); def insertAction(self, newAction, time): ''' Adds an action at a time. Time corresponds to a position in the actor list. ''' self.actions.insert(time, newAction); copyFrame= self.frames[time].copy(); self.frames.insert(time+1, copyFrame); self.frames[time+1][newAction.subject].applyAction(newAction);## self.applyAction(time+2, newAction); def removeAction(self, time): ''' Removes an action at a time. Time corresponds to a position in the actor list. ''' changed= self.actions.pop(time); self.frames.pop(time+1); previous= self.frames[time][changed.subject]; changed.objects= previous.getFromAction(changed); self.applyAction(time+1, changed); def changeAction(self, newAction, time): """ Changes an action at a time. Time corresponds to a position in the actor list. """ self.removeAction(time); self.insertAction(newAction, time); def changeActor(self, time, actor): """ Changes the actor who performs each of the subsequent actions, until another FOCUS change is reached. """ while time < len(self.actions): newAction= self.actions[time]; if newAction.verb != verbs.FOCUS: newAction= action.Action(actor, newAction.verb, newAction.objects); self.changeAction(newAction, time); time+= 1; else: break; def addActor(self, actor): ''' Add an actor to the script ''' self.actors.append(actor); for aFrame in self.frames: aFrame[actor]= actor.state; #The first actor, the narrator, is not drawn if self.actors: actor.add(self.theater); def killActors(self): """ Remove an actor from the Theater. Note that the author of Broadway does NOT encourage killing actors when you're done with them. That's probably really illegal and stuff. """ for anActor in self.actors: anActor.kill(); def removeActor(self, actor): ''' Remove an actor and all it's actions from the script ''' # Remove from actors list self.actors.remove(actor); # Remove from theater if len(self.actors): actor.kill(); print [x.rect for x in self.theater.sprites()]; #self.theater.remove(actor); i= 0; # Remove from actions while i < len(self.actions): if actor == self.actions[i].subject: self.removeAction(i); else: i+= 1; # Remove from frame for aFrame in self.frames: del aFrame[actor]; def getFocusedActor(self, time): ''' Walk backward through the actions till a FOCUS event is found ''' if time < 0: return self.actors[0]; elif time >= len(self.actions): time= len(self.actions)-1; while self.actions[time].verb != verbs.FOCUS and time > 0: time-= 1; return self.actions[time].subject; def chooseInterval(self, amount): """ Determine a reasonable interval to grab frames for export""" if amount == "Tons": return 1 elif amount == "None": return -1 elif len(self.frames) < 25: if amount == "Many": 5 else: return 8 elif amount == "Many": return 8 else: return 16 def export(self, arguments): """ Given a file path and export type (value), creates an exported copy of this script. """ path, fancy, amount = arguments if fancy == 'Plain': try: textFile = open(path, 'w') textFile.write(self.toPrettyText()) textFile.close() except Exception, e: print e, "Couldn't export file for some reason." elif fancy == 'Fancy': self.refreshTheater() spyral.director.get_camera().draw(True) self.recorder.startShuttering(self.chooseInterval(amount)) for time, action in enumerate(self.actions): self.director.goto(time, None) # Hack in the possibility for a saying action to have the actor's mouth open action = self.director.getCurrentAction() if action.verb == verbs.SAY: action.subject.changeFaceState(True) self.theater.draw() self.refreshTheater() changes= spyral.director.get_camera().draw(True) self.recorder.addShutter(changes) action.subject.changeFaceState(False) else: self.director.forwardAll() self.theater.draw() self.refreshTheater() changes= spyral.director.get_camera().draw(True) self.recorder.addShutter(changes, True) frames= self.recorder.stopShuttering() try: htmlFile = open(path, 'w') htmlFile.write(self.toHtml(frames)) htmlFile.close() except Exception, e: print e, "Couldn't export file for some reason"