def test_action(self): h = TextHistory() action = InsertAction(pos=0, text='abc', from_version=0, to_version=10) self.assertEqual(10, h.action(action)) self.assertEqual('abc', h.text) self.assertEqual(10, h.version)
def test_action__bad(self): h = TextHistory() action = InsertAction(pos=0, text='abc', from_version=10, to_version=10) with self.assertRaises(ValueError): h.action(action)
def test_delete(self): h = TextHistory() self.assertEqual(1, h.insert('abc xyz')) self.assertEqual(2, h.delete(pos=1, length=2)) self.assertEqual('a xyz', h.text) self.assertEqual(2, h.version) self.assertEqual(3, h.delete(pos=3, length=0)) self.assertEqual('a xyz', h.text) self.assertEqual(3, h.version)
def test_delete__bad(self): h = TextHistory() self.assertEqual(1, h.insert('abc')) with self.assertRaises(ValueError): h.delete(pos=10, length=2) with self.assertRaises(ValueError): h.delete(pos=1, length=3) with self.assertRaises(ValueError): h.delete(pos=-1, length=2)
def test_insert_opt(self): h = TextHistory() h.insert('a' * 10) h.insert('x', pos=3) h.insert('yz', pos=4) self.assertEqual('aaaxyzaaaaaaa', h.text) actions = h.get_actions(1) self.assertEqual(1, len(actions)) action = actions[0] self.assertIsInstance(action, InsertAction) opt_value = InsertAction(3, 'xyz', 1, 3) self.assertEqual(action.__dict__, opt_value.__dict__)
def test_insert_merge(self): h = TextHistory() h.insert('abcd qwe') h.insert('gg') h.insert('pop') actions = h.get_actions() self.assertEqual(1, len(actions)) v1 = actions[0] self.assertEqual('abcd qweggpop', v1.text) self.assertEqual(0, v1.pos) self.assertEqual(3, v1.to_version)
def test_add_replace(self): h = TextHistory() h.insert('abcdef') h.replace('xyz', pos=2) h.replace('QR', pos=2) actions = h.get_actions() self.assertEqual(2, len(actions)) v2 = actions[1] self.assertEqual('QRz', v2.text) self.assertEqual(2, v2.pos) self.assertEqual(3, v2.to_version)
def test_optimized_insert(self): h = TextHistory() h.insert('AB') self.assertEqual('AB', h.text) self.assertEqual(1, h.version) h.insert('CC', pos=2) self.assertEqual('ABCC', h.text) self.assertEqual(2, h.version) h.insert('D', pos=4) self.assertEqual('ABCCD', h.text) self.assertEqual(3, h.version) self.assertEqual(1, len(h.get_actions()))
def test_optimized_delete(self): h = TextHistory() h.insert('ABCCDEEE') h.delete(2, 2) h.delete(2, 3) self.assertEqual('ABE', h.text) self.assertEqual(2, len(h.get_actions()))
def test_delete_opt(self): h = TextHistory() h.insert('a1b2c3d4e5f6g7h8') self.assertEqual('a1b2c3d4e5f6g7h8', h.text) h.delete(pos=3, length=2) self.assertEqual('a1b3d4e5f6g7h8', h.text) h.delete(pos=3, length=6) self.assertEqual('a1b6g7h8', h.text) actions = h.get_actions(1) self.assertEqual(1, len(actions)) action = actions[0] self.assertIsInstance(action, DeleteAction) opt_value = DeleteAction(3, 8, 1, 3) self.assertEqual(action.__dict__, opt_value.__dict__)
def test_optimize_delete(self): h = TextHistory() h.insert('abcdefg') h.delete(2, 2) h.delete(2, 1) actions = h.get_actions() self.assertEqual(2, len(h.get_actions())) insert, delete = actions self.assertIsInstance(insert, InsertAction) self.assertIsInstance(delete, DeleteAction)
def test_get_actions__optimyze_2(self): h = TextHistory() h.insert('a') h.insert('b', 0) h.replace("abc") actions = h.get_actions() self.assertEqual(2, len(actions)) insert, replace = actions self.assertIsInstance(insert, InsertAction) self.assertIsInstance(replace, ReplaceAction) self.assertEqual(0, insert.from_version) self.assertEqual(2, insert.to_version) self.assertEqual("b", insert.text)
def test_get_optimized_actions__merge_insert(self): h = TextHistory() h.insert('abc') h.insert('BEGIN', 0) h.insert('END') self.assertEqual('BEGINabcEND', h.text) actions = h.get_actions(optimize=True) self.assertEqual(1, len(actions)) insert = actions[0] self.assertIsInstance(insert, InsertAction) self.assertEqual(3, insert.to_version) self.assertEqual(0, insert.from_version) self.assertEqual(0, insert.pos)
def test_replace__bad(self): h = TextHistory() self.assertEqual(1, h.insert('abc')) with self.assertRaises(ValueError): h.replace('abc', pos=10) with self.assertRaises(ValueError): h.replace('abc', pos=-1)
def test_optimize_insert(self): h = TextHistory() h.insert('a') h.insert('b') h.insert('c') actions = h.get_actions() self.assertEqual(1, len(actions)) action, = actions self.assertIsInstance(action, InsertAction)
def test_insert_mix_merge(self): h = TextHistory() h.insert('abcd qwe') h.insert('gg', pos=2) actions = h.get_actions() self.assertEqual(1, len(actions)) v1 = actions[0] self.assertEqual('abggcd qwe', v1.text) self.assertEqual(0, v1.pos) self.assertEqual(2, v1.to_version)
def test_get_optimized_actions__merge_delete(self): h = TextHistory() h.insert('0123456789') h.delete(3, 2) h.delete(3, 3) h.delete(2, 1) self.assertEqual('0189', h.text) actions = h.get_actions(optimize=True) self.assertEqual(2, len(actions)) insert, delete = actions self.assertIsInstance(insert, InsertAction) self.assertIsInstance(delete, DeleteAction) self.assertEqual(4, delete.to_version) self.assertEqual(1, delete.from_version) self.assertEqual(2, delete.pos) self.assertEqual(6, delete.length)
def test_trivial_replace(self): h = TextHistory() h.insert('abcdef') h.replace('A', pos=0) h.replace('B', pos=1) actions = h.get_actions() self.assertEqual(2, len(actions)) v2 = actions[1] self.assertEqual('AB', v2.text) self.assertEqual(0, v2.pos) self.assertEqual(3, v2.to_version)
def test_get_actions_insert_delete_optimization(self): h = TextHistory() h.insert(text="abdc") h.delete(pos=1, length=2) self.assertEqual(h.text, "ac") all_actions = h.get_actions() self.assertEqual(len(all_actions), 1) insert = all_actions[0] self.assertIsInstance(insert, InsertAction) self.assertEqual(insert.text, "ac")
def test_get_actions__optimyze_1(self): h = TextHistory() h.insert('a') h.insert('b') h.delete(0, 1) h.delete(0, 1) h.replace("abc") actions = h.get_actions() self.assertEqual(3, len(actions)) insert, delete, replace = actions self.assertIsInstance(insert, InsertAction) self.assertIsInstance(delete, DeleteAction) self.assertIsInstance(replace, ReplaceAction) # insert self.assertEqual(0, insert.from_version) self.assertEqual(2, insert.to_version) self.assertEqual("ab", insert.text) #delete self.assertEqual(2, delete.from_version) self.assertEqual(4, delete.to_version) self.assertEqual(2, delete.length)
def test_replace(self): h = TextHistory() self.assertEqual(1, h.replace('abc')) self.assertEqual('abc', h.text) self.assertEqual(1, h.version) self.assertEqual(2, h.replace('xyz', pos=2)) self.assertEqual('abxyz', h.text) self.assertEqual(2, h.version) self.assertEqual(3, h.replace('X', pos=2)) self.assertEqual('abXyz', h.text) self.assertEqual(3, h.version) self.assertEqual(4, h.replace('END')) self.assertEqual('abXyzEND', h.text) self.assertEqual(4, h.version)
def test_insert(self): h = TextHistory() self.assertEqual(1, h.insert('abc')) self.assertEqual('abc', h.text) self.assertEqual(1, h.version) self.assertEqual(2, h.insert('xyz', pos=2)) self.assertEqual('abxyzc', h.text) self.assertEqual(2, h.version) self.assertEqual(3, h.insert('END')) self.assertEqual('abxyzcEND', h.text) self.assertEqual(3, h.version) self.assertEqual(4, h.insert('BEGIN', pos=0)) self.assertEqual('BEGINabxyzcEND', h.text) self.assertEqual(4, h.version)
def test_version__trivial(self): h = TextHistory() self.assertEqual(0, h.version) with self.assertRaises(AttributeError): h.version = 42
def test_get_optimized_actions__merge_replace(self): h = TextHistory() h.insert('01234567891011') h.replace('aaa', 6) h.replace('LEFT', 2) h.replace('RIGHT', 9) self.assertEqual('01LEFTaaaRIGHT', h.text) actions = h.get_actions(optimize=True) self.assertEqual(2, len(actions)) insert, replace = actions self.assertIsInstance(insert, InsertAction) self.assertIsInstance(replace, ReplaceAction) self.assertEqual(1, replace.from_version) self.assertEqual(4, replace.to_version) self.assertEqual(2, replace.pos)
def test_text__trivial(self): h = TextHistory() self.assertEqual('', h.text) with self.assertRaises(AttributeError): h.text = 'NEW'
class StoryManager(SogalForm): """Story controller of Sogal Controls the whole story scene. Mainly for logic control And graphics is on other Attributes: gameTextBox: the current GameTextBox, useful for user scripting storyView: the current StoryView, useful for user scripting """ script_space = {} _currentDump = None __destroyed = False def __init__(self): self.step = 0 #shows how many commands line it had run self.scrStack = [] self.commandList = [] self.__currentPtr = None if not runtime_data.RuntimeData.command_ptr: self.nextPtr = 0 else: self.nextPtr = runtime_data.RuntimeData.command_ptr if not runtime_data.RuntimeData.command_stack: runtime_data.RuntimeData.command_stack = self.scrStack else: self.scrStack = runtime_data.RuntimeData.command_stack if not runtime_data.RuntimeData.command_list: runtime_data.RuntimeData.command_list = self.commandList else: self.commandList = runtime_data.RuntimeData.command_list self._frame = DirectFrame(parent = aspect2d) # @UndefinedVariable pydev在傲娇而已不用管 self._frame.setTransparency(TransparencyAttrib.MAlpha) self.storyView = StoryView() self.audioPlayer = base.audioPlayer # @UndefinedVariable pydev在傲娇而已不用管 self.menu = StoryMenuBar() self.gameTextBox = GameTextBox() self.textHistory = TextHistory() self.button_auto = self.menu.addButton(text = 'Auto',state = DGG.NORMAL,command = self.autoPlayButton) self.button_history = self.menu.addButton(text = 'History',state = DGG.NORMAL,command = self.showTextHistoryButton) self.button_skip = self.menu.addButton(text = 'Skip',state = DGG.DISABLED,command = self.startSkippingButton) self.button_lastchoice = self.menu.addButton(text = 'Last Choice',state = DGG.DISABLED,command = self.lastChoice) self.button_save = self.menu.addButton(text = 'Save',state = DGG.DISABLED, command = self.saveButton) self.button_load = self.menu.addButton(text = 'Load',state = DGG.NORMAL,command = self.loadButton) self.button_quicksave = self.menu.addButton(text = 'Quick Save',state = DGG.DISABLED,command = self.quickSaveButton) self.button_quickload = self.menu.addButton(text = 'Quick Load',state = DGG.DISABLED,command = self.quickLoadButton) self.button_config = self.menu.addButton(text = 'Options', state = DGG.NORMAL, command = self._configButton) self.button_title = self.menu.addButton(text = 'Title',state = DGG.NORMAL,command = self.returnToTitle) self._inputReady = True self.__arrow_shown = False self._choiceReady = True self._currentMessage = '' self.__currentSelection = None self.__finishing = False self.__lock = Lock() self.forcejump = False self.__autoInput = False self.__focused = False self.intervals = [] self.__skipping = False self.__autoplaying = False self.__autoInterval = None self.__autoplaystep = None self.mapScriptSpace() SogalForm.__init__(self) self.show() taskMgr.add(self.loopTask,'story_manager_loop',sort = 2,priority = 1) # @UndefinedVariable 傲娇的pydev……因为panda3D的"黑魔法"…… taskMgr.doMethodLater(runtime_data.game_settings['jump_span'],self.__jumpCheck,'story_jump_check', sort = 5, priority = 1) # @UndefinedVariable def focused(self): if not self.__focused: self.accept('mouse1', self.input, [1]) self.accept('enter', self.input, [1]) self.accept('mouse3', self.input, [3]) self.accept('wheel_up', self.showTextHistory) self.accept('escape', self.showMenu) self.accept('control',self.setForceJump, [True]) self.accept('control-up',self.setForceJump, [False]) SogalForm.focused(self) self.__focused = True def defocused(self): if self.__focused: self.ignore('mouse1') self.ignore('enter') self.ignore('mouse3') self.ignore('wheel_up') self.ignore('escape') self.ignore('control') self.ignore('control-up') self.setForceJump(False) SogalForm.defocused(self) self.__arrow_shown = False self.gameTextBox.hideArrow() self.__focused = False def destroy(self): self.__destroyed = True taskMgr.remove('story_manager_loop') # @UndefinedVariable taskMgr.remove('story_jump_check') # @UndefinedVariable if self._frame: self._frame.destroy() self._frame = None if self.__currentSelection: self.__currentSelection.destroy() self.gameTextBox.destroy() self.storyView.destroy() self.menu.destroy() self.textHistory.destroy() SogalForm.destroy(self) def loopTask(self,task): ''' The task loop of StoryManager, trying to advance every task frame ''' if not self.__destroyed: if self.hasFocus(): self.forward(False) return task.cont else: return task.done def _enableSavingButton(self): self.button_save['state'] = DGG.NORMAL self.button_quicksave['state'] = DGG.NORMAL def _disableSavingButton(self): self.button_save['state'] = DGG.DISABLED self.button_quicksave['state'] = DGG.DISABLED def presave(self): if self.nextPtr is not None: runtime_data.RuntimeData.command_ptr = self.nextPtr self.gameTextBox.presave() self.storyView.presave() self.audioPlayer.presave() def reload(self): taskMgr.remove('storyManagerLoop') # @UndefinedVariable self.nextPtr = runtime_data.RuntimeData.command_ptr self.mapScriptSpace() self.gameTextBox.reload() self.storyView.reload() self.audioPlayer.reload() taskMgr.add(self.loopTask,'storyManagerLoop',sort = 2,priority = 1) # @UndefinedVariable 傲娇的pydev……因为panda3D的"黑魔法"…… def mapScriptSpace(self): if runtime_data.RuntimeData.script_space: #map script space self.script_space = runtime_data.RuntimeData.script_space else: runtime_data.RuntimeData.script_space = self.script_space script_global['goto'] = self.goto script_global['story_manager'] = self script_global['game_text_box'] = self.gameTextBox script_global['story_view'] = self.storyView script_global['audio_player'] = self.audioPlayer def quickfinish(self): self.__lock.acquire() if not self.__finishing: self.__finishing = True self.storyView.quickfinish() self.gameTextBox.quickFinish() for itv in self.intervals: itv.finish() self.__lock.release() def input(self,type = 1): self.stopSkipping() self.stopAutoPlaying() if not self.hasFocus(): return #left mouse button or enter key if type == 1 : if not self.getSceneReady(): self.quickfinish() else: self.setTextInputReady(True) #right mouse button elif type == 3: self.quickfinish() if self.getSceneReady(): self.menu.show() def showMenu(self): self.stopSkipping() self.stopAutoPlaying() self.quickfinish() if self.getSceneReady() and not self.forcejump: self.menu.show() def showTextHistory(self): if self.getSceneReady() and not self.forcejump: self.textHistory.show() def saveButton(self): self.menu.hide() base.saveForm.setData(self._currentDump, self._currentMessage) base.saveForm.show() def loadButton(self): self.menu.hide() base.loadForm.show() def quickSaveButton(self): '''quicksave the data''' self.menu.hide() self.button_quicksave['state'] = DGG.DISABLED self.button_quickload['state'] = DGG.DISABLED if self._currentDump: messenger.send('quick_save', [self._currentDump,self._currentMessage]) def quickLoadButton(self): ConfirmDialog(text= '要读取吗?',command= self.__confirmedQuickLoad) def _configButton(self): messenger.send('config_form') def returnToTitle(self): ConfirmDialog(text= '回到标题界面?',command= self.__confirmedReturnToTitle) def __confirmedReturnToTitle(self): messenger.send('return_to_title') def showTextHistoryButton(self): self.menu.hide() self.showTextHistory() def startSkippingButton(self): self.menu.hide() self.startSkipping() def autoPlayButton(self): self.menu.hide() if self.__autoplaying: self.stopAutoPlaying() else: self.startAutoPlaying() def lastChoice(self): ConfirmDialog(text= '要回到上一个选择枝吗?',command= self.__confirmedLastChoice) def __confirmedQuickLoad(self): messenger.send('quick_load') # @UndefinedVariable def __confirmedLastChoice(self): if runtime_data.RuntimeData.last_choice: messenger.send('load_memory',[runtime_data.RuntimeData.last_choice]) # @UndefinedVariable def autoSave(self,info = ''): if not info: info = self._currentMessage messenger.send('auto_save',[self._currentDump,info]) def getSceneReady(self): '''Get if the scene is ready''' textbox_ready = False view_ready = False intervals_ready = True if not self.gameTextBox.getIsWaiting(): textbox_ready = True if not self.storyView.getIsWaiting(): view_ready = True for itv in self.intervals: if itv.isPlaying(): intervals_ready = False break scene_ready = textbox_ready and view_ready and intervals_ready if not scene_ready: return False #auto play span if scene_ready and self.__autoplaying: if self.__autoplaystep != self.step: self.__autoplaystep = self.step self.__autoInterval = Wait(runtime_data.game_settings['auto_span']) self.__autoInterval.start() scene_ready = False else: if self.__autoInterval.isPlaying(): scene_ready = False if self.audioPlayer.isVoicePlaying(): scene_ready = False return scene_ready def getInputReady(self): '''define is user's 'next' command given''' textinput_ready = self._inputReady or self.__autoInput choice_ready = self.getChoiceReady() if self.__autoInput: self.__autoInput = False if textinput_ready and choice_ready: return True return False def setTextInputReady(self, value): self._inputReady = value def getChoiceReady(self): return self._choiceReady def forward(self,is_user = False): '''run nextCommand() or finish current operations quickly @param is_user: define if this operation is started by the player ''' scene_ready = self.getSceneReady() if scene_ready and not self.__arrow_shown: self.gameTextBox.showArrow() self.__arrow_shown = False if scene_ready and self.getInputReady(): self.nextCommand() if self.forcejump or self.__skipping: self.quickfinish() def nextCommand(self): '''Process the next command in the non-processed queue ''' #TODO: 还要添加循环的支持,用到runtime_data.command_stack来暂存需要回跳的命令组和回跳节点 #TODO:添加对条件和选择的支持 self.__finishing = False self.__arrow_shown = False self.gameTextBox.hideArrow() #Dumps story data for saving or further use if self.__destroyed: return self.presave() self._currentDump = copy.deepcopy(runtime_data.RuntimeData) if self.nextPtr < 0: self.nextPtr = 0 self.__currentPtr = self.nextPtr self.nextPtr += 1 if not self.commandList: return if len(self.commandList) > self.__currentPtr: handled = False if self.commandList[self.__currentPtr].command: comline = self.commandList[self.__currentPtr].command.strip() if comline.startswith('mark ')or comline.startswith('mark:'): handled = True # 条件判断处理 If condition elif comline.startswith('if '): splited = space_cutter.split(comline, 1) if len(splited)<2: raise Exception('没条件玩毛线') if self.scriptEval(splited[1]): handled = True else: relative_depth = 0 #if not match the condition, try to jump to elif or else for i in range(self.__currentPtr+1,len(self.commandList)): cli = self.commandList[i] if cli.command: cl = cli.command.strip() else: continue #一个嵌套循环的情况! A inner if if cl.startswith('if '): relative_depth += 1 continue elif relative_depth == 0 and cl.startswith('elif '): splited = space_cutter.split(cl, 1) if len(splited)<2: raise Exception('没条件玩毛线') if self.scriptEval(splited[1]): self.nextPtr = i + 1 handled = True break else: continue elif relative_depth == 0 and cl == 'else': self.nextPtr = i + 1 handled = True break elif cl == 'end' or cl.startswith('end '): if relative_depth == 0: self.nextPtr = i + 1 handled = True break else: relative_depth -= 1 continue #if we meet else or elif then jump to end elif comline.startswith('elif ') or comline == 'else': relative_depth = 0 for i in range(self.__currentPtr+1,len(self.commandList)): cli = self.commandList[i] if cli.command: cl = cli.command.strip() else: continue if cl.startswith('if '): relative_depth += 1 continue elif cl == 'end' or cl.startswith('end '): if relative_depth == 0: self.nextPtr = i + 1 handled = True break else: relative_depth -= 1 continue #ignore end elif comline == 'end' or comline.startswith('end '): handled = True if not handled: self.processCommand(self.commandList[self.__currentPtr]) #self.scrPtr = self.scrPtr + 1 #runtime_data.RuntimeData.command_ptr = self.scrPtr if base.hasQuickData(): self.button_quickload['state'] = DGG.NORMAL if runtime_data.RuntimeData.last_choice: self.button_lastchoice['state'] = DGG.NORMAL if self._currentDump: self._enableSavingButton() self.step += 1 #mark step def goto(self, target): '''Jump to a mark''' for i in range(0, len(self.commandList)): if self.commandList[i].command: mark = mark_cutter.split(self.commandList[i].command , 1) if len(mark) > 1: markText = mark[1].strip() if markText == target: self.nextPtr = i #Solved: #this is not a good solution but this method runs at 'nextCommand', and ths scrPtr would plus 1 afterwards return safeprint('unable to find mark') def processCommand(self,command): '''Process a StoryCommand @param command: The StoryCommand to deal with ''' def seval(strs): return self.scriptEval(strs) #Mark read if not runtime_data.read_text.has_key(command.fileLoc): runtime_data.read_text[command.fileLoc] = {} if not runtime_data.read_text[command.fileLoc].has_key(command.index): already_read = False self.stopSkipping() else: already_read = True runtime_data.read_text[command.fileLoc][command.index] = True if already_read: self.button_skip['state'] = DGG.NORMAL else: self.button_skip['state'] = DGG.DISABLED self.storyView.clearQuickItems() #clear out quick items name = '' continuous = False is_script = False is_selection = False spaceCutter = space_cutter hidingtext = False voiceFlag = False #it will be True if voice is stopped in this line of command #used for disable cross voice of different command lines #but enable one command line with multiple voices autoSaving = False #Mark if autosaving is needed at the end of this step autoSavingInfo = '' #read command line if command.command: commands = command.command.split(',') comm = '' for item in commands: comm = item.strip() if comm: messenger.send('sogalcommand',[comm]) #allow other tools to deal with it @UndefinedVariable #名字设置命令 if comm.startswith('name ') or comm.startswith('name='): nameCutter = re.compile(ur'name *=?',re.UNICODE) name = nameCutter.split(comm,1)[1].strip() #改变文本框格式命令 elif comm.startswith('textboxstyle '): if self.gameTextBox: self.gameTextBox.setTextBoxStyle(spaceCutter.split(comm, 1)[1]) self.gameTextBox.applyStyle() #文本框分段命令 elif comm == 'p': if self.gameTextBox: self.gameTextBox.paragraphSparator() elif comm == 'c': continuous = True elif comm.startswith('wait '): temp = spaceCutter.split(comm,1) if len(temp) > 1: self.sceneWait(seval(temp[1])) #文本框属性设置命令 elif comm.startswith('textbox '): temp = spaceCutter.split(comm,2) if temp[1] == 'apply': self.gameTextBox.applyTextBoxProperties() elif len(temp)>=3: self.gameTextBox.setTextBoxProperty(temp[1],seval(temp[2])) else: safeprint('Not enough: ' + comm) #背景设置命令 elif comm.startswith('bg '): temp = spaceCutter.split(comm,2) if len(temp) >= 3: fadein = seval(temp[2]) else: fadein = 0 self.storyView.changeBackground(temp[1],fadein) #图片显示命令 elif comm.startswith('p '): temp = spaceCutter.split(comm,6) if len(temp) >= 7: fadein = seval(temp[6]) else: fadein = 0 if len(temp) >= 6: scale = seval(temp[5]) else: scale = 1 if len(temp) >= 5: location = (seval(temp[3]),0,seval(temp[4])) else: if self.storyView.itemEntries.has_key(temp[1]): location = self.storyView.itemEntries[temp[1]].pos else: location = (0,0,0) svie = StoryViewItemEntry(temp[1],temp[2],SVIC.FG,pos = location,scale = (scale,scale,scale),color = (1,1,1,1),fadein = fadein) self.storyView.newItem(svie) elif comm.startswith('del '): temp = spaceCutter.split(comm,2) if len(temp)>=3: self.storyView.deleteItem(temp[1], seval(temp[2])) else: self.storyView.deleteItem(temp[1]) elif comm.startswith('ploc '): temp = spaceCutter.split(comm,5) if len(temp) >= 5: location = (seval(temp[2]),seval(temp[3]),seval(temp[4])) else: location = (seval(temp[2]),0,seval(temp[3])) if len(temp) >= 6: fadein = seval(temp[5]) else: fadein = 0 self.storyView.changePosColorScale(temp[1], pos = location,time = fadein) elif comm.startswith('pcolor '): temp = spaceCutter.split(comm,6) color = (seval(temp[2]),seval(temp[3]),seval(temp[4]),seval(temp[5])) if len(temp) >= 7: fadein = seval(temp[6]) else: fadein = 0 self.storyView.changePosColorScale(temp[1], color = color, time = fadein) elif comm.startswith('pscale '): temp = spaceCutter.split(comm,5) if len(temp) >= 5: scale = (seval(temp[2]),seval(temp[3]),seval(temp[4])) else: scale = (seval(temp[2]),seval(temp[2]),seval(temp[2])) if len(temp) == 6: fadein = seval(temp[5]) elif len(temp) == 4: fadein = seval(temp[3]) else: fadein = 0 self.storyView.changePosColorScale(temp[1], scale = scale, time = fadein) elif comm.startswith('o3d '): temp = spaceCutter.split(comm) svie = StoryViewItemEntry(temp[1],temp[2],SVIC.O3D,pos = (seval(temp[3]),seval(temp[4]),seval(temp[5])) ,scale = (seval(temp[10]),seval(temp[11]),seval(temp[12])),color = (seval(temp[6]),seval(temp[7]),seval(temp[8]),seval(temp[9]))) self.storyView.newItem(svie) elif comm.startswith('o2d '): temp = spaceCutter.split(comm) svie = StoryViewItemEntry(temp[1],temp[2],SVIC.O2D,pos = (seval(temp[3]),seval(temp[4]),seval(temp[5])) ,scale = (seval(temp[10]),seval(temp[11]),seval(temp[12])),color = (seval(temp[6]),seval(temp[7]),seval(temp[8]),seval(temp[9]))) self.storyView.newItem(svie) elif comm.startswith('pa '): temp = spaceCutter.split(comm) if len(temp) >= 8: fadein = seval(temp[7]) else: fadein = 0 svie = StoryViewItemEntry(temp[1],temp[2],SVIC.AFG,pos = (seval(temp[3]),0,seval(temp[4])) ,scale = (seval(temp[5]),1,seval(temp[6])),fadein = fadein) self.storyView.newItem(svie) elif comm == 'clear': hidingtext = True self.storyView.clear() elif comm.startswith('clear '): hidingtext = True temp = spaceCutter.split(comm,2) if len(temp)>=3: self.storyView.clear(seval(temp[1]),temp[2]) elif len(temp)==2: self.storyView.clear(seval(temp[1])) else: self.storyView.clear() elif comm.startswith('delbg '): temp = spaceCutter.split(comm,1) if len('temp')>=2: self.storyView.deleteItem('__bg__', seval(temp[1])) else: self.storyView.deleteItem('__bg__') elif comm.startswith('qp '): temp = spaceCutter.split(comm,3) svie = StoryViewItemEntry('quickitem',temp[1],SVIC.FG, pos = (seval(temp[2]),0,seval(temp[3])), quickitem = True ) self.storyView.newItem(svie) elif comm.startswith('v '): if not voiceFlag: self.audioPlayer.stopVoice() voiceFlag = True temp = spaceCutter.split(comm , 2) if len(temp)>=3: volume = seval(temp[2]) else: volume = 1 self.audioPlayer.playVoice(temp[1],volume) elif comm.startswith('se '): temp = spaceCutter.split(comm , 2) if len(temp)>=3: volume = seval(temp[2]) else: volume = 1 self.audioPlayer.playSound(temp[1],volume) elif comm == 'vstop': self.audioPlayer.stopVoice() voiceFlag = True elif comm == 'sestop': self.audioPlayer.stopSound() elif comm.startswith('bgm '): temp = spaceCutter.split(comm , 4) if len(temp)>=3: fadein = seval(temp[2]) else: fadein = 0 if len(temp)>=4: volume = seval(temp[3]) else: volume = 1 if len(temp)>=5: loop = bool(seval(temp[4])) else: loop = True self.audioPlayer.playBGM(temp[1], fadein=fadein, volume=volume, loop=loop) elif comm.startswith('env '): temp = spaceCutter.split(comm , 4) if len(temp)>=3: fadein = seval(temp[2]) else: fadein = 0 if len(temp)>=4: volume = seval(temp[3]) else: volume = 1 if len(temp)>=5: loop = bool(seval(temp[4])) else: loop = True self.audioPlayer.playENV(temp[1], fadein=fadein, volume=volume, loop=loop) elif comm.startswith('bgmstop ') or comm == 'bgmstop': temp = spaceCutter.split(comm , 1) if len(temp)>=2: fadeout = seval(temp[1]) else: fadeout = 0 self.audioPlayer.stopBGM(fadeout) elif comm.startswith('envstop ') or comm == 'envstop': temp = spaceCutter.split(comm , 1) if len(temp)>=2: fadeout = seval(temp[1]) else: fadeout = 0 self.audioPlayer.stopENV(fadeout) elif comm.startswith('audiostop ') or comm == 'audiostop': temp = spaceCutter.split(comm , 1) if len(temp)>=2: fadeout = seval(temp[1]) else: fadeout = 0 self.audioPlayer.stopAll(fadeout) elif comm == 'script': is_script = True elif comm.startswith('script '): temp = spaceCutter.split(comm , 1) self.runScriptFile(temp[1]) elif comm == 'choice' or comm.startswith('choice '): ''' Selection ''' is_selection = True temp = spaceCutter.split(comm, 1) if len(temp) > 1: striped = temp[1].strip() if striped: self.pushText(text = striped, speaker = None, needInput = False, read = already_read) elif comm.startswith('jump '): temp = spaceCutter.split(comm , 1) self.beginScene(temp[1].strip()) elif comm.startswith('expand '): temp = spaceCutter.split(comm , 1) self.expandScene(temp[1].strip()) elif comm.startswith('goto '): temp = spaceCutter.split(comm , 1) self.goto(temp[1].strip()) elif comm.startswith('theme '): temp = spaceCutter.split(comm , 1) self.reloadTheme(temp[1].strip()) elif comm == 'autosave' or comm.startswith('autosave '): if self.step > 0: autoSaving = True temp = spaceCutter.split(comm , 1) if len(temp) > 1: autoSavingInfo = temp[1] else: if comm: safeprint('extra command: ' + comm) if command.text: if is_script: self.runScript(command.text) elif is_selection: ''' If encountered a selection ''' choiceList = [] enablesList = [] textlines = command.text.splitlines() for tl in textlines: if tl.startswith('--'): #--means disabled text = tl[2:] enablesList.append(False) else: text = tl enablesList.append(True) choiceList.append(text) self.showSelection(choiceList = choiceList, enablesList = enablesList) else: self.pushText(text = command.text, speaker = name, continuous = continuous, read = already_read) else: if hidingtext: self.gameTextBox.hide() #better to hide the textbox when 'vclear' if self.gameTextBox.newText: runtime_data.RuntimeData.latest_text = self.gameTextBox.newText if runtime_data.RuntimeData.latest_text: self._currentMessage = runtime_data.RuntimeData.latest_text #Autosave if autoSaving: if autoSavingInfo: self.autoSave(autoSavingInfo) else: self.autoSave() def pushText(self, text, speaker = None, continuous = False, needInput = True, read = False): def translate(t): ''' ,实现通配,__代替空行已经在一开始实现 ''' return t.replace(ur'\:', ur':').replace(ur'\:',ur':').replace(ur'\#',ur'#') #检查有无在文本中的name #name: formation checking textlines = text.splitlines() first_line = unicode(textlines[0]) #匹配第一行中是否有表示name的冒号,正则表达式表示前面不是\的冒号(@name 命令行的简写形式判断) pattern = re.compile(ur'(?<!\\)[:(:)]',re.UNICODE) splited = pattern.split(first_line,maxsplit = 1) #print(splited) #测试用,废弃 #如果存在name即分割成功 if len(splited)>1: speaker = translate(splited[0]).strip() if splited[1].strip(): textlines[0] = splited[1] else: textlines[0] = None final_text = '' #生成文本并解决转义符 #Generate the final text for item in textlines: if item: final_text += translate(item) + '\n' if final_text: self.textHistory.append(final_text, speaker, None) self.gameTextBox.pushText(text = final_text, speaker = speaker, continuous = continuous,read= read) if needInput: self._inputReady = False self.gameTextBox.show() def runScript(self,pscriptText): exec(pscriptText,script_global,self.script_space) def runScriptFile(self,fileName): #'''note that would ignore panda virtual pathes''' pathes = runtime_data.game_settings['pscriptpathes'] types = runtime_data.game_settings['pscripttypes'] for ft in ((folder,type) for folder in pathes for type in types): if exists(ft[0] + fileName + ft[1]): handle = open(ft[0] + fileName + ft[1]) script = handle.read() handle.close() break if script: self.runScript(script) else: safeprint("file not find: "+ fileName) def beginScene(self,fileName): '''Load target .sogal script file and go to that file. ''' self.nextPtr = 0 self.scrStack = [] #used for stack controller pointers self.commandList = loadScriptData(fileName) runtime_data.RuntimeData.command_stack = self.scrStack runtime_data.RuntimeData.command_list = self.commandList def expandScene(self,fileName): '''expand a scene, inserting another sogal file in to current point''' expanding = loadScriptData(fileName) if len(self.commandList)> self.__currentPtr+1: self.commandList = self.commandList[:self.__currentPtr] + expanding + self.commandList[self.__currentPtr+1:] else: self.commandList = self.commandList[:self.__currentPtr] + expanding runtime_data.RuntimeData.command_stack = self.scrStack runtime_data.RuntimeData.command_list = self.commandList self.nextPtr = self.__currentPtr #Solved: # this is not a good solution but this method runs at 'nextCommand', and ths scrPtr would plus 1 afterwards def showSelection(self,choiceList = ['A','B'],enablesList = None): '''This method shows a selection, which sets 'last_choice' in script space to player's choice. (0 is the first, 1 is the second etc.) you can disable some of the selections with enablesList for example for choiceList ['A','B','C'] and enablesList ''' #Store the last selection rdc = copy.deepcopy(self._currentDump) rdc.last_choice = None self.__tempDumpedLastChoice = pickle.dumps(rdc, 2) self._choiceReady = False startPos = (0,0,0.1 * len(choiceList)) frameSize = (-0.6,0.6,-0.05 - 0.1*len(choiceList), 0.1 + 0.1*len(choiceList)) buttonSize = (-0.5,0.5,-0.050,0.10) margin = 0.05 self.__currentSelection = SogalDialog(enableMask = False, fadeScreen= None, command = self.__selected, textList= choiceList, enablesList= enablesList, sortType= 1,noFocus = True, startPos = startPos,frameSize = frameSize,margin = margin, buttonSize = buttonSize) def __selected(self,choice): #Store the last selection if self.__tempDumpedLastChoice: runtime_data.RuntimeData.last_choice = self.__tempDumpedLastChoice self.script_space['last_choice'] = choice self._choiceReady = True def scriptEval(self,str): return eval(str,script_global,runtime_data.RuntimeData.script_space) def setForceJump(self,forcejump): if forcejump: if not self.forcejump: self.forcejump = True self.setTextInputReady(True) self.__skipping = False self.stopAutoPlaying() else: self.forcejump = False def __jumpCheck(self,task): if self.hasFocus(): if self.forcejump or self.__skipping or self.__autoplaying: self.__autoInput = True return task.again def sceneWait(self,time,hidetextbox = True): waitinterval = Wait(time) self.intervals.append(waitinterval) waitinterval.start() if hidetextbox: self.gameTextBox.hide() def reloadTheme(self,theme): base.setStyle(theme) self.menu.reloadTheme() self.textHistory.reloadTheme() self.gameTextBox.reloadTheme() def stopSkipping(self): self.__skipping = False if not self.forcejump: self.__autoInput = False def startSkipping(self): self.stopAutoPlaying() self.__skipping = True def startAutoPlaying(self): self.__skipping = False self.__autoplaying = True self.button_auto['text'] = 'Stop Auto' def stopAutoPlaying(self): self.__autoplaying = False if self.__autoInterval: self.__autoInterval.pause() self.button_auto['text'] = 'Auto'
def test_get_actions__bad(self): h = TextHistory() h.insert('a') h.insert('b') h.insert('c') with self.assertRaises(ValueError): h.get_actions(0, 10) with self.assertRaises(ValueError): h.get_actions(10, 10) with self.assertRaises(ValueError): h.get_actions(2, 1) with self.assertRaises(ValueError): h.get_actions(-1, 1)
def test_get_actions__empty(self): h = TextHistory() self.assertEqual([], h.get_actions()) h.insert('a') self.assertEqual([], h.get_actions(0, 0))
def __init__(self): self.step = 0 #shows how many commands line it had run self.scrStack = [] self.commandList = [] self.__currentPtr = None if not runtime_data.RuntimeData.command_ptr: self.nextPtr = 0 else: self.nextPtr = runtime_data.RuntimeData.command_ptr if not runtime_data.RuntimeData.command_stack: runtime_data.RuntimeData.command_stack = self.scrStack else: self.scrStack = runtime_data.RuntimeData.command_stack if not runtime_data.RuntimeData.command_list: runtime_data.RuntimeData.command_list = self.commandList else: self.commandList = runtime_data.RuntimeData.command_list self._frame = DirectFrame(parent = aspect2d) # @UndefinedVariable pydev在傲娇而已不用管 self._frame.setTransparency(TransparencyAttrib.MAlpha) self.storyView = StoryView() self.audioPlayer = base.audioPlayer # @UndefinedVariable pydev在傲娇而已不用管 self.menu = StoryMenuBar() self.gameTextBox = GameTextBox() self.textHistory = TextHistory() self.button_auto = self.menu.addButton(text = 'Auto',state = DGG.NORMAL,command = self.autoPlayButton) self.button_history = self.menu.addButton(text = 'History',state = DGG.NORMAL,command = self.showTextHistoryButton) self.button_skip = self.menu.addButton(text = 'Skip',state = DGG.DISABLED,command = self.startSkippingButton) self.button_lastchoice = self.menu.addButton(text = 'Last Choice',state = DGG.DISABLED,command = self.lastChoice) self.button_save = self.menu.addButton(text = 'Save',state = DGG.DISABLED, command = self.saveButton) self.button_load = self.menu.addButton(text = 'Load',state = DGG.NORMAL,command = self.loadButton) self.button_quicksave = self.menu.addButton(text = 'Quick Save',state = DGG.DISABLED,command = self.quickSaveButton) self.button_quickload = self.menu.addButton(text = 'Quick Load',state = DGG.DISABLED,command = self.quickLoadButton) self.button_config = self.menu.addButton(text = 'Options', state = DGG.NORMAL, command = self._configButton) self.button_title = self.menu.addButton(text = 'Title',state = DGG.NORMAL,command = self.returnToTitle) self._inputReady = True self.__arrow_shown = False self._choiceReady = True self._currentMessage = '' self.__currentSelection = None self.__finishing = False self.__lock = Lock() self.forcejump = False self.__autoInput = False self.__focused = False self.intervals = [] self.__skipping = False self.__autoplaying = False self.__autoInterval = None self.__autoplaystep = None self.mapScriptSpace() SogalForm.__init__(self) self.show() taskMgr.add(self.loopTask,'story_manager_loop',sort = 2,priority = 1) # @UndefinedVariable 傲娇的pydev……因为panda3D的"黑魔法"…… taskMgr.doMethodLater(runtime_data.game_settings['jump_span'],self.__jumpCheck,'story_jump_check', sort = 5, priority = 1) # @UndefinedVariable
def test_get_actions(self): h = TextHistory() h.insert('a') h.insert('bc') h.replace('B', pos=1) h.delete(pos=0, length=1) self.assertEqual('Bc', h.text) actions = h.get_actions(1) self.assertEqual(3, len(actions)) insert, replace, delete = actions self.assertIsInstance(insert, InsertAction) self.assertIsInstance(replace, ReplaceAction) self.assertIsInstance(delete, DeleteAction) # insert self.assertEqual(1, insert.from_version) self.assertEqual(2, insert.to_version) self.assertEqual('bc', insert.text) self.assertEqual(1, insert.pos) # replace self.assertEqual(2, replace.from_version) self.assertEqual(3, replace.to_version) self.assertEqual('B', replace.text) self.assertEqual(1, replace.pos) # delete self.assertEqual(3, delete.from_version) self.assertEqual(4, delete.to_version) self.assertEqual(0, delete.pos) self.assertEqual(1, delete.length)