Poodletooth-iLand/toontown/battle/BattleCalculatorAI.py

1588 lines
70 KiB
Python

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]
propAcc = min(propAcc * 1.3, 100)
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] = min(ExperienceCap, 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)