409 lines
17 KiB
Python
409 lines
17 KiB
Python
import random
|
|
from pandac.PandaModules import Point3
|
|
from direct.fsm import ClassicFSM
|
|
from direct.fsm import State
|
|
from direct.distributed.ClockDelta import globalClockDelta
|
|
from direct.task import Task
|
|
from toontown.minigame import DistributedMinigameAI
|
|
from toontown.minigame import MinigameGlobals
|
|
from toontown.minigame import CogThiefGameGlobals
|
|
CTGG = CogThiefGameGlobals
|
|
|
|
class DistributedCogThiefGameAI(DistributedMinigameAI.DistributedMinigameAI):
|
|
notify = directNotify.newCategory('DistributedCogThiefGameAI')
|
|
ExplodeWaitTime = 6.0 + CTGG.LyingDownDuration
|
|
|
|
def __init__(self, air, minigameId):
|
|
try:
|
|
self.DistributedCogThiefGameAI_initialized
|
|
except:
|
|
self.DistributedCogThiefGameAI_initialized = 1
|
|
DistributedMinigameAI.DistributedMinigameAI.__init__(self, air, minigameId)
|
|
self.gameFSM = ClassicFSM.ClassicFSM('DistributedCogThiefGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive')
|
|
self.addChildGameFSM(self.gameFSM)
|
|
self.cogInfo = {}
|
|
self.barrelInfo = {}
|
|
self.initBarrelInfo()
|
|
|
|
def generate(self):
|
|
self.notify.debug('generate')
|
|
DistributedMinigameAI.DistributedMinigameAI.generate(self)
|
|
|
|
def delete(self):
|
|
self.notify.debug('delete')
|
|
del self.gameFSM
|
|
self.removeAllTasks()
|
|
DistributedMinigameAI.DistributedMinigameAI.delete(self)
|
|
|
|
def setGameReady(self):
|
|
self.notify.debug('setGameReady')
|
|
self.initCogInfo()
|
|
DistributedMinigameAI.DistributedMinigameAI.setGameReady(self)
|
|
|
|
def setGameStart(self, timestamp):
|
|
self.notify.debug('setGameStart')
|
|
DistributedMinigameAI.DistributedMinigameAI.setGameStart(self, timestamp)
|
|
self.gameFSM.request('play')
|
|
|
|
def setGameAbort(self):
|
|
self.notify.debug('setGameAbort')
|
|
if self.gameFSM.getCurrentState():
|
|
self.gameFSM.request('cleanup')
|
|
DistributedMinigameAI.DistributedMinigameAI.setGameAbort(self)
|
|
|
|
def gameOver(self):
|
|
self.notify.debug('gameOver')
|
|
score = int(CTGG.calcScore(self.getCurrentGameTime()))
|
|
for avId in self.avIdList:
|
|
self.scoreDict[avId] = score
|
|
|
|
if self.getNumBarrelsStolen() == 0:
|
|
for avId in self.avIdList:
|
|
self.scoreDict[avId] += CTGG.PerfectBonus[len(self.avIdList) - 1]
|
|
self.logPerfectGame(avId)
|
|
|
|
self.gameFSM.request('cleanup')
|
|
DistributedMinigameAI.DistributedMinigameAI.gameOver(self)
|
|
|
|
def enterInactive(self):
|
|
self.notify.debug('enterInactive')
|
|
|
|
def exitInactive(self):
|
|
pass
|
|
|
|
def enterPlay(self):
|
|
self.notify.debug('enterPlay')
|
|
self.startSuitGoals()
|
|
if not config.GetBool('cog-thief-endless', 0):
|
|
taskMgr.doMethodLater(CTGG.GameTime, self.timerExpired, self.taskName('gameTimer'))
|
|
|
|
def exitPlay(self):
|
|
pass
|
|
|
|
def enterCleanup(self):
|
|
self.notify.debug('enterCleanup')
|
|
taskMgr.remove(self.taskName('gameTimer'))
|
|
self.gameFSM.request('inactive')
|
|
|
|
def exitCleanup(self):
|
|
pass
|
|
|
|
def initCogInfo(self):
|
|
for cogIndex in xrange(self.getNumCogs()):
|
|
self.cogInfo[cogIndex] = {'pos': Point3(CogThiefGameGlobals.CogStartingPositions[cogIndex]),
|
|
'goal': CTGG.NoGoal,
|
|
'goalId': CTGG.InvalidGoalId,
|
|
'barrel': CTGG.NoBarrelCarried}
|
|
|
|
def initBarrelInfo(self):
|
|
for barrelIndex in xrange(CogThiefGameGlobals.NumBarrels):
|
|
self.barrelInfo[barrelIndex] = {'pos': Point3(CogThiefGameGlobals.BarrelStartingPositions[barrelIndex]),
|
|
'carriedBy': CTGG.BarrelOnGround,
|
|
'stolen': False}
|
|
|
|
def makeCogCarryBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex):
|
|
if cogIndex in self.cogInfo and barrelIndex in self.barrelInfo:
|
|
self.barrelInfo[barrelIndex]['carriedBy'] = cogIndex
|
|
self.cogInfo[cogIndex]['barrel'] = barrelIndex
|
|
else:
|
|
self.notify.warning('makeCogCarryBarrel invalid cogIndex=%s barrelIndex=%s' % (cogIndex, barrelIndex))
|
|
|
|
def b_makeCogCarryBarrel(self, clientStamp, cogIndex, barrelIndex):
|
|
timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
|
|
self.notify.debug('b_makeCogCarryBarrel timeStamp=%s clientStamp=%s cog=%s barrel=%s' % (timestamp,
|
|
clientStamp,
|
|
cogIndex,
|
|
barrelIndex))
|
|
self.makeCogCarryBarrel(timestamp, clientStamp, cogIndex, barrelIndex)
|
|
self.d_makeCogCarryBarrel(timestamp, clientStamp, cogIndex, barrelIndex)
|
|
|
|
def d_makeCogCarryBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex):
|
|
pos = self.cogInfo[cogIndex]['pos']
|
|
gameTime = self.getCurrentGameTime()
|
|
self.sendUpdate('makeCogCarryBarrel', [timestamp,
|
|
clientStamp,
|
|
cogIndex,
|
|
barrelIndex,
|
|
pos[0],
|
|
pos[1],
|
|
pos[2]])
|
|
|
|
def makeCogDropBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex, barrelPos):
|
|
if cogIndex in self.cogInfo and barrelIndex in self.barrelInfo:
|
|
self.barrelInfo[barrelIndex]['carriedBy'] = CTGG.BarrelOnGround
|
|
self.cogInfo[cogIndex]['barrel'] = CTGG.NoBarrelCarried
|
|
else:
|
|
self.notify.warning('makeCogDropBarrel invalid cogIndex=%s barrelIndex=%s' % (cogIndex, barrelIndex))
|
|
|
|
def b_makeCogDropBarrel(self, clientStamp, cogIndex, barrelIndex, barrelPos):
|
|
if self.barrelInfo[barrelIndex]['carriedBy'] != cogIndex:
|
|
self.notify.error("self.barrelInfo[%s]['carriedBy'] != %s" % (barrelIndex, cogIndex))
|
|
timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
|
|
self.makeCogDropBarrel(timestamp, clientStamp, cogIndex, barrelIndex, barrelPos)
|
|
self.d_makeCogDropBarrel(timestamp, clientStamp, cogIndex, barrelIndex, barrelPos)
|
|
|
|
def d_makeCogDropBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex, barrelPos):
|
|
pos = barrelPos
|
|
gameTime = self.getCurrentGameTime()
|
|
self.sendUpdate('makeCogDropBarrel', [timestamp,
|
|
clientStamp,
|
|
cogIndex,
|
|
barrelIndex,
|
|
pos[0],
|
|
pos[1],
|
|
pos[2]])
|
|
|
|
def isCogCarryingABarrel(self, cogIndex):
|
|
result = self.cogInfo[cogIndex]['barrel'] > CTGG.NoBarrelCarried
|
|
return result
|
|
|
|
def isCogCarryingThisBarrel(self, cogIndex, barrelIndex):
|
|
result = self.cogInfo[cogIndex]['barrel'] == barrelIndex
|
|
return result
|
|
|
|
def startSuitGoals(self):
|
|
delayTimes = []
|
|
for cogIndex in xrange(self.getNumCogs()):
|
|
delayTimes.append(cogIndex * 1.0)
|
|
|
|
random.shuffle(delayTimes)
|
|
for cogIndex in xrange(self.getNumCogs()):
|
|
self.doMethodLater(delayTimes[cogIndex], self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % cogIndex), extraArgs=[cogIndex])
|
|
|
|
def chaseToon(self, suitNum, avId):
|
|
goalType = CTGG.ToonGoal
|
|
goalId = avId
|
|
self.cogInfo[suitNum]['goal'] = goalType
|
|
self.cogInfo[suitNum]['goalId'] = goalId
|
|
pos = self.cogInfo[suitNum]['pos']
|
|
timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
|
|
self.notify.debug('chaseToon time=%s suitNum=%s, avId=%s' % (timestamp, suitNum, avId))
|
|
gameTime = self.getCurrentGameTime()
|
|
self.sendUpdate('updateSuitGoal', [timestamp,
|
|
timestamp,
|
|
suitNum,
|
|
goalType,
|
|
goalId,
|
|
pos[0],
|
|
pos[1],
|
|
pos[2]])
|
|
|
|
def hitBySuit(self, avId, timestamp, suitNum, x, y, z):
|
|
if suitNum >= self.getNumCogs():
|
|
self.notify.warning('hitBySuit, possible hacker avId=%s' % avId)
|
|
return
|
|
barrelIndex = self.cogInfo[suitNum]['barrel']
|
|
if barrelIndex >= 0:
|
|
barrelPos = Point3(x, y, z)
|
|
self.b_makeCogDropBarrel(timestamp, suitNum, barrelIndex, barrelPos)
|
|
startPos = CTGG.CogStartingPositions[suitNum]
|
|
self.cogInfo[suitNum]['pos'] = startPos
|
|
self.cogInfo[suitNum]['goal'] = CTGG.NoGoal
|
|
self.cogInfo[suitNum]['goalId'] = CTGG.InvalidGoalId
|
|
self.sendSuitSync(timestamp, suitNum)
|
|
self.doMethodLater(self.ExplodeWaitTime, self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % suitNum), extraArgs=[suitNum])
|
|
|
|
def sendSuitSync(self, clientstamp, suitNum):
|
|
pos = self.cogInfo[suitNum]['pos']
|
|
timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
|
|
goalType = self.cogInfo[suitNum]['goal']
|
|
goalId = self.cogInfo[suitNum]['goalId']
|
|
gameTime = self.getCurrentGameTime()
|
|
self.sendUpdate('updateSuitGoal', [timestamp,
|
|
clientstamp,
|
|
suitNum,
|
|
goalType,
|
|
goalId,
|
|
pos[0],
|
|
pos[1],
|
|
pos[2]])
|
|
|
|
def chooseSuitGoal(self, suitNum):
|
|
barrelIndex = self.findClosestUnassignedBarrel(suitNum)
|
|
if barrelIndex >= 0:
|
|
self.chaseBarrel(suitNum, barrelIndex)
|
|
else:
|
|
noOneChasing = self.avIdList[:]
|
|
for key in self.cogInfo:
|
|
if self.cogInfo[key]['goal'] == CTGG.ToonGoal:
|
|
toonId = self.cogInfo[key]['goalId']
|
|
if toonId in noOneChasing:
|
|
noOneChasing.remove(toonId)
|
|
|
|
chaseToonId = self.avIdList[0]
|
|
if noOneChasing:
|
|
chaseToonId = random.choice(noOneChasing)
|
|
else:
|
|
chaseToonId = random.choice(self.avIdList)
|
|
self.chaseToon(suitNum, chaseToonId)
|
|
|
|
def chaseBarrel(self, suitNum, barrelIndex):
|
|
goalType = CTGG.BarrelGoal
|
|
goalId = barrelIndex
|
|
self.cogInfo[suitNum]['goal'] = goalType
|
|
self.cogInfo[suitNum]['goalId'] = goalId
|
|
pos = self.cogInfo[suitNum]['pos']
|
|
timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
|
|
self.notify.debug('chaseBarrel time=%s suitNum=%s, barrelIndex=%s' % (timestamp, suitNum, barrelIndex))
|
|
gameTime = self.getCurrentGameTime()
|
|
self.sendUpdate('updateSuitGoal', [timestamp,
|
|
timestamp,
|
|
suitNum,
|
|
goalType,
|
|
goalId,
|
|
pos[0],
|
|
pos[1],
|
|
pos[2]])
|
|
|
|
def getCogCarryingBarrel(self, barrelIndex):
|
|
return self.barrelInfo[barrelIndex]['carriedBy']
|
|
|
|
def cogHitBarrel(self, clientStamp, cogIndex, barrelIndex, x, y, z):
|
|
if cogIndex >= self.getNumCogs():
|
|
self.notify.warning('cogHitBarrel, possible hacker cogIndex=%s' % cogIndex)
|
|
return
|
|
if barrelIndex >= CTGG.NumBarrels:
|
|
self.notify.warning('cogHitBarrel, possible hacker barrelIndex=%s' % barrelIndex)
|
|
return
|
|
if self.isCogCarryingABarrel(cogIndex):
|
|
self.notify.debug('cog is already carrying a barrel ignore')
|
|
return
|
|
if self.cogInfo[cogIndex]['goal'] == CTGG.NoGoal:
|
|
self.notify.debug('ignoring barrel hit as cog %d has no goal' % cogIndex)
|
|
return
|
|
if self.getCogCarryingBarrel(barrelIndex) == CTGG.BarrelOnGround:
|
|
pos = Point3(x, y, z)
|
|
returnPosIndex = self.chooseReturnPos(cogIndex, pos)
|
|
self.runAway(clientStamp, cogIndex, pos, barrelIndex, returnPosIndex)
|
|
|
|
def chooseReturnPos(self, cogIndex, cogPos):
|
|
shortestDistance = 10000
|
|
shortestReturnIndex = -1
|
|
for retIndex in xrange(len(CTGG.CogReturnPositions)):
|
|
retPos = CTGG.CogReturnPositions[retIndex]
|
|
distance = (cogPos - retPos).length()
|
|
if distance < shortestDistance:
|
|
shortestDistance = distance
|
|
shortestReturnIndex = retIndex
|
|
self.notify.debug('shortest distance=%s index=%s' % (shortestDistance, shortestReturnIndex))
|
|
|
|
self.notify.debug('chooseReturnpos returning %s' % shortestReturnIndex)
|
|
return shortestReturnIndex
|
|
|
|
def runAway(self, clientStamp, cogIndex, cogPos, barrelIndex, returnPosIndex):
|
|
self.cogInfo[cogIndex]['pos'] = cogPos
|
|
self.b_makeCogCarryBarrel(clientStamp, cogIndex, barrelIndex)
|
|
goalType = CTGG.RunAwayGoal
|
|
goalId = returnPosIndex
|
|
self.cogInfo[cogIndex]['goal'] = goalType
|
|
self.cogInfo[cogIndex]['goalId'] = 0
|
|
timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
|
|
gameTime = self.getCurrentGameTime()
|
|
self.sendUpdate('updateSuitGoal', [timestamp,
|
|
clientStamp,
|
|
cogIndex,
|
|
goalType,
|
|
goalId,
|
|
cogPos[0],
|
|
cogPos[1],
|
|
cogPos[2]])
|
|
|
|
def pieHitSuit(self, avId, timestamp, suitNum, x, y, z):
|
|
if suitNum >= self.getNumCogs():
|
|
self.notify.warning('hitBySuit, possible hacker avId=%s' % avId)
|
|
return
|
|
barrelIndex = self.cogInfo[suitNum]['barrel']
|
|
if barrelIndex >= 0:
|
|
barrelPos = Point3(x, y, z)
|
|
self.b_makeCogDropBarrel(timestamp, suitNum, barrelIndex, barrelPos)
|
|
startPos = CTGG.CogStartingPositions[suitNum]
|
|
self.cogInfo[suitNum]['pos'] = startPos
|
|
self.cogInfo[suitNum]['goal'] = CTGG.NoGoal
|
|
self.cogInfo[suitNum]['goalId'] = CTGG.InvalidGoalId
|
|
self.sendSuitSync(timestamp, suitNum)
|
|
self.doMethodLater(self.ExplodeWaitTime, self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % suitNum), extraArgs=[suitNum])
|
|
|
|
def findClosestUnassignedBarrel(self, suitNum):
|
|
possibleBarrels = []
|
|
for key in self.barrelInfo:
|
|
info = self.barrelInfo[key]
|
|
if info['carriedBy'] == CTGG.BarrelOnGround and not info['stolen']:
|
|
if not self.isCogGoingForBarrel(key):
|
|
possibleBarrels.append(key)
|
|
|
|
shortestDistance = 10000
|
|
shortestBarrelIndex = -1
|
|
cogPos = self.cogInfo[suitNum]['pos']
|
|
for possibleIndex in possibleBarrels:
|
|
barrelPos = self.barrelInfo[possibleIndex]['pos']
|
|
distance = (cogPos - barrelPos).length()
|
|
if distance < shortestDistance:
|
|
shortestDistance = distance
|
|
shortestBarrelIndex = possibleIndex
|
|
|
|
return shortestBarrelIndex
|
|
|
|
def isCogGoingForBarrel(self, barrelIndex):
|
|
result = False
|
|
for suitNum in self.cogInfo:
|
|
cogInfo = self.cogInfo[suitNum]
|
|
if cogInfo['goal'] == CTGG.BarrelGoal and cogInfo['goalId'] == barrelIndex:
|
|
result = True
|
|
break
|
|
|
|
return result
|
|
|
|
def markBarrelStolen(self, clientStamp, barrelIndex):
|
|
timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
|
|
self.sendUpdate('markBarrelStolen', [timestamp, clientStamp, barrelIndex])
|
|
self.barrelInfo[barrelIndex]['stolen'] = True
|
|
|
|
def getNumBarrelsStolen(self):
|
|
numStolen = 0
|
|
for barrel in self.barrelInfo.values():
|
|
if barrel['stolen']:
|
|
numStolen += 1
|
|
|
|
return numStolen
|
|
|
|
def cogAtReturnPos(self, clientstamp, cogIndex, barrelIndex):
|
|
if cogIndex not in self.cogInfo or barrelIndex not in self.barrelInfo:
|
|
avId = self.air.getAvatarIdFromSender()
|
|
self.air.writeServerEvent('suspicious cogAtReturnPos avId=%s, cogIndex=%s, barrelIndex=%s' % (avId, cogIndex, barrelIndex))
|
|
return
|
|
if self.cogInfo[cogIndex]['goal'] == CTGG.RunAwayGoal:
|
|
if self.isCogCarryingThisBarrel(cogIndex, barrelIndex):
|
|
self.markBarrelStolen(clientstamp, barrelIndex)
|
|
returnPosIndex = self.cogInfo[cogIndex]['goalId']
|
|
retPos = CTGG.CogReturnPositions[returnPosIndex]
|
|
self.b_makeCogDropBarrel(clientstamp, cogIndex, barrelIndex, retPos)
|
|
startPos = CTGG.CogStartingPositions[cogIndex]
|
|
self.cogInfo[cogIndex]['pos'] = startPos
|
|
self.cogInfo[cogIndex]['goal'] = CTGG.NoGoal
|
|
self.cogInfo[cogIndex]['goalId'] = CTGG.InvalidGoalId
|
|
self.doMethodLater(0.5, self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % cogIndex), extraArgs=[cogIndex])
|
|
self.checkForGameOver()
|
|
|
|
def checkForGameOver(self):
|
|
numStolen = 0
|
|
for key in self.barrelInfo:
|
|
if self.barrelInfo[key]['stolen']:
|
|
numStolen += 1
|
|
|
|
self.notify.debug('numStolen = %s' % numStolen)
|
|
if simbase.config.GetBool('cog-thief-check-barrels', 1):
|
|
if not simbase.config.GetBool('cog-thief-endless', 0):
|
|
if numStolen == len(self.barrelInfo):
|
|
self.gameOver()
|
|
|
|
def timerExpired(self, task):
|
|
self.notify.debug('timer expired')
|
|
self.gameOver()
|
|
return Task.done
|
|
|
|
def getNumCogs(self):
|
|
result = simbase.config.GetInt('cog-thief-num-cogs', 0)
|
|
if not result:
|
|
safezone = self.getSafezoneId()
|
|
result = CTGG.calculateCogs(self.numPlayers, safezone)
|
|
return result
|