ToonTownRewritten/toontown/election/DistributedInvasionSuitAI.py

350 lines
13 KiB
Python

from direct.directnotify import DirectNotifyGlobal
from direct.distributed.ClockDelta import *
from direct.fsm.FSM import FSM
from direct.task.Task import Task
from toontown.toonbase import ToontownGlobals
from toontown.suit.DistributedSuitBaseAI import DistributedSuitBaseAI
from toontown.suit import SuitTimings
from toontown.battle import SuitBattleGlobals
import SafezoneInvasionGlobals
from InvasionSuitBase import InvasionSuitBase
from InvasionSuitBrainAI import InvasionSuitBrainAI
import SafezoneInvasionGlobals
from random import random, choice, randint
class DistributedInvasionSuitAI(DistributedSuitBaseAI, InvasionSuitBase, FSM):
notify = DirectNotifyGlobal.directNotify.newCategory("DistributedInvasionSuitAI")
def __init__(self, air, invasion):
DistributedSuitBaseAI.__init__(self, air)
InvasionSuitBase.__init__(self)
FSM.__init__(self, 'InvasionSuitFSM')
self.invasion = invasion
self.stateTime = globalClockDelta.getRealNetworkTime()
self.spawnPointId = 0
self.brain = InvasionSuitBrainAI(self)
self.lastMarchTime = 0.0
self.__walkTimer = None
self.finale = False
self._explodeDelay = None
def announceGenerate(self):
if self.spawnPointId == 99:
x, y, z, h = SafezoneInvasionGlobals.FirstSuitSpawnPoint
elif self.spawnPointId == 100:
x, y, z, h = SafezoneInvasionGlobals.FinaleSuitSpawnPoint
else:
x, y, z, h = SafezoneInvasionGlobals.SuitSpawnPoints[self.spawnPointId]
self.freezeLerp(x, y)
if self.invasion.state == 'Finale':
self.walkSpeed = ToontownGlobals.SuitWalkSpeed # The Boss should walk slower than other high-level cogs
else:
self.walkSpeed = (ToontownGlobals.SuitWalkSpeed *
SuitBattleGlobals.SuitSizes[self.dna.name] / 4.0)
def delete(self):
DistributedSuitBaseAI.delete(self)
self.demand('Off')
self.brain.stop()
try:
self.invasion.suitDied(self)
except Exception, e:
self.notify.debug('Exception: %s' % e)
if self._explodeDelay:
self._explodeDelay.remove()
'''
STANDARD COGS
The cogs have three main states: March, Attack, and Idle.
Standard Cogs find a toon to attack, then March towards them until the toon is gone or they have died.
'''
def start(self):
# Start the brain, if it hasn't been started already:
self.brain.start()
def idle(self):
self.b_setState('Idle')
def enterIdle(self):
# We do nothing. We wait for the invasion manager to shift into the
# 'Wave' state, and we all begin marching at once.
pass
def enterFlyDown(self):
# We set a delay to wait for the Cog to finish flying down, then switch
# states.
if self.invasion.state == 'Finale':
self.b_setInvasionFinale(True)
self._delay = taskMgr.doMethodLater((SuitTimings.fromSky + 1.0), self.__flyDownComplete,
self.uniqueName('fly-down-animation'))
def __flyDownComplete(self, task):
if self.invasion.state == 'Finale':
self.b_setState('FinalePhrases')
self.finaleMarchDelay = taskMgr.doMethodLater(15, self.startFinaleMarch, self.uniqueName('FinaleMarch-Later'))
return
self.b_setState('Idle')
if self.invasion.state != 'BeginWave':
self.start()
def exitFlyDown(self):
self._delay.remove()
def walkTo(self, x, y):
# Begin walking to a given point. It's OK to call this before the suit
# finishes reaching its old waypoint; if that happens, the AI will
# calculate the suit's current position and walk from there.
oldX, oldY = self.getCurrentPos()
self.b_setMarchLerp(oldX, oldY, x, y)
self.__startWalkTimer()
if self.state != 'March':
self.b_setState('March')
def __startWalkTimer(self):
self.__stopWalkTimer()
self.__walkTimer = taskMgr.doMethodLater(self._lerpDelay, self.__walkTimerOver,
self.uniqueName('walkTimer'))
def __stopWalkTimer(self):
if self.__walkTimer:
self.__walkTimer.remove()
self.__walkTimer = None
def __walkTimerOver(self, task):
if self.state != 'March':
self.notify.warning('Walk timer ran out, but not in March state!')
return
self.brain.suitFinishedWalking()
def enterMarch(self):
pass
def exitMarch(self):
x, y = self.getCurrentPos()
self.d_setStaticPoint(x, y, self._idealH)
self.freezeLerp(x, y)
self.__stopWalkTimer()
def attack(self, who):
attacks = ['clip-on-tie', 'redtape', 'newspaper', 'pink-slip', 'power-tie']
damage = round(float(self.getActualLevel())/2.0)
if damage <= 0:
damage = 1
self.sendUpdate('setAttackInfo', [who, choice(attacks), damage])
self.b_setState('Attack')
def enterAttack(self):
if self.brain.suit.dna.body in ['a', 'b']:
self._delay = taskMgr.doMethodLater(4.6, self.__attackDone,
self.uniqueName('attack'))
else:
self._delay = taskMgr.doMethodLater(3.3, self.__attackDone,
self.uniqueName('attack'))
def __attackDone(self, task):
self.brain.suitFinishedAttacking()
return task.done
def exitAttack(self):
self._delay.remove()
def getAttackInfo(self):
return (0, '', 0) # This is only set dynamically.
def takeDamage(self, hp, bypassFinale = False):
if self.state == 'FlyDown':
return # We can't/shouldn't take damage in this state.
hp = min(hp, self.currHP) # Don't take more damage than we have...
self.b_setHP(self.currHP - hp)
if self.finale:
if not bypassFinale:
self.b_setHP(self.currHP + hp) # We dont want the big guy to die
return
if self.state != 'Stunned':
self.b_setState('Stunned')
def enterStunned(self):
self.brain.stop()
self._delay = taskMgr.doMethodLater(SuitTimings.suitStun, self.__unstun,
self.uniqueName('stunned'))
def __unstun(self, task):
if self.finale:
if self.currHP < 1:
self.b_setState('Explode') # Done with him
return
if self.currHP < 1:
# We're dead!
self.b_setState('Explode')
else:
# Not dead, we can go back to thinking:
self.demand('Idle')
self.brain.start()
return task.done
def exitStunned(self):
self._delay.remove()
def enterExplode(self):
self._explodeDelay = taskMgr.doMethodLater(SuitTimings.suitDeath, self.__exploded,
self.uniqueName('explode'))
def __exploded(self, task):
self.requestDelete()
def exitExplode(self):
pass
def getCurrentPos(self):
return self.getPosAt(globalClock.getRealTime() - self.lastMarchTime)
def setSpawnPoint(self, pointId):
self.spawnPointId = pointId
def getSpawnPoint(self):
return self.spawnPointId
def setMarchLerp(self, x1, y1, x2, y2):
self.setLerpPoints(x1, y1, x2, y2)
self.lastMarchTime = globalClock.getRealTime()
def d_setMarchLerp(self, x1, y1, x2, y2):
self.sendUpdate('setMarchLerp', [x1, y1, x2, y2,
globalClockDelta.getRealNetworkTime()])
def b_setMarchLerp(self, x1, y1, x2, y2):
self.setMarchLerp(x1, y1, x2, y2)
self.d_setMarchLerp(x1, y1, x2, y2)
def d_setStaticPoint(self, x, y, h):
self.sendUpdate('setStaticPoint', [x, y, h])
def d_sayFaceoffTaunt(self, custom = False, phrase = ""):
self.sendUpdate('sayFaceoffTaunt', [custom, phrase])
def d_makeSkelecog(self):
self.sendUpdate('makeSkelecog', [])
def setState(self, state):
self.demand(state)
def d_setState(self, state):
self.stateTime = globalClockDelta.getRealNetworkTime()
self.sendUpdate('setState', [state, self.stateTime])
def b_setState(self, state):
self.setState(state)
self.d_setState(state)
def getState(self):
return (self.state, self.stateTime)
'''
MOVER AND SHAKERS
Mover and Shakers are the same as standard cogs, though they stomp the ground as they march.
Toons within a certain radius of them will lose laff, forcing them to hit them from a distance.
'''
def takeShakerDamage(self, damage):
avId = self.air.getAvatarIdFromSender()
toon = self.air.doId2do.get(avId)
if not toon:
self.air.writeServerEvent('suspicious', avId=avId, issue='DistributedInvasionSuitAI.takeShakerDamage Nonexistent Toon tried to get hit!')
return
if toon.getHp() > 0:
toon.takeDamage(damage)
'''
THE INVASION FINALE
To end the invasion, we send in the Boss of it. He brainstorms ideas and occasionally loses his temper.
The appears to take damage but actually takes none, as his death is a scripted sequence.
'''
def b_setInvasionFinale(self, finale):
self.setInvasionFinale(finale)
self.d_setInvasionFinale(finale)
def setInvasionFinale(self, finale):
self.finale = finale
def d_setInvasionFinale(self, finale):
self.sendUpdate('setInvasionFinale', [finale])
def getInvasionFinale(self):
return self.finale
def startFinaleMarch(self, task):
self.finaleMarch = taskMgr.add(self.enterFinaleMarch, self.uniqueName('FinaleMarch'))
self.finaleDestinationPoint = 0
self.finaleX, self.finaleY = SafezoneInvasionGlobals.FinaleSuitDestinations[0]
self.finaleNextX, self.finaleNextY = SafezoneInvasionGlobals.FinaleSuitDestinations[1]
self.brain.navigateTo(self.finaleX, self.finaleY)
# Pick a random time to change colors, just to add variation between recorded videos
# and a bit more realism.
damage1Delay = randint(25, 35)
damage2Delay = randint(60, 65)
self.takeDamage1 = taskMgr.doMethodLater(damage1Delay, self.takeDamage, self.uniqueName('YellowMeter'), extraArgs=[36, True])
self.takeDamage2 = taskMgr.doMethodLater(damage2Delay, self.takeDamage, self.uniqueName('OrangeMeter'), extraArgs=[36, True])
self.finaleAttack = taskMgr.doMethodLater(3, self.doFinaleAttack, self.uniqueName('FinaleAttack-Later'))
return task.done
def enterFinaleMarch(self, task):
oldX, oldY = self.getCurrentPos()
finalX, finalY, = SafezoneInvasionGlobals.FinaleSuitDestinations[4]
if (self.finaleX - 1.0 <= oldX <= self.finaleX + 1.0) and (self.finaleY - 1.0 <= oldY <= self.finaleY + 1.0): # Check if it hit its destination
if (self.finaleX - 1.0 <= finalX <= self.finaleX + 1.0) and (self.finaleY - 1.0 <= finalY <= self.finaleY + 1.0): # Check if it hit the final destination
self.invasion.election.b_setState('InvasionEnd')
self.idle()
self.finaleAttack.remove()
taskMgr.remove('FinaleAttack-Later')
return task.done
else:
self.finaleDestinationPoint = self.finaleDestinationPoint + 1
self.finaleX, self.finaleY = SafezoneInvasionGlobals.FinaleSuitDestinations[self.finaleDestinationPoint]
self.brain.navigateTo(self.finaleX, self.finaleY)
return task.cont
return task.cont
def enterFinalePhrases(self):
pass
def doFinaleAttack(self, task):
if random() < 0.5:
self.b_setState('FinaleBrainstormAttack')
else:
self.b_setState('FinaleStompAttack')
def enterFinaleBrainstormAttack(self):
self.brain.stop()
self._stormDelay = taskMgr.doMethodLater(10.0, self.__finaleAttackDone, self.uniqueName('BrainstormAttackExit'))
def exitFinaleBrainstormAttack(self):
self._stormDelay.remove()
def enterFinaleStompAttack(self):
self.brain.stop()
self._stompDelay = taskMgr.doMethodLater(5.5, self.__finaleAttackDone, self.uniqueName('StompAttackExit'))
def exitFinaleStompAttack(self):
self._stompDelay.remove()
def __finaleAttackDone(self, task):
self.brain.navigateTo(self.finaleX, self.finaleY)
self.finaleAttack = taskMgr.doMethodLater(SafezoneInvasionGlobals.FinaleSuitAttackDelay, self.doFinaleAttack, self.uniqueName('FinaleAttack-Later'))
return task.done