Exemple #1
0
 def tick(self, tick):
     battalion = tick.target
     # The archers cannot break ranks
     currcell = battalion._placement.GetBattlefieldCell()
     # Check if they are ready to move
     if (not battalion._action.IsMoveReady(
             displacement=currcell.GetCellSize(),
             penalty=currcell.GetPenalty())):
         Log('%s: ... moving ...' % (battalion.GetLabel()))
         return b3.FAILURE
     # Get the suitable cell to move
     listAdjacentCells = currcell.GetAdjacentCells(
         battalionfree=False, towerfree=True
     )  # Collision check with other battalions is performed next
     # Check those cells with battalions and their movement priority
     lstcell = []
     for c in listAdjacentCells:
         if ((c.HasBattalion()
              and battalion._action.AvaiablePassThrough(c.GetBattalion()))
                 or (not c.HasBattalion())):
             lstcell.append(c)
     if (len(lstcell) == 0):
         Log('%s: We can\'t move, sir!' % (battalion.GetLabel()))
         return b3.FAILURE
     tick.blackboard.set('candidateCellList', lstcell, tick.tree.id, None)
     return b3.SUCCESS
Exemple #2
0
 def tick(self, tick):
     battalion = tick.target
     currcell = battalion._placement.GetBattlefieldCell()       
     # Check if they are ready to move
     if not battalion._action.IsMoveReady(displacement = currcell.GetCellSize(), penalty = currcell.GetPenalty()):
         Log('%s: ... moving ...' % (battalion.GetLabel()))
         return b3.FAILURE
     tick.blackboard.set('currcell', currcell, tick.tree.id, None)
     return b3.SUCCESS
Exemple #3
0
    def tick(self, tick):
#    def Attack(self, against, castle, shoots):
        #print "###############################"
        #print "attacking from Attack node",
        #print "###############################"
        battalion = tick.target
        #against = tick.blackboard.get('against', tick.tree.id, None)
        shoots = tick.blackboard.get('shoots', tick.tree.id, None)
        castle = tick.blackboard.get('castle', tick.tree.id, None)
        (pos, c) = tick.blackboard.get('posAndConstruction', tick.tree.id, None)
        
        # Siege towers attack
        # Simple attack mode
        
        # Each siege tower has a number of archers placed in each level. Shoot for each one        
        i = 0
        while (i < battalion._number):            
            l = 0            
            while (l < battalion._nLevels):                
                # Set the shoot level
                pos.z = Battles.Utils.Settings.SETTINGS.Get_F('Army', 'SiegeTowers', 'LevelHeight') * l
                j = 0
                while (j < battalion._nArchersPerLevel):                   
                    # Get a random construction battalion
                    target = c.GetRandomBattalion()
                    # Get random battle field cell position
                    pos = battalion._placement.GetRandomPosition()
                    # Get cell position
                    cpos = target.GetCenterPosition()
                     
                    # Calculate the shoot success
                    # If the target is too far, the archer doesn't shoot (or he's idiot ...)
                    if (battalion._action.InAttackRange(currPos = pos, targetPos = cpos, castle = castle, constructionTarget = c)):                         
                        hit = battalion._action.ShootToArmy(target)
                        if (hit):
                            Log('%s kill 1 of %s' % (battalion.GetLabel(), target.GetLabel()))
               
                        if (shoots != None):
                            # Store the shoot
                            s = Shoot(origin = pos, destination = cpos, success = hit, attackertype = "Archers", targetObj = target, armytype = Shoot.SHOOT_FROM_ATTACKER)
                            shoots.append(s)
                        
                        # Check if target construction has more troops. If not, get again the closest populated construction
                        if (not c.HasBattalions()):
                            # Get closest construction element with troops
                            c = castle.GetClosestConstruction(populated = True, posfrom = pos, tilesrequired = False, reachable = False)
                            if (c == None):
                                # Game end?
                                return b3.FAILURE
                    
                    j += 1 
                
                l += 1
                
            i += 1
        return b3.SUCCESS
