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, DistributedPetProxyAI 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] NumRoundsLured = [2, 2, 3, 3, 4, 4, 15] 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) hasAccuracyBuff = False toon = simbase.air.doId2do.get(attack[TOON_ID_COL]) if toon: if toon.hasBuff(BGagAccuracy): if not ZoneUtil.isDynamicZone(toon.zoneId): if ZoneUtil.getWhereName(toon.zoneId, True) in ('street', 'factoryExterior', 'cogHQExterior'): hasAccuracyBuff = True 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 hasAccuracyBuff: propAcc *= BGagAccuracyMultiplier 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): result = False if self.battle.getInteractivePropTrackBonus() == track: result = True return result 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 = self.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 = self.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: result = 1 elif 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): if track == PETSOS: return 0 return 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)