コード例 #1
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    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)
コード例 #2
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    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)
コード例 #3
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    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)
コード例 #4
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    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)
コード例 #5
0
 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__)
コード例 #6
0
    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)
コード例 #7
0
    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)
コード例 #8
0
    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()))
コード例 #9
0
 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()))
コード例 #10
0
 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__)
コード例 #11
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    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)
コード例 #12
0
    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)
コード例 #13
0
    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)
コード例 #14
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    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)
コード例 #15
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    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)
コード例 #16
0
    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)
コード例 #17
0
    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)
コード例 #18
0
    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)
コード例 #19
0
    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")
コード例 #20
0
    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)
コード例 #21
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    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)
コード例 #22
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    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)
コード例 #23
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    def test_version__trivial(self):
        h = TextHistory()

        self.assertEqual(0, h.version)
        with self.assertRaises(AttributeError):
            h.version = 42
コード例 #24
0
    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)
コード例 #25
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    def test_text__trivial(self):
        h = TextHistory()

        self.assertEqual('', h.text)
        with self.assertRaises(AttributeError):
            h.text = 'NEW'
コード例 #26
0
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'
コード例 #27
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    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)
コード例 #28
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    def test_get_actions__empty(self):
        h = TextHistory()
        self.assertEqual([], h.get_actions())

        h.insert('a')
        self.assertEqual([], h.get_actions(0, 0))
コード例 #29
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
コード例 #30
0
ファイル: test_text_history.py プロジェクト: Xenobyte42/hw3
    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)