Exemple #4
0
    def tick(self, tick):
        battalion = tick.target
        currcell = tick.blackboard.get('currcell', tick.tree.id, None)
        
        # Get the suitable cell to move
        lstc = currcell.GetAdjacentCells(battalionfree = False, towerfree = True)  # Collision check with other battalions is performed next
        
        # Avoid entering into a bad cell
        for lc in lstc:
            if battalion._placement.IsBadCell(lc):
                lstc.remove(lc)
        
        if not lstc:
            print "ERROR: Infantry.Move -> None cell to move!"
            return b3.FAILURE

        # Give more priority to non visited cells or wall-attached cells
        lstnoprev = []
        nonvisited = True
        for c in lstc:
            if (not battalion._placement.IsVisitedCell(c)) or c.HasWalls() or c.HasBattalion():
                lstnoprev.append(c)
        if not lstnoprev:
            
            # If all of near cells are already visited, this is a bad cell. Note that cells are previously filtered by castle obstacles (except walls), so the battalion is
            # very close to a tower or it is in a battlefield cell that produces a deadlock on the battalion advancement.
            battalion._placement.SetBadCell(currcell)
            
            nonvisited = False
            lstnoprev = lstc    # Use already visited cells if there arent any nonvisited
                           
        # Check those cells with battalions and their movement priority
        lstcell = []
        for c in lstnoprev:
            if (c.HasBattalion() and battalion._action.AvaiablePassThrough(c.GetBattalion())) or (not c.HasBattalion()):
                lstcell.append(c)
                                            
        if (not lstcell) and nonvisited:
            
            # Choose another path with already visited cells
            lstcell = []
            for c in lstc:
                if (c.HasBattalion() and battalion._action.AvaiablePassThrough(c.GetBattalion())) or (not c.HasBattalion()):
                    lstcell.append(c)
             
        if not lstcell:
            Log('%s: We can\'t move, sir!' % (battalion.GetLabel())) # Wait until blocking battalions let the movement
            return b3.FAILURE
        
        tick.blackboard.set('lstcell', lstcell, tick.tree.id, None)
        return b3.SUCCESS
Exemple #5
0
    def tick(self, tick):
#    def Move(self, castle, selfarmy, againstarmy, battlefield, movedList):
        #print "###############################"
        #print "moving from Move node",
        #print "###############################"
        battalion = tick.target
        castle = tick.blackboard.get('castle', tick.tree.id, None)
        movedList = tick.blackboard.get('movedList', tick.tree.id, None)
        # Moves troops (usually in battlefield)
        # The moved troops are stored into movedList (used for drawing update reasons)
        #print "Army Move!!!", self.GetCommand().GetType()
        lstcell = tick.blackboard.get('lstcell', tick.tree.id, None)
        # Move troops on the battlefield
        # =============================================================================
        # Choose the nearest target
        pos = battalion._placement.GetCenterPosition()
        # Get closest construction element (with or without troops)
        target = castle.GetClosestWall(populated = False, posfrom = pos)
        if target == None:
            Log("WARNING: Battalion without goal - None closest wall to attack", VERBOSE_WARNING)
            return b3.FAILURE
        
        tilesmanager = target.GetTileManager()
        hasgateway = tilesmanager.HasGateways()
                        
        # Choose the closest battlefield cell to closest construction
        cell = None # just some initializatio values...
        minD = 0 
        for c in lstcell:
            
            # Choose a random cell position to get the closest goal. This tries to avoid the "crazy selection". 
            # "crazy selection" : From current cell, named Y by example, search the around cell that is closer to selected goal, X by example. Then, on the next call, 
            # choose again Y cell due it is closer to other target
            fuzzycellpoint = c.GetRandomCellPosition()   

            if hasgateway:
                #d = tilesmanager.GetDistanceClosestGateway(c.center)
                d = tilesmanager.GetDistanceClosestGateway(fuzzycellpoint)
            else:
                #d = target.DistanceFromPoint(c.center)
                d = target.DistanceFromPoint(fuzzycellpoint)

            if (not cell) or (d < minD):
                    minD = d
                    cell = c
         
        # Move    
        battalion._placement.MoveTo(cell, movedList)
        tick.blackboard.set('cell', cell, tick.tree.id, None)
        return b3.SUCCESS
