from BattleBase import * from DistributedBattleAI import * from toontown.toonbase.ToontownBattleGlobals import * import random from toontown.suit import DistributedSuitBaseAI import SuitBattleGlobals import BattleExperienceAI from toontown.toon import NPCToons from toontown.pets import PetTricks from toontown.hood import ZoneUtil from direct.showbase.PythonUtil import lerp import sys class BattleCalculatorAI: AccuracyBonuses = [0, 20, 40, 60] DamageBonuses = [0, 20, 20, 20] AttackExpPerTrack = [0, 10, 20, 30, 40, 50, 60] TRAP_CONFLICT = -2 APPLY_HEALTH_ADJUSTMENTS = 1 TOONS_TAKE_NO_DAMAGE = 0 CAP_HEALS = 1 CLEAR_SUIT_ATTACKERS = 1 SUITS_UNLURED_IMMEDIATELY = 1 CLEAR_MULTIPLE_TRAPS = 0 KBBONUS_LURED_FLAG = 0 KBBONUS_TGT_LURED = 1 notify = DirectNotifyGlobal.directNotify.newCategory('BattleCalculatorAI') toonsAlwaysHit = simbase.config.GetBool('toons-always-hit', 0) toonsAlwaysMiss = simbase.config.GetBool('toons-always-miss', 0) toonsAlways5050 = simbase.config.GetBool('toons-always-5050', 0) suitsAlwaysHit = simbase.config.GetBool('suits-always-hit', 0) suitsAlwaysMiss = simbase.config.GetBool('suits-always-miss', 0) immortalSuits = simbase.config.GetBool('immortal-suits', 0) propAndOrganicBonusStack = simbase.config.GetBool('prop-and-organic-bonus-stack', 0) def __init__(self, battle, tutorialFlag = 0): self.battle = battle self.SuitAttackers = {} self.currentlyLuredSuits = {} self.successfulLures = {} self.toonAtkOrder = [] self.toonHPAdjusts = {} self.toonSkillPtsGained = {} self.traps = {} self.npcTraps = {} self.suitAtkStats = {} self.__clearBonuses(hp=1) self.__clearBonuses(hp=0) self.delayedUnlures = [] self.__skillCreditMultiplier = simbase.air.baseXpMultiplier self.tutorialFlag = tutorialFlag self.trainTrapTriggered = False def setSkillCreditMultiplier(self, mult): self.__skillCreditMultiplier = simbase.air.baseXpMultiplier * mult def getSkillCreditMultiplier(self): return self.__skillCreditMultiplier def cleanup(self): self.battle = None return def __calcToonAtkHit(self, attackIndex, atkTargets): if len(atkTargets) == 0: return (0, 0) if self.tutorialFlag: return (1, 95) if self.toonsAlways5050: roll = random.randint(0, 99) if roll < 50: return (1, 95) else: return (0, 0) if self.toonsAlwaysHit: return (1, 95) elif self.toonsAlwaysMiss: return (0, 0) debug = self.notify.getDebug() attack = self.battle.toonAttacks[attackIndex] atkTrack, atkLevel = self.__getActualTrackLevel(attack) if atkTrack == NPCSOS: return (1, 95) if atkTrack == FIRE: return (1, 95) if atkTrack == TRAP: if debug: self.notify.debug('Attack is a trap, so it hits regardless') attack[TOON_ACCBONUS_COL] = 0 return (1, 100) elif atkTrack == DROP and attack[TOON_TRACK_COL] == NPCSOS: unluredSuits = 0 for tgt in atkTargets: if not self.__suitIsLured(tgt.getDoId()): unluredSuits = 1 if unluredSuits == 0: attack[TOON_ACCBONUS_COL] = 1 return (0, 0) elif atkTrack == DROP: allLured = True for i in xrange(len(atkTargets)): if self.__suitIsLured(atkTargets[i].getDoId()): pass else: allLured = False if allLured: attack[TOON_ACCBONUS_COL] = 1 return (0, 0) elif atkTrack == PETSOS: return self.__calculatePetTrickSuccess(attack) tgtDef = 0 numLured = 0 if atkTrack != HEAL: for currTarget in atkTargets: thisSuitDef = self.__targetDefense(currTarget, atkTrack) if debug: self.notify.debug('Examining suit def for toon attack: ' + str(thisSuitDef)) tgtDef = min(thisSuitDef, tgtDef) if self.__suitIsLured(currTarget.getDoId()): numLured += 1 trackExp = self.__toonTrackExp(attack[TOON_ID_COL], atkTrack) for currOtherAtk in self.toonAtkOrder: if currOtherAtk != attack[TOON_ID_COL]: nextAttack = self.battle.toonAttacks[currOtherAtk] nextAtkTrack = self.__getActualTrack(nextAttack) if atkTrack == nextAtkTrack and attack[TOON_TGT_COL] == nextAttack[TOON_TGT_COL]: currTrackExp = self.__toonTrackExp(nextAttack[TOON_ID_COL], atkTrack) if debug: self.notify.debug('Examining toon track exp bonus: ' + str(currTrackExp)) trackExp = max(currTrackExp, trackExp) if debug: if atkTrack == HEAL: self.notify.debug('Toon attack is a heal, no target def used') else: self.notify.debug('Suit defense used for toon attack: ' + str(tgtDef)) self.notify.debug('Toon track exp bonus used for toon attack: ' + str(trackExp)) if attack[TOON_TRACK_COL] == NPCSOS: randChoice = 0 else: randChoice = random.randint(0, 99) propAcc = AvPropAccuracy[atkTrack][atkLevel] if atkTrack == LURE: treebonus = self.__toonCheckGagBonus(attack[TOON_ID_COL], atkTrack, atkLevel) propBonus = self.__checkPropBonus(atkTrack) if self.propAndOrganicBonusStack: propAcc = 0 if treebonus: self.notify.debug('using organic bonus lure accuracy') propAcc += AvLureBonusAccuracy[atkLevel] if propBonus: self.notify.debug('using prop bonus lure accuracy') propAcc += AvLureBonusAccuracy[atkLevel] elif treebonus or propBonus: self.notify.debug('using oragnic OR prop bonus lure accuracy') propAcc = AvLureBonusAccuracy[atkLevel] attackAcc = propAcc + trackExp + tgtDef currAtk = self.toonAtkOrder.index(attackIndex) if currAtk > 0 and atkTrack != HEAL: prevAtkId = self.toonAtkOrder[currAtk - 1] prevAttack = self.battle.toonAttacks[prevAtkId] prevAtkTrack = self.__getActualTrack(prevAttack) lure = atkTrack == LURE and (not attackAffectsGroup(atkTrack, atkLevel, attack[TOON_TRACK_COL]) and attack[TOON_TGT_COL] in self.successfulLures or attackAffectsGroup(atkTrack, atkLevel, attack[TOON_TRACK_COL])) if atkTrack == prevAtkTrack and (attack[TOON_TGT_COL] == prevAttack[TOON_TGT_COL] or lure): if prevAttack[TOON_ACCBONUS_COL] == 1: if debug: self.notify.debug('DODGE: Toon attack track dodged') elif prevAttack[TOON_ACCBONUS_COL] == 0: if debug: self.notify.debug('HIT: Toon attack track hit') attack[TOON_ACCBONUS_COL] = prevAttack[TOON_ACCBONUS_COL] return (not attack[TOON_ACCBONUS_COL], attackAcc) atkAccResult = attackAcc if debug: self.notify.debug('setting atkAccResult to %d' % atkAccResult) acc = attackAcc + self.__calcToonAccBonus(attackIndex) if atkTrack != LURE and atkTrack != HEAL: if atkTrack != DROP: if numLured == len(atkTargets): if debug: self.notify.debug('all targets are lured, attack hits') attack[TOON_ACCBONUS_COL] = 0 return (1, 100) else: luredRatio = float(numLured) / float(len(atkTargets)) accAdjust = 100 * luredRatio if accAdjust > 0 and debug: self.notify.debug(str(numLured) + ' out of ' + str(len(atkTargets)) + ' targets are lured, so adding ' + str(accAdjust) + ' to attack accuracy') acc += accAdjust elif numLured == len(atkTargets): if debug: self.notify.debug('all targets are lured, attack misses') attack[TOON_ACCBONUS_COL] = 0 return (0, 0) if acc > MaxToonAcc: acc = MaxToonAcc if randChoice < acc: if debug: self.notify.debug('HIT: Toon attack rolled' + str(randChoice) + 'to hit with an accuracy of' + str(acc)) attack[TOON_ACCBONUS_COL] = 0 else: if debug: self.notify.debug('MISS: Toon attack rolled' + str(randChoice) + 'to hit with an accuracy of' + str(acc)) attack[TOON_ACCBONUS_COL] = 1 return (not attack[TOON_ACCBONUS_COL], atkAccResult) def __toonTrackExp(self, toonId, track): toon = self.battle.getToon(toonId) if toon != None: toonExpLvl = toon.experience.getExpLevel(track) exp = self.AttackExpPerTrack[toonExpLvl] if track == HEAL: exp = exp * 0.5 self.notify.debug('Toon track exp: ' + str(toonExpLvl) + ' and resulting acc bonus: ' + str(exp)) return exp else: return 0 return def __toonCheckGagBonus(self, toonId, track, level): toon = self.battle.getToon(toonId) if toon != None: return toon.checkGagBonus(track, level) else: return False return def __checkPropBonus(self, track): return self.battle.getInteractivePropTrackBonus() == track def __targetDefense(self, suit, atkTrack): if atkTrack == HEAL: return 0 suitDef = SuitBattleGlobals.SuitAttributes[suit.dna.name]['def'][suit.getLevel()] return -suitDef def __createToonTargetList(self, attackIndex): attack = self.battle.toonAttacks[attackIndex] atkTrack, atkLevel = self.__getActualTrackLevel(attack) targetList = [] if atkTrack == NPCSOS: return targetList if not attackAffectsGroup(atkTrack, atkLevel, attack[TOON_TRACK_COL]): if atkTrack == HEAL: target = attack[TOON_TGT_COL] else: target = self.battle.findSuit(attack[TOON_TGT_COL]) if target != None: targetList.append(target) elif atkTrack == HEAL or atkTrack == PETSOS: if attack[TOON_TRACK_COL] == NPCSOS or atkTrack == PETSOS: targetList = self.battle.activeToons else: for currToon in self.battle.activeToons: if attack[TOON_ID_COL] != currToon: targetList.append(currToon) else: targetList = self.battle.activeSuits return targetList def __prevAtkTrack(self, attackerId, toon = 1): if toon: prevAtkIdx = self.toonAtkOrder.index(attackerId) - 1 if prevAtkIdx >= 0: prevAttackerId = self.toonAtkOrder[prevAtkIdx] attack = self.battle.toonAttacks[prevAttackerId] return self.__getActualTrack(attack) else: return NO_ATTACK def getSuitTrapType(self, suitId): if suitId in self.traps: if self.traps[suitId][0] == self.TRAP_CONFLICT: return NO_TRAP else: return self.traps[suitId][0] else: return NO_TRAP def __suitTrapDamage(self, suitId): if suitId in self.traps: return self.traps[suitId][2] else: return 0 def addTrainTrapForJoiningSuit(self, suitId): self.notify.debug('addTrainTrapForJoiningSuit suit=%d self.traps=%s' % (suitId, self.traps)) trapInfoToUse = None for trapInfo in self.traps.values(): if trapInfo[0] == UBER_GAG_LEVEL_INDEX: trapInfoToUse = trapInfo break if trapInfoToUse: self.traps[suitId] = trapInfoToUse else: self.notify.warning('huh we did not find a train trap?') return def __addSuitGroupTrap(self, suitId, trapLvl, attackerId, allSuits, npcDamage = 0): if npcDamage == 0: if suitId in self.traps: if self.traps[suitId][0] == self.TRAP_CONFLICT: pass else: self.traps[suitId][0] = self.TRAP_CONFLICT for suit in allSuits: id = suit.doId if id in self.traps: self.traps[id][0] = self.TRAP_CONFLICT else: self.traps[id] = [self.TRAP_CONFLICT, 0, 0] else: toon = self.battle.getToon(attackerId) organicBonus = toon.checkGagBonus(TRAP, trapLvl) propBonus = self.__checkPropBonus(TRAP) damage = getAvPropDamage(TRAP, trapLvl, toon.experience.getExp(TRAP), organicBonus, propBonus, self.propAndOrganicBonusStack) if self.itemIsCredit(TRAP, trapLvl): self.traps[suitId] = [trapLvl, attackerId, damage] else: self.traps[suitId] = [trapLvl, 0, damage] self.notify.debug('calling __addLuredSuitsDelayed') self.__addLuredSuitsDelayed(attackerId, targetId=-1, ignoreDamageCheck=True) elif suitId in self.traps: if self.traps[suitId][0] == self.TRAP_CONFLICT: self.traps[suitId] = [trapLvl, 0, npcDamage] elif not self.__suitIsLured(suitId): self.traps[suitId] = [trapLvl, 0, npcDamage] def __addSuitTrap(self, suitId, trapLvl, attackerId, npcDamage = 0): if npcDamage == 0: if suitId in self.traps: if self.traps[suitId][0] == self.TRAP_CONFLICT: pass else: self.traps[suitId][0] = self.TRAP_CONFLICT else: toon = self.battle.getToon(attackerId) organicBonus = toon.checkGagBonus(TRAP, trapLvl) propBonus = self.__checkPropBonus(TRAP) damage = getAvPropDamage(TRAP, trapLvl, toon.experience.getExp(TRAP), organicBonus, propBonus, self.propAndOrganicBonusStack) if self.itemIsCredit(TRAP, trapLvl): self.traps[suitId] = [trapLvl, attackerId, damage] else: self.traps[suitId] = [trapLvl, 0, damage] elif suitId in self.traps: if self.traps[suitId][0] == self.TRAP_CONFLICT: self.traps[suitId] = [trapLvl, 0, npcDamage] elif not self.__suitIsLured(suitId): self.traps[suitId] = [trapLvl, 0, npcDamage] def __removeSuitTrap(self, suitId): if suitId in self.traps: del self.traps[suitId] def __clearTrapCreator(self, creatorId, suitId = None): if suitId == None: for currTrap in self.traps.keys(): if creatorId == self.traps[currTrap][1]: self.traps[currTrap][1] = 0 elif suitId in self.traps: self.traps[suitId][1] = 0 return def __trapCreator(self, suitId): if suitId in self.traps: return self.traps[suitId][1] else: return 0 def __initTraps(self): self.trainTrapTriggered = False keysList = self.traps.keys() for currTrap in keysList: if self.traps[currTrap][0] == self.TRAP_CONFLICT: del self.traps[currTrap] def __calcToonAtkHp(self, toonId): attack = self.battle.toonAttacks[toonId] targetList = self.__createToonTargetList(toonId) atkHit, atkAcc = self.__calcToonAtkHit(toonId, targetList) atkTrack, atkLevel, atkHp = self.__getActualTrackLevelHp(attack) if not atkHit and atkTrack != HEAL: return validTargetAvail = 0 lureDidDamage = 0 currLureId = -1 for currTarget in xrange(len(targetList)): attackLevel = -1 attackTrack = None attackDamage = 0 toonTarget = 0 targetLured = 0 if atkTrack == HEAL or atkTrack == PETSOS: targetId = targetList[currTarget] toonTarget = 1 else: targetId = targetList[currTarget].getDoId() if atkTrack == LURE: if self.getSuitTrapType(targetId) == NO_TRAP: if self.notify.getDebug(): self.notify.debug('Suit lured, but no trap exists') if self.SUITS_UNLURED_IMMEDIATELY: if not self.__suitIsLured(targetId, prevRound=1): if not self.__combatantDead(targetId, toon=toonTarget): validTargetAvail = 1 rounds = NumRoundsLured[atkLevel] wakeupChance = 100 - atkAcc * 2 npcLurer = attack[TOON_TRACK_COL] == NPCSOS currLureId = self.__addLuredSuitInfo(targetId, -1, rounds, wakeupChance, toonId, atkLevel, lureId=currLureId, npc=npcLurer) if self.notify.getDebug(): self.notify.debug('Suit lured for ' + str(rounds) + ' rounds max with ' + str(wakeupChance) + '% chance to wake up each round') targetLured = 1 else: attackTrack = TRAP if targetId in self.traps: trapInfo = self.traps[targetId] attackLevel = trapInfo[0] else: attackLevel = NO_TRAP attackDamage = self.__suitTrapDamage(targetId) trapCreatorId = self.__trapCreator(targetId) if trapCreatorId > 0: self.notify.debug('Giving trap EXP to toon ' + str(trapCreatorId)) self.__addAttackExp(attack, track=TRAP, level=attackLevel, attackerId=trapCreatorId) self.__clearTrapCreator(trapCreatorId, targetId) lureDidDamage = 1 if self.notify.getDebug(): self.notify.debug('Suit lured right onto a trap! (' + str(AvProps[attackTrack][attackLevel]) + ',' + str(attackLevel) + ')') if not self.__combatantDead(targetId, toon=toonTarget): validTargetAvail = 1 targetLured = 1 if not self.SUITS_UNLURED_IMMEDIATELY: if not self.__suitIsLured(targetId, prevRound=1): if not self.__combatantDead(targetId, toon=toonTarget): validTargetAvail = 1 rounds = NumRoundsLured[atkLevel] wakeupChance = 100 - atkAcc * 2 npcLurer = attack[TOON_TRACK_COL] == NPCSOS currLureId = self.__addLuredSuitInfo(targetId, -1, rounds, wakeupChance, toonId, atkLevel, lureId=currLureId, npc=npcLurer) if self.notify.getDebug(): self.notify.debug('Suit lured for ' + str(rounds) + ' rounds max with ' + str(wakeupChance) + '% chance to wake up each round') targetLured = 1 if attackLevel != -1: self.__addLuredSuitsDelayed(toonId, targetId) if targetLured and (not targetId in self.successfulLures or targetId in self.successfulLures and self.successfulLures[targetId][1] < atkLevel): self.notify.debug('Adding target ' + str(targetId) + ' to successfulLures list') self.successfulLures[targetId] = [toonId, atkLevel, atkAcc, -1] else: if atkTrack == TRAP: npcDamage = 0 if attack[TOON_TRACK_COL] == NPCSOS: npcDamage = atkHp if self.CLEAR_MULTIPLE_TRAPS: if self.getSuitTrapType(targetId) != NO_TRAP: self.__clearAttack(toonId) return if atkLevel == UBER_GAG_LEVEL_INDEX: self.__addSuitGroupTrap(targetId, atkLevel, toonId, targetList, npcDamage) if self.__suitIsLured(targetId): self.notify.debug('Train Trap on lured suit %d, \n indicating with KBBONUS_COL flag' % targetId) tgtPos = self.battle.activeSuits.index(targetList[currTarget]) attack[TOON_KBBONUS_COL][tgtPos] = self.KBBONUS_LURED_FLAG else: self.__addSuitTrap(targetId, atkLevel, toonId, npcDamage) elif self.__suitIsLured(targetId) and atkTrack == SOUND: self.notify.debug('Sound on lured suit, ' + 'indicating with KBBONUS_COL flag') tgtPos = self.battle.activeSuits.index(targetList[currTarget]) attack[TOON_KBBONUS_COL][tgtPos] = self.KBBONUS_LURED_FLAG attackLevel = atkLevel attackTrack = atkTrack toon = self.battle.getToon(toonId) if attack[TOON_TRACK_COL] == NPCSOS and lureDidDamage != 1 or attack[TOON_TRACK_COL] == PETSOS: attackDamage = atkHp elif atkTrack == FIRE: suit = self.battle.findSuit(targetId) if suit: slips = toon.getPinkSlips() if slips < 1: simbase.air.writeServerEvent('suspicious', toonId, 'Toon attempting to fire a cog without any pinkslips') else: suit.skeleRevives = 0 attackDamage = suit.getHP() toon.b_setPinkSlips(slips - 1) else: attackDamage = 0 bonus = 0 else: organicBonus = toon.checkGagBonus(attackTrack, attackLevel) propBonus = self.__checkPropBonus(attackTrack) attackDamage = getAvPropDamage(attackTrack, attackLevel, toon.experience.getExp(attackTrack), organicBonus, propBonus, self.propAndOrganicBonusStack) if not self.__combatantDead(targetId, toon=toonTarget): if self.__suitIsLured(targetId) and atkTrack == DROP: self.notify.debug('not setting validTargetAvail, since drop on a lured suit') else: validTargetAvail = 1 if attackLevel == -1 and not atkTrack == FIRE: result = LURE_SUCCEEDED elif atkTrack != TRAP: result = attackDamage if atkTrack == HEAL: if not self.__attackHasHit(attack, suit=0): result = result * 0.2 if self.notify.getDebug(): self.notify.debug('toon does ' + str(result) + ' healing to toon(s)') else: if self.__suitIsLured(targetId) and atkTrack == DROP: result = 0 self.notify.debug('setting damage to 0, since drop on a lured suit') if self.notify.getDebug(): self.notify.debug('toon does ' + str(result) + ' damage to suit') else: result = 0 if result != 0 or atkTrack == PETSOS: targets = self.__getToonTargets(attack) if targetList[currTarget] not in targets: if self.notify.getDebug(): self.notify.debug('Target of toon is not accessible!') continue targetIndex = targets.index(targetList[currTarget]) if atkTrack == HEAL: result = result / len(targetList) if self.notify.getDebug(): self.notify.debug('Splitting heal among ' + str(len(targetList)) + ' targets') if targetId in self.successfulLures and atkTrack == LURE: self.notify.debug('Updating lure damage to ' + str(result)) self.successfulLures[targetId][3] = result else: attack[TOON_HP_COL][targetIndex] = result if result > 0 and atkTrack != HEAL and atkTrack != DROP and atkTrack != PETSOS: attackTrack = LURE lureInfos = self.__getLuredExpInfo(targetId) for currInfo in lureInfos: if currInfo[3]: self.notify.debug('Giving lure EXP to toon ' + str(currInfo[0])) self.__addAttackExp(attack, track=attackTrack, level=currInfo[1], attackerId=currInfo[0]) self.__clearLurer(currInfo[0], lureId=currInfo[2]) if lureDidDamage: if self.itemIsCredit(atkTrack, atkLevel): self.notify.debug('Giving lure EXP to toon ' + str(toonId)) self.__addAttackExp(attack) if not validTargetAvail and self.__prevAtkTrack(toonId) != atkTrack: self.__clearAttack(toonId) return def __getToonTargets(self, attack): track = self.__getActualTrack(attack) if track == HEAL or track == PETSOS: return self.battle.activeToons else: return self.battle.activeSuits def __attackHasHit(self, attack, suit = 0): if suit == 1: for dmg in attack[SUIT_HP_COL]: if dmg > 0: return 1 return 0 else: track = self.__getActualTrack(attack) return not attack[TOON_ACCBONUS_COL] and track != NO_ATTACK def __attackDamage(self, attack, suit = 0): if suit: for dmg in attack[SUIT_HP_COL]: if dmg > 0: return dmg return 0 else: for dmg in attack[TOON_HP_COL]: if dmg > 0: return dmg return 0 def __attackDamageForTgt(self, attack, tgtPos, suit = 0): if suit: return attack[SUIT_HP_COL][tgtPos] else: return attack[TOON_HP_COL][tgtPos] def __calcToonAccBonus(self, attackKey): numPrevHits = 0 attackIdx = self.toonAtkOrder.index(attackKey) for currPrevAtk in xrange(attackIdx - 1, -1, -1): attack = self.battle.toonAttacks[attackKey] atkTrack, atkLevel = self.__getActualTrackLevel(attack) prevAttackKey = self.toonAtkOrder[currPrevAtk] prevAttack = self.battle.toonAttacks[prevAttackKey] prvAtkTrack, prvAtkLevel = self.__getActualTrackLevel(prevAttack) if self.__attackHasHit(prevAttack) and (attackAffectsGroup(prvAtkTrack, prvAtkLevel, prevAttack[TOON_TRACK_COL]) or attackAffectsGroup(atkTrack, atkLevel, attack[TOON_TRACK_COL]) or attack[TOON_TGT_COL] == prevAttack[TOON_TGT_COL]) and atkTrack != prvAtkTrack: numPrevHits += 1 if numPrevHits > 0 and self.notify.getDebug(): self.notify.debug('ACC BONUS: toon attack received accuracy ' + 'bonus of ' + str(self.AccuracyBonuses[numPrevHits]) + ' from previous attack by (' + str(attack[TOON_ID_COL]) + ') which hit') return self.AccuracyBonuses[numPrevHits] def __applyToonAttackDamages(self, toonId, hpbonus = 0, kbbonus = 0): totalDamages = 0 if not self.APPLY_HEALTH_ADJUSTMENTS: return totalDamages attack = self.battle.toonAttacks[toonId] track = self.__getActualTrack(attack) if track != NO_ATTACK and track != SOS and track != TRAP and track != NPCSOS: targets = self.__getToonTargets(attack) for position in xrange(len(targets)): if hpbonus: if targets[position] in self.__createToonTargetList(toonId): damageDone = attack[TOON_HPBONUS_COL] else: damageDone = 0 elif kbbonus: if targets[position] in self.__createToonTargetList(toonId): damageDone = attack[TOON_KBBONUS_COL][position] else: damageDone = 0 else: damageDone = attack[TOON_HP_COL][position] if damageDone <= 0 or self.immortalSuits: continue if track == HEAL or track == PETSOS: currTarget = targets[position] if self.CAP_HEALS: toonHp = self.__getToonHp(currTarget) toonMaxHp = self.__getToonMaxHp(currTarget) if toonHp + damageDone > toonMaxHp: damageDone = toonMaxHp - toonHp attack[TOON_HP_COL][position] = damageDone self.toonHPAdjusts[currTarget] += damageDone totalDamages = totalDamages + damageDone continue currTarget = targets[position] currTarget.setHP(currTarget.getHP() - damageDone) targetId = currTarget.getDoId() if self.notify.getDebug(): if hpbonus: self.notify.debug(str(targetId) + ': suit takes ' + str(damageDone) + ' damage from HP-Bonus') elif kbbonus: self.notify.debug(str(targetId) + ': suit takes ' + str(damageDone) + ' damage from KB-Bonus') else: self.notify.debug(str(targetId) + ': suit takes ' + str(damageDone) + ' damage') totalDamages = totalDamages + damageDone if currTarget.getHP() <= 0: if currTarget.getSkeleRevives() >= 1: currTarget.useSkeleRevive() attack[SUIT_REVIVE_COL] = attack[SUIT_REVIVE_COL] | 1 << position else: self.suitLeftBattle(targetId) attack[SUIT_DIED_COL] = attack[SUIT_DIED_COL] | 1 << position if self.notify.getDebug(): self.notify.debug('Suit' + str(targetId) + 'bravely expired in combat') return totalDamages def __combatantDead(self, avId, toon): if toon: if self.__getToonHp(avId) <= 0: return 1 else: suit = self.battle.findSuit(avId) if suit.getHP() <= 0: return 1 return 0 def __combatantJustRevived(self, avId): suit = self.battle.findSuit(avId) if suit.reviveCheckAndClear(): return 1 else: return 0 def __addAttackExp(self, attack, track = -1, level = -1, attackerId = -1): trk = -1 lvl = -1 id = -1 if track != -1 and level != -1 and attackerId != -1: trk = track lvl = level id = attackerId elif self.__attackHasHit(attack): if self.notify.getDebug(): self.notify.debug('Attack ' + repr(attack) + ' has hit') trk = attack[TOON_TRACK_COL] lvl = attack[TOON_LVL_COL] id = attack[TOON_ID_COL] if trk != -1 and trk != NPCSOS and trk != PETSOS and lvl != -1 and id != -1: expList = self.toonSkillPtsGained.get(id, None) if expList == None: expList = [0, 0, 0, 0, 0, 0, 0] self.toonSkillPtsGained[id] = expList expList[trk] = expList[trk] + (lvl + 1) * self.__skillCreditMultiplier return def __clearTgtDied(self, tgt, lastAtk, currAtk): position = self.battle.activeSuits.index(tgt) currAtkTrack = self.__getActualTrack(currAtk) lastAtkTrack = self.__getActualTrack(lastAtk) if currAtkTrack == lastAtkTrack and lastAtk[SUIT_DIED_COL] & 1 << position and self.__attackHasHit(currAtk, suit=0): if self.notify.getDebug(): self.notify.debug('Clearing suit died for ' + str(tgt.getDoId()) + ' at position ' + str(position) + ' from toon attack ' + str(lastAtk[TOON_ID_COL]) + ' and setting it for ' + str(currAtk[TOON_ID_COL])) lastAtk[SUIT_DIED_COL] = lastAtk[SUIT_DIED_COL] ^ 1 << position self.suitLeftBattle(tgt.getDoId()) currAtk[SUIT_DIED_COL] = currAtk[SUIT_DIED_COL] | 1 << position def __addDmgToBonuses(self, dmg, attackIndex, hp = 1): toonId = self.toonAtkOrder[attackIndex] attack = self.battle.toonAttacks[toonId] atkTrack = self.__getActualTrack(attack) if atkTrack == HEAL or atkTrack == PETSOS: return tgts = self.__createToonTargetList(toonId) for currTgt in tgts: tgtPos = self.battle.activeSuits.index(currTgt) attackerId = self.toonAtkOrder[attackIndex] attack = self.battle.toonAttacks[attackerId] track = self.__getActualTrack(attack) if hp: if track in self.hpBonuses[tgtPos]: self.hpBonuses[tgtPos][track].append([attackIndex, dmg]) else: self.hpBonuses[tgtPos][track] = [[attackIndex, dmg]] elif self.__suitIsLured(currTgt.getDoId()): if track in self.kbBonuses[tgtPos]: self.kbBonuses[tgtPos][track].append([attackIndex, dmg]) else: self.kbBonuses[tgtPos][track] = [[attackIndex, dmg]] def __clearBonuses(self, hp = 1): if hp: self.hpBonuses = [{}, {}, {}, {}] else: self.kbBonuses = [{}, {}, {}, {}] def __bonusExists(self, tgtSuit, hp = 1): tgtPos = self.activeSuits.index(tgtSuit) if hp: bonusLen = len(self.hpBonuses[tgtPos]) else: bonusLen = len(self.kbBonuses[tgtPos]) if bonusLen > 0: return 1 return 0 def __processBonuses(self, hp = 1): if hp: bonusList = self.hpBonuses self.notify.debug('Processing hpBonuses: ' + repr(self.hpBonuses)) else: bonusList = self.kbBonuses self.notify.debug('Processing kbBonuses: ' + repr(self.kbBonuses)) tgtPos = 0 for currTgt in bonusList: for currAtkType in currTgt.keys(): if len(currTgt[currAtkType]) > 1 or not hp and len(currTgt[currAtkType]) > 0: totalDmgs = 0 for currDmg in currTgt[currAtkType]: totalDmgs += currDmg[1] numDmgs = len(currTgt[currAtkType]) attackIdx = currTgt[currAtkType][numDmgs - 1][0] attackerId = self.toonAtkOrder[attackIdx] attack = self.battle.toonAttacks[attackerId] if hp: attack[TOON_HPBONUS_COL] = math.ceil(totalDmgs * (self.DamageBonuses[numDmgs - 1] * 0.01)) if self.notify.getDebug(): self.notify.debug('Applying hp bonus to track ' + str(attack[TOON_TRACK_COL]) + ' of ' + str(attack[TOON_HPBONUS_COL])) elif len(attack[TOON_KBBONUS_COL]) > tgtPos: attack[TOON_KBBONUS_COL][tgtPos] = totalDmgs * 0.5 if self.notify.getDebug(): self.notify.debug('Applying kb bonus to track ' + str(attack[TOON_TRACK_COL]) + ' of ' + str(attack[TOON_KBBONUS_COL][tgtPos]) + ' to target ' + str(tgtPos)) else: self.notify.warning('invalid tgtPos for knock back bonus: %d' % tgtPos) tgtPos += 1 if hp: self.__clearBonuses() else: self.__clearBonuses(hp=0) def __handleBonus(self, attackIdx, hp = 1): attackerId = self.toonAtkOrder[attackIdx] attack = self.battle.toonAttacks[attackerId] atkDmg = self.__attackDamage(attack, suit=0) atkTrack = self.__getActualTrack(attack) if atkDmg > 0: if hp: if atkTrack != LURE: self.notify.debug('Adding dmg of ' + str(atkDmg) + ' to hpBonuses list') self.__addDmgToBonuses(atkDmg, attackIdx) elif self.__knockBackAtk(attackerId, toon=1): self.notify.debug('Adding dmg of ' + str(atkDmg) + ' to kbBonuses list') self.__addDmgToBonuses(atkDmg, attackIdx, hp=0) def __clearAttack(self, attackIdx, toon = 1): if toon: if self.notify.getDebug(): self.notify.debug('clearing out toon attack for toon ' + str(attackIdx) + '...') attack = self.battle.toonAttacks[attackIdx] self.battle.toonAttacks[attackIdx] = getToonAttack(attackIdx) longest = max(len(self.battle.activeToons), len(self.battle.activeSuits)) taList = self.battle.toonAttacks for j in xrange(longest): taList[attackIdx][TOON_HP_COL].append(-1) taList[attackIdx][TOON_KBBONUS_COL].append(-1) if self.notify.getDebug(): self.notify.debug('toon attack is now ' + repr(self.battle.toonAttacks[attackIdx])) else: self.notify.warning('__clearAttack not implemented for suits!') def __rememberToonAttack(self, suitId, toonId, damage): if not suitId in self.SuitAttackers: self.SuitAttackers[suitId] = {toonId: damage} elif not toonId in self.SuitAttackers[suitId]: self.SuitAttackers[suitId][toonId] = damage elif self.SuitAttackers[suitId][toonId] <= damage: self.SuitAttackers[suitId] = [toonId, damage] def __postProcessToonAttacks(self): self.notify.debug('__postProcessToonAttacks()') lastTrack = -1 lastAttacks = [] self.__clearBonuses() for currToonAttack in self.toonAtkOrder: if currToonAttack != -1: attack = self.battle.toonAttacks[currToonAttack] atkTrack, atkLevel = self.__getActualTrackLevel(attack) if atkTrack != HEAL and atkTrack != SOS and atkTrack != NO_ATTACK and atkTrack != NPCSOS and atkTrack != PETSOS: targets = self.__createToonTargetList(currToonAttack) allTargetsDead = 1 for currTgt in targets: damageDone = self.__attackDamage(attack, suit=0) if damageDone > 0: self.__rememberToonAttack(currTgt.getDoId(), attack[TOON_ID_COL], damageDone) if atkTrack == TRAP: if currTgt.doId in self.traps: trapInfo = self.traps[currTgt.doId] currTgt.battleTrap = trapInfo[0] targetDead = 0 if currTgt.getHP() > 0: allTargetsDead = 0 else: targetDead = 1 if atkTrack != LURE: for currLastAtk in lastAttacks: self.__clearTgtDied(currTgt, currLastAtk, attack) tgtId = currTgt.getDoId() if tgtId in self.successfulLures and atkTrack == LURE: lureInfo = self.successfulLures[tgtId] self.notify.debug('applying lure data: ' + repr(lureInfo)) toonId = lureInfo[0] lureAtk = self.battle.toonAttacks[toonId] tgtPos = self.battle.activeSuits.index(currTgt) if currTgt.doId in self.traps: trapInfo = self.traps[currTgt.doId] if trapInfo[0] == UBER_GAG_LEVEL_INDEX: self.notify.debug('train trap triggered for %d' % currTgt.doId) self.trainTrapTriggered = True self.__removeSuitTrap(tgtId) lureAtk[TOON_KBBONUS_COL][tgtPos] = self.KBBONUS_TGT_LURED lureAtk[TOON_HP_COL][tgtPos] = lureInfo[3] elif self.__suitIsLured(tgtId) and atkTrack == DROP: self.notify.debug('Drop on lured suit, ' + 'indicating with KBBONUS_COL ' + 'flag') tgtPos = self.battle.activeSuits.index(currTgt) attack[TOON_KBBONUS_COL][tgtPos] = self.KBBONUS_LURED_FLAG if targetDead and atkTrack != lastTrack: tgtPos = self.battle.activeSuits.index(currTgt) attack[TOON_HP_COL][tgtPos] = 0 attack[TOON_KBBONUS_COL][tgtPos] = -1 if allTargetsDead and atkTrack != lastTrack: if self.notify.getDebug(): self.notify.debug('all targets of toon attack ' + str(currToonAttack) + ' are dead') self.__clearAttack(currToonAttack, toon=1) attack = self.battle.toonAttacks[currToonAttack] atkTrack, atkLevel = self.__getActualTrackLevel(attack) damagesDone = self.__applyToonAttackDamages(currToonAttack) self.__applyToonAttackDamages(currToonAttack, hpbonus=1) if atkTrack != LURE and atkTrack != DROP and atkTrack != SOUND: self.__applyToonAttackDamages(currToonAttack, kbbonus=1) if lastTrack != atkTrack: lastAttacks = [] lastTrack = atkTrack lastAttacks.append(attack) if self.itemIsCredit(atkTrack, atkLevel): if atkTrack == TRAP or atkTrack == LURE: pass elif atkTrack == HEAL: if damagesDone != 0: self.__addAttackExp(attack) else: self.__addAttackExp(attack) if self.trainTrapTriggered: for suit in self.battle.activeSuits: suitId = suit.doId self.__removeSuitTrap(suitId) suit.battleTrap = NO_TRAP self.notify.debug('train trap triggered, removing trap from %d' % suitId) if self.notify.getDebug(): for currToonAttack in self.toonAtkOrder: attack = self.battle.toonAttacks[currToonAttack] self.notify.debug('Final Toon attack: ' + str(attack)) def __allTargetsDead(self, attackIdx, toon = 1): allTargetsDead = 1 if toon: targets = self.__createToonTargetList(attackIdx) for currTgt in targets: if currTgt.getHp() > 0: allTargetsDead = 0 break else: self.notify.warning('__allTargetsDead: suit ver. not implemented!') return allTargetsDead def __clearLuredSuitsByAttack(self, toonId, kbBonusReq = 0, targetId = -1): if self.notify.getDebug(): self.notify.debug('__clearLuredSuitsByAttack') if targetId != -1 and self.__suitIsLured(t.getDoId()): self.__removeLured(t.getDoId()) else: tgtList = self.__createToonTargetList(toonId) for t in tgtList: if self.__suitIsLured(t.getDoId()) and (not kbBonusReq or self.__bonusExists(t, hp=0)): self.__removeLured(t.getDoId()) if self.notify.getDebug(): self.notify.debug('Suit %d stepping from lured spot' % t.getDoId()) else: self.notify.debug('Suit ' + str(t.getDoId()) + ' not found in currently lured suits') def __clearLuredSuitsDelayed(self): if self.notify.getDebug(): self.notify.debug('__clearLuredSuitsDelayed') for t in self.delayedUnlures: if self.__suitIsLured(t): self.__removeLured(t) if self.notify.getDebug(): self.notify.debug('Suit %d stepping back from lured spot' % t) else: self.notify.debug('Suit ' + str(t) + ' not found in currently lured suits') self.delayedUnlures = [] def __addLuredSuitsDelayed(self, toonId, targetId = -1, ignoreDamageCheck = False): if self.notify.getDebug(): self.notify.debug('__addLuredSuitsDelayed') if targetId != -1: self.delayedUnlures.append(targetId) else: tgtList = self.__createToonTargetList(toonId) for t in tgtList: if self.__suitIsLured(t.getDoId()) and t.getDoId() not in self.delayedUnlures and (self.__attackDamageForTgt(self.battle.toonAttacks[toonId], self.battle.activeSuits.index(t), suit=0) > 0 or ignoreDamageCheck): self.delayedUnlures.append(t.getDoId()) def __calculateToonAttacks(self): self.notify.debug('__calculateToonAttacks()') self.__clearBonuses(hp=0) currTrack = None self.notify.debug('Traps: ' + str(self.traps)) maxSuitLevel = 0 for cog in self.battle.activeSuits: maxSuitLevel = max(maxSuitLevel, cog.getActualLevel()) self.creditLevel = maxSuitLevel for toonId in self.toonAtkOrder: if self.__combatantDead(toonId, toon=1): if self.notify.getDebug(): self.notify.debug("Toon %d is dead and can't attack" % toonId) continue attack = self.battle.toonAttacks[toonId] atkTrack = self.__getActualTrack(attack) if atkTrack != NO_ATTACK and atkTrack != SOS and atkTrack != NPCSOS: if self.notify.getDebug(): self.notify.debug('Calculating attack for toon: %d' % toonId) if self.SUITS_UNLURED_IMMEDIATELY: if currTrack and atkTrack != currTrack: self.__clearLuredSuitsDelayed() currTrack = atkTrack self.__calcToonAtkHp(toonId) attackIdx = self.toonAtkOrder.index(toonId) self.__handleBonus(attackIdx, hp=0) self.__handleBonus(attackIdx, hp=1) lastAttack = self.toonAtkOrder.index(toonId) >= len(self.toonAtkOrder) - 1 unlureAttack = self.__attackHasHit(attack, suit=0) and self.__unlureAtk(toonId, toon=1) if unlureAttack: if lastAttack: self.__clearLuredSuitsByAttack(toonId) else: self.__addLuredSuitsDelayed(toonId) if lastAttack: self.__clearLuredSuitsDelayed() self.__processBonuses(hp=0) self.__processBonuses(hp=1) self.__postProcessToonAttacks() return def __knockBackAtk(self, attackIndex, toon = 1): if toon and (self.battle.toonAttacks[attackIndex][TOON_TRACK_COL] == THROW or self.battle.toonAttacks[attackIndex][TOON_TRACK_COL] == SQUIRT): if self.notify.getDebug(): self.notify.debug('attack is a knockback') return 1 return 0 def __unlureAtk(self, attackIndex, toon = 1): attack = self.battle.toonAttacks[attackIndex] track = self.__getActualTrack(attack) if toon and (track == THROW or track == SQUIRT or track == SOUND): if self.notify.getDebug(): self.notify.debug('attack is an unlure') return 1 return 0 def __calcSuitAtkType(self, attackIndex): theSuit = self.battle.activeSuits[attackIndex] attacks = SuitBattleGlobals.SuitAttributes[theSuit.dna.name]['attacks'] atk = SuitBattleGlobals.pickSuitAttack(attacks, theSuit.getLevel()) return atk def __calcSuitTarget(self, attackIndex): attack = self.battle.suitAttacks[attackIndex] suitId = attack[SUIT_ID_COL] if suitId in self.SuitAttackers and random.randint(0, 99) < 75: totalDamage = 0 for currToon in self.SuitAttackers[suitId].keys(): totalDamage += self.SuitAttackers[suitId][currToon] dmgs = [] for currToon in self.SuitAttackers[suitId].keys(): dmgs.append(self.SuitAttackers[suitId][currToon] / totalDamage * 100) dmgIdx = SuitBattleGlobals.pickFromFreqList(dmgs) if dmgIdx == None: toonId = self.__pickRandomToon(suitId) else: toonId = self.SuitAttackers[suitId].keys()[dmgIdx] if toonId == -1 or toonId not in self.battle.activeToons: return -1 self.notify.debug('Suit attacking back at toon ' + str(toonId)) return self.battle.activeToons.index(toonId) else: return self.__pickRandomToon(suitId) return def __pickRandomToon(self, suitId): liveToons = [] for currToon in self.battle.activeToons: if not self.__combatantDead(currToon, toon=1): liveToons.append(self.battle.activeToons.index(currToon)) if len(liveToons) == 0: self.notify.debug('No tgts avail. for suit ' + str(suitId)) return -1 chosen = random.choice(liveToons) self.notify.debug('Suit randomly attacking toon ' + str(self.battle.activeToons[chosen])) return chosen def __suitAtkHit(self, attackIndex): if self.suitsAlwaysHit: return 1 elif self.suitsAlwaysMiss: return 0 theSuit = self.battle.activeSuits[attackIndex] atkType = self.battle.suitAttacks[attackIndex][SUIT_ATK_COL] atkInfo = SuitBattleGlobals.getSuitAttack(theSuit.dna.name, theSuit.getLevel(), atkType) atkAcc = atkInfo['acc'] suitAcc = SuitBattleGlobals.SuitAttributes[theSuit.dna.name]['acc'][theSuit.getLevel()] acc = atkAcc randChoice = random.randint(0, 99) if self.notify.getDebug(): self.notify.debug('Suit attack rolled ' + str(randChoice) + ' to hit with an accuracy of ' + str(acc) + ' (attackAcc: ' + str(atkAcc) + ' suitAcc: ' + str(suitAcc) + ')') if randChoice < acc: return 1 return 0 def __suitAtkAffectsGroup(self, attack): atkType = attack[SUIT_ATK_COL] theSuit = self.battle.findSuit(attack[SUIT_ID_COL]) atkInfo = SuitBattleGlobals.getSuitAttack(theSuit.dna.name, theSuit.getLevel(), atkType) return atkInfo['group'] != SuitBattleGlobals.ATK_TGT_SINGLE def __createSuitTargetList(self, attackIndex): attack = self.battle.suitAttacks[attackIndex] targetList = [] if attack[SUIT_ATK_COL] == NO_ATTACK: self.notify.debug('No attack, no targets') return targetList debug = self.notify.getDebug() if not self.__suitAtkAffectsGroup(attack): targetList.append(self.battle.activeToons[attack[SUIT_TGT_COL]]) if debug: self.notify.debug('Suit attack is single target') else: if debug: self.notify.debug('Suit attack is group target') for currToon in self.battle.activeToons: if debug: self.notify.debug('Suit attack will target toon' + str(currToon)) targetList.append(currToon) return targetList def __calcSuitAtkHp(self, attackIndex): targetList = self.__createSuitTargetList(attackIndex) attack = self.battle.suitAttacks[attackIndex] for currTarget in xrange(len(targetList)): toonId = targetList[currTarget] toon = self.battle.getToon(toonId) result = 0 if (toon and toon.immortalMode) or self.TOONS_TAKE_NO_DAMAGE: result = 0 elif self.__suitAtkHit(attackIndex): atkType = attack[SUIT_ATK_COL] theSuit = self.battle.findSuit(attack[SUIT_ID_COL]) atkInfo = SuitBattleGlobals.getSuitAttack(theSuit.dna.name, theSuit.getLevel(), atkType) result = atkInfo['hp'] targetIndex = self.battle.activeToons.index(toonId) attack[SUIT_HP_COL][targetIndex] = result def __getToonHp(self, toonDoId): handle = self.battle.getToon(toonDoId) if handle != None and toonDoId in self.toonHPAdjusts: return handle.hp + self.toonHPAdjusts[toonDoId] else: return 0 return def __getToonMaxHp(self, toonDoId): handle = self.battle.getToon(toonDoId) if handle != None: return handle.maxHp else: return 0 return def __applySuitAttackDamages(self, attackIndex): attack = self.battle.suitAttacks[attackIndex] if self.APPLY_HEALTH_ADJUSTMENTS: for t in self.battle.activeToons: position = self.battle.activeToons.index(t) if attack[SUIT_HP_COL][position] <= 0: continue toonHp = self.__getToonHp(t) if toonHp - attack[SUIT_HP_COL][position] <= 0: if self.notify.getDebug(): self.notify.debug('Toon %d has died, removing' % t) self.toonLeftBattle(t) attack[TOON_DIED_COL] = attack[TOON_DIED_COL] | 1 << position if self.notify.getDebug(): self.notify.debug('Toon ' + str(t) + ' takes ' + str(attack[SUIT_HP_COL][position]) + ' damage') self.toonHPAdjusts[t] -= attack[SUIT_HP_COL][position] self.notify.debug('Toon ' + str(t) + ' now has ' + str(self.__getToonHp(t)) + ' health') def __suitCanAttack(self, suitId): if self.__combatantDead(suitId, toon=0) or self.__suitIsLured(suitId) or self.__combatantJustRevived(suitId): return 0 return 1 def __updateSuitAtkStat(self, toonId): if toonId in self.suitAtkStats: self.suitAtkStats[toonId] += 1 else: self.suitAtkStats[toonId] = 1 def __printSuitAtkStats(self): self.notify.debug('Suit Atk Stats:') for currTgt in self.suitAtkStats.keys(): if currTgt not in self.battle.activeToons: continue tgtPos = self.battle.activeToons.index(currTgt) self.notify.debug(' toon ' + str(currTgt) + ' at position ' + str(tgtPos) + ' was attacked ' + str(self.suitAtkStats[currTgt]) + ' times') self.notify.debug('\n') def __calculateSuitAttacks(self): for i in xrange(len(self.battle.suitAttacks)): if i < len(self.battle.activeSuits): suitId = self.battle.activeSuits[i].doId self.battle.suitAttacks[i][SUIT_ID_COL] = suitId if not self.__suitCanAttack(suitId): if self.notify.getDebug(): self.notify.debug("Suit %d can't attack" % suitId) continue if self.battle.pendingSuits.count(self.battle.activeSuits[i]) > 0 or self.battle.joiningSuits.count(self.battle.activeSuits[i]) > 0: continue attack = self.battle.suitAttacks[i] attack[SUIT_ID_COL] = self.battle.activeSuits[i].doId attack[SUIT_ATK_COL] = self.__calcSuitAtkType(i) attack[SUIT_TGT_COL] = self.__calcSuitTarget(i) if attack[SUIT_TGT_COL] == -1: self.battle.suitAttacks[i] = getDefaultSuitAttack() attack = self.battle.suitAttacks[i] self.notify.debug('clearing suit attack, no avail targets') self.__calcSuitAtkHp(i) if attack[SUIT_ATK_COL] != NO_ATTACK: if self.__suitAtkAffectsGroup(attack): for currTgt in self.battle.activeToons: self.__updateSuitAtkStat(currTgt) else: tgtId = self.battle.activeToons[attack[SUIT_TGT_COL]] self.__updateSuitAtkStat(tgtId) targets = self.__createSuitTargetList(i) allTargetsDead = 1 for currTgt in targets: if self.__getToonHp(currTgt) > 0: allTargetsDead = 0 break if allTargetsDead: self.battle.suitAttacks[i] = getDefaultSuitAttack() if self.notify.getDebug(): self.notify.debug('clearing suit attack, targets dead') self.notify.debug('suit attack is now ' + repr(self.battle.suitAttacks[i])) self.notify.debug('all attacks: ' + repr(self.battle.suitAttacks)) attack = self.battle.suitAttacks[i] if self.__attackHasHit(attack, suit=1): self.__applySuitAttackDamages(i) if self.notify.getDebug(): self.notify.debug('Suit attack: ' + str(self.battle.suitAttacks[i])) attack[SUIT_BEFORE_TOONS_COL] = 0 def __updateLureTimeouts(self): if self.notify.getDebug(): self.notify.debug('__updateLureTimeouts()') self.notify.debug('Lured suits: ' + str(self.currentlyLuredSuits)) noLongerLured = [] for currLuredSuit in self.currentlyLuredSuits.keys(): self.__incLuredCurrRound(currLuredSuit) if self.__luredMaxRoundsReached(currLuredSuit) or self.__luredWakeupTime(currLuredSuit): noLongerLured.append(currLuredSuit) for currLuredSuit in noLongerLured: self.__removeLured(currLuredSuit) if self.notify.getDebug(): self.notify.debug('Lured suits: ' + str(self.currentlyLuredSuits)) def __initRound(self): if self.CLEAR_SUIT_ATTACKERS: self.SuitAttackers = {} self.toonAtkOrder = [] attacks = findToonAttack(self.battle.activeToons, self.battle.toonAttacks, PETSOS) for atk in attacks: self.toonAtkOrder.append(atk[TOON_ID_COL]) attacks = findToonAttack(self.battle.activeToons, self.battle.toonAttacks, FIRE) for atk in attacks: self.toonAtkOrder.append(atk[TOON_ID_COL]) for track in xrange(HEAL, DROP + 1): attacks = findToonAttack(self.battle.activeToons, self.battle.toonAttacks, track) if track == TRAP: sortedTraps = [] for atk in attacks: if atk[TOON_TRACK_COL] == TRAP: sortedTraps.append(atk) for atk in attacks: if atk[TOON_TRACK_COL] == NPCSOS: sortedTraps.append(atk) attacks = sortedTraps for atk in attacks: self.toonAtkOrder.append(atk[TOON_ID_COL]) specials = findToonAttack(self.battle.activeToons, self.battle.toonAttacks, NPCSOS) toonsHit = 0 cogsMiss = 0 for special in specials: npc_track = NPCToons.getNPCTrack(special[TOON_TGT_COL]) if npc_track == NPC_TOONS_HIT: BattleCalculatorAI.toonsAlwaysHit = 1 toonsHit = 1 elif npc_track == NPC_COGS_MISS: BattleCalculatorAI.suitsAlwaysMiss = 1 cogsMiss = 1 if self.notify.getDebug(): self.notify.debug('Toon attack order: ' + str(self.toonAtkOrder)) self.notify.debug('Active toons: ' + str(self.battle.activeToons)) self.notify.debug('Toon attacks: ' + str(self.battle.toonAttacks)) self.notify.debug('Active suits: ' + str(self.battle.activeSuits)) self.notify.debug('Suit attacks: ' + str(self.battle.suitAttacks)) self.toonHPAdjusts = {} for t in self.battle.activeToons: self.toonHPAdjusts[t] = 0 self.__clearBonuses() self.__updateActiveToons() self.delayedUnlures = [] self.__initTraps() self.successfulLures = {} return (toonsHit, cogsMiss) def calculateRound(self): longest = max(len(self.battle.activeToons), len(self.battle.activeSuits)) for t in self.battle.activeToons: for j in xrange(longest): self.battle.toonAttacks[t][TOON_HP_COL].append(-1) self.battle.toonAttacks[t][TOON_KBBONUS_COL].append(-1) for i in xrange(4): for j in xrange(len(self.battle.activeToons)): self.battle.suitAttacks[i][SUIT_HP_COL].append(-1) toonsHit, cogsMiss = self.__initRound() for suit in self.battle.activeSuits: if suit.isGenerated(): suit.b_setHP(suit.getHP()) for suit in self.battle.activeSuits: if not hasattr(suit, 'dna'): self.notify.warning('a removed suit is in this battle!') return None self.__calculateToonAttacks() self.__updateLureTimeouts() self.__calculateSuitAttacks() if toonsHit == 1: BattleCalculatorAI.toonsAlwaysHit = 0 if cogsMiss == 1: BattleCalculatorAI.suitsAlwaysMiss = 0 if self.notify.getDebug(): self.notify.debug('Toon skills gained after this round: ' + repr(self.toonSkillPtsGained)) self.__printSuitAtkStats() return None def toonLeftBattle(self, toonId): if self.notify.getDebug(): self.notify.debug('toonLeftBattle()' + str(toonId)) if toonId in self.toonSkillPtsGained: del self.toonSkillPtsGained[toonId] if toonId in self.suitAtkStats: del self.suitAtkStats[toonId] if not self.CLEAR_SUIT_ATTACKERS: oldSuitIds = [] for s in self.SuitAttackers.keys(): if toonId in self.SuitAttackers[s]: del self.SuitAttackers[s][toonId] if len(self.SuitAttackers[s]) == 0: oldSuitIds.append(s) for oldSuitId in oldSuitIds: del self.SuitAttackers[oldSuitId] self.__clearTrapCreator(toonId) self.__clearLurer(toonId) def suitLeftBattle(self, suitId): if self.notify.getDebug(): self.notify.debug('suitLeftBattle(): ' + str(suitId)) self.__removeLured(suitId) if suitId in self.SuitAttackers: del self.SuitAttackers[suitId] self.__removeSuitTrap(suitId) def __updateActiveToons(self): if self.notify.getDebug(): self.notify.debug('updateActiveToons()') if not self.CLEAR_SUIT_ATTACKERS: oldSuitIds = [] for s in self.SuitAttackers.keys(): for t in self.SuitAttackers[s].keys(): if t not in self.battle.activeToons: del self.SuitAttackers[s][t] if len(self.SuitAttackers[s]) == 0: oldSuitIds.append(s) for oldSuitId in oldSuitIds: del self.SuitAttackers[oldSuitId] for trap in self.traps.keys(): if self.traps[trap][1] not in self.battle.activeToons: self.notify.debug('Trap for toon ' + str(self.traps[trap][1]) + ' will no longer give exp') self.traps[trap][1] = 0 def getSkillGained(self, toonId, track): return BattleExperienceAI.getSkillGained(self.toonSkillPtsGained, toonId, track) def getLuredSuits(self): luredSuits = self.currentlyLuredSuits.keys() self.notify.debug('Lured suits reported to battle: ' + repr(luredSuits)) return luredSuits def __suitIsLured(self, suitId, prevRound = 0): inList = suitId in self.currentlyLuredSuits if prevRound: return inList and self.currentlyLuredSuits[suitId][0] != -1 return inList def __findAvailLureId(self, lurerId): luredSuits = self.currentlyLuredSuits.keys() lureIds = [] for currLured in luredSuits: lurerInfo = self.currentlyLuredSuits[currLured][3] lurers = lurerInfo.keys() for currLurer in lurers: currId = lurerInfo[currLurer][1] if currLurer == lurerId and currId not in lureIds: lureIds.append(currId) lureIds.sort() currId = 1 for currLureId in lureIds: if currLureId != currId: return currId currId += 1 return currId def __addLuredSuitInfo(self, suitId, currRounds, maxRounds, wakeChance, lurer, lureLvl, lureId = -1, npc = 0): if lureId == -1: availLureId = self.__findAvailLureId(lurer) else: availLureId = lureId if npc == 1: credit = 0 else: credit = self.itemIsCredit(LURE, lureLvl) if suitId in self.currentlyLuredSuits: lureInfo = self.currentlyLuredSuits[suitId] if not lurer in lureInfo[3]: lureInfo[1] += maxRounds if wakeChance < lureInfo[2]: lureInfo[2] = wakeChance lureInfo[3][lurer] = [lureLvl, availLureId, credit] else: lurerInfo = {lurer: [lureLvl, availLureId, credit]} self.currentlyLuredSuits[suitId] = [currRounds, maxRounds, wakeChance, lurerInfo] self.notify.debug('__addLuredSuitInfo: currLuredSuits -> %s' % repr(self.currentlyLuredSuits)) return availLureId def __getLurers(self, suitId): if self.__suitIsLured(suitId): return self.currentlyLuredSuits[suitId][3].keys() return [] def __getLuredExpInfo(self, suitId): returnInfo = [] lurers = self.__getLurers(suitId) if len(lurers) == 0: return returnInfo lurerInfo = self.currentlyLuredSuits[suitId][3] for currLurer in lurers: returnInfo.append([currLurer, lurerInfo[currLurer][0], lurerInfo[currLurer][1], lurerInfo[currLurer][2]]) return returnInfo def __clearLurer(self, lurerId, lureId = -1): luredSuits = self.currentlyLuredSuits.keys() for currLured in luredSuits: lurerInfo = self.currentlyLuredSuits[currLured][3] lurers = lurerInfo.keys() for currLurer in lurers: if currLurer == lurerId and (lureId == -1 or lureId == lurerInfo[currLurer][1]): del lurerInfo[currLurer] def __setLuredMaxRounds(self, suitId, rounds): if self.__suitIsLured(suitId): self.currentlyLuredSuits[suitId][1] = rounds def __setLuredWakeChance(self, suitId, chance): if self.__suitIsLured(suitId): self.currentlyLuredSuits[suitId][2] = chance def __incLuredCurrRound(self, suitId): if self.__suitIsLured(suitId): self.currentlyLuredSuits[suitId][0] += 1 def __removeLured(self, suitId): if self.__suitIsLured(suitId): del self.currentlyLuredSuits[suitId] def __luredMaxRoundsReached(self, suitId): return self.__suitIsLured(suitId) and self.currentlyLuredSuits[suitId][0] >= self.currentlyLuredSuits[suitId][1] def __luredWakeupTime(self, suitId): return self.__suitIsLured(suitId) and self.currentlyLuredSuits[suitId][0] > 0 and random.randint(0, 99) < self.currentlyLuredSuits[suitId][2] def itemIsCredit(self, track, level): return track != PETSOS and level < self.creditLevel def __getActualTrack(self, toonAttack): if toonAttack[TOON_TRACK_COL] == NPCSOS: track = NPCToons.getNPCTrack(toonAttack[TOON_TGT_COL]) if track != None: return track else: self.notify.warning('No NPC with id: %d' % toonAttack[TOON_TGT_COL]) return toonAttack[TOON_TRACK_COL] def __getActualTrackLevel(self, toonAttack): if toonAttack[TOON_TRACK_COL] == NPCSOS: track, level, hp = NPCToons.getNPCTrackLevelHp(toonAttack[TOON_TGT_COL]) if track != None: return (track, level) else: self.notify.warning('No NPC with id: %d' % toonAttack[TOON_TGT_COL]) return (toonAttack[TOON_TRACK_COL], toonAttack[TOON_LVL_COL]) def __getActualTrackLevelHp(self, toonAttack): if toonAttack[TOON_TRACK_COL] == NPCSOS: track, level, hp = NPCToons.getNPCTrackLevelHp(toonAttack[TOON_TGT_COL]) if track != None: return (track, level, hp) else: self.notify.warning('No NPC with id: %d' % toonAttack[TOON_TGT_COL]) elif toonAttack[TOON_TRACK_COL] == PETSOS: trick = toonAttack[TOON_LVL_COL] petProxyId = toonAttack[TOON_TGT_COL] trickId = toonAttack[TOON_LVL_COL] healRange = PetTricks.TrickHeals[trickId] hp = 0 if petProxyId in simbase.air.doId2do: petProxy = simbase.air.doId2do[petProxyId] if trickId < len(petProxy.trickAptitudes): aptitude = petProxy.trickAptitudes[trickId] hp = int(lerp(healRange[0], healRange[1], aptitude)) else: self.notify.warning('pet proxy: %d not in doId2do!' % petProxyId) return (toonAttack[TOON_TRACK_COL], toonAttack[TOON_LVL_COL], hp) return (toonAttack[TOON_TRACK_COL], toonAttack[TOON_LVL_COL], 0) def __calculatePetTrickSuccess(self, toonAttack): petProxyId = toonAttack[TOON_TGT_COL] if not petProxyId in simbase.air.doId2do: self.notify.warning('pet proxy %d not in doId2do!' % petProxyId) toonAttack[TOON_ACCBONUS_COL] = 1 return (0, 0) petProxy = simbase.air.doId2do[petProxyId] trickId = toonAttack[TOON_LVL_COL] toonAttack[TOON_ACCBONUS_COL] = petProxy.attemptBattleTrick(trickId) if toonAttack[TOON_ACCBONUS_COL] == 1: return (0, 0) else: return (1, 100)