349 lines
14 KiB
Python
349 lines
14 KiB
Python
|
import random
|
||
|
from direct.distributed.ClockDelta import globalClockDelta
|
||
|
from DistCogdoGameAI import DistCogdoGameAI
|
||
|
import CogdoFlyingGameGlobals as Globals
|
||
|
|
||
|
class DistCogdoFlyingGameAI(DistCogdoGameAI):
|
||
|
notify = directNotify.newCategory('DistCogdoFlyingGameAI')
|
||
|
EagleExitCooldownTaskName = 'CFG_EagleExitCooldownTask-%s'
|
||
|
InvulBuffRemoveTaskName = 'CFG_InvulBuffRemoveTask-%s'
|
||
|
AnnounceGameDoneTimerTaskName = 'CFG_AnnounceGameDoneTimerTask'
|
||
|
|
||
|
def __init__(self, air, id):
|
||
|
DistCogdoGameAI.__init__(self, air, id)
|
||
|
self.toonsOnEndPlatform = []
|
||
|
self.toonsInWinState = []
|
||
|
self.eagleId2targetIds = {}
|
||
|
self.pickups = []
|
||
|
self.memoCount = 0
|
||
|
self.toonId2buffList = {}
|
||
|
self.broadcastedGotoWinState = False
|
||
|
self.broadcastedGameFinished = False
|
||
|
self._gameState = False
|
||
|
if __debug__ and simbase.config.GetBool('schellgames-dev', True):
|
||
|
self.accept('onCodeReload', self._DistCogdoFlyingGameAI__sgOnCodeReload)
|
||
|
|
||
|
def getLegalEagleAttackRoundTime(self, fromCooldown = False):
|
||
|
time = 0.0
|
||
|
time += Globals.LegalEagle.LiftOffTime
|
||
|
time += Globals.LegalEagle.LockOnTime
|
||
|
time += Globals.LegalEagle.ChargeUpTime
|
||
|
time += Globals.LegalEagle.PreAttackTime
|
||
|
time += Globals.LegalEagle.PostAttackTime
|
||
|
time += Globals.LegalEagle.RetreatToSkyTime
|
||
|
time += Globals.LegalEagle.CooldownTime
|
||
|
if fromCooldown:
|
||
|
time += Globals.LegalEagle.ExtraPostCooldownTime
|
||
|
|
||
|
return time
|
||
|
|
||
|
def delete(self):
|
||
|
self.ignoreAll()
|
||
|
DistCogdoGameAI.delete(self)
|
||
|
|
||
|
def areAllToonsOnPlatform(self):
|
||
|
if self.getNumPlayers() == 0:
|
||
|
return False
|
||
|
|
||
|
return self.getNumPlayers() == len(self.toonsOnEndPlatform)
|
||
|
|
||
|
def areAllToonsInWinState(self):
|
||
|
if self.getNumPlayers() == 0:
|
||
|
return False
|
||
|
|
||
|
return self.getNumPlayers() == len(self.toonsInWinState)
|
||
|
|
||
|
def getCurrentNetworkTime(self):
|
||
|
return globalClockDelta.localToNetworkTime(globalClock.getRealTime())
|
||
|
|
||
|
def addBuff(self, toonId, pickupType):
|
||
|
if toonId in self.toonId2buffList:
|
||
|
buffList = self.toonId2buffList[toonId]
|
||
|
if pickupType in buffList:
|
||
|
return 0
|
||
|
else:
|
||
|
buffList.append(pickupType)
|
||
|
else:
|
||
|
self.toonId2buffList[toonId] = [
|
||
|
pickupType]
|
||
|
return 1
|
||
|
|
||
|
def removeBuff(self, toonId, pickupType):
|
||
|
if toonId in self.toonId2buffList:
|
||
|
buffList = self.toonId2buffList[toonId]
|
||
|
if pickupType in buffList:
|
||
|
buffList.remove(pickupType)
|
||
|
return 1
|
||
|
|
||
|
return 0
|
||
|
|
||
|
def isBuffOnToon(self, toonId, pickupType):
|
||
|
if toonId in self.toonId2buffList:
|
||
|
buffList = self.toonId2buffList[toonId]
|
||
|
if pickupType in buffList:
|
||
|
return 1
|
||
|
|
||
|
return 0
|
||
|
|
||
|
def updateGotoWinState(self):
|
||
|
if not (self.broadcastedGotoWinState) and self.areAllToonsOnPlatform():
|
||
|
self.forceDebuffAll()
|
||
|
self.broadcastedGotoWinState = True
|
||
|
self.d_broadcastDoAction(Globals.AI.GameActions.GotoWinState)
|
||
|
|
||
|
def updateGameFinished(self):
|
||
|
if not (self.broadcastedGameFinished) and self.areAllToonsInWinState():
|
||
|
self.broadcastedGameFinished = True
|
||
|
self._handleGameFinished()
|
||
|
|
||
|
def _isPlayingGame(self, senderId):
|
||
|
if self._gameState:
|
||
|
return True
|
||
|
elif self.fsm.getCurrentState() not in ('Finish',):
|
||
|
self._reportSuspiciousEvent(senderId, 'Client %s has made a game action request from illegal state: %s.' % (senderId, self.fsm.getCurrentState()))
|
||
|
|
||
|
return False
|
||
|
|
||
|
def requestAction(self, action, data):
|
||
|
senderId = self.air.getAvatarIdFromSender()
|
||
|
if not self._validateSenderId(senderId) or not self._isPlayingGame(senderId):
|
||
|
return False
|
||
|
|
||
|
av = simbase.air.doId2do.get(senderId)
|
||
|
if action == Globals.AI.GameActions.LandOnWinPlatform:
|
||
|
if senderId not in self.toonsOnEndPlatform:
|
||
|
self.toonsOnEndPlatform.append(senderId)
|
||
|
self.d_broadcastDoAction(action, senderId)
|
||
|
self.updateGotoWinState()
|
||
|
else:
|
||
|
self._reportSuspiciousEvent(senderId, 'Client %s has landed on the win platform twice.' % senderId)
|
||
|
elif action == Globals.AI.GameActions.WinStateFinished:
|
||
|
if senderId not in self.toonsInWinState:
|
||
|
self.toonsInWinState.append(senderId)
|
||
|
self.d_broadcastDoAction(action, senderId)
|
||
|
self.updateGameFinished()
|
||
|
else:
|
||
|
self._reportSuspiciousEvent(senderId, 'Client %s wants to exit from the win state multiple times.' % senderId)
|
||
|
elif action == Globals.AI.GameActions.HitWhirlwind:
|
||
|
if Globals.Dev.Invincibility != True:
|
||
|
self.damageToon(av, Globals.AI.SafezoneId2WhirlwindDamage)
|
||
|
|
||
|
elif action == Globals.AI.GameActions.HitLegalEagle:
|
||
|
if Globals.Dev.Invincibility != True:
|
||
|
self.damageToon(av, Globals.AI.SafezoneId2LegalEagleDamage)
|
||
|
|
||
|
elif action == Globals.AI.GameActions.HitMinion:
|
||
|
if Globals.Dev.Invincibility != True:
|
||
|
self.damageToon(av, Globals.AI.SafezoneId2MinionDamage)
|
||
|
|
||
|
elif action == Globals.AI.GameActions.RequestEnterEagleInterest:
|
||
|
self.b_toonSetAsEagleTarget(senderId, data, self.getCurrentNetworkTime())
|
||
|
elif action == Globals.AI.GameActions.RequestExitEagleInterest:
|
||
|
self.b_toonClearAsEagleTarget(senderId, data, self.getCurrentNetworkTime())
|
||
|
elif action == Globals.AI.GameActions.RanOutOfTimePenalty:
|
||
|
self.memoCount = 0
|
||
|
elif action == Globals.AI.GameActions.Died:
|
||
|
self.toonDied(av)
|
||
|
self.sendUpdate('toonDied', [
|
||
|
senderId,
|
||
|
self.getCurrentNetworkTime()])
|
||
|
elif action == Globals.AI.GameActions.Spawn:
|
||
|
self.sendUpdate('toonSpawn', [
|
||
|
senderId,
|
||
|
self.getCurrentNetworkTime()])
|
||
|
elif action == Globals.AI.GameActions.SetBlades:
|
||
|
if data in Globals.Gameplay.FuelStates:
|
||
|
self.sendUpdate('toonSetBlades', [
|
||
|
senderId,
|
||
|
data])
|
||
|
else:
|
||
|
self._reportSuspiciousEvent(senderId, "Client %s has requested a fuel state that doesn't exist:%s." % (senderId, data))
|
||
|
elif action == Globals.AI.GameActions.BladeLost:
|
||
|
self.sendUpdate('toonBladeLost', [
|
||
|
senderId])
|
||
|
else:
|
||
|
self._reportSuspiciousEvent(senderId, 'Client %s has made an illegal game action request: %s.' % (senderId, action))
|
||
|
|
||
|
def requestPickUp(self, pickupNum, pickupType):
|
||
|
senderId = self.air.getAvatarIdFromSender()
|
||
|
if not self._validateSenderId(senderId) or not self._isPlayingGame(senderId):
|
||
|
return False
|
||
|
|
||
|
if pickupType not in Globals.Level.GatherableTypes:
|
||
|
self._reportSuspiciousEvent(senderId, 'Client %s has requested an illegal pickup type: %s.' % (senderId, pickupType))
|
||
|
return False
|
||
|
|
||
|
if pickupType == Globals.Level.GatherableTypes.Memo:
|
||
|
if pickupNum not in self.pickups:
|
||
|
self.pickups.append(pickupNum)
|
||
|
self.memoCount += 1
|
||
|
self.d_broadcastPickup(senderId, pickupNum, self.getCurrentNetworkTime())
|
||
|
|
||
|
else:
|
||
|
self.d_broadcastPickup(senderId, pickupNum, self.getCurrentNetworkTime())
|
||
|
if pickupType == Globals.Level.GatherableTypes.LaffPowerup:
|
||
|
self.pickedUpLaffPowerup(senderId)
|
||
|
elif pickupType == Globals.Level.GatherableTypes.InvulPowerup:
|
||
|
self.pickedUpInvulPowerup(senderId)
|
||
|
|
||
|
def damageToon(self, av, damageDict):
|
||
|
safezoneId = self.getSafezoneId()
|
||
|
if safezoneId in damageDict:
|
||
|
av.takeDamage(damageDict[safezoneId], quietly = 0)
|
||
|
else:
|
||
|
self.notify.warning('Safezone Id: %s is not in flying game damage dictionary' % safezoneId)
|
||
|
|
||
|
def toonDied(self, av):
|
||
|
if Globals.Dev.Invincibility != True:
|
||
|
self.damageToon(av, Globals.AI.SafezoneId2DeathDamage)
|
||
|
|
||
|
def d_broadcastDoAction(self, action, data = 0):
|
||
|
self.sendUpdate('doAction', [
|
||
|
action,
|
||
|
data])
|
||
|
|
||
|
def b_broadcastDebuffPowerup(self, senderId, pickupType):
|
||
|
if self.removeBuff(senderId, pickupType):
|
||
|
self.d_broadcastDebuffPowerup(senderId, pickupType)
|
||
|
|
||
|
def d_broadcastDebuffPowerup(self, senderId, pickupType):
|
||
|
self.sendUpdate('debuffPowerup', [
|
||
|
senderId,
|
||
|
pickupType,
|
||
|
self.getCurrentNetworkTime()])
|
||
|
|
||
|
def pickedUpLaffPowerup(self, senderId):
|
||
|
av = simbase.air.doId2do.get(senderId)
|
||
|
if av:
|
||
|
if av.hp > 0 and av.hp < av.maxHp:
|
||
|
safezoneId = self.getSafezoneId()
|
||
|
if safezoneId in Globals.Gameplay.SafezoneId2LaffPickupHealAmount:
|
||
|
av.toonUp(Globals.Gameplay.SafezoneId2LaffPickupHealAmount[safezoneId])
|
||
|
else:
|
||
|
self.notify.warning('Safezone Id: %s is not in SafezoneId2LaffPickupHealAmount' % safezoneId)
|
||
|
|
||
|
def pickedUpInvulPowerup(self, senderId):
|
||
|
taskMgr.remove(self.uniqueName(DistCogdoFlyingGameAI.InvulBuffRemoveTaskName % senderId))
|
||
|
self.addBuff(senderId, Globals.Level.GatherableTypes.InvulPowerup)
|
||
|
taskMgr.doMethodLater(Globals.Gameplay.InvulBuffTime, self.b_broadcastDebuffPowerup, self.uniqueName(DistCogdoFlyingGameAI.InvulBuffRemoveTaskName % senderId), extraArgs = [
|
||
|
senderId,
|
||
|
Globals.Level.GatherableTypes.InvulPowerup])
|
||
|
|
||
|
def forceDebuffAll(self):
|
||
|
tasks = taskMgr.getTasksMatching(DistCogdoFlyingGameAI.InvulBuffRemoveTaskName % '*')
|
||
|
for task in tasks:
|
||
|
taskMgr.remove(task.getName())
|
||
|
|
||
|
for toonId in self.toonId2buffList:
|
||
|
buffList = self.toonId2buffList[toonId][:]
|
||
|
for buff in buffList:
|
||
|
self.b_broadcastDebuffPowerup(toonId, buff)
|
||
|
|
||
|
def d_broadcastPickup(self, senderId, pickupNum, networkTime):
|
||
|
self.sendUpdate('pickUp', [
|
||
|
senderId,
|
||
|
pickupNum,
|
||
|
networkTime])
|
||
|
|
||
|
def b_toonSetAsEagleTarget(self, toonId, eagleId, networkTime):
|
||
|
if eagleId not in self.eagleId2targetIds:
|
||
|
self.eagleId2targetIds[eagleId] = [
|
||
|
toonId]
|
||
|
if not taskMgr.hasTaskNamed(DistCogdoFlyingGameAI.EagleExitCooldownTaskName % eagleId):
|
||
|
taskMgr.doMethodLater(self.getLegalEagleAttackRoundTime(), self.d_broadcastEagleExitCooldown, self.uniqueName(DistCogdoFlyingGameAI.EagleExitCooldownTaskName % eagleId), extraArgs = [
|
||
|
eagleId])
|
||
|
|
||
|
self.d_broadcastToonSetAsEagleTarget(toonId, eagleId, networkTime)
|
||
|
else:
|
||
|
self.eagleId2targetIds[eagleId].append(toonId)
|
||
|
|
||
|
def b_toonClearAsEagleTarget(self, toonId, eagleId, networkTime):
|
||
|
if eagleId in self.eagleId2targetIds:
|
||
|
toonIds = self.eagleId2targetIds[eagleId]
|
||
|
toonIds.remove(toonId)
|
||
|
self.d_broadcastToonClearAsEagleTarget(toonId, eagleId, networkTime)
|
||
|
if len(toonIds) == 0:
|
||
|
del self.eagleId2targetIds[eagleId]
|
||
|
else:
|
||
|
index = random.randint(0, len(toonIds) - 1)
|
||
|
self.d_broadcastToonSetAsEagleTarget(toonIds[index], eagleId, networkTime)
|
||
|
|
||
|
def d_broadcastEagleExitCooldown(self, eagleId):
|
||
|
self.sendUpdate('eagleExitCooldown', [
|
||
|
eagleId,
|
||
|
self.getCurrentNetworkTime()])
|
||
|
if eagleId in self.eagleId2targetIds:
|
||
|
taskMgr.remove(self.uniqueName(DistCogdoFlyingGameAI.EagleExitCooldownTaskName % eagleId))
|
||
|
if eagleId in self.eagleId2targetIds:
|
||
|
taskMgr.doMethodLater(self.getLegalEagleAttackRoundTime(fromCooldown = True), self.d_broadcastEagleExitCooldown, self.uniqueName(DistCogdoFlyingGameAI.EagleExitCooldownTaskName % eagleId), extraArgs = [
|
||
|
eagleId])
|
||
|
|
||
|
def d_broadcastToonSetAsEagleTarget(self, toonId, eagleId, networkTime):
|
||
|
self.sendUpdate('toonSetAsEagleTarget', [
|
||
|
toonId,
|
||
|
eagleId,
|
||
|
networkTime])
|
||
|
|
||
|
def d_broadcastToonClearAsEagleTarget(self, toonId, eagleId, networkTime):
|
||
|
self.sendUpdate('toonClearAsEagleTarget', [
|
||
|
toonId,
|
||
|
eagleId,
|
||
|
networkTime])
|
||
|
|
||
|
def enterGame(self):
|
||
|
DistCogdoGameAI.enterGame(self)
|
||
|
self._gameState = True
|
||
|
|
||
|
def handleGameTimerExpired(self):
|
||
|
self._handleGameFinished()
|
||
|
|
||
|
def handleToonDisconnected(self, toonId):
|
||
|
DistCogdoGameAI.handleToonDisconnected(self, toonId)
|
||
|
if toonId in self.toonsOnEndPlatform:
|
||
|
self.toonsOnEndPlatform.remove(toonId)
|
||
|
|
||
|
if toonId in self.toonsInWinState:
|
||
|
self.toonsInWinState.remove(toonId)
|
||
|
|
||
|
self.updateGotoWinState()
|
||
|
self.updateGameFinished()
|
||
|
|
||
|
def handleToonWentSad(self, toonId):
|
||
|
DistCogdoGameAI.handleToonWentSad(self, toonId)
|
||
|
if toonId in self.toonsOnEndPlatform:
|
||
|
self.toonsOnEndPlatform.remove(toonId)
|
||
|
|
||
|
if toonId in self.toonsInWinState:
|
||
|
self.toonsInWinState.remove(toonId)
|
||
|
|
||
|
self.updateGotoWinState()
|
||
|
self.updateGameFinished()
|
||
|
|
||
|
def exitGame(self):
|
||
|
self._gameState = False
|
||
|
DistCogdoGameAI.exitGame(self)
|
||
|
if hasattr(self, '_gameTimerExpiredTask'):
|
||
|
taskMgr.remove(self._gameTimerExpiredTask)
|
||
|
del self._gameTimerExpiredTask
|
||
|
|
||
|
taskMgr.removeTasksMatching(DistCogdoFlyingGameAI.InvulBuffRemoveTaskName % '*')
|
||
|
taskMgr.removeTasksMatching(DistCogdoFlyingGameAI.EagleExitCooldownTaskName % '*')
|
||
|
for toonId in self.toonId2buffList:
|
||
|
del self.toonId2buffList[toonId][:]
|
||
|
|
||
|
self.toonId2buffList.clear()
|
||
|
self.eagleId2targetIds.clear()
|
||
|
del self.pickups[:]
|
||
|
del self.toonsOnEndPlatform[:]
|
||
|
del self.toonsInWinState[:]
|
||
|
|
||
|
def enterFinish(self):
|
||
|
DistCogdoGameAI.enterFinish(self)
|
||
|
self.ignoreAll()
|
||
|
self._announceGameDoneTask = taskMgr.doMethodLater(Globals.Gameplay.FinishDurationSeconds, self.announceGameDone, self.taskName(DistCogdoFlyingGameAI.AnnounceGameDoneTimerTaskName), [])
|
||
|
|
||
|
def exitFinish(self):
|
||
|
DistCogdoGameAI.exitFinish(self)
|
||
|
taskMgr.remove(self._announceGameDoneTask)
|
||
|
del self._announceGameDoneTask
|