Exemple #6
0
 def tick(self, tick):
     battalion = tick.target
     cell = battalion._placement.GetBattlefieldCell()        
     # Check if they are ready to move
     if (not battalion._action.IsMoveReady(displacement = cell.GetCellSize(), penalty = cell.GetPenalty())):
         Log('%s: ... moving ...' % (battalion.GetLabel()))
         return b3.FAILURE            
     # Follow the path
     # Check the first movement
     if (cell == battalion._path[0]):
         battalion._path = battalion._path[1:]
         if (not battalion._path):
             return b3.FAILURE
     nextcell = battalion._path[0]
     tick.blackboard.set('nextcell', nextcell, tick.tree.id, None)
     return b3.SUCCESS
Exemple #7
0
 def Kill(self, number, respawn = False):
     # Kills elements in current battalion
     # If given number is -1, kills the whole battalion
     # Respawn is only allowed for defender archers
     
     if number == -1:
         number = self.GetNumber()
     
     #self._number -= number;
     self.SetNumber(self.GetNumber() - number)
     
     if self._number <= 0:
         self._isDead = True
         self._number = 0
         Log('Battalion of %s exterminated' % (self.GetLabel()))
         
     self._placement.Kill(self._number)
Exemple #8
0
    def tick(self, tick):
        #print "defending from Defend node",
        # Archers defend (from castle)
        #startTime =
        battalion = tick.target
        #against = tick.blackboard.get('against', tick.tree.id, None)
        #defenders = None
        #battlefield = None
        #shoots = tick.blackboard.get('shoots', tick.tree.id, None)
        #castle = tick.blackboard.get('castle', tick.tree.id, None)

        # Archers have to fire against battlefield troops
        # Check if they are ready to shoot
        if (not battalion._action.IsReloadReady()):
            battalion._action.UpdateReloadTime()
            Log('%s reloading ...' % (battalion.GetLabel()))
            return b3.FAILURE
        else:
            return b3.SUCCESS
Exemple #9
0
    def tick(self, tick):
        #print "defending from Defend node",
        # Archers defend (from castle)
        #startTime =
        battalion = tick.target
        against = tick.blackboard.get('against', tick.tree.id, None)
        #defenders = None
        #battlefield = None
        shoots = tick.blackboard.get('shoots', tick.tree.id, None)
        castle = tick.blackboard.get('castle', tick.tree.id, None)
        target = tick.blackboard.get('target', tick.tree.id, None)

        battalion._action.UpdateReloadTime()

        # Simple defend mode

        defaultdoublecheck = Battles.Utils.Settings.SETTINGS.Get_B(
            'Army', 'Archers', 'DefenseShootDoubleCheck')

        # Shoot as many arrows as troops number, each archer shots ONE arrow at a time
        i = 0
        while (i < battalion._number):
            # Get random construction cell position
            pos = battalion._placement.GetRandomPosition()
            # Get the most suitable point to attack
            cpos = target._placement.GetRandomPosition()

            # Calculate the shoot success
            # If the target is too far or the attack angle is too graze, the archer doesn't shoot (or he's idiot ...)
            # The range attack is checked before, but with cell central points. That is, the archer first search the most suitable battalion to shoot. Then some
            # random data is take into account with real positions on cells. This could succeed with a shoot fail.
            doublecheck = False
            if (defaultdoublecheck):
                doublecheck = battalion._action.InAttackRange(
                    currPos=pos,
                    targetPos=cpos,
                    castle=castle,
                    constructionTarget=None,
                    excludeConstruction=battalion._placement.
                    GetDefendingConstruction())
            if ((not defaultdoublecheck)
                    or (defaultdoublecheck and doublecheck)):
                if (battalion._action.ShootToArmy(target)):
                    # Kills one element
                    hit = True
                    if (target.IsDefeated()):
                        against.RemoveBattalion(target)
                    else:
                        Log('%s kill 1 of %s' %
                            (battalion.GetLabel(), target.GetLabel()))
                else:
                    hit = False
                if (shoots != None):
                    # Store the shoot
                    s = Shoot(origin=pos,
                              destination=cpos,
                              success=hit,
                              attackertype="Archers",
                              targetObj=target,
                              armytype=Shoot.SHOOT_FROM_DEFENDER)
                    shoots.append(s)
                # Check if battalion is defeated and get another one (check it here to allow storing the shoot)
                if (target.IsDefeated()):
                    target = battalion._GetClimberInAttackRange(frompos=pos,
                                                                castle=castle)
                    if (not target):
                        target = against.GetClosestBattalionInAttackRange(
                            posfrom=pos,
                            castle=castle,
                            action=battalion._action,
                            excludeConstruction=battalion._placement.
                            GetDefendingConstruction())
                        if (target == None):
                            return b3.FAILURE

            i += 1
        return b3.SUCCESS
