Beispiel #1
0
class BattleSystem(Scene):
    def __init__(self, engine):

        self.engine = engine
        self.camera = self.engine.viewport.camera   #for simplicity's sake
        
        w, h = self.engine.w, self.engine.h

        musicpath = os.path.join("audio", "music", "battle")
        battleSongs = self.engine.listPath(musicpath, "ogg|mp3")
        musicpath = os.path.join("audio", "music", "victory")
        victorySongs = self.engine.listPath(musicpath, "ogg|mp3")
        
        if len(battleSongs) > 0:
            self.battleSong = os.path.join("battle", random.choice(battleSongs))
            
        if len(victorySongs) > 0:
            self.victorySong = os.path.join("victory", random.choice(victorySongs))
            
        if not pygame.mixer.music.get_busy():
            self.music = BGMObj(self.battleSong)
        
        battlepath = os.path.join("scenes", "battlesystem")
        self.activeHighlight = ImgObj(Texture(os.path.join(battlepath, "active_highlight.png")))
        
        self.diffStar = ImgObj(Texture(os.path.join(battlepath, "star.png")))
        
        self.header = ImgObj(os.path.join(battlepath, "header.png"))
        self.footer = ImgObj(os.path.join(battlepath, "footer.png"))
        self.infohud = ImgObj(os.path.join(battlepath, "infohud.png"))
        self.vs = ImgObj(os.path.join(battlepath, "vs.png"))
        self.boost = ImgObj(os.path.join(battlepath, "boost.png"))
        self.defend = ImgObj(os.path.join(battlepath, "defend.png"))
        self.flee = ImgObj(os.path.join(battlepath, "flee.png"))
        
        
        fontStyle = self.engine.data.defaultFont
        self.text = FontObj(fontStyle, size = 32)
        self.hudtext = FontObj(fontStyle, size = 32)
        self.smtext = FontObj(fontStyle, size = 14)
        self.bigText = FontObj(fontStyle, size = 64)

        self.party = [m for m in self.engine.family.party.members]
        self.formation = [e for e in self.engine.formation.enemies]

        self.terrain = Terrain(self, self.engine.formation.terrain)
        
        self.incapParty = 0     #keeps track of how many in the party are incapacitated
                                #when all members are then the battle is over
        
        #self.start = ImgObj(Texture("start.png"))
        #self.start.setPosition(self.engine.w / 2, self.engine.h / 2)

        self.huds = [BattleHUDCharacter(character, scale = .5) for character in self.party] #player huds
        self.eHuds = [BattleHUDEnemy(enemy) for enemy in self.formation]        #enemy hud
        self.engageHud = None                                                   #battle engage hud
        self.commandMenu = BattleMenu(self, self.party[0])                     #player's commands
        self.inMenu = False

        self.active = 0         #which character is currently selecting commands
        self.activeActor = None #which character/enemy is currently performing an action
        self.battling = False   #are commands being selected or is fighting occuring?

        #turn order lists
        self.turn = 0           #whose turn is it
        self.totalTurns = 0     #keeps track of how many player turns it has taken to defeat the enemy group
        self.turns = {}
        for character in self.party:
            character.initForBattle()
            self.turns[character] = 0
            
        for enemy in self.formation:
            enemy.initForBattle()
            self.turns[enemy] = 0
            
        self.order = []

        #target selection
        self.targetMenu = None
        self.targeting = False
        self.pointer = ImgObj(Texture(os.path.join(battlepath, "pointer.png")), frameX = 2)
        
        self.displayDelay = 0   #little delay ticker for displaying damage dealt upon a target
        self.introDelay = 100   #little intro rendering
        self.fade = ImgObj(Texture(surface = pygame.Surface((self.engine.w, self.engine.h))))
    
        #battle lost
        self.lose = False       
        self.loseMenu = MenuObj(self, ["Retry", "Give Up"], position = (self.engine.w/2, self.engine.h/2))
        
        #victory!
        self.victoryPanel = None
        
        self.additionHUD = None
        
        self.numbers = ImgObj(Texture(os.path.join(battlepath, "numbers.png")), frameX = 10)
        self.hitImg = ImgObj(Texture(os.path.join(battlepath, "hit.png")))
        self.hitImg.setAlignment("right")
        self.hitImg.setPosition(self.engine.w*.9, self.engine.h*.8)
        
        self.fade = 0
        
    #make the additionHUD if the active actor has selected to perform an addition
    def makeAdditionHUD(self):
        if isinstance(self.activeActor, Character):
            if isinstance(self.activeActor.command, ComboAttack):
                self.additionHUD = BattleHUDAddition(self.engine, self.activeActor)

    #process key input for the scene
    def keyPressed(self, key, char):
        if self.introDelay > 0: #disable input during the intro
            return
            
        if self.lose:
            self.loseMenu.keyPressed(key)
        elif self.victoryPanel:
            self.victoryPanel.keyPressed(key)
        elif self.battling:
            if self.additionHUD:
                if not self.additionHUD.end:
                    self.additionHUD.keyPressed(key)
        else:
            if key == Input.BButton:
                if self.commandMenu.step == 0:
                    self.active -= 1
                    if self.active < 0:
                        self.active = 0
                    self.commandMenu = BattleMenu(self, self.party[self.active])
                if self.targeting:
                    self.targeting = False
                    
            self.commandMenu.keyPressed(key)
    
    def select(self, index):
        if self.lose:
            if index == 0:
                self.engine.changeScene("BattleSystem")
            else:
                self.flee()
        elif self.targeting:
            self.party[self.active].target = self.formation[index]
            self.next()
               
    def run(self):
        for hud in self.huds:
            hud.update()
            
        #win battle
        if len(self.formation) == 0 and not self.victoryPanel:
            self.victory()
            
        #lose battle
        elif self.incapParty == len(self.party):
            self.lose = True
        
        if self.battling:
            if self.additionHUD is not None:
                if not self.additionHUD.end:
                    self.additionHUD.run()
                else:
                    self.additionHUD = None
            else:
                self.execute()
        
    #organizes all the turns for order of execution
    def battleStart(self):
        self.turn = 0
        
        #this needs to be called in case an enemy or actor has died
        self.turns = {}
        for character in self.party:
            if not character.incap:
                self.turns[character] = 0
            
        for enemy in self.formation:
            self.turns[enemy] = 0

        for character in self.party:
            self.turns[character] = random.randint(0, 50) + character.spd

        for enemy in self.formation:
            self.turns[enemy] = random.randint(0, 50) + enemy.spd
            enemy.getCommand(self.generateEnemyTargets(enemy))  #gets enemy's command and target
        
        #automatically puts actors who are defending or boosting first in order
        for actor in self.turns.keys():
            if isinstance(actor, Defend) or isinstance(actor, Boost):
                self.turns[actor] = 1000
                
        self.order = sorted(self.turns.items(), key=itemgetter(1))
    
        self.activeActor = self.order[self.turn][0]
        self.battling = True
        
        self.makeAdditionHUD()
            
    #executes all of the commands
    def execute(self):
        actor = self.activeActor
        if actor.incap:
            self.next()
        
        '''
        if self.displayDelay >= 100:
            actor.turnStart()
            anim = actor.command.animation
            if anim:
                self.displayDelay = 100
            else:
                self.displayDelay = 110
        '''    
        if self.displayDelay <= 110:
            return
        if self.displayDelay >= 360:
            if actor.target != None:
                if not actor.damage == "Miss":
                    actor.target.currentHP -= actor.damage
                if actor.target.currentHP <= 0:
                    if isinstance(actor.target, Character):
                        self.incapParty += 1
                    if isinstance(actor.target, Enemy):
                        self.formation.remove(actor.target)
                    actor.target.incap = True
                    #makes sure to remove the target from the order so they 
                    #don't attack if they die before their turn
                    for i, target in enumerate(self.order):
                        if target[0] == actor.target:
                            self.order.pop(i)
                            if i < self.turn:
                                self.turn -= 1
                            break
                self.next()
            else:
                if isinstance(actor.command, Flee):
                    if actor.command.success:
                        self.end()
                self.next()

    #generates the target list for enemies and auto-targetting
    def generateEnemyTargets(self, actor):
        targets = []
        if isinstance(actor, Character):
            targetGroup = self.formation
        else:
            targetGroup = self.party
        for t in targetGroup:
            if not t.incap:
                targets.append(t)
        return targets
       
    #generates the target list for allies
    def generateTargets(self, actor):

        if isinstance(actor, Character):
            targets = [enemy.name for enemy in self.formation]
        else:
            targets= [member.name for member in self.party]

        position = (self.engine.w - 150, self.engine.h/2 + 30*len(targets)/2)
        return targets
    
    #creates the menu for target selection and activates targeting
    def selectTarget(self):
        self.targetMenu = self.generateTargets(self.party[self.active])
        self.targeting = True
        
    #advances the character for command selection
    def next(self):
        if self.battling:
            self.additionHUD = None
            #if a character in the user's party just acted, then increment the number of total turns
            if isinstance(self.activeActor, Character):
                self.totalTurns += 1
            self.displayDelay = 0
            self.turn += 1
            if self.turn >= len(self.order) or len(self.formation) == 0:
                self.battling = False
                self.active = -1
                for actor in self.party:
                    actor.turnEnd()
                for actor in self.formation:
                    actor.turnEnd()
                self.activeActor = None
                self.engageHud = None
                self.next()
                return
            self.activeActor = self.order[self.turn][0]
            #should be performed before the turn officially executes
            if self.activeActor.target != None:
                #if the actor's target was knocked out during this phase then a new target
                # is automatically selected and damage is recalculated
                if self.activeActor.target.incap:
                    targets = self.generateEnemyTargets(self.activeActor)
                    if len(targets) < 1:
                        self.next()
                        return
                    self.activeActor.target = random.choice(targets)

            self.makeAdditionHUD()
            
        else:
            self.active += 1
            if self.active < len(self.party):
                self.commandMenu = BattleMenu(self, self.party[self.active])
            else:
                self.commandMenu = None
                self.battleStart()
            self.targeting = False
       
    def renderBase(self, visibility):
        self.header.setPosition(self.engine.w/2, self.engine.h-self.header.height/2)
        self.header.draw()
        
        if self.battling:
            self.text.setText("Fight it out")
        elif self.targeting:
            self.text.setText("Select your target")
        else:
            self.text.setText("Choose a command")
        
        self.text.setAlignment("left")
        self.text.setPosition(32.0, self.engine.h-self.header.height/2)
        self.text.draw()
        
        self.footer.setPosition(self.engine.w/2, self.footer.height/2)
        self.footer.draw()
    
    def renderBattleInterface(self, visibility):
        actor = self.activeActor
        target = actor.target
        
        y = self.footer.height/2
        
        f = self.hudtext
        
        f.setColor((1,1,1,self.activeActor.getSprite().color[3]))
            
        f.setAlignment("center")
        f.setText(actor.name)
        f.setPosition(self.engine.w/8, y+16)
        f.draw()
        f.setText("HP: %s" % actor.hp)
        f.setPosition(self.engine.w/8+16, y-24)
        f.draw()
        
        if target is not actor and target is not None:
            f.setText(target.name)
            f.setPosition(self.engine.w/8*7, y+16)
            f.draw()
            f.setText("HP: %s" % target.hp)
            f.setPosition(self.engine.w/8*7-16, y-24)
            f.draw()
            
            self.vs.setColor((1,1,1,self.activeActor.getSprite().color[3]))
            self.vs.setPosition(self.engine.w/2, self.footer.height/2)
            self.vs.draw()
        else:
            if isinstance(actor.command, Boost):
                drawing = self.boost
            elif isinstance(actor.command, Defend):
                drawing = self.defend
            elif isinstance(actor.command, Flee):
                drawing = self.flee
            drawing.setColor((1,1,1,self.activeActor.getSprite().color[3]))
            drawing.setPosition(self.engine.w/8*5, y)
            drawing.draw()
            
            
    def renderCommandInterface(self, visibility):
        actor = self.party[self.active]
        sprite = actor.getSprite()
        
        if self.targeting:
            actor = self.formation[self.commandMenu.index]
            sprite = actor.getSprite()
            align = "right"
            x = self.engine.w
            y = self.footer.height+self.infohud.height/2
                
            self.infohud.setAlignment(align)
            self.infohud.setPosition(x, y)
            self.infohud.setScale(1,1)
            self.infohud.draw()
        
            self.smtext.setText(actor.name)
            self.smtext.setAlignment(align)
            self.smtext.setPosition(x, y+16)
            self.smtext.draw()
            self.smtext.setText("HP: %s" % actor.hp)
            self.smtext.setAlignment(align)
            self.smtext.setPosition(x, y)
            self.smtext.draw()

        for i, hud in enumerate(self.huds):
            hud.setPosition(20, self.engine.h - self.header.height - 50 - 110*i)
            hud.draw()
            
        self.commandMenu.render(visibility)
            
    #renders the spiffy intro animation
    def renderIntro(self, visibility):
      
        for i, member in enumerate(self.party):
            sprite = member.getSprite()
            sprite.setPosition(self.engine.w*.8 + 15*i, self.engine.h*.6 - 45*i)
            self.engine.drawAnimation(sprite, loop = True, reverse = False, delay = 20)
        
        for enemy in self.formation:
            sprite = enemy.getSprite()
            sprite.draw()
            
        self.bigText.setText(self.engine.formation.name)
        self.bigText.setPosition(self.engine.w/2, self.engine.h/2 + 30)
        if self.introDelay < 20:
            self.bigText.fade((1,1,1,0), 20)
        else:
            self.bigText.fade((1,1,1,1), 60)
        self.bigText.draw()
         
        difficulty = self.engine.formation.getSelfDifficulty(self.party)
        if difficulty > 0:
            for i in range(int(difficulty)):
                pos = (self.engine.w/2 - (self.diffStar.width/2 + 10)*(difficulty-1) + (self.diffStar.width + 10) * i,
                        self.engine.h/2 - 30)
                self.engine.drawImage(self.diffStar, position = pos)
        self.introDelay -= 2
        
    def renderBattle(self, visibility):
        actor = self.activeActor
        target = actor.target
        spriteA = actor.getSprite()
        spriteA.setPosition(self.engine.w*.25+20*min(self.displayDelay/100.0,1), self.engine.h*.5)
        spriteA.setScale(-1,1)
        spriteA.setColor((1,1,1,self.displayDelay/100.0))
        self.engine.drawAnimation(spriteA)

        if target is not actor and target is not None:
            spriteB = target.getSprite()
            spriteB.setPosition(self.engine.w*.75-20*min(self.displayDelay/100.0,1), self.engine.h*.5)
            spriteB.setScale(1,1)
            spriteB.setColor((1,1,1,self.displayDelay/100.0))
            self.engine.drawAnimation(spriteB)
        else:
            spriteB = spriteA
            
        self.displayDelay += 5
        '''
        if self.displayDelay >= 100 and self.displayDelay < 110:
            if actor.command.draw():
                self.displayDelay = 100
                return
            else:
                self.displayDelay = 110
        '''
        if target != None and self.displayDelay >= 110:
            
            #draws the damage on screen
            if self.displayDelay > 300:
                a = (360 - self.displayDelay)/60.0
            else:
                a = 1

            d = str(actor.damage)
            if d is not "Miss":
                x = self.hitImg.position[0]
                y = self.hitImg.position[1]
                for i, n in enumerate(d[::-1]):
                    self.engine.drawImage(self.numbers, position = (x - self.hitImg.width - 3 + (self.numbers.width-25)*i+2, y-2), frameX = int(n), color = (0,0,0,a))
                    self.engine.drawImage(self.numbers, position = (x - self.hitImg.width - 3 + (self.numbers.width-25)*i, y), frameX = int(n), color = (1,1,1,a))
                self.engine.drawImage(self.hitImg, position = (x+2, y-2), color = (0,0,0,a))
                self.engine.drawImage(self.hitImg, position = (x, y), color = (1,1,1,a))

    def render(self, visibility):
        self.terrain.drawBackground()
        
        #if the battle is lost nothing else needs to be drawn or processed
        if self.lose:
            self.loseMenu.render(visibility)
            return
            
        #if the battle is won nothing else needs to be drawn
        if self.victoryPanel:
            self.victoryPanel.render()
            return
            
        if self.introDelay <= 0:
            self.renderBase(visibility)
            if self.battling:
                self.renderBattle(visibility)
                self.renderBattleInterface(visibility)
            else:
                self.renderCommandInterface(visibility)
        else:
            self.renderIntro(visibility)
    
    #show the victory screen
    def victory(self):
        self.victoryPanel = VictoryPanel(self, self.totalTurns)
        self.music = BGMObj(self.victorySong)
                
    def end(self):
        self.music.fadeToStop()
        self.engine.viewport.pop(self)
Beispiel #2
0
 def makeAdditionHUD(self):
     if isinstance(self.activeActor, Character):
         if isinstance(self.activeActor.command, ComboAttack):
             self.additionHUD = BattleHUDAddition(self.engine, self.activeActor)