Exemple #10
0
    def tick(self, tick):
        #print "###############################"
        #print "attacking from Attack node",
        #print "###############################"
        battalion = tick.target
        #against = tick.blackboard.get('against', tick.tree.id, None)
        shoots = tick.blackboard.get('shoots', tick.tree.id, None)
        castle = tick.blackboard.get('castle', tick.tree.id, None)

        # Archers have to fire against castle troops

        # Check if they are ready to shoot
        battalion._action.UpdateReloadTime()

        # Simple attack mode

        # Get battalion simple position to calculate an estimated distance to closest construction element
        # A 2D point is enough to get the closest construction element
        pos = battalion._placement.GetCenterPosition()
        # Get closest construction element with troops. Dont worry if it isnt reachable, so archers goal is to kill defender units
        c = castle.GetClosestConstruction(populated=True,
                                          posfrom=pos,
                                          tilesrequired=False,
                                          reachable=False)
        if (c == None):
            # Game end?
            return b3.FAILURE

        # Shoot as many arrows as troops number
        nshoots = 0
        i = 0
        while (i < battalion._number):
            # Get a random construction battalion. First get archers and throwers. Then, cannons
            target = c.GetRandomBattalion()
            # Get random battle field cell position
            pos = battalion._placement.GetRandomPosition()
            # Get cell position
            cpos = target.GetCenterPosition()

            # Calculate the shoot success
            # If the target is too far, the archer doesn't shoot (or he's idiot ...)
            if (battalion._action.InAttackRange(currPos=pos,
                                                targetPos=cpos,
                                                castle=castle,
                                                constructionTarget=c)):
                nshoots += 1
                hit = battalion._action.ShootToArmy(target)
                if (hit):
                    Log('%s kill 1 of %s' %
                        (battalion.GetLabel(), target.GetLabel()))
                if (shoots != None):
                    # Store the shoot
                    s = Shoot(origin=pos,
                              destination=cpos,
                              success=hit,
                              attackertype="Archers",
                              targetObj=target,
                              armytype=Shoot.SHOOT_FROM_ATTACKER)
                    shoots.append(s)
                # Check if target construction has more troops. If not, get again the closest populated construction
                if (not c.HasBattalions()):
                    # Get closest construction element with troops
                    c = castle.GetClosestConstruction(populated=True,
                                                      posfrom=pos,
                                                      tilesrequired=False,
                                                      reachable=False)
                    if (c == None):
                        # Game end?
                        return b3.FAILURE
            i += 1

        # We use a simple heuristic to know if the battalion has to move, calculating if more than a half units have shoot to the construction
        # This is an empirical decision. The aiming is random. If all units have aimed to a too far position, all of them will fail
        # By the other hand, the alternative would be to search if there any construction point in attack range, spending a lot of computational time
        # and turns trying to shoot. Unfortunately this produces a side effect on trenches, where some battalions can left the trench, loosing its defensive factor
        if (battalion._number > 0):
            stay = False
            if ((float(nshoots) / float(battalion._number)) >=
                    Battles.Utils.Settings.SETTINGS.Get_F(
                        'Army', 'Archers', 'ShootsToStay')):
                # Introduce a new decision factor about trenchs. If battalion is not in any trench and there is anyone near, it has to move, althought current position will be effective
                # Its assumed the principle of archer conservation ...
                battlefield = battalion._placement.GetBattlefieldCell(
                ).GetBattlefield()
                targett = battlefield.GetClosestTrench(
                    frompos=battalion._placement.GetCenterPosition(),
                    free=True,
                    searchradius=Battles.Utils.Settings.SETTINGS.Get_F(
                        'Army', 'Archers', 'SearchRadiusTrench'),
                    castle=castle,
                    battalion=battalion)
                if (not targett):
                    stay = True
            battalion._action.SetStationary(stay)

        return b3.SUCCESS
Exemple #11
0
    def tick(self, tick):
#    def Attack(self, against, castle, shoots):
        #print "###############################"
        #print "moving from Move node",
        #print "###############################"
        # cannons defend (from castle)
        battalion = tick.target
        castle = tick.blackboard.get('castle', tick.tree.id, None)
        #battalionarmy = tick.blackboard.get('battalionarmy', tick.tree.id, None)
        #againstarmy = tick.blackboard.get('against', tick.tree.id, None)
        #battlefield = tick.blackboard.get('battlefield', tick.tree.id, None)
        #movedList = tick.blackboard.get('movedList', tick.tree.id, None)
        shots = tick.blackboard.get('shots', tick.tree.id, None)
        
        # Cannons attack
        # Against parameter is not used here, only the castle
        # Return a list of shoot lines to be displayed
        
        #Army.ArmyComponent.Attack(battalion, against, castle, shoots)
        if (battalion.IsDefeated()):
            return b3.FAILURE
        
        if (not battalion._action.GetCommand().IsAttack()):
            return b3.FAILURE
        
        if (not battalion._placement.IsInBattlefield()):
            print 'ERROR: Current cannon battalion is not placed on battlefield'
            return b3.FAILURE
                
        # cannons have to fire against castle troops
        
        
        # Check if they are ready to shoot
        if (not battalion._action.IsReloadReady()):
            battalion._action.UpdateReloadTime()
            Log('%s reloading ...' % (battalion.GetLabel()))
            return b3.FAILURE
        battalion._action.UpdateReloadTime()
        
        
        if (castle.IsDefeated()):
            # Game end? ...
            return b3.FAILURE
        
        # Simple attack mode
        
        # Get battalion simple position to calculate an estimated distance to closest construction element
        pos = battalion._placement.GetCenterPosition()

        # Get closest construction element (with or without troops). The goal must be reachable because the goal of a cannon is to make a gateway to allow attackers enter inside the castle
        target = castle.GetClosestConstruction(populated = False, posfrom = pos, tilesrequired = True, reachable = True)
        if (target == None):
            # Game end?... no castle?... amazing!
            return b3.FAILURE
         
        # Shoot one time for each battalion cannon
        i = 0
        while (i < battalion._number):
            # Get center battle field cell position (a cannon cannot move randomly as a soldier)

            tilemanager = target.GetTileManager()

            # Get the most suitable construction tile to shoot
            # Returned target is a dictionary with tile row and column values
            tile = tilemanager.GetBestTileToShoot(pos)
            if (tile == None):
                print 'ERROR: Cannon cannot choose a fallen wall!'
                return b3.FAILURE
                
            
            # Get central tile position as reference
            targetpos = tilemanager.GetTileCenter(tile)

             
            # Calculate the shoot success
            
            # If the target is too far, the cannon doesn't shoot (or he's idiot ...)
            if (battalion._action.InAttackRange(currPos = pos, targetPos = targetpos, castle = castle, constructionTarget = target)): 
                
                hitpoint = battalion._action.ShootToConstruction(target, pos, targetpos)
                if (hitpoint["Hit"]):                    
                    Log('%s hits on construction %s' % (battalion.GetLabel(), target.GetLabel()))
                    
                    if (target.IsDefeated()):
                        # End game?
                        Log('The %s has fallen!!!' % (target.GetLabel()))
                        return b3.FAILURE
                    
                    # Check if tile is broken and it is the first one (the top tile), killing all defenders that are on top
                    if (tilemanager.IsTopTile(tile) and tile.IsHole()):
                        defenders = target.GetDefendersOverTile(tile)                        
                        for d in defenders:
                            d.Kill(-1)  # Kill whole battalion and avoid the respawn
                            # Update the drawing of defeated defenders
                            Board.redrawObjects.append(d)
                        
                    
                    
                    if (shots != None):
                        # Store the shoot
                        s = Shoot(origin = pos, destination = hitpoint["Intersection"], success = hitpoint["Hit"], attackertype = "Cannons", targetObj = target, armytype = Shoot.SHOOT_FROM_ATTACKER)
                        shots.append(s)
                
      
            i += 1
        return b3.SUCCESS
Exemple #12
0
    def tick(self, tick):
        #print "###############################"
        #print "moving from Move node",
        #print "###############################"
        # cannons defend (from castle)
        battalion = tick.target
        castle = tick.blackboard.get('castle', tick.tree.id, None)
        #battalionarmy = tick.blackboard.get('battalionarmy', tick.tree.id, None)
        againstarmy = tick.blackboard.get('against', tick.tree.id, None)
        battlefield = tick.blackboard.get('battlefield', tick.tree.id, None)
        #movedList = tick.blackboard.get('movedList', tick.tree.id, None)
        shots = tick.blackboard.get('shots', tick.tree.id, None)
        
#    def Defend(battalion, against, defenders, battlefield, shoots, castle): they shoot shots 
        # Cannons defend (from castle)
        
        if (battalion.IsDefeated()):
            return b3.FAILURE
        
        if (not battalion._action.GetCommand().IsDefend()):
            return b3.FAILURE
        
        if (not battalion._placement.IsInDefendingLine()):
            print 'ERROR: Current cannon  is not placed on a defending line'
            return b3.FAILURE

        # Cannons have to fire against battlefield troops
        
        # Check if they are ready to shoot
        if (not battalion._action.IsReloadReady()):
            battalion._action.UpdateReloadTime()
            Log('%s reloading ...' % (battalion.GetLabel()))
            return b3.FAILURE
        battalion._action.UpdateReloadTime()
        
        # Simple defend mode
        
        # Get battalion simple position to calculate an estimated distance to closest construction element
        pos = battalion._placement.GetCenterPosition()
        
        # Get the most suitable battalion to shoot.
        
        # Get the closest enemy battalion
        # Cannons have a priority on targets: cannons, cannons and infantry/cannons
        target = againstarmy.GetClosestBattalionInAttackRange(posfrom = pos, castle = castle, action = battalion._action, excludeConstruction = battalion._placement.GetDefendingConstruction(), battaliontype = "Cannons") 
        if (not target):
            target = againstarmy.GetClosestBattalionInAttackRange(posfrom = pos, castle = castle, action = battalion._action, excludeConstruction = battalion._placement.GetDefendingConstruction(), battaliontype = "cannons") 
            if (not target):
                target = againstarmy.GetClosestBattalionInAttackRange(posfrom = pos, castle = castle, action = battalion._action, excludeConstruction = battalion._placement.GetDefendingConstruction(), battaliontype = None) 
                if (not target):
                    return b3.FAILURE

        # Shoot as many times as troops number
        i = 0
        while (i < battalion._number):
            
            # Get the most suitable point to attack
            cpos = target._placement.GetRandomPosition()
        
            
            # Calculate the shoot success
            
            # If the target is too far or the attack angle is too graze, the cannon doesn't shoot (or he's idiot ...)
            # The range attack is checked before, but with cell central points. That is, the cannon first search the most suitable battalion to shoot. Then some
            # random data is take into account with real positions on cells. This could succeed with a shoot fail.
            doublecheck = False
            if (Battles.Utils.Settings.SETTINGS.Get_B('Army','Cannons','DefenseShootDoubleCheck')):
                doublecheck = battalion._action.InAttackRange(currPos = pos, targetPos = cpos, castle = castle, constructionTarget = None, excludeConstruction = battalion._placement.GetDefendingConstruction())
            if ((not Battles.Utils.Settings.SETTINGS.Get_B('Army','Cannons','DefenseShootDoubleCheck')) or (Battles.Utils.Settings.SETTINGS.Get_B('Army','Cannons','DefenseShootDoubleCheck') and doublecheck)): 
                
                hit = False
                finaltarget = target
                hitpos = cpos
                
                hitsuccess = battalion._action.ShootToBattlefield(battlefield, pos, cpos)
                if (hitsuccess["Hit"]):
                    
                    hit = True
                    finaltarget = hitsuccess["Battalion"]   # Final shooted target could be another than initial target for aiming reasons
                    if (finaltarget.IsDefeated()):
                        againstarmy.RemoveBattalion(finaltarget)
                    
                    
                    """
                    # This will happens ever.... if the battlefield isnt a stamp, obviously....
                    # We need to check if the shoot has hit on anyone (perhaps, not the target, but another victim ....)
                    
                    if (hitsuccess["Cell"].HasBattalion()):
                        
                        
                        # We need to know how many soldiers have been killed
                        # Take in consideration that all battalion is affected. Then, each soldier will pass a test between his defensive value and cannon attack value, in weighted random way
                        finaltarget = hitsuccess["Cell"].GetBattalion()
                        j = 0
                        k = 0 
                        while (j < finaltarget.GetNumber()):
                            if (battalion._action.ShootDuel(finaltarget)):
                                
                                if (not hit):
                                    hit = True
                                    hitpos = hitsuccess["Intersection"]

                                k += 1
                            j += 1
                    
                        finaltarget.Kill(k)
                        if (finaltarget.IsDefeated()):
                            against.RemoveBattalion(finaltarget)
                            
                      """      
       
                if (shots != None):
                    # Store the shoot
                    s = Shoot(origin = pos, destination = hitpos, success = hit, attackertype = "Cannons", targetObj = finaltarget, armytype = Shoot.SHOOT_FROM_DEFENDER)
                    shots.append(s)
                
                
                # Check if battalion is defeated and get another one (check it here to allow storing the shoot)
                # Note that we could be killed another target. Here we check if we need to aim to another target
                if (target.IsDefeated()):
                    target = againstarmy.GetClosestBattalionInAttackRange(posfrom = pos, castle = castle, action = battalion._action, excludeConstruction = battalion._placement.GetDefendingConstruction(), battaliontype = "Cannons") 
                    if (not target):
                        target = againstarmy.GetClosestBattalionInAttackRange(posfrom = pos, castle = castle, action = battalion._action, excludeConstruction = battalion._placement.GetDefendingConstruction(), battaliontype = "cannons") 
                        if (not target):
                            target = againstarmy.GetClosestBattalionInAttackRange(posfrom = pos, castle = castle, action = battalion._action, excludeConstruction = battalion._placement.GetDefendingConstruction(), battaliontype = None) 
                            if (not target):
                                return b3.FAILURE
       
            i += 1
        return b3.SUCCESS