diff --git a/etc/Configrc.prc b/etc/Configrc.prc
new file mode 100644
index 0000000..232af38
--- /dev/null
+++ b/etc/Configrc.prc
@@ -0,0 +1,108 @@
+cull-bin gui-popup 60 unsorted
+want-render2dp 1
+text-encoding utf8
+direct-wtext 0
+text-never-break-before ,.-:?!;
+ime-aware 1
+ime-hide 1
+textures-power-2 down
+paranoid-clock 1
+lock-to-one-cpu 1
+collect-tcp 1
+collect-tcp-interval 0.2
+server-version sv1.0.47.38
+server-version-suffix
+cull-bin shadow 15 fixed
+cull-bin ground 14 fixed
+model-path .
+sound-path .
+plugin-path .
+dc-file phase_3/etc/toon.dc
+dc-file phase_3/etc/otp.dc
+window-title Toontown
+verify-ssl 0
+ssl-cipher-list DEFAULT
+http-preapproved-server-certificate-filename ttown4.online.disney.com:46667 gameserver.txt
+chan-config-sanity-check #f
+require-window 0
+language english
+icon-filename toontown.ico
+dx-management 1
+tt-specific-login 1
+decompressor-buffer-size 32768
+extractor-buffer-size 32768
+patcher-buffer-size 512000
+downloader-timeout 15
+downloader-timeout-retries 4
+downloader-disk-write-frequency 4
+downloader-byte-rate 125000
+downloader-frequency 0.1
+http-connect-timeout 20
+http-timeout 30
+contents-xml-dl-attempts 2
+load-display pandadx9
+aux-display pandadx9
+aux-display pandadx8
+aux-display pandagl
+aux-display tinydisplay
+win-size 800 600
+fullscreen #t
+load-file-type toontown
+compress-channels #t
+display-lists 0
+early-random-seed 1
+ssl-cipher-list DEFAULT
+respect-prev-transform 1
+notify-level-collide warning
+notify-level-chan warning
+notify-level-gobj warning
+notify-level-loader warning
+notify-timestamp #t
+vfs-mount phase_3.mf . 0
+vfs-mount phase_3.5.mf . 0
+vfs-mount phase_4.mf . 0
+vfs-mount phase_5.mf . 0
+vfs-mount phase_5.5.mf . 0
+vfs-mount phase_6.mf . 0
+vfs-mount phase_7.mf . 0
+vfs-mount phase_8.mf . 0
+vfs-mount phase_9.mf . 0
+vfs-mount phase_10.mf . 0
+vfs-mount phase_11.mf . 0
+default-model-extension .bam
+decompressor-step-time 0.5
+extractor-step-time 0.5
+required-login playToken
+server-failover 80 443
+want-fog #t
+dx-use-rangebased-fog #t
+aspect-ratio 1.333333
+on-screen-debug-font ImpressBT.ttf
+temp-hpr-fix 1
+vertex-buffers 0
+dx-broken-max-index 1
+vfs-case-sensitive 0
+inactivity-timeout 180
+merge-lod-bundles 0
+early-event-sphere 1
+accept-clock-skew 1
+extra-ssl-handshake-time 20.0
+clock-mode limited
+clock-frame-rate 120
+prefer-parasite-buffer 0
+audio-library-name miles_audio
+cursor-filename toonmono.cur
+audio-loader mp3
+audio-loader midi
+audio-loader wav
+audio-software-midi #t
+audio-sfx-active #t
+audio-music-active #t
+audio-master-sfx-volume 1
+audio-master-music-volume 1
+server-type prod
+##!
+##!sig b1b9ca3e3322f3b65884483ec2b21300ec8f6f0bae1cfe70f8151b0d7c53fa2b
+##!sig fb1cc546e32345e09b8eba50624ab167c684e87d1ff4debb11da5322eb573f05
+##!sig c1dadb9a3589c86fbe0186afe1efa885223c4c64fd6ded3417f64ca17a5eaa12
+##!sig 3f317329bf49ce60607de5b408bc730c6ccd54973a5166ea08782fb9fe31a0c2
diff --git a/otp/__init__.py b/otp/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/ai/AIBase.py b/otp/ai/AIBase.py
new file mode 100644
index 0000000..8b471c0
--- /dev/null
+++ b/otp/ai/AIBase.py
@@ -0,0 +1,175 @@
+from pandac.PandaModules import *
+from direct.directnotify.DirectNotifyGlobal import *
+from direct.showbase.MessengerGlobal import *
+from direct.showbase.BulletinBoardGlobal import *
+from direct.task.TaskManagerGlobal import *
+from direct.showbase.JobManagerGlobal import *
+from direct.showbase.EventManagerGlobal import *
+from direct.showbase.PythonUtil import *
+from direct.showbase import PythonUtil
+from direct.interval.IntervalManager import ivalMgr
+from direct.task import Task
+from direct.showbase import EventManager
+from direct.showbase import ExceptionVarDump
+import math
+import sys
+import time
+import gc
+
+class AIBase:
+ notify = directNotify.newCategory('AIBase')
+
+ def __init__(self):
+ self.config = getConfigShowbase()
+ __builtins__['__dev__'] = self.config.GetBool('want-dev', 0)
+ logStackDump = (self.config.GetBool('log-stack-dump', (not __dev__)) or self.config.GetBool('ai-log-stack-dump', (not __dev__)))
+ uploadStackDump = self.config.GetBool('upload-stack-dump', 0)
+ if logStackDump or uploadStackDump:
+ ExceptionVarDump.install(logStackDump, uploadStackDump)
+ if self.config.GetBool('use-vfs', 1):
+ vfs = VirtualFileSystem.getGlobalPtr()
+ else:
+ vfs = None
+ self.wantTk = self.config.GetBool('want-tk', 0)
+ self.AISleep = self.config.GetFloat('ai-sleep', 0.04)
+ self.AIRunningNetYield = self.config.GetBool('ai-running-net-yield', 0)
+ self.AIForceSleep = self.config.GetBool('ai-force-sleep', 0)
+ self.eventMgr = eventMgr
+ self.messenger = messenger
+ self.bboard = bulletinBoard
+ self.taskMgr = taskMgr
+ Task.TaskManager.taskTimerVerbose = self.config.GetBool('task-timer-verbose', 0)
+ Task.TaskManager.extendedExceptions = self.config.GetBool('extended-exceptions', 0)
+ self.sfxManagerList = None
+ self.musicManager = None
+ self.jobMgr = jobMgr
+ self.hidden = NodePath('hidden')
+ self.graphicsEngine = GraphicsEngine()
+ globalClock = ClockObject.getGlobalClock()
+ self.trueClock = TrueClock.getGlobalPtr()
+ globalClock.setRealTime(self.trueClock.getShortTime())
+ globalClock.setAverageFrameRateInterval(30.0)
+ globalClock.tick()
+ taskMgr.globalClock = globalClock
+ __builtins__['ostream'] = Notify.out()
+ __builtins__['globalClock'] = globalClock
+ __builtins__['vfs'] = vfs
+ __builtins__['hidden'] = self.hidden
+ AIBase.notify.info('__dev__ == %s' % __dev__)
+ PythonUtil.recordFunctorCreationStacks()
+ __builtins__['wantTestObject'] = self.config.GetBool('want-test-object', 0)
+ self.wantStats = self.config.GetBool('want-pstats', 0)
+ Task.TaskManager.pStatsTasks = self.config.GetBool('pstats-tasks', 0)
+ taskMgr.resumeFunc = PStatClient.resumeAfterPause
+ defaultValue = 1
+ if __dev__:
+ defaultValue = 0
+ wantFakeTextures = self.config.GetBool('want-fake-textures-ai', defaultValue)
+ if wantFakeTextures:
+ loadPrcFileData('aibase', 'textures-header-only 1')
+ self.wantPets = self.config.GetBool('want-pets', 1)
+ if self.wantPets:
+ if game.name == 'toontown':
+ from toontown.pets import PetConstants
+ self.petMoodTimescale = self.config.GetFloat('pet-mood-timescale', 1.0)
+ self.petMoodDriftPeriod = self.config.GetFloat('pet-mood-drift-period', PetConstants.MoodDriftPeriod)
+ self.petThinkPeriod = self.config.GetFloat('pet-think-period', PetConstants.ThinkPeriod)
+ self.petMovePeriod = self.config.GetFloat('pet-move-period', PetConstants.MovePeriod)
+ self.petPosBroadcastPeriod = self.config.GetFloat('pet-pos-broadcast-period', PetConstants.PosBroadcastPeriod)
+ self.wantBingo = self.config.GetBool('want-fish-bingo', 1)
+ self.wantKarts = self.config.GetBool('wantKarts', 1)
+ self.newDBRequestGen = self.config.GetBool('new-database-request-generate', 1)
+ self.waitShardDelete = self.config.GetBool('wait-shard-delete', 1)
+ self.blinkTrolley = self.config.GetBool('blink-trolley', 0)
+ self.fakeDistrictPopulations = self.config.GetBool('fake-district-populations', 0)
+ self.wantSwitchboard = self.config.GetBool('want-switchboard', 0)
+ self.wantSwitchboardHacks = self.config.GetBool('want-switchboard-hacks', 0)
+ self.GEMdemoWhisperRecipientDoid = self.config.GetBool('gem-demo-whisper-recipient-doid', 0)
+ self.sqlAvailable = self.config.GetBool('sql-available', 1)
+ self.createStats()
+ self.restart()
+ return
+
+ def setupCpuAffinities(self, minChannel):
+ if game.name == 'uberDog':
+ affinityMask = self.config.GetInt('uberdog-cpu-affinity-mask', -1)
+ else:
+ affinityMask = self.config.GetInt('ai-cpu-affinity-mask', -1)
+ if affinityMask != -1:
+ TrueClock.getGlobalPtr().setCpuAffinity(affinityMask)
+ else:
+ autoAffinity = self.config.GetBool('auto-single-cpu-affinity', 0)
+ if game.name == 'uberDog':
+ affinity = self.config.GetInt('uberdog-cpu-affinity', -1)
+ if autoAffinity and affinity == -1:
+ affinity = 2
+ else:
+ affinity = self.config.GetInt('ai-cpu-affinity', -1)
+ if autoAffinity and affinity == -1:
+ affinity = 1
+ if affinity != -1:
+ TrueClock.getGlobalPtr().setCpuAffinity(1 << affinity)
+ elif autoAffinity:
+ if game.name == 'uberDog':
+ channelSet = int(minChannel / 1000000)
+ channelSet -= 240
+ affinity = channelSet + 3
+ TrueClock.getGlobalPtr().setCpuAffinity(1 << affinity % 4)
+
+ def taskManagerDoYield(self, frameStartTime, nextScheuledTaksTime):
+ minFinTime = frameStartTime + self.MaxEpockSpeed
+ if nextScheuledTaksTime > 0 and nextScheuledTaksTime < minFinTime:
+ minFinTime = nextScheuledTaksTime
+ delta = minFinTime - globalClock.getRealTime()
+ while delta > 0.002:
+ time.sleep(delta)
+ delta = minFinTime - globalClock.getRealTime()
+
+ def createStats(self, hostname = None, port = None):
+ if not self.wantStats:
+ return False
+ if PStatClient.isConnected():
+ PStatClient.disconnect()
+ if hostname is None:
+ hostname = ''
+ if port is None:
+ port = -1
+ PStatClient.connect(hostname, port)
+ return PStatClient.isConnected()
+
+ def __sleepCycleTask(self, task):
+ time.sleep(self.AISleep)
+ return Task.cont
+
+ def __resetPrevTransform(self, state):
+ PandaNode.resetAllPrevTransform()
+ return Task.cont
+
+ def __ivalLoop(self, state):
+ ivalMgr.step()
+ return Task.cont
+
+ def __igLoop(self, state):
+ self.graphicsEngine.renderFrame()
+ return Task.cont
+
+ def shutdown(self):
+ self.taskMgr.remove('ivalLoop')
+ self.taskMgr.remove('igLoop')
+ self.taskMgr.remove('aiSleep')
+ self.eventMgr.shutdown()
+
+ def restart(self):
+ self.shutdown()
+ self.taskMgr.add(self.__resetPrevTransform, 'resetPrevTransform', priority=-51)
+ self.taskMgr.add(self.__ivalLoop, 'ivalLoop', priority=20)
+ self.taskMgr.add(self.__igLoop, 'igLoop', priority=50)
+ if self.AISleep >= 0 and (not self.AIRunningNetYield or self.AIForceSleep):
+ self.taskMgr.add(self.__sleepCycleTask, 'aiSleep', priority=55)
+ self.eventMgr.restart()
+
+ def getRepository(self):
+ return self.air
+
+ def run(self):
+ self.taskMgr.run()
diff --git a/otp/ai/AIBaseGlobal.py b/otp/ai/AIBaseGlobal.py
new file mode 100644
index 0000000..3f8185a
--- /dev/null
+++ b/otp/ai/AIBaseGlobal.py
@@ -0,0 +1,26 @@
+from AIBase import *
+__builtins__['simbase'] = AIBase()
+__builtins__['ostream'] = Notify.out()
+__builtins__['run'] = simbase.run
+__builtins__['taskMgr'] = simbase.taskMgr
+__builtins__['jobMgr'] = simbase.jobMgr
+__builtins__['eventMgr'] = simbase.eventMgr
+__builtins__['messenger'] = simbase.messenger
+__builtins__['bboard'] = simbase.bboard
+__builtins__['config'] = simbase.config
+__builtins__['directNotify'] = directNotify
+from direct.showbase import Loader
+simbase.loader = Loader.Loader(simbase)
+__builtins__['loader'] = simbase.loader
+directNotify.setDconfigLevels()
+
+def inspect(anObject):
+ from direct.tkpanels import Inspector
+ Inspector.inspect(anObject)
+
+
+__builtins__['inspect'] = inspect
+if not __debug__ and __dev__:
+ notify = directNotify.newCategory('ShowBaseGlobal')
+ notify.error("You must set 'want-dev' to false in non-debug mode.")
+taskMgr.finalInit()
diff --git a/otp/ai/AIInterestHandles.py b/otp/ai/AIInterestHandles.py
new file mode 100644
index 0000000..f8f88a8
--- /dev/null
+++ b/otp/ai/AIInterestHandles.py
@@ -0,0 +1,8 @@
+PIRATES_CARDGAME = 1
+PIRATES_CREW = 2
+PIRATES_GUILD = 3
+PIRATES_FRIENDS = 4
+PIRATES_BAND = 5
+PIRATES_PVP_RESPAWN = 6
+PIRATES_TREASUREMAP = 7
+PIRATES_SHIPPVP = 8
diff --git a/otp/ai/AIMsgTypes.py b/otp/ai/AIMsgTypes.py
new file mode 100644
index 0000000..c656e80
--- /dev/null
+++ b/otp/ai/AIMsgTypes.py
@@ -0,0 +1,83 @@
+from otp.distributed.OtpDoGlobals import *
+from direct.showbase.PythonUtil import invertDictLossless
+OTP_SERVER_ROOT_DO_ID = 4007
+CHANNEL_CLIENT_BROADCAST = 4014
+BAD_CHANNEL_ID = 0
+BAD_ZONE_ID = 0
+BAD_DO_ID = 0
+CONTROL_MESSAGE = 4001
+CONTROL_SET_CHANNEL = 2001
+CONTROL_REMOVE_CHANNEL = 2002
+CONTROL_SET_CON_NAME = 2004
+CONTROL_SET_CON_URL = 2005
+CONTROL_ADD_RANGE = 2008
+CONTROL_REMOVE_RANGE = 2009
+CONTROL_ADD_POST_REMOVE = 2010
+CONTROL_CLEAR_POST_REMOVE = 2011
+AIMsgName2Id = {'STATESERVER_OBJECT_GENERATE_WITH_REQUIRED': 2001,
+ 'STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER': 2003,
+ 'STATESERVER_OBJECT_UPDATE_FIELD': 2004,
+ 'STATESERVER_OBJECT_UPDATE_FIELD_MULTIPLE': 2005,
+ 'STATESERVER_OBJECT_DELETE_RAM': 2007,
+ 'STATESERVER_OBJECT_SET_ZONE': 2008,
+ 'STATESERVER_OBJECT_CHANGE_ZONE': 2009,
+ 'STATESERVER_OBJECT_NOTFOUND': 2015,
+ 'STATESERVER_QUERY_OBJECT_ALL': 2020,
+ 'STATESERVER_QUERY_ZONE_OBJECT_ALL': 2021,
+ 'STATESERVER_OBJECT_LOCATE': 2022,
+ 'STATESERVER_OBJECT_LOCATE_RESP': 2023,
+ 'STATESERVER_OBJECT_QUERY_FIELD': 2024,
+ 'STATESERVER_QUERY_OBJECT_ALL_RESP': 2030,
+ 'STATESERVER_SHARD_REST': 2061,
+ 'STATESERVER_ADD_AI_RECV': 2045,
+ 'STATESERVER_QUERY_ZONE_OBJECT_ALL_DONE': 2046,
+ 'STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT': 2050,
+ 'STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT': 2051,
+ 'STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT_RESP': 2052,
+ 'STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT_RESP': 2053,
+ 'STATESERVER_OBJECT_DELETE_DISK': 2060,
+ 'STATESERVER_OBJECT_QUERY_FIELD_RESP': 2062,
+ 'STATESERVER_OBJECT_ENTERZONE_WITH_REQUIRED_OTHER': 2066,
+ 'STATESERVER_OBJECT_ENTER_AI_RECV': 2067,
+ 'STATESERVER_OBJECT_LEAVING_AI_INTEREST': 2033,
+ 'STATESERVER_OBJECT_ENTER_OWNER_RECV': 2068,
+ 'STATESERVER_OBJECT_CHANGE_OWNER_RECV': 2069,
+ 'STATESERVER_OBJECT_SET_OWNER_RECV': 2070,
+ 'STATESERVER_OBJECT_QUERY_FIELDS': 2080,
+ 'STATESERVER_OBJECT_QUERY_FIELDS_RESP': 2081,
+ 'STATESERVER_OBJECT_QUERY_FIELDS_STRING': 2082,
+ 'STATESERVER_OBJECT_QUERY_MANAGING_AI': 2083,
+ 'STATESERVER_BOUNCE_MESSAGE': 2086,
+ 'STATESERVER_QUERY_OBJECT_CHILDREN_LOCAL': 2087,
+ 'STATESERVER_QUERY_OBJECT_CHILDREN_LOCAL_DONE': 2089,
+ 'STATESERVER_QUERY_OBJECT_CHILDREN_RESP': 2087,
+ 'ACCOUNT_AVATAR_USAGE': 3005,
+ 'ACCOUNT_ACCOUNT_USAGE': 3006,
+ 'CLIENT_AGENT_OPEN_CHANNEL': 3104,
+ 'CLIENT_AGENT_CLOSE_CHANNEL': 3105,
+ 'CLIENT_AGENT_SET_INTEREST': 3106,
+ 'CLIENT_AGENT_REMOVE_INTEREST': 3107,
+ 'CHANNEL_PUPPET_ACTION': 4004,
+ 'DBSERVER_MAKE_FRIENDS': 1017,
+ 'DBSERVER_MAKE_FRIENDS_RESP': 1031,
+ 'DBSERVER_REQUEST_SECRET': 1025,
+ 'DBSERVER_REQUEST_SECRET_RESP': 1026,
+ 'DBSERVER_SUBMIT_SECRET': 1027,
+ 'DBSERVER_SUBMIT_SECRET_RESP': 1028,
+ 'DBSERVER_CREATE_STORED_OBJECT': 1003,
+ 'DBSERVER_CREATE_STORED_OBJECT_RESP': 1004,
+ 'DBSERVER_DELETE_STORED_OBJECT': 1008,
+ 'DBSERVER_GET_STORED_VALUES': 1012,
+ 'DBSERVER_GET_STORED_VALUES_RESP': 1013,
+ 'DBSERVER_SET_STORED_VALUES': 1014,
+ 'SERVER_PING': 5002}
+AIMsgId2Names = invertDictLossless(AIMsgName2Id)
+if not isClient():
+ print 'EXECWARNING AIMsgTypes: %s' % AIMsgName2Id
+ printStack()
+for name, value in AIMsgName2Id.items():
+ exec '%s = %s' % (name, value)
+
+del name
+del value
+DBSERVER_ID = 4003
diff --git a/otp/ai/AIZoneData.py b/otp/ai/AIZoneData.py
new file mode 100644
index 0000000..0d067c2
--- /dev/null
+++ b/otp/ai/AIZoneData.py
@@ -0,0 +1,217 @@
+from pandac.PandaModules import *
+from direct.distributed import ParentMgr
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from direct.task import Task
+from direct.showbase import LeakDetectors
+from otp.otpbase import OTPGlobals
+import random
+
+class AIZoneData:
+ notify = directNotify.newCategory('AIZoneData')
+
+ def __init__(self, air, parentId, zoneId):
+ self._air = air
+ self._parentId = parentId
+ self._zoneId = zoneId
+ self._data = self._air.getZoneDataStore().getDataForZone(self._parentId, self._zoneId)
+
+ def destroy(self):
+ del self._data
+ self._air.getZoneDataStore().releaseDataForZone(self._parentId, self._zoneId)
+ del self._zoneId
+ del self._parentId
+ del self._air
+
+ def __getattr__(self, attr):
+ return getattr(self._data, attr)
+
+
+class AIZoneDataObj:
+ notify = directNotify.newCategory('AIZoneDataObj')
+ DefaultCTravName = 'default'
+
+ def __init__(self, parentId, zoneId):
+ self._parentId = parentId
+ self._zoneId = zoneId
+ self._refCount = 0
+ self._collTravs = {}
+ self._collTravsStarted = set()
+
+ def __str__(self):
+ output = str(self._collTravs)
+ output += '\n'
+ totalColliders = 0
+ totalTraversers = 0
+ for currCollTrav in self._collTravs.values():
+ totalTraversers += 1
+ totalColliders += currCollTrav.getNumColliders()
+
+ output += 'Num traversers: %s Num total colliders: %s' % (totalTraversers, totalColliders)
+ return output
+
+ def _incRefCount(self):
+ self._refCount += 1
+
+ def _decRefCount(self):
+ self._refCount -= 1
+
+ def _getRefCount(self):
+ return self._refCount
+
+ def destroy(self):
+ for name in list(self._collTravsStarted):
+ self.stopCollTrav(cTravName=name)
+
+ del self._collTravsStarted
+ del self._collTravs
+ if hasattr(self, '_nonCollidableParent'):
+ self._nonCollidableParent.removeNode()
+ del self._nonCollidableParent
+ if hasattr(self, '_render'):
+ if hasattr(self, '_renderLeakDetector'):
+ self._renderLeakDetector.destroy()
+ del self._renderLeakDetector
+ self._render.removeNode()
+ del self._render
+ if hasattr(self, '_parentMgr'):
+ self._parentMgr.destroy()
+ del self._parentMgr
+ del self._zoneId
+ del self._parentId
+
+ def getLocation(self):
+ return (self._parentId, self._zoneId)
+
+ def getRender(self):
+ if not hasattr(self, '_render'):
+ self._render = NodePath('render-%s-%s' % (self._parentId, self._zoneId))
+ if config.GetBool('leak-scene-graph', 0):
+ self._renderLeakDetector = LeakDetectors.SceneGraphLeakDetector(self._render)
+ return self._render
+
+ def getNonCollidableParent(self):
+ if not hasattr(self, '_nonCollidableParent'):
+ render = self.getRender()
+ self._nonCollidableParent = render.attachNewNode('nonCollidables')
+ if __dev__:
+ pass
+ return self._nonCollidableParent
+
+ def getParentMgr(self):
+ if not hasattr(self, '_parentMgr'):
+ self._parentMgr = ParentMgr.ParentMgr()
+ self._parentMgr.registerParent(OTPGlobals.SPHidden, hidden)
+ self._parentMgr.registerParent(OTPGlobals.SPRender, self.getRender())
+ return self._parentMgr
+
+ def hasCollTrav(self, name = None):
+ if name is None:
+ name = AIZoneDataObj.DefaultCTravName
+ return name in self._collTravs
+
+ def getCollTrav(self, name = None):
+ if name is None:
+ name = AIZoneDataObj.DefaultCTravName
+ if name not in self._collTravs:
+ self._collTravs[name] = CollisionTraverser('cTrav-%s-%s-%s' % (name, self._parentId, self._zoneId))
+ return self._collTravs[name]
+
+ def removeCollTrav(self, name):
+ if self._collTravs.has_key(name):
+ del self._collTravs[name]
+
+ def _getCTravTaskName(self, name = None):
+ if name is None:
+ name = AIZoneDataObj.DefaultCTravName
+ return 'collTrav-%s-%s-%s' % (name, self._parentId, self._zoneId)
+
+ def _doCollisions(self, task = None, topNode = None, cTravName = None):
+ render = self.getRender()
+ curTime = globalClock.getFrameTime()
+ render.setTag('lastTraverseTime', str(curTime))
+ if topNode is not None:
+ if not render.isAncestorOf(topNode):
+ self.notify.warning('invalid topNode for collision traversal in %s: %s' % (self.getLocation(), topNode))
+ else:
+ topNode = render
+ if cTravName is None:
+ cTravName = AIZoneDataObj.DefaultCTravName
+ collTrav = self._collTravs[cTravName]
+ messenger.send('preColl-' + collTrav.getName())
+ collTrav.traverse(topNode)
+ messenger.send('postColl-' + collTrav.getName())
+ return Task.cont
+
+ def doCollTrav(self, topNode = None, cTravName = None):
+ self.getCollTrav(cTravName)
+ self._doCollisions(topNode=topNode, cTravName=cTravName)
+
+ def startCollTrav(self, respectPrevTransform = 1, cTravName = None):
+ if cTravName is None:
+ cTravName = AIZoneDataObj.DefaultCTravName
+ if cTravName not in self._collTravsStarted:
+ self.getCollTrav(name=cTravName)
+ taskMgr.add(self._doCollisions, self._getCTravTaskName(name=cTravName), priority=OTPGlobals.AICollisionPriority, extraArgs=[self._zoneId])
+ self._collTravsStarted.add(cTravName)
+ self.setRespectPrevTransform(respectPrevTransform, cTravName=cTravName)
+ return
+
+ def stopCollTrav(self, cTravName = None):
+ if cTravName is None:
+ cTravName = AIZoneDataObj.DefaultCTravName
+ self.notify.debug('stopCollTrav(%s, %s, %s)' % (cTravName, self._parentId, self._zoneId))
+ if cTravName in self._collTravsStarted:
+ self.notify.info('removing %s collision traversal for (%s, %s)' % (cTravName, self._parentId, self._zoneId))
+ taskMgr.remove(self._getCTravTaskName(name=cTravName))
+ self._collTravsStarted.remove(cTravName)
+ return
+
+ def setRespectPrevTransform(self, flag, cTravName = None):
+ if cTravName is None:
+ cTravName = AIZoneDataObj.DefaultCTravName
+ self._collTravs[cTravName].setRespectPrevTransform(flag)
+ return
+
+ def getRespectPrevTransform(self, cTravName = None):
+ if cTravName is None:
+ cTravName = AIZoneDataObj.DefaultCTravName
+ return self._collTravs[cTravName].getRespectPrevTransform()
+
+
+class AIZoneDataStore:
+ notify = directNotify.newCategory('AIZoneDataStore')
+
+ def __init__(self):
+ self._zone2data = {}
+
+ def destroy(self):
+ for zone, data in self._zone2data.items():
+ data.destroy()
+
+ del self._zone2data
+
+ def hasDataForZone(self, parentId, zoneId):
+ key = (parentId, zoneId)
+ return key in self._zone2data
+
+ def getDataForZone(self, parentId, zoneId):
+ key = (parentId, zoneId)
+ if key not in self._zone2data:
+ self._zone2data[key] = AIZoneDataObj(parentId, zoneId)
+ self.printStats()
+ data = self._zone2data[key]
+ data._incRefCount()
+ return data
+
+ def releaseDataForZone(self, parentId, zoneId):
+ key = (parentId, zoneId)
+ data = self._zone2data[key]
+ data._decRefCount()
+ refCount = data._getRefCount()
+ if refCount == 0:
+ del self._zone2data[key]
+ data.destroy()
+ self.printStats()
+
+ def printStats(self):
+ self.notify.debug('%s zones have zone data allocated' % len(self._zone2data))
diff --git a/otp/ai/BanManagerAI.py b/otp/ai/BanManagerAI.py
new file mode 100644
index 0000000..53b3326
--- /dev/null
+++ b/otp/ai/BanManagerAI.py
@@ -0,0 +1,74 @@
+import urllib
+import os
+from pandac.PandaModules import HTTPClient, Ramfile
+from direct.directnotify import DirectNotifyGlobal
+
+class BanManagerAI:
+ notify = DirectNotifyGlobal.directNotify.newCategory('BanManagerAI')
+ BanUrl = simbase.config.GetString('ban-base-url', 'http://vapps.disl.starwave.com:8005/dis-hold/action/event')
+ App = simbase.config.GetString('ban-app-name', 'TTWorldAI')
+ Product = simbase.config.GetString('ban-product', 'Toontown')
+ EventName = simbase.config.GetString('ban-event-name', 'tthackattempt')
+
+ def __init__(self):
+ self.curBanRequestNum = 0
+ self.channels = {}
+ self.ramFiles = {}
+
+ def ban(self, avatarId, dislid, comment):
+ parameters = ''
+ parameters += 'app=%s' % self.App
+ parameters += '&product=%s' % self.Product
+ parameters += '&user_id=%s' % dislid
+ parameters += '&event_name=%s' % self.EventName
+ commentWithAvatarId = 'avId-%s ' % avatarId
+ commentWithAvatarId += comment
+ parameters += '&comments=%s' % urllib.quote(str(commentWithAvatarId))
+ baseUrlToUse = self.BanUrl
+ osBaseUrl = os.getenv('BAN_URL')
+ if osBaseUrl:
+ baseUrlToUse = osBaseUrl
+ fullUrl = baseUrlToUse + '?' + parameters
+ self.notify.info('ban request %s dislid=%s comment=%s fullUrl=%s' % (self.curBanRequestNum,
+ dislid,
+ comment,
+ fullUrl))
+ simbase.air.writeServerEvent('ban_request', avatarId, '%s|%s|%s' % (dislid, comment, fullUrl))
+ if simbase.config.GetBool('do-actual-ban', True):
+ newTaskName = 'ban-task-%d' % self.curBanRequestNum
+ newTask = taskMgr.add(self.doBanUrlTask, newTaskName)
+ newTask.banRequestNum = self.curBanRequestNum
+ http = HTTPClient.getGlobalPtr()
+ channel = http.makeChannel(False)
+ self.channels[self.curBanRequestNum] = channel
+ rf = Ramfile()
+ self.ramFiles[self.curBanRequestNum] = rf
+ channel.beginGetDocument(fullUrl)
+ channel.downloadToRam(rf)
+ self.curBanRequestNum += 1
+
+ def cleanupBanReq(self, banReq):
+ channel = self.channels.get(banReq)
+ if channel:
+ del self.channels[banReq]
+ ramfile = self.ramFiles.get(banReq)
+ if ramfile:
+ del self.ramFiles[banReq]
+
+ def doBanUrlTask(self, task):
+ banReq = task.banRequestNum
+ channel = self.channels.get(banReq)
+ if channel:
+ if channel.run():
+ return task.cont
+ else:
+ self.notify.warning('no channel for ban req %s' % banReq)
+ self.cleanupBanReq(banReq)
+ return task.done
+ result = ''
+ ramfile = self.ramFiles.get(banReq)
+ if ramfile:
+ result = ramfile.getData()
+ self.notify.info('done processing ban request %s, ramFile=%s' % (banReq, result))
+ self.cleanupBanReq(banReq)
+ return task.done
diff --git a/otp/ai/Barrier.py b/otp/ai/Barrier.py
new file mode 100644
index 0000000..dce9a04
--- /dev/null
+++ b/otp/ai/Barrier.py
@@ -0,0 +1,83 @@
+from otp.ai.AIBase import *
+from direct.task import Task
+from direct.showbase import DirectObject
+import random
+
+class Barrier(DirectObject.DirectObject):
+ notify = directNotify.newCategory('Barrier')
+
+ def __init__(self, name, uniqueName, avIdList, timeout, clearedFunc = None, timeoutFunc = None, doneFunc = None):
+ self.name = name
+ self.uniqueName = uniqueName + '-Barrier'
+ self.avIdList = avIdList[:]
+ self.pendingAvatars = self.avIdList[:]
+ self.timeout = timeout
+ self.clearedFunc = clearedFunc
+ self.timeoutFunc = timeoutFunc
+ self.doneFunc = doneFunc
+ if len(self.pendingAvatars) == 0:
+ self.notify.debug('%s: barrier with empty list' % self.uniqueName)
+ self.active = 0
+ if self.clearedFunc:
+ self.clearedFunc()
+ if self.doneFunc:
+ self.doneFunc(self.avIdList)
+ return
+ self.taskName = self.uniqueName + '-Timeout'
+ origTaskName = self.taskName
+ while taskMgr.hasTaskNamed(self.taskName):
+ self.taskName = origTaskName + '-' + str(random.randint(0, 10000))
+
+ taskMgr.doMethodLater(self.timeout, self.__timerExpired, self.taskName)
+ for avId in self.avIdList:
+ event = simbase.air.getAvatarExitEvent(avId)
+ self.acceptOnce(event, self.__handleUnexpectedExit, extraArgs=[avId])
+
+ self.notify.debug('%s: expecting responses from %s within %s seconds' % (self.uniqueName, self.avIdList, self.timeout))
+ self.active = 1
+
+ def cleanup(self):
+ if self.active:
+ taskMgr.remove(self.taskName)
+ self.active = 0
+ self.ignoreAll()
+
+ def clear(self, avId):
+ if avId not in self.pendingAvatars:
+ self.notify.warning('%s: tried to clear %s, who was not listed.' % (self.uniqueName, avId))
+ return
+ self.notify.debug('%s: clearing avatar %s' % (self.uniqueName, avId))
+ self.pendingAvatars.remove(avId)
+ if len(self.pendingAvatars) == 0:
+ self.notify.debug('%s: barrier cleared by %s' % (self.uniqueName, self.avIdList))
+ self.cleanup()
+ if self.clearedFunc:
+ self.clearedFunc()
+ if self.doneFunc:
+ self.doneFunc(self.avIdList)
+
+ def isActive(self):
+ return self.active
+
+ def getPendingAvatars(self):
+ return self.pendingAvatars[:]
+
+ def __timerExpired(self, task):
+ self.notify.warning('%s: timeout expired; responses not received from %s' % (self.uniqueName, self.pendingAvatars))
+ self.cleanup()
+ if self.timeoutFunc:
+ self.timeoutFunc(self.pendingAvatars[:])
+ if self.doneFunc:
+ clearedAvIds = self.avIdList[:]
+ for avId in self.pendingAvatars:
+ clearedAvIds.remove(avId)
+
+ self.doneFunc(clearedAvIds)
+ return Task.done
+
+ def __handleUnexpectedExit(self, avId):
+ if avId not in self.avIdList:
+ return
+ self.avIdList.remove(avId)
+ if avId in self.pendingAvatars:
+ self.clear(avId)
diff --git a/otp/ai/GarbageLeakServerEventAggregator.py b/otp/ai/GarbageLeakServerEventAggregator.py
new file mode 100644
index 0000000..7585145
--- /dev/null
+++ b/otp/ai/GarbageLeakServerEventAggregator.py
@@ -0,0 +1,44 @@
+from direct.showbase.DirectObject import DirectObject
+from direct.showbase import GarbageReport
+
+class GarbageLeakServerEventAggregator(DirectObject):
+
+ def __init__(self, cr):
+ self.cr = cr
+ self._doLaterName = None
+ self._sentLeakDesc2num = {}
+ self._curLeakDesc2num = {}
+ self.accept(GarbageReport.GarbageCycleCountAnnounceEvent, self._handleCycleCounts)
+ return
+
+ def destroy(self):
+ self._stopSending()
+ self.ignoreAll()
+ del self.cr
+
+ def _handleCycleCounts(self, desc2num):
+ self._curLeakDesc2num = desc2num
+ self._startSending()
+
+ def _startSending(self):
+ if not self._doLaterName:
+ self._sendLeaks()
+ self._doLaterName = uniqueName('%s-sendGarbageLeakInfo' % self.__class__.__name__)
+ self.doMethodLater(60 * 60.0, self._sendLeaks, self._doLaterName)
+
+ def _stopSending(self):
+ if self._doLaterName:
+ self.removeTask(self._doLaterName)
+ self._doLaterName = None
+ return
+
+ def _sendLeaks(self, task = None):
+ for desc, curNum in self._curLeakDesc2num.iteritems():
+ self._sentLeakDesc2num.setdefault(desc, 0)
+ num = curNum - self._sentLeakDesc2num[desc]
+ if num > 0:
+ base.cr.timeManager.d_setClientGarbageLeak(num, desc)
+ self._sentLeakDesc2num[desc] = curNum
+
+ if task:
+ return task.again
diff --git a/otp/ai/MagicWordManager.py b/otp/ai/MagicWordManager.py
new file mode 100644
index 0000000..6a38b76
--- /dev/null
+++ b/otp/ai/MagicWordManager.py
@@ -0,0 +1,875 @@
+from pandac.PandaModules import *
+from direct.showbase import GarbageReport, ContainerReport, MessengerLeakDetector
+from direct.distributed import DistributedObject
+from direct.directnotify import DirectNotifyGlobal
+from direct.showbase.InputStateGlobal import inputState
+from direct.showbase.ObjectCount import ObjectCount
+from direct.task import Task
+from direct.task.TaskProfiler import TaskProfiler
+from otp.avatar import Avatar
+import string
+from direct.showbase import PythonUtil
+from direct.showbase.PythonUtil import Functor, DelayedCall, ScratchPad
+from otp.otpbase import OTPGlobals
+from direct.distributed.ClockDelta import *
+from direct.showutil.TexViewer import TexViewer
+
+class MagicWordManager(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('MagicWordManager')
+ neverDisable = 1
+ GameAvatarClass = None
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.shownFontNode = None
+ self.csShown = 0
+ self.guiPopupShown = 0
+ self.texViewer = None
+ return
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+ self.accept('magicWord', self.b_setMagicWord)
+ self.autoMagicWordEvent = localAvatar.getArrivedOnDistrictEvent()
+ if localAvatar.isGeneratedOnDistrict():
+ self.doLoginMagicWords()
+ else:
+ self.accept(self.autoMagicWordEvent, self.doLoginMagicWords)
+
+ def doLoginMagicWords(self):
+ pass
+
+ def disable(self):
+ self.ignore(self.autoMagicWordEvent)
+ del self.autoMagicWordEvent
+ self.ignore('magicWord')
+ self.hidefont()
+ DistributedObject.DistributedObject.disable(self)
+
+ def setMagicWord(self, word, avId, zoneId):
+ try:
+ self.doMagicWord(word, avId, zoneId)
+ except:
+ response = PythonUtil.describeException(backTrace=1)
+ self.notify.warning('Ignoring error in magic word:\n%s' % response)
+ self.setMagicWordResponse(response)
+
+ def wordIs(self, word, w):
+ return word == w or word[:len(w) + 1] == '%s ' % w
+
+ def getWordIs(self, word):
+ return Functor(self.wordIs, word)
+
+ def doMagicWord(self, word, avId, zoneId):
+ wordIs = self.getWordIs(word)
+ print word
+ if wordIs('~oobe'):
+ base.oobe()
+ elif wordIs('~oobeCull'):
+ base.oobeCull()
+ elif wordIs('~tex'):
+ self.doTex(word)
+ elif wordIs('~texmem'):
+ base.toggleTexMem()
+ elif wordIs('~verts'):
+ base.toggleShowVertices()
+ elif wordIs('~wire'):
+ base.toggleWireframe()
+ elif wordIs('~stereo'):
+ base.toggleStereo()
+ elif wordIs('~showfont'):
+ self.showfont(word[9:])
+ elif wordIs('~hidefont'):
+ self.hidefont()
+ elif wordIs('~guiPopup'):
+ self.toggleGuiPopup()
+ elif wordIs('~showCS') or wordIs('~showcs'):
+ bitmask = self.getCSBitmask(word[7:])
+ render.showCS(bitmask)
+ self.csShown = 1
+ elif wordIs('~hideCS') or wordIs('~hidecs'):
+ bitmask = self.getCSBitmask(word[7:])
+ render.hideCS(bitmask)
+ self.csShown = 0
+ elif wordIs('~cs'):
+ bitmask = self.getCSBitmask(word[3:])
+ if self.csShown:
+ render.hideCS(bitmask)
+ self.csShown = 0
+ else:
+ render.showCS(bitmask)
+ self.csShown = 1
+ elif wordIs('~showShadowCollisions'):
+ self.showShadowCollisions()
+ elif wordIs('~hideShadowCollisions'):
+ self.hideShadowCollisions()
+ elif wordIs('~showCollisions'):
+ self.showCollisions()
+ elif wordIs('~hideCollisions'):
+ self.hideCollisions()
+ elif wordIs('~showCameraCollisions'):
+ self.showCameraCollisions()
+ elif wordIs('~hideCameraCollisions'):
+ self.hideCameraCollisions()
+ elif wordIs('~collidespam'):
+ n = Notify.ptr().getCategory(':collide')
+ if hasattr(self, '_collideSpamSeverity'):
+ n.setSeverity(self._collideSpamSeverity)
+ del self._collideSpamSeverity
+ else:
+ self._collideSpamSeverity = n.getSeverity()
+ n.setSeverity(NSSpam)
+ elif wordIs('~notify'):
+ args = word.split()
+ n = Notify.ptr().getCategory(args[1])
+ n.setSeverity({'error': NSError,
+ 'warning': NSWarning,
+ 'info': NSInfo,
+ 'debug': NSDebug,
+ 'spam': NSSpam}[args[2]])
+ elif wordIs('~stress'):
+ factor = word[7:]
+ if factor:
+ factor = float(factor)
+ LOD.setStressFactor(factor)
+ response = 'Set LOD stress factor to %s' % factor
+ else:
+ factor = LOD.getStressFactor()
+ response = 'LOD stress factor is %s' % factor
+ self.setMagicWordResponse(response)
+ elif wordIs('~for'):
+ self.forAnother(word, avId, zoneId)
+ elif wordIs('~badname'):
+ word = '~for %s ~badname' % word[9:]
+ print 'word is %s' % word
+ self.forAnother(word, avId, zoneId)
+ elif wordIs('~avId'):
+ self.setMagicWordResponse(str(localAvatar.doId))
+ elif wordIs('~doId'):
+ name = string.strip(word[6:])
+ objs = self.identifyDistributedObjects(name)
+ if len(objs) == 0:
+ response = '%s is unknown.' % name
+ else:
+ response = ''
+ for name, obj in objs:
+ response += '\n%s %d' % (name, obj.doId)
+
+ response = response[1:]
+ self.setMagicWordResponse(response)
+ elif wordIs('~exec'):
+ from otp.chat import ChatManager
+ ChatManager.ChatManager.execChat = 1
+ elif wordIs('~run'):
+ self.toggleRun()
+ elif wordIs('~runFaster'):
+ if config.GetBool('want-running', 1):
+ args = word.split()
+ if len(args) > 1:
+ base.debugRunningMultiplier = float(args[1])
+ else:
+ base.debugRunningMultiplier = 10
+ inputState.set('debugRunning', True)
+ elif wordIs('~who'):
+ avIds = []
+ for av in Avatar.Avatar.ActiveAvatars:
+ if hasattr(av, 'getFriendsList'):
+ avIds.append(av.doId)
+
+ self.d_setWho(avIds)
+ elif wordIs('~sync'):
+ tm = self.cr.timeManager
+ if tm == None:
+ response = 'No TimeManager.'
+ self.setMagicWordResponse(response)
+ else:
+ tm.extraSkew = 0.0
+ skew = string.strip(word[5:])
+ if skew != '':
+ tm.extraSkew = float(skew)
+ globalClockDelta.clear()
+ tm.handleHotkey()
+ elif wordIs('~period'):
+ timeout = string.strip(word[7:])
+ if timeout != '':
+ seconds = int(timeout)
+ self.cr.stopPeriodTimer()
+ self.cr.resetPeriodTimer(seconds)
+ self.cr.startPeriodTimer()
+ if self.cr.periodTimerExpired:
+ response = 'Period timer has expired.'
+ elif self.cr.periodTimerStarted:
+ elapsed = globalClock.getFrameTime() - self.cr.periodTimerStarted
+ secondsRemaining = self.cr.periodTimerSecondsRemaining - elapsed
+ response = 'Period timer expires in %s seconds.' % int(secondsRemaining)
+ else:
+ response = 'Period timer not set.'
+ self.setMagicWordResponse(response)
+ elif wordIs('~DIRECT'):
+ args = word.split()
+ fEnableLight = 0
+ if len(args) > 1:
+ if direct and args[1] == 'CAM':
+ direct.enable()
+ taskMgr.removeTasksMatching('updateSmartCamera*')
+ camera.wrtReparentTo(render)
+ direct.cameraControl.enableMouseFly()
+ self.setMagicWordResponse('Enabled DIRECT camera')
+ return
+ elif args[1] == 'LIGHT':
+ fEnableLight = 1
+ base.startTk()
+ from direct.directtools import DirectSession
+ if fEnableLight:
+ direct.enableLight()
+ else:
+ direct.enable()
+ self.setMagicWordResponse('Enabled DIRECT')
+ elif wordIs('~TT'):
+ if not direct:
+ return
+ args = word.split()
+ if len(args) > 1:
+ if args[1] == 'CAM':
+ direct.cameraControl.disableMouseFly()
+ camera.wrtReparentTo(base.localAvatar)
+ base.localAvatar.startUpdateSmartCamera()
+ self.setMagicWordResponse('Disabled DIRECT camera')
+ return
+ direct.disable()
+ camera.wrtReparentTo(base.localAvatar)
+ base.localAvatar.startUpdateSmartCamera()
+ self.setMagicWordResponse('Disabled DIRECT')
+ elif wordIs('~net'):
+ if self.cr.networkPlugPulled():
+ self.cr.restoreNetworkPlug()
+ self.cr.startHeartbeat()
+ response = 'Network restored.'
+ else:
+ self.cr.pullNetworkPlug()
+ self.cr.stopHeartbeat()
+ response = 'Network disconnected.'
+ self.setMagicWordResponse(response)
+ elif wordIs('~disconnect'):
+ base.cr.distributedDistrict.sendUpdate('broadcastMessage')
+ elif wordIs('~model'):
+ args = word.split()
+ path = args[1]
+ model = loader.loadModel(path)
+ model.reparentTo(localAvatar)
+ model.wrtReparentTo(render)
+ self.setMagicWordResponse('loaded %s' % path)
+ elif wordIs('~axis'):
+ axis = loader.loadModel('models/misc/xyzAxis.bam')
+ axis.reparentTo(render)
+ axis.setPos(base.localAvatar, 0, 0, 0)
+ axis.setHpr(render, 0, 0, 0)
+ axis10 = loader.loadModel('models/misc/xyzAxis.bam')
+ axis10.reparentTo(render)
+ axis10.setPos(base.localAvatar, 0, 0, 0)
+ axis10.setScale(10)
+ axis10.setHpr(render, 0, 0, 0)
+ axis10.setColorScale(1, 1, 1, 0.4)
+ axis10.setTransparency(1)
+ elif wordIs('~clearAxes') or wordIs('~clearAxis'):
+ render.findAllMatches('**/xyzAxis.egg').detach()
+ elif wordIs('~myAxis'):
+ if hasattr(self, 'myAxis'):
+ self.myAxis.detachNode()
+ del self.myAxis
+ else:
+ self.myAxis = loader.loadModel('models/misc/xyzAxis.bam')
+ self.myAxis.reparentTo(localAvatar)
+ elif wordIs('~osd'):
+ onScreenDebug.enabled = not onScreenDebug.enabled
+ elif wordIs('~osdScale'):
+ args = word.split()
+ defScale = 0.05
+ if len(args) > 1:
+ scale = float(args[1])
+ else:
+ scale = 1.0
+ onScreenDebug.onScreenText.setScale(defScale * scale)
+ elif wordIs('~osdTaskMgr'):
+ if taskMgr.osdEnabled():
+ taskMgr.stopOsd()
+ else:
+ if not onScreenDebug.enabled:
+ onScreenDebug.enabled = True
+ taskMgr.startOsd()
+ elif wordIs('~fps'):
+ self.doFps(word, avId, zoneId)
+ elif wordIs('~sleep'):
+ args = word.split()
+ if len(args) > 1:
+ s = float(args[1])
+ base.setSleep(s)
+ response = 'sleeping %s' % s
+ else:
+ base.setSleep(0.0)
+ response = 'not sleeping'
+ self.setMagicWordResponse(response)
+ elif wordIs('~objects'):
+ args = word.split()
+ from direct.showbase import ObjectReport
+ report = ObjectReport.ObjectReport('client ~objects')
+ if 'all' in args:
+ self.notify.info('printing full object set...')
+ report.getObjectPool().printObjsByType(printReferrers='ref' in args)
+ if hasattr(self, 'baselineObjReport'):
+ self.notify.info('calculating diff from baseline ObjectReport...')
+ self.lastDiff = self.baselineObjReport.diff(report)
+ self.lastDiff.printOut(full='diff' in args or 'dif' in args)
+ if 'baseline' in args or not hasattr(self, 'baselineObjReport'):
+ self.notify.info('recording baseline ObjectReport...')
+ if hasattr(self, 'baselineObjReport'):
+ self.baselineObjReport.destroy()
+ self.baselineObjReport = report
+ self.setMagicWordResponse('objects logged')
+ elif wordIs('~objectcount'):
+
+ def handleObjectCountDone(objectCount):
+ self.setMagicWordResponse('object count logged')
+
+ oc = ObjectCount('~objectcount', doneCallback=handleObjectCountDone)
+ elif wordIs('~objecthg'):
+ import gc
+ objs = gc.get_objects()
+ type2count = {}
+ for obj in objs:
+ tn = safeTypeName(obj)
+ type2count.setdefault(tn, 0)
+ type2count[tn] += 1
+
+ count2type = invertDictLossless(type2count)
+ counts = count2type.keys()
+ counts.sort()
+ counts.reverse()
+ for count in counts:
+ print '%s: %s' % (count, count2type[count])
+
+ self.setMagicWordResponse('~aiobjecthg complete')
+ elif wordIs('~containers'):
+ args = word.split()
+ limit = 30
+ if 'full' in args:
+ limit = None
+ ContainerReport.ContainerReport('~containers', log=True, limit=limit, threaded=True)
+ elif wordIs('~garbage'):
+ args = word.split()
+ full = 'full' in args
+ safeMode = 'safe' in args
+ delOnly = 'delonly' in args
+ GarbageReport.GarbageLogger('~garbage', fullReport=full, threaded=True, safeMode=safeMode, delOnly=delOnly, doneCallback=self.garbageReportDone)
+ elif wordIs('~guicreates'):
+ base.printGuiCreates = True
+ self.setMagicWordResponse('printing gui creation stacks')
+ elif wordIs('~creategarbage'):
+ GarbageReport._createGarbage()
+ elif wordIs('~leakTask'):
+
+ def leakTask(task):
+ return task.cont
+
+ taskMgr.add(leakTask, uniqueName('leakedTask'))
+ leakTask = None
+ elif wordIs('~leakmessage'):
+ MessengerLeakDetector._leakMessengerObject()
+ self.down_setMagicWordResponse(senderId, 'messenger leak object created')
+ elif wordIs('~pstats'):
+ args = word.split()
+ hostname = None
+ port = None
+ if len(args) > 1:
+ hostname = args[1]
+ if len(args) > 2:
+ port = int(args[2])
+ base.wantStats = 1
+ Task.TaskManager.pStatsTasks = 1
+ result = base.createStats(hostname, port)
+ connectionName = '%s' % hostname
+ if port is not None:
+ connectionName += ':%s' % port
+ if result:
+ response = 'connected client pstats to %s' % connectionName
+ else:
+ response = 'could not connect pstats to %s' % connectionName
+ self.setMagicWordResponse(response)
+ elif wordIs('~profile'):
+ args = word.split()
+ if len(args) > 1:
+ num = int(args[1])
+ else:
+ num = 5
+ session = taskMgr.getProfileSession('~profile')
+ session.setLogAfterProfile(True)
+ taskMgr.profileFrames(num, session)
+ self.setMagicWordResponse('profiling %s client frames...' % num)
+ elif wordIs('~frameprofile'):
+ args = word.split()
+ wasOn = bool(taskMgr.getProfileFrames())
+ if len(args) > 1:
+ setting = bool(int(args[1]))
+ else:
+ setting = not wasOn
+ taskMgr.setProfileFrames(setting)
+ self.setMagicWordResponse('frame profiling %s%s' % (choice(setting, 'ON', 'OFF'), choice(wasOn == setting, ' already', '')))
+ elif wordIs('~taskprofile'):
+ args = word.split()
+ wasOn = bool(taskMgr.getProfileTasks())
+ if len(args) > 1:
+ setting = bool(int(args[1]))
+ else:
+ setting = not wasOn
+ taskMgr.setProfileTasks(setting)
+ self.setMagicWordResponse('task profiling %s%s' % (choice(setting, 'ON', 'OFF'), choice(wasOn == setting, ' already', '')))
+ elif wordIs('~taskspikethreshold'):
+ args = word.split()
+ if len(args) > 1:
+ threshold = float(args[1])
+ response = 'task spike threshold set to %ss' % threshold
+ else:
+ threshold = TaskProfiler.GetDefaultSpikeThreshold()
+ response = 'task spike threshold reset to %ss' % threshold
+ TaskProfiler.SetSpikeThreshold(threshold)
+ self.setMagicWordResponse(response)
+ elif wordIs('~logtaskprofiles'):
+ args = word.split()
+ if len(args) > 1:
+ name = args[1]
+ else:
+ name = None
+ taskMgr.logTaskProfiles(name)
+ response = 'logged task profiles%s' % choice(name, ' for %s' % name, '')
+ self.setMagicWordResponse(response)
+ elif wordIs('~taskprofileflush'):
+ args = word.split()
+ if len(args) > 1:
+ name = args[1]
+ else:
+ name = None
+ taskMgr.flushTaskProfiles(name)
+ response = 'flushed AI task profiles%s' % choice(name, ' for %s' % name, '')
+ self.setMagicWordResponse(response)
+ elif wordIs('~dobjectcount'):
+ base.cr.printObjectCount()
+ self.setMagicWordResponse('logging client distributed object count...')
+ elif wordIs('~taskmgr'):
+ print taskMgr
+ self.setMagicWordResponse('logging client taskMgr...')
+ elif wordIs('~jobmgr'):
+ print jobMgr
+ self.setMagicWordResponse('logging client jobMgr...')
+ elif wordIs('~jobtime'):
+ args = word.split()
+ if len(args) > 1:
+ time = float(args[1])
+ else:
+ time = None
+ response = ''
+ if time is None:
+ time = jobMgr.getDefaultTimeslice()
+ response = 'reset client jobMgr timeslice to %s ms' % time
+ else:
+ response = 'set client jobMgr timeslice to %s ms' % time
+ time = time / 1000.0
+ jobMgr.setTimeslice(time)
+ self.setMagicWordResponse(response)
+ elif wordIs('~detectleaks'):
+ started = self.cr.startLeakDetector()
+ self.setMagicWordResponse(choice(started, 'leak detector started', 'leak detector already started'))
+ elif wordIs('~taskthreshold'):
+ args = word.split()
+ if len(args) > 1.0:
+ threshold = float(args[1])
+ else:
+ threshold = None
+ response = ''
+ if threshold is None:
+ threshold = taskMgr.DefTaskDurationWarningThreshold
+ response = 'reset task duration warning threshold to %s' % threshold
+ else:
+ response = 'set task duration warning threshold to %s' % threshold
+ taskMgr.setTaskDurationWarningThreshold(threshold)
+ self.setMagicWordResponse(response)
+ elif wordIs('~messenger'):
+ print messenger
+ self.setMagicWordResponse('logging client messenger...')
+ elif wordIs('~clientcrash'):
+ DelayedCall(Functor(self.notify.error, '~clientcrash: simulating a client crash'))
+ elif wordIs('~badDelete'):
+ doId = 0
+ while doId in base.cr.doId2do:
+ doId += 1
+
+ DelayedCall(Functor(base.cr.deleteObjectLocation, ScratchPad(doId=doId), 1, 1))
+ self.setMagicWordResponse('doing bad delete')
+ elif wordIs('~idTags'):
+ messenger.send('nameTagShowAvId', [])
+ base.idTags = 1
+ elif wordIs('~nameTags'):
+ messenger.send('nameTagShowName', [])
+ base.idTags = 0
+ elif wordIs('~hideNames'):
+ if NametagGlobals.getMasterNametagsVisible():
+ NametagGlobals.setMasterNametagsVisible(0)
+ else:
+ NametagGlobals.setMasterNametagsVisible(1)
+ elif wordIs('~hideGui'):
+ if aspect2d.isHidden():
+ aspect2d.show()
+ else:
+ aspect2d.hide()
+ elif wordIs('~flush'):
+ base.cr.doDataCache.flush()
+ base.cr.cache.flush()
+ self.setMagicWordResponse('client object and data caches flushed')
+ elif wordIs('~prof'):
+ import time
+ name = 'default'
+ p = Point3()
+ ts = time.time()
+ for i in xrange(1000000):
+ p.set(1, 2, 3)
+
+ tf = time.time()
+ dt = tf - ts
+ response = 'prof(%s): %s secs' % (name, dt)
+ print response
+ self.setMagicWordResponse(response)
+ elif wordIs('~gptc'):
+ args = word.split()
+ if len(args) > 1.0 and hasattr(self.cr, 'leakDetector'):
+ gptcJob = self.cr.leakDetector.getPathsToContainers('~gptc', args[1], Functor(self._handleGPTCfinished, args[1]))
+ else:
+ self.setMagicWordResponse('error')
+ elif wordIs('~gptcn'):
+ args = word.split()
+ if len(args) > 1.0 and hasattr(self.cr, 'leakDetector'):
+ gptcnJob = self.cr.leakDetector.getPathsToContainersNamed('~gptcn', args[1], Functor(self._handleGPTCNfinished, args[1]))
+ else:
+ self.setMagicWordResponse('error')
+ else:
+ return 0
+ return 1
+
+ def toggleRun(self):
+ if config.GetBool('want-running', 1):
+ inputState.set('debugRunning', inputState.isSet('debugRunning') != True)
+
+ def d_setMagicWord(self, magicWord, avId, zoneId):
+ self.sendUpdate('setMagicWord', [magicWord,
+ avId,
+ zoneId,
+ base.cr.userSignature])
+
+ def b_setMagicWord(self, magicWord, avId=None, zoneId=None):
+ if self.cr.wantMagicWords:
+ if avId == None:
+ avId = base.localAvatar.doId
+
+ if zoneId == None:
+ try:
+ zoneId = self.cr.playGame.getPlace().getZoneId()
+ except:
+ pass
+
+ if zoneId == None:
+ zoneId = 0
+
+ self.d_setMagicWord(magicWord, avId, zoneId)
+ if magicWord.count("~crash"):
+ args = magicWord.split()
+ errorCode = 12
+ if len(args) > 1:
+ errorCode = int(args[1])
+ self.notify.info("Simulating client crash: exit error = %s" % errorCode)
+ base.exitShow(errorCode)
+
+ if magicWord.count("~exception"):
+ self.notify.error("~exception: simulating a client exception...")
+ s = ""
+ while 1:
+ s += "INVALIDNAME"
+ eval(s)
+
+ self.setMagicWord(magicWord, avId, zoneId)
+
+ def setMagicWordResponse(self, response):
+ self.notify.info(response)
+ base.localAvatar.setChatAbsolute(response, CFSpeech | CFTimeout)
+ base.talkAssistant.receiveDeveloperMessage(response)
+
+ def d_setWho(self, avIds):
+ self.sendUpdate('setWho', [avIds])
+
+ def _handleGPTCfinished(self, ct, gptcJob):
+ self.setMagicWordResponse('gptc(%s) finished' % ct)
+
+ def _handleGPTCNfinished(self, cn, gptcnJob):
+ self.setMagicWordResponse('gptcn(%s) finished' % cn)
+
+ def forAnother(self, word, avId, zoneId):
+ b = 5
+ while word[b:b + 2] != ' ~':
+ b += 1
+ if b >= len(word):
+ self.setMagicWordResponse('No next magic word!')
+ return
+
+ nextWord = word[b + 1:]
+ name = string.strip(word[5:b])
+ id = self.identifyAvatar(name)
+ if id == None:
+ self.setMagicWordResponse("Don't know who %s is." % name)
+ return
+ self.d_setMagicWord(nextWord, id, zoneId)
+ return
+
+ def identifyAvatar(self, name):
+ self.notify.error('Pure virtual - please override me.')
+
+ def identifyDistributedObjects(self, name):
+ result = []
+ lowerName = string.lower(name)
+ for obj in self.cr.doId2do.values():
+ className = obj.__class__.__name__
+ try:
+ name = obj.getName()
+ except:
+ name = className
+
+ if string.lower(name) == lowerName or string.lower(className) == lowerName or string.lower(className) == 'distributed' + lowerName:
+ result.append((name, obj))
+
+ return result
+
+ def doTex(self, word):
+ args = word.split()
+ if len(args) <= 1:
+ if self.texViewer:
+ self.texViewer.cleanup()
+ self.texViewer = None
+ return
+ base.toggleTexture()
+ return
+ if self.texViewer:
+ self.texViewer.cleanup()
+ self.texViewer = None
+ tex = TexturePool.findTexture(args[1])
+ if not tex:
+ tex = TexturePool.findTexture('*%s*' % args[1])
+ if not tex:
+ self.setMagicWordResponse('Unknown texture: %s' % args[1])
+ return
+ self.texViewer = TexViewer(tex)
+ return
+
+ def getCSBitmask(self, str):
+ words = string.lower(str).split()
+ if len(words) == 0:
+ return None
+ invalid = ''
+ bitmask = BitMask32.allOff()
+ for w in words:
+ if w == 'wall':
+ bitmask |= OTPGlobals.WallBitmask
+ elif w == 'floor':
+ bitmask |= OTPGlobals.FloorBitmask
+ elif w == 'cam':
+ bitmask |= OTPGlobals.CameraBitmask
+ elif w == 'catch':
+ bitmask |= OTPGlobals.CatchBitmask
+ elif w == 'ghost':
+ bitmask |= OTPGlobals.GhostBitmask
+ elif w == 'pet':
+ bitmask |= OTPGlobals.PetBitmask
+ elif w == 'furniture':
+ bitmask |= OTPGlobals.FurnitureSideBitmask | OTPGlobals.FurnitureTopBitmask | OTPGlobals.FurnitureDragBitmask
+ elif w == 'furnitureside':
+ bitmask |= OTPGlobals.FurnitureSideBitmask
+ elif w == 'furnituretop':
+ bitmask |= OTPGlobals.FurnitureTopBitmask
+ elif w == 'furnituredrag':
+ bitmask |= OTPGlobals.FurnitureDragBitmask
+ elif w == 'pie':
+ bitmask |= OTPGlobals.PieBitmask
+ else:
+ try:
+ bitmask |= BitMask32.bit(int(w))
+ print bitmask
+ except ValueError:
+ invalid += ' ' + w
+
+ if invalid:
+ self.setMagicWordResponse('Unknown CS keyword(s): %s' % invalid)
+ return bitmask
+
+ def getFontByName(self, fontname):
+ if fontname == 'default':
+ return TextNode.getDefaultFont()
+ elif fontname == 'interface':
+ return OTPGlobals.getInterfaceFont()
+ elif fontname == 'sign':
+ return OTPGlobals.getSignFont()
+ else:
+ return None
+ return None
+
+ def showfont(self, fontname):
+ fontname = string.strip(string.lower(fontname))
+ font = self.getFontByName(fontname)
+ if font == None:
+ self.setMagicWordResponse('Unknown font: %s' % fontname)
+ return
+ if not isinstance(font, DynamicTextFont):
+ self.setMagicWordResponse('Font %s is not dynamic.' % fontname)
+ return
+ self.hidefont()
+ self.shownFontNode = aspect2d.attachNewNode('shownFont')
+ tn = TextNode('square')
+ tn.setCardActual(0.0, 1.0, -1.0, 0.0)
+ tn.setFrameActual(0.0, 1.0, -1.0, 0.0)
+ tn.setCardColor(1, 1, 1, 0.5)
+ tn.setFrameColor(1, 1, 1, 1)
+ tn.setFont(font)
+ tn.setText(' ')
+ numXPages = 2
+ numYPages = 2
+ pageScale = 0.8
+ pageMargin = 0.1
+ numPages = font.getNumPages()
+ x = 0
+ y = 0
+ for pi in range(numPages):
+ page = font.getPage(pi)
+ tn.setCardTexture(page)
+ np = self.shownFontNode.attachNewNode(tn.generate())
+ np.setScale(pageScale)
+ (np.setPos(float(x) / numXPages * 2 - 1 + pageMargin, 0, 1 - float(y) / numYPages * 2 - pageMargin),)
+ x += 1
+ if x >= numXPages:
+ y += 1
+ x = 0
+
+ return
+
+ def hidefont(self):
+ if self.shownFontNode != None:
+ self.shownFontNode.removeNode()
+ self.shownFontNode = None
+ return
+
+ def showShadowCollisions(self):
+ try:
+ base.shadowTrav.showCollisions(render)
+ except:
+ self.setMagicWordResponse('CollisionVisualizer is not compiled in.')
+
+ def hideShadowCollisions(self):
+ base.shadowTrav.hideCollisions()
+
+ def showCollisions(self):
+ try:
+ base.cTrav.showCollisions(render)
+ except:
+ self.setMagicWordResponse('CollisionVisualizer is not compiled in.')
+
+ def hideCollisions(self):
+ base.cTrav.hideCollisions()
+
+ def showCameraCollisions(self):
+ try:
+ localAvatar.ccTrav.showCollisions(render)
+ except:
+ self.setMagicWordResponse('CollisionVisualizer is not compiled in.')
+
+ def hideCameraCollisions(self):
+ localAvatar.ccTrav.hideCollisions()
+
+ def doFps(self, word, avId, zoneId):
+ args = word.split()
+ response = None
+ if len(args) == 1 or args[1] == 'normal':
+ if globalClock.getMode() != ClockObject.MNormal:
+ globalClock.setMode(ClockObject.MNormal)
+ response = 'Normal frame rate set.'
+ else:
+ base.setFrameRateMeter(not base.frameRateMeter)
+ elif args[1] == 'forced':
+ fps = float(args[2])
+ globalClock.setMode(ClockObject.MForced)
+ globalClock.setDt(1.0 / fps)
+ response = 'Frame rate forced to %s fps.' % fps
+ base.setFrameRateMeter(1)
+ elif args[1] == 'degrade':
+ factor = float(args[2])
+ globalClock.setMode(ClockObject.MDegrade)
+ globalClock.setDegradeFactor(factor)
+ response = 'Frame rate degraded by factor of %s.' % factor
+ base.setFrameRateMeter(1)
+ elif args[1][-1] == '%':
+ percent = float(args[1][:-1])
+ if percent == 100:
+ globalClock.setMode(ClockObject.MNormal)
+ response = 'Normal frame rate set.'
+ else:
+ globalClock.setMode(ClockObject.MDegrade)
+ globalClock.setDegradeFactor(100.0 / percent)
+ response = 'Frame rate degraded to %s percent.' % percent
+ base.setFrameRateMeter(1)
+ else:
+ try:
+ fps = float(args[1])
+ except:
+ fps = None
+
+ if fps != None:
+ globalClock.setMode(ClockObject.MForced)
+ globalClock.setDt(1.0 / fps)
+ response = 'Frame rate forced to %s fps.' % fps
+ base.setFrameRateMeter(1)
+ else:
+ response = 'Unknown fps command: ~s' % args[1]
+ if base.frameRateMeter:
+ globalClock.setAverageFrameRateInterval(ConfigVariableDouble('average-frame-rate-interval').getValue())
+ if response != None:
+ self.setMagicWordResponse(response)
+ return
+
+ def identifyAvatar(self, name):
+ for av in Avatar.Avatar.ActiveAvatars:
+ if isinstance(av, self.GameAvatarClass) and av.getName() == name:
+ return av.doId
+
+ lowerName = string.lower(name)
+ for av in Avatar.Avatar.ActiveAvatars:
+ if isinstance(av, self.GameAvatarClass) and string.strip(string.lower(av.getName())) == lowerName:
+ return av.doId
+
+ try:
+ avId = int(name)
+ return avId
+ except:
+ pass
+
+ return None
+
+ def toggleGuiPopup(self):
+ if self.guiPopupShown:
+ base.mouseWatcherNode.hideRegions()
+ self.guiPopupShown = 0
+ else:
+ base.mouseWatcherNode.showRegions(render2d, 'gui-popup', 0)
+ self.guiPopupShown = 1
+
+ def garbageReportDone(self, garbageReport):
+ self.setMagicWordResponse('%s garbage cycles' % garbageReport.getNumCycles())
+
+
+def magicWord(mw):
+ messenger.send('magicWord', [mw])
+
+
+import __builtin__
+__builtin__.magicWord = magicWord
diff --git a/otp/ai/TimeManager.py b/otp/ai/TimeManager.py
new file mode 100644
index 0000000..c7dc949
--- /dev/null
+++ b/otp/ai/TimeManager.py
@@ -0,0 +1,350 @@
+from pandac.PandaModules import *
+from direct.showbase.DirectObject import *
+from direct.distributed.ClockDelta import *
+from direct.task import Task
+from direct.distributed import DistributedObject
+from direct.directnotify import DirectNotifyGlobal
+from otp.otpbase import OTPGlobals
+from direct.showbase import PythonUtil
+from direct.showbase import GarbageReport
+import base64
+import time
+import os
+import sys
+import re
+
+class TimeManager(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('TimeManager')
+ neverDisable = 1
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.updateFreq = base.config.GetFloat('time-manager-freq', 1800)
+ self.minWait = base.config.GetFloat('time-manager-min-wait', 10)
+ self.maxUncertainty = base.config.GetFloat('time-manager-max-uncertainty', 1)
+ self.maxAttempts = base.config.GetInt('time-manager-max-attempts', 5)
+ self.extraSkew = base.config.GetInt('time-manager-extra-skew', 0)
+ if self.extraSkew != 0:
+ self.notify.info('Simulating clock skew of %0.3f s' % self.extraSkew)
+ self.reportFrameRateInterval = base.config.GetDouble('report-frame-rate-interval', 300.0)
+ self.talkResult = 0
+ self.thisContext = -1
+ self.nextContext = 0
+ self.attemptCount = 0
+ self.start = 0
+ self.lastAttempt = -self.minWait * 2
+ self.setFrameRateInterval(self.reportFrameRateInterval)
+ self._numClientGarbage = 0
+
+ def generate(self):
+ self._gotFirstTimeSync = False
+ if self.cr.timeManager != None:
+ self.cr.timeManager.delete()
+ self.cr.timeManager = self
+ DistributedObject.DistributedObject.generate(self)
+ self.accept(OTPGlobals.SynchronizeHotkey, self.handleHotkey)
+ self.accept('clock_error', self.handleClockError)
+ if __dev__ and base.config.GetBool('enable-garbage-hotkey', 0):
+ self.accept(OTPGlobals.DetectGarbageHotkey, self.handleDetectGarbageHotkey)
+ if self.updateFreq > 0:
+ self.startTask()
+ return
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ self.synchronize('TimeManager.announceGenerate')
+
+ def gotInitialTimeSync(self):
+ return self._gotFirstTimeSync
+
+ def disable(self):
+ self.ignore(OTPGlobals.SynchronizeHotkey)
+ if __dev__:
+ self.ignore(OTPGlobals.DetectGarbageHotkey)
+ self.ignore('clock_error')
+ self.stopTask()
+ taskMgr.remove('frameRateMonitor')
+ if self.cr.timeManager == self:
+ self.cr.timeManager = None
+ del self._gotFirstTimeSync
+ DistributedObject.DistributedObject.disable(self)
+ return
+
+ def delete(self):
+ self.ignore(OTPGlobals.SynchronizeHotkey)
+ self.ignore(OTPGlobals.DetectGarbageHotkey)
+ self.ignore('clock_error')
+ self.stopTask()
+ taskMgr.remove('frameRateMonitor')
+ if self.cr.timeManager == self:
+ self.cr.timeManager = None
+ DistributedObject.DistributedObject.delete(self)
+ return
+
+ def startTask(self):
+ self.stopTask()
+ taskMgr.doMethodLater(self.updateFreq, self.doUpdate, 'timeMgrTask')
+
+ def stopTask(self):
+ taskMgr.remove('timeMgrTask')
+
+ def doUpdate(self, task):
+ self.synchronize('timer')
+ taskMgr.doMethodLater(self.updateFreq, self.doUpdate, 'timeMgrTask')
+ return Task.done
+
+ def handleHotkey(self):
+ self.lastAttempt = -self.minWait * 2
+ if self.synchronize('user hotkey'):
+ self.talkResult = 1
+ else:
+ base.localAvatar.setChatAbsolute('Too soon.', CFSpeech | CFTimeout)
+
+ def handleClockError(self):
+ self.synchronize('clock error')
+
+ def synchronize(self, description):
+ now = globalClock.getRealTime()
+ if now - self.lastAttempt < self.minWait:
+ self.notify.debug('Not resyncing (too soon): %s' % description)
+ return 0
+ self.talkResult = 0
+ self.thisContext = self.nextContext
+ self.attemptCount = 0
+ self.nextContext = self.nextContext + 1 & 255
+ self.notify.info('Clock sync: %s' % description)
+ self.start = now
+ self.lastAttempt = now
+ self.sendUpdate('requestServerTime', [self.thisContext])
+ return 1
+
+ def serverTime(self, context, timestamp, timeOfDay):
+ end = globalClock.getRealTime()
+ aiTimeSkew = timeOfDay - self.cr.getServerTimeOfDay()
+ if context != self.thisContext:
+ self.notify.info('Ignoring TimeManager response for old context %d' % context)
+ return
+ elapsed = end - self.start
+ self.attemptCount += 1
+ self.notify.info('Clock sync roundtrip took %0.3f ms' % (elapsed * 1000.0))
+ self.notify.info('AI time delta is %s from server delta' % PythonUtil.formatElapsedSeconds(aiTimeSkew))
+ average = (self.start + end) / 2.0 - self.extraSkew
+ uncertainty = (end - self.start) / 2.0 + abs(self.extraSkew)
+ globalClockDelta.resynchronize(average, timestamp, uncertainty)
+ self.notify.info('Local clock uncertainty +/- %.3f s' % globalClockDelta.getUncertainty())
+ if globalClockDelta.getUncertainty() > self.maxUncertainty:
+ if self.attemptCount < self.maxAttempts:
+ self.notify.info('Uncertainty is too high, trying again.')
+ self.start = globalClock.getRealTime()
+ self.sendUpdate('requestServerTime', [self.thisContext])
+ return
+ self.notify.info('Giving up on uncertainty requirement.')
+ if self.talkResult:
+ base.localAvatar.setChatAbsolute('latency %0.0f ms, sync \xc2\xb1%0.0f ms' % (elapsed * 1000.0, globalClockDelta.getUncertainty() * 1000.0), CFSpeech | CFTimeout)
+ self._gotFirstTimeSync = True
+ messenger.send('gotTimeSync')
+
+ def setDisconnectReason(self, disconnectCode):
+ self.notify.info('Client disconnect reason %s.' % disconnectCode)
+ self.sendUpdate('setDisconnectReason', [disconnectCode])
+
+ def setExceptionInfo(self):
+ info = PythonUtil.describeException()
+ self.notify.info('Client exception: %s' % info)
+ self.sendUpdate('setExceptionInfo', [info])
+ self.cr.flush()
+
+ def setStackDump(self, dump):
+ self.notify.debug('Stack dump: %s' % fastRepr(dump))
+ maxLen = 900
+ dataLeft = base64.b64encode(dump)
+ index = 0
+ while dataLeft:
+ if len(dataLeft) >= maxLen:
+ data = dataLeft[:maxLen]
+ dataLeft = dataLeft[maxLen:]
+ else:
+ data = dataLeft
+ dataLeft = None
+ self.sendUpdate('setStackDump', [index, data])
+ index += 1
+ self.cr.flush()
+
+ return
+
+ def d_setSignature(self, signature, hash, pyc):
+ self.sendUpdate('setSignature', [signature, hash, pyc])
+
+ def sendCpuInfo(self):
+ if not base.pipe:
+ return
+ di = base.pipe.getDisplayInformation()
+ if di.getNumCpuCores() == 0 and hasattr(base.pipe, 'lookupCpuData'):
+ base.pipe.lookupCpuData()
+ di = base.pipe.getDisplayInformation()
+ di.updateCpuFrequency(0)
+ try:
+ cacheStatus = preloadCache()
+ except NameError:
+ cacheStatus = ''
+
+ ooghz = 1e-09
+ cpuSpeed = (di.getMaximumCpuFrequency() * ooghz, di.getCurrentCpuFrequency() * ooghz)
+ numCpuCores = di.getNumCpuCores()
+ numLogicalCpus = di.getNumLogicalCpus()
+ info = '%s|%s|%d|%d|%s|%s cpus' % (di.getCpuVendorString(),
+ di.getCpuBrandString(),
+ di.getCpuVersionInformation(),
+ di.getCpuBrandIndex(),
+ '%0.03f,%0.03f' % cpuSpeed,
+ '%d,%d' % (numCpuCores, numLogicalCpus))
+ print 'cpu info: %s' % info
+ self.sendUpdate('setCpuInfo', [info, cacheStatus])
+
+ def setFrameRateInterval(self, frameRateInterval):
+ if frameRateInterval == 0:
+ return
+ if not base.frameRateMeter:
+ maxFrameRateInterval = base.config.GetDouble('max-frame-rate-interval', 30.0)
+ globalClock.setAverageFrameRateInterval(min(frameRateInterval, maxFrameRateInterval))
+ taskMgr.remove('frameRateMonitor')
+ taskMgr.doMethodLater(frameRateInterval, self.frameRateMonitor, 'frameRateMonitor')
+
+ def frameRateMonitor(self, task):
+ from otp.avatar.Avatar import Avatar
+ vendorId = 0
+ deviceId = 0
+ processMemory = 0
+ pageFileUsage = 0
+ physicalMemory = 0
+ pageFaultCount = 0
+ osInfo = (os.name,
+ 0,
+ 0,
+ 0)
+ cpuSpeed = (0, 0)
+ numCpuCores = 0
+ numLogicalCpus = 0
+ apiName = 'None'
+ if getattr(base, 'pipe', None):
+ di = base.pipe.getDisplayInformation()
+ if di.getDisplayState() == DisplayInformation.DSSuccess:
+ vendorId = di.getVendorId()
+ deviceId = di.getDeviceId()
+ di.updateMemoryInformation()
+ oomb = 1.0 / (1024.0 * 1024.0)
+ processMemory = di.getProcessMemory() * oomb
+ pageFileUsage = di.getPageFileUsage() * oomb
+ physicalMemory = di.getPhysicalMemory() * oomb
+ pageFaultCount = di.getPageFaultCount() / 1000.0
+ osInfo = (os.name,
+ di.getOsPlatformId(),
+ di.getOsVersionMajor(),
+ di.getOsVersionMinor())
+ if sys.platform == 'darwin':
+ osInfo = self.getMacOsInfo(osInfo)
+ di.updateCpuFrequency(0)
+ ooghz = 1e-09
+ cpuSpeed = (di.getMaximumCpuFrequency() * ooghz, di.getCurrentCpuFrequency() * ooghz)
+ numCpuCores = di.getNumCpuCores()
+ numLogicalCpus = di.getNumLogicalCpus()
+ apiName = base.pipe.getInterfaceName()
+ self.d_setFrameRate(max(0, globalClock.getAverageFrameRate()), max(0, globalClock.calcFrameRateDeviation()), len(Avatar.ActiveAvatars), base.locationCode or '', max(0, time.time() - base.locationCodeChanged), max(0, globalClock.getRealTime()), base.gameOptionsCode, vendorId, deviceId, processMemory, pageFileUsage, physicalMemory, pageFaultCount, osInfo, cpuSpeed, numCpuCores, numLogicalCpus, apiName)
+ return task.again
+
+ def d_setFrameRate(self, fps, deviation, numAvs, locationCode, timeInLocation, timeInGame, gameOptionsCode, vendorId, deviceId, processMemory, pageFileUsage, physicalMemory, pageFaultCount, osInfo, cpuSpeed, numCpuCores, numLogicalCpus, apiName):
+ info = '%0.1f fps|%0.3fd|%s avs|%s|%d|%d|%s|0x%04x|0x%04x|%0.1fMB|%0.1fMB|%0.1fMB|%d|%s|%s|%s cpus|%s' % (fps,
+ deviation,
+ numAvs,
+ locationCode,
+ timeInLocation,
+ timeInGame,
+ gameOptionsCode,
+ vendorId,
+ deviceId,
+ processMemory,
+ pageFileUsage,
+ physicalMemory,
+ pageFaultCount,
+ '%s.%d.%d.%d' % osInfo,
+ '%0.03f,%0.03f' % cpuSpeed,
+ '%d,%d' % (numCpuCores, numLogicalCpus),
+ apiName)
+ print 'frame rate: %s' % info
+ self.sendUpdate('setFrameRate', [fps,
+ deviation,
+ numAvs,
+ locationCode,
+ timeInLocation,
+ timeInGame,
+ gameOptionsCode,
+ vendorId,
+ deviceId,
+ processMemory,
+ pageFileUsage,
+ physicalMemory,
+ pageFaultCount,
+ osInfo,
+ cpuSpeed,
+ numCpuCores,
+ numLogicalCpus,
+ apiName])
+
+ if __dev__:
+
+ def handleDetectGarbageHotkey(self):
+ self._numClientGarbage = GarbageReport.b_checkForGarbageLeaks(wantReply=True)
+ if self._numClientGarbage:
+ s = '%s client garbage cycles found, see log' % self._numClientGarbage
+ else:
+ s = '0 client garbage cycles found'
+ localAvatar.setChatAbsolute(s, CFSpeech | CFTimeout)
+
+ def d_checkForGarbageLeaks(self, wantReply):
+ self.sendUpdate('checkForGarbageLeaks', [wantReply])
+
+ def setNumAIGarbageLeaks(self, numLeaks):
+ if self._numClientGarbage and numLeaks:
+ s = '%s client and %s AI garbage cycles found, see logs' % (self._numClientGarbage, numLeaks)
+ elif numLeaks:
+ s = '0 client and %s AI garbage cycles found, see log' % numLeaks
+ else:
+ s = '0 client and 0 AI garbage cycles found'
+ localAvatar.setChatAbsolute(s, CFSpeech | CFTimeout)
+
+ def d_setClientGarbageLeak(self, num, description):
+ self.sendUpdate('setClientGarbageLeak', [num, description])
+
+ def getMacOsInfo(self, defaultOsInfo):
+ result = defaultOsInfo
+ try:
+ theFile = open('/System/Library/CoreServices/SystemVersion.plist')
+ except IOError:
+ pass
+ else:
+ key = re.search('ProductUserVisibleVersion\\s*' + '(.*?)', theFile.read())
+ theFile.close()
+ if key is not None:
+ try:
+ verString = key.group(1)
+ parts = verString.split('.')
+ major = int(parts[0])
+ minor = int(parts[1])
+ bugfix = int(parts[2])
+ result = (sys.platform,
+ bugfix,
+ major,
+ minor)
+ except Exception, e:
+ self.notify.debug('getMacOsInfo %s' % str(e))
+
+ self.notify.debug('getMacOsInfo returning %s' % str(result))
+ return result
+
+ def checkAvOnDistrict(self, av, context):
+ self.sendUpdate('checkAvOnDistrict', [context, av.doId])
+
+ def checkAvOnDistrictResult(self, context, avId, present):
+ av = self.cr.getDo(avId)
+ if av:
+ av._zombieCheckResult(context, present)
diff --git a/otp/ai/__init__.py b/otp/ai/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/avatar/Avatar.py b/otp/avatar/Avatar.py
new file mode 100644
index 0000000..c77ce36
--- /dev/null
+++ b/otp/avatar/Avatar.py
@@ -0,0 +1,587 @@
+from pandac.PandaModules import *
+from libotp import Nametag, NametagGroup
+from libotp import CFSpeech, CFThought, CFTimeout, CFPageButton, CFNoQuitButton, CFQuitButton
+from otp.otpbase import OTPGlobals
+from otp.otpbase import OTPLocalizer
+from direct.actor.Actor import Actor
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import ClockDelta
+from otp.avatar.ShadowCaster import ShadowCaster
+import random
+from otp.otpbase import OTPRender
+from direct.showbase.PythonUtil import recordCreationStack
+teleportNotify = DirectNotifyGlobal.directNotify.newCategory('Teleport')
+teleportNotify.showTime = True
+if config.GetBool('want-teleport-debug', 1):
+ teleportNotify.setDebug(1)
+
+def reconsiderAllUnderstandable():
+ for av in Avatar.ActiveAvatars:
+ av.considerUnderstandable()
+
+
+class Avatar(Actor, ShadowCaster):
+ notify = directNotify.newCategory('Avatar')
+ ActiveAvatars = []
+ ManagesNametagAmbientLightChanged = False
+
+ def __init__(self, other = None):
+ self.name = ''
+ try:
+ self.Avatar_initialized
+ return
+ except:
+ self.Avatar_initialized = 1
+
+ Actor.__init__(self, None, None, other, flattenable=0, setFinal=1)
+ ShadowCaster.__init__(self)
+ self.__font = OTPGlobals.getInterfaceFont()
+ self.soundChatBubble = None
+ self.avatarType = ''
+ self.nametagNodePath = None
+ self.__nameVisible = 1
+ self.nametag = NametagGroup()
+ self.nametag.setAvatar(self)
+ self.nametag.setFont(OTPGlobals.getInterfaceFont())
+ self.nametag2dContents = Nametag.CName | Nametag.CSpeech
+ self.nametag2dDist = Nametag.CName | Nametag.CSpeech
+ self.nametag2dNormalContents = Nametag.CName | Nametag.CSpeech
+ self.nametag3d = self.attachNewNode('nametag3d')
+ self.nametag3d.setTag('cam', 'nametag')
+ self.nametag3d.setLightOff()
+ if self.ManagesNametagAmbientLightChanged:
+ self.acceptNametagAmbientLightChange()
+ OTPRender.renderReflection(False, self.nametag3d, 'otp_avatar_nametag', None)
+ self.getGeomNode().showThrough(OTPRender.ShadowCameraBitmask)
+ self.nametag3d.hide(OTPRender.ShadowCameraBitmask)
+ self.collTube = None
+ self.battleTube = None
+ self.scale = 1.0
+ self.nametagScale = 1.0
+ self.height = 0.0
+ self.battleTubeHeight = 0.0
+ self.battleTubeRadius = 0.0
+ self.style = None
+ self.commonChatFlags = 0
+ self.understandable = 1
+ self.setPlayerType(NametagGroup.CCNormal)
+ self.ghostMode = 0
+ self.__chatParagraph = None
+ self.__chatMessage = None
+ self.__chatFlags = 0
+ self.__chatPageNumber = None
+ self.__chatAddressee = None
+ self.__chatDialogueList = []
+ self.__chatSet = 0
+ self.__chatLocal = 0
+ self.__currentDialogue = None
+ self.whitelistChatFlags = 0
+ return
+
+ def delete(self):
+ try:
+ self.Avatar_deleted
+ except:
+ self.deleteNametag3d()
+ Actor.cleanup(self)
+ if self.ManagesNametagAmbientLightChanged:
+ self.ignoreNametagAmbientLightChange()
+ self.Avatar_deleted = 1
+ del self.__font
+ del self.style
+ del self.soundChatBubble
+ del self.nametag
+ self.nametag3d.removeNode()
+ ShadowCaster.delete(self)
+ Actor.delete(self)
+
+ def acceptNametagAmbientLightChange(self):
+ self.accept('nametagAmbientLightChanged', self.nametagAmbientLightChanged)
+
+ def ignoreNametagAmbientLightChange(self):
+ self.ignore('nametagAmbientLightChanged')
+
+ def isLocal(self):
+ return 0
+
+ def isPet(self):
+ return False
+
+ def isProxy(self):
+ return False
+
+ def setPlayerType(self, playerType):
+ self.playerType = playerType
+ if not hasattr(self, 'nametag'):
+ self.notify.warning('no nametag attributed, but would have been used.')
+ return
+ if self.isUnderstandable():
+ self.nametag.setColorCode(self.playerType)
+ else:
+ self.nametag.setColorCode(NametagGroup.CCNoChat)
+
+ def setCommonChatFlags(self, commonChatFlags):
+ self.commonChatFlags = commonChatFlags
+ self.considerUnderstandable()
+ if self == base.localAvatar:
+ reconsiderAllUnderstandable()
+
+ def setWhitelistChatFlags(self, whitelistChatFlags):
+ self.whitelistChatFlags = whitelistChatFlags
+ self.considerUnderstandable()
+ if self == base.localAvatar:
+ reconsiderAllUnderstandable()
+
+ def considerUnderstandable(self):
+ speed = 0
+ if self.playerType in (NametagGroup.CCNormal, NametagGroup.CCFreeChat, NametagGroup.CCSpeedChat):
+ self.setPlayerType(NametagGroup.CCSpeedChat)
+ speed = 1
+ if hasattr(base, 'localAvatar') and self == base.localAvatar:
+ self.understandable = 1
+ self.setPlayerType(NametagGroup.CCFreeChat)
+ elif self.playerType == NametagGroup.CCSuit:
+ self.understandable = 1
+ self.setPlayerType(NametagGroup.CCSuit)
+ elif self.playerType not in (NametagGroup.CCNormal, NametagGroup.CCFreeChat, NametagGroup.CCSpeedChat):
+ self.understandable = 1
+ self.setPlayerType(NametagGroup.CCNoChat)
+ elif hasattr(base, 'localAvatar') and self.commonChatFlags & base.localAvatar.commonChatFlags & OTPGlobals.CommonChat:
+ self.understandable = 1
+ self.setPlayerType(NametagGroup.CCFreeChat)
+ elif self.commonChatFlags & OTPGlobals.SuperChat:
+ self.understandable = 1
+ self.setPlayerType(NametagGroup.CCFreeChat)
+ elif hasattr(base, 'localAvatar') and base.localAvatar.commonChatFlags & OTPGlobals.SuperChat:
+ self.understandable = 1
+ self.setPlayerType(NametagGroup.CCFreeChat)
+ elif base.cr.getFriendFlags(self.doId) & OTPGlobals.FriendChat:
+ self.understandable = 1
+ self.setPlayerType(NametagGroup.CCFreeChat)
+ elif base.cr.playerFriendsManager.findPlayerIdFromAvId(self.doId) is not None:
+ playerInfo = base.cr.playerFriendsManager.findPlayerInfoFromAvId(self.doId)
+ if playerInfo.openChatFriendshipYesNo:
+ self.understandable = 1
+ self.nametag.setColorCode(NametagGroup.CCFreeChat)
+ elif playerInfo.isUnderstandable():
+ self.understandable = 1
+ else:
+ self.understandable = 0
+ elif hasattr(base, 'localAvatar') and self.whitelistChatFlags & base.localAvatar.whitelistChatFlags:
+ self.understandable = 1
+ else:
+ self.understandable = 0
+ if not hasattr(self, 'nametag'):
+ self.notify.warning('no nametag attributed, but would have been used')
+ else:
+ self.nametag.setColorCode(self.playerType)
+ return
+
+ def isUnderstandable(self):
+ return self.understandable
+
+ def setDNAString(self, dnaString):
+ pass
+
+ def setDNA(self, dna):
+ pass
+
+ def getAvatarScale(self):
+ return self.scale
+
+ def setAvatarScale(self, scale):
+ if self.scale != scale:
+ self.scale = scale
+ self.getGeomNode().setScale(scale)
+ self.setHeight(self.height)
+
+ def getNametagScale(self):
+ return self.nametagScale
+
+ def setNametagScale(self, scale):
+ self.nametagScale = scale
+ self.nametag3d.setScale(scale)
+
+ def adjustNametag3d(self, parentScale = 1.0):
+ self.nametag3d.setPos(0, 0, self.height + 0.5)
+
+ def getHeight(self):
+ return self.height
+
+ def setHeight(self, height):
+ self.height = height
+ self.adjustNametag3d()
+ if self.collTube:
+ self.collTube.setPointB(0, 0, height - self.getRadius())
+ if self.collNodePath:
+ self.collNodePath.forceRecomputeBounds()
+ if self.battleTube:
+ self.battleTube.setPointB(0, 0, height - self.getRadius())
+
+ def getRadius(self):
+ return OTPGlobals.AvatarDefaultRadius
+
+ def getName(self):
+ return self.name
+
+ def getType(self):
+ return self.avatarType
+
+ def setName(self, name):
+ if hasattr(self, 'isDisguised'):
+ if self.isDisguised:
+ return
+ self.name = name
+ if hasattr(self, 'nametag'):
+ self.nametag.setName(name)
+
+ def setDisplayName(self, str):
+ if hasattr(self, 'isDisguised'):
+ if self.isDisguised:
+ return
+ self.nametag.setDisplayName(str)
+
+ def getFont(self):
+ return self.__font
+
+ def setFont(self, font):
+ self.__font = font
+ self.nametag.setFont(font)
+
+ def getStyle(self):
+ return self.style
+
+ def setStyle(self, style):
+ self.style = style
+
+ def getDialogueArray(self):
+ return None
+
+ def playCurrentDialogue(self, dialogue, chatFlags, interrupt = 1):
+ if interrupt and self.__currentDialogue is not None:
+ self.__currentDialogue.stop()
+ self.__currentDialogue = dialogue
+ if dialogue:
+ base.playSfx(dialogue, node=self)
+ elif chatFlags & CFSpeech != 0 and self.nametag.getNumChatPages() > 0:
+ self.playDialogueForString(self.nametag.getChat())
+ if self.soundChatBubble != None:
+ base.playSfx(self.soundChatBubble, node=self)
+ return
+
+ def playDialogueForString(self, chatString):
+ searchString = chatString.lower()
+ if searchString.find(OTPLocalizer.DialogSpecial) >= 0:
+ type = 'special'
+ elif searchString.find(OTPLocalizer.DialogExclamation) >= 0:
+ type = 'exclamation'
+ elif searchString.find(OTPLocalizer.DialogQuestion) >= 0:
+ type = 'question'
+ elif random.randint(0, 1):
+ type = 'statementA'
+ else:
+ type = 'statementB'
+ stringLength = len(chatString)
+ if stringLength <= OTPLocalizer.DialogLength1:
+ length = 1
+ elif stringLength <= OTPLocalizer.DialogLength2:
+ length = 2
+ elif stringLength <= OTPLocalizer.DialogLength3:
+ length = 3
+ else:
+ length = 4
+ self.playDialogue(type, length)
+
+ def playDialogue(self, type, length):
+ dialogueArray = self.getDialogueArray()
+ if dialogueArray == None:
+ return
+ sfxIndex = None
+ if type == 'statementA' or type == 'statementB':
+ if length == 1:
+ sfxIndex = 0
+ elif length == 2:
+ sfxIndex = 1
+ elif length >= 3:
+ sfxIndex = 2
+ elif type == 'question':
+ sfxIndex = 3
+ elif type == 'exclamation':
+ sfxIndex = 4
+ elif type == 'special':
+ sfxIndex = 5
+ else:
+ notify.error('unrecognized dialogue type: ', type)
+ if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None:
+ base.playSfx(dialogueArray[sfxIndex], node=self)
+ return
+
+ def getDialogueSfx(self, type, length):
+ retval = None
+ dialogueArray = self.getDialogueArray()
+ if dialogueArray == None:
+ return
+ sfxIndex = None
+ if type == 'statementA' or type == 'statementB':
+ if length == 1:
+ sfxIndex = 0
+ elif length == 2:
+ sfxIndex = 1
+ elif length >= 3:
+ sfxIndex = 2
+ elif type == 'question':
+ sfxIndex = 3
+ elif type == 'exclamation':
+ sfxIndex = 4
+ elif type == 'special':
+ sfxIndex = 5
+ else:
+ notify.error('unrecognized dialogue type: ', type)
+ if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None:
+ retval = dialogueArray[sfxIndex]
+ return retval
+
+ def setChatAbsolute(self, chatString, chatFlags, dialogue = None, interrupt = 1):
+ self.nametag.setChat(chatString, chatFlags)
+ self.playCurrentDialogue(dialogue, chatFlags, interrupt)
+
+ def setChatMuted(self, chatString, chatFlags, dialogue = None, interrupt = 1, quiet = 0):
+ pass
+
+ def displayTalk(self, chatString):
+ if not base.cr.avatarFriendsManager.checkIgnored(self.doId):
+ if base.talkAssistant.isThought(chatString):
+ self.nametag.setChat(base.talkAssistant.removeThoughtPrefix(chatString), CFThought)
+ else:
+ self.nametag.setChat(chatString, CFSpeech | CFTimeout)
+
+ def clearChat(self):
+ self.nametag.clearChat()
+
+ def isInView(self):
+ pos = self.getPos(camera)
+ eyePos = Point3(pos[0], pos[1], pos[2] + self.getHeight())
+ return base.camNode.isInView(eyePos)
+
+ def getNameVisible(self):
+ return self.__nameVisible
+
+ def setNameVisible(self, bool):
+ self.__nameVisible = bool
+ if bool:
+ self.showName()
+ if not bool:
+ self.hideName()
+
+ def hideName(self):
+ self.nametag.getNametag3d().setContents(Nametag.CSpeech | Nametag.CThought)
+
+ def showName(self):
+ if self.__nameVisible and not self.ghostMode:
+ self.nametag.getNametag3d().setContents(Nametag.CName | Nametag.CSpeech | Nametag.CThought)
+
+ def hideNametag2d(self):
+ self.nametag2dContents = 0
+ self.nametag.getNametag2d().setContents(self.nametag2dContents & self.nametag2dDist)
+
+ def showNametag2d(self):
+ self.nametag2dContents = self.nametag2dNormalContents
+ if self.ghostMode:
+ self.nametag2dContents = Nametag.CSpeech
+ self.nametag.getNametag2d().setContents(self.nametag2dContents & self.nametag2dDist)
+
+ def hideNametag3d(self):
+ self.nametag.getNametag3d().setContents(0)
+
+ def showNametag3d(self):
+ if self.__nameVisible and not self.ghostMode:
+ self.nametag.getNametag3d().setContents(Nametag.CName | Nametag.CSpeech | Nametag.CThought)
+ else:
+ self.nametag.getNametag3d().setContents(0)
+
+ def setPickable(self, flag):
+ self.nametag.setActive(flag)
+
+ def clickedNametag(self):
+ if self.nametag.hasButton():
+ self.advancePageNumber()
+ elif self.nametag.isActive():
+ messenger.send('clickedNametag', [self])
+
+ def setPageChat(self, addressee, paragraph, message, quitButton, extraChatFlags = None, dialogueList = [], pageButton = True):
+ self.__chatAddressee = addressee
+ self.__chatPageNumber = None
+ self.__chatParagraph = paragraph
+ self.__chatMessage = message
+ if extraChatFlags is None:
+ self.__chatFlags = CFSpeech
+ else:
+ self.__chatFlags = CFSpeech | extraChatFlags
+ self.__chatDialogueList = dialogueList
+ self.__chatSet = 0
+ self.__chatLocal = 0
+ self.__updatePageChat()
+ if addressee == base.localAvatar.doId:
+ if pageButton:
+ self.__chatFlags |= CFPageButton
+ if quitButton == None:
+ self.__chatFlags |= CFNoQuitButton
+ elif quitButton:
+ self.__chatFlags |= CFQuitButton
+ self.b_setPageNumber(self.__chatParagraph, 0)
+ return
+
+ def setLocalPageChat(self, message, quitButton, extraChatFlags = None, dialogueList = []):
+ self.__chatAddressee = base.localAvatar.doId
+ self.__chatPageNumber = None
+ self.__chatParagraph = None
+ self.__chatMessage = message
+ if extraChatFlags is None:
+ self.__chatFlags = CFSpeech
+ else:
+ self.__chatFlags = CFSpeech | extraChatFlags
+ self.__chatDialogueList = dialogueList
+ self.__chatSet = 1
+ self.__chatLocal = 1
+ self.__chatFlags |= CFPageButton
+ if quitButton == None:
+ self.__chatFlags |= CFNoQuitButton
+ elif quitButton:
+ self.__chatFlags |= CFQuitButton
+ if len(dialogueList) > 0:
+ dialogue = dialogueList[0]
+ else:
+ dialogue = None
+ self.clearChat()
+ self.setChatAbsolute(message, self.__chatFlags, dialogue)
+ self.setPageNumber(None, 0)
+ return
+
+ def setPageNumber(self, paragraph, pageNumber, timestamp = None):
+ if timestamp == None:
+ elapsed = 0.0
+ else:
+ elapsed = ClockDelta.globalClockDelta.localElapsedTime(timestamp)
+ self.__chatPageNumber = [paragraph, pageNumber]
+ self.__updatePageChat()
+ if hasattr(self, 'uniqueName'):
+ if pageNumber >= 0:
+ messenger.send(self.uniqueName('nextChatPage'), [pageNumber, elapsed])
+ else:
+ messenger.send(self.uniqueName('doneChatPage'), [elapsed])
+ elif pageNumber >= 0:
+ messenger.send('nextChatPage', [pageNumber, elapsed])
+ else:
+ messenger.send('doneChatPage', [elapsed])
+ return
+
+ def advancePageNumber(self):
+ if self.__chatAddressee == base.localAvatar.doId and self.__chatPageNumber != None and self.__chatPageNumber[0] == self.__chatParagraph:
+ pageNumber = self.__chatPageNumber[1]
+ if pageNumber >= 0:
+ pageNumber += 1
+ if pageNumber >= self.nametag.getNumChatPages():
+ pageNumber = -1
+ if self.__chatLocal:
+ self.setPageNumber(self.__chatParagraph, pageNumber)
+ else:
+ self.b_setPageNumber(self.__chatParagraph, pageNumber)
+ return
+
+ def __updatePageChat(self):
+ if self.__chatPageNumber != None and self.__chatPageNumber[0] == self.__chatParagraph:
+ pageNumber = self.__chatPageNumber[1]
+ if pageNumber >= 0:
+ if not self.__chatSet:
+ if len(self.__chatDialogueList) > 0:
+ dialogue = self.__chatDialogueList[0]
+ else:
+ dialogue = None
+ self.setChatAbsolute(self.__chatMessage, self.__chatFlags, dialogue)
+ self.__chatSet = 1
+ if pageNumber < self.nametag.getNumChatPages():
+ self.nametag.setPageNumber(pageNumber)
+ if pageNumber > 0:
+ if len(self.__chatDialogueList) > pageNumber:
+ dialogue = self.__chatDialogueList[pageNumber]
+ else:
+ dialogue = None
+ self.playCurrentDialogue(dialogue, self.__chatFlags)
+ else:
+ self.clearChat()
+ else:
+ self.clearChat()
+ return
+
+ def getAirborneHeight(self):
+ height = self.getPos(self.shadowPlacer.shadowNodePath)
+ return height.getZ() + 0.025
+
+ def initializeNametag3d(self):
+ self.deleteNametag3d()
+ nametagNode = self.nametag.getNametag3d()
+ self.nametagNodePath = self.nametag3d.attachNewNode(nametagNode)
+ iconNodePath = self.nametag.getNameIcon()
+ for cJoint in self.getNametagJoints():
+ cJoint.clearNetTransforms()
+ cJoint.addNetTransform(nametagNode)
+
+ def nametagAmbientLightChanged(self, newlight):
+ self.nametag3d.setLightOff()
+ if newlight:
+ self.nametag3d.setLight(newlight)
+
+ def deleteNametag3d(self):
+ if self.nametagNodePath:
+ self.nametagNodePath.removeNode()
+ self.nametagNodePath = None
+ return
+
+ def initializeBodyCollisions(self, collIdStr):
+ self.collTube = CollisionTube(0, 0, 0.5, 0, 0, self.height - self.getRadius(), self.getRadius())
+ self.collNode = CollisionNode(collIdStr)
+ self.collNode.addSolid(self.collTube)
+ self.collNodePath = self.attachNewNode(self.collNode)
+ if self.ghostMode:
+ self.collNode.setCollideMask(OTPGlobals.GhostBitmask)
+ else:
+ self.collNode.setCollideMask(OTPGlobals.WallBitmask)
+
+ def stashBodyCollisions(self):
+ if hasattr(self, 'collNodePath'):
+ self.collNodePath.stash()
+
+ def unstashBodyCollisions(self):
+ if hasattr(self, 'collNodePath'):
+ self.collNodePath.unstash()
+
+ def disableBodyCollisions(self):
+ if hasattr(self, 'collNodePath'):
+ self.collNodePath.removeNode()
+ del self.collNodePath
+ self.collTube = None
+ return
+
+ def addActive(self):
+ if base.wantNametags:
+ try:
+ Avatar.ActiveAvatars.remove(self)
+ except ValueError:
+ pass
+
+ Avatar.ActiveAvatars.append(self)
+ self.nametag.manage(base.marginManager)
+ self.accept(self.nametag.getUniqueId(), self.clickedNametag)
+
+ def removeActive(self):
+ if base.wantNametags:
+ try:
+ Avatar.ActiveAvatars.remove(self)
+ except ValueError:
+ pass
+
+ self.nametag.unmanage(base.marginManager)
+ self.ignore(self.nametag.getUniqueId())
+
+ def loop(self, animName, restart = 1, partName = None, fromFrame = None, toFrame = None):
+ return Actor.loop(self, animName, restart, partName, fromFrame, toFrame)
diff --git a/otp/avatar/AvatarDNA.py b/otp/avatar/AvatarDNA.py
new file mode 100644
index 0000000..f7e7f65
--- /dev/null
+++ b/otp/avatar/AvatarDNA.py
@@ -0,0 +1,26 @@
+from pandac.PandaModules import *
+from direct.directnotify.DirectNotifyGlobal import *
+import random
+from direct.distributed.PyDatagram import PyDatagram
+from direct.distributed.PyDatagramIterator import PyDatagramIterator
+notify = directNotify.newCategory('AvatarDNA')
+
+class AvatarDNA:
+
+ def __str__(self):
+ return 'avatar parent class: type undefined'
+
+ def makeNetString(self):
+ notify.error('called makeNetString on avatarDNA parent class')
+
+ def printNetString(self):
+ string = self.makeNetString()
+ dg = PyDatagram(string)
+ dg.dumpHex(ostream)
+
+ def makeFromNetString(self, string):
+ notify.error('called makeFromNetString on avatarDNA parent class')
+
+ def getType(self):
+ notify.error('Invalid DNA type: ', self.type)
+ return type
diff --git a/otp/avatar/AvatarDetail.py b/otp/avatar/AvatarDetail.py
new file mode 100644
index 0000000..662fe6f
--- /dev/null
+++ b/otp/avatar/AvatarDetail.py
@@ -0,0 +1,51 @@
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from otp.avatar import Avatar
+
+class AvatarDetail:
+ notify = directNotify.newCategory('AvatarDetail')
+
+ def __init__(self, doId, callWhenDone):
+ self.id = doId
+ self.callWhenDone = callWhenDone
+ self.enterQuery()
+
+ def isReady(self):
+ return true
+
+ def getId(self):
+ return self.id
+
+ def enterQuery(self):
+ self.avatar = base.cr.doId2do.get(self.id)
+ if self.avatar != None and not self.avatar.ghostMode:
+ self.createdAvatar = 0
+ dclass = self.getDClass()
+ self.__handleResponse(True, self.avatar, dclass)
+ else:
+ self.avatar = self.createHolder()
+ self.createdAvatar = 1
+ self.avatar.doId = self.id
+ dclass = self.getDClass()
+ base.cr.getAvatarDetails(self.avatar, self.__handleResponse, dclass)
+ return
+
+ def exitQuery(self):
+ return true
+
+ def createHolder(self):
+ pass
+
+ def getDClass(self):
+ pass
+
+ def __handleResponse(self, gotData, avatar, dclass):
+ if avatar != self.avatar:
+ self.notify.warning('Ignoring unexpected request for avatar %s' % avatar.doId)
+ return
+ if gotData:
+ self.callWhenDone(self.avatar)
+ del self.callWhenDone
+ else:
+ self.callWhenDone(None)
+ del self.callWhenDone
+ return
diff --git a/otp/avatar/AvatarHandle.py b/otp/avatar/AvatarHandle.py
new file mode 100644
index 0000000..211122c
--- /dev/null
+++ b/otp/avatar/AvatarHandle.py
@@ -0,0 +1,24 @@
+
+
+class AvatarHandle:
+ dclassName = 'AvatarHandle'
+
+ def getName(self):
+ if __dev__:
+ pass
+ return ''
+
+ def isOnline(self):
+ if __dev__:
+ pass
+ return False
+
+ def isUnderstandable(self):
+ if __dev__:
+ pass
+ return True
+
+ def setTalkWhisper(self, fromAV, fromAC, avatarName, chat, mods, flags):
+ newText, scrubbed = localAvatar.scrubTalk(chat, mods)
+ base.talkAssistant.receiveWhisperTalk(fromAV, avatarName, fromAC, None, self.avatarId, self.getName(), newText, scrubbed)
+ return
diff --git a/otp/avatar/AvatarPanel.py b/otp/avatar/AvatarPanel.py
new file mode 100644
index 0000000..5a00d56
--- /dev/null
+++ b/otp/avatar/AvatarPanel.py
@@ -0,0 +1,75 @@
+from pandac.PandaModules import *
+from direct.gui.DirectGui import *
+from direct.showbase import DirectObject
+import Avatar
+from direct.distributed import DistributedObject
+
+class AvatarPanel(DirectObject.DirectObject):
+ currentAvatarPanel = None
+
+ def __init__(self, avatar, FriendsListPanel = None):
+ if AvatarPanel.currentAvatarPanel:
+ AvatarPanel.currentAvatarPanel.cleanup()
+ AvatarPanel.currentAvatarPanel = self
+ self.friendsListShown = False
+ self.FriendsListPanel = FriendsListPanel
+ if FriendsListPanel:
+ self.friendsListShown = FriendsListPanel.isFriendsListShown()
+ FriendsListPanel.hideFriendsList()
+ if avatar:
+ self.avatar = avatar
+ self.avName = avatar.getName()
+ else:
+ self.avatar = None
+ self.avName = 'Player'
+ if hasattr(avatar, 'uniqueName'):
+ self.avId = avatar.doId
+ self.avDisableName = avatar.uniqueName('disable')
+ self.avGenerateName = avatar.uniqueName('generate')
+ self.avHpChangeName = avatar.uniqueName('hpChange')
+ if base.cr.doId2do.has_key(self.avId):
+ self.avatar = base.cr.doId2do[self.avId]
+ else:
+ self.avDisableName = None
+ self.avGenerateName = None
+ self.avHpChangeName = None
+ self.avId = None
+ if self.avDisableName:
+ self.accept(self.avDisableName, self.__handleDisableAvatar)
+ return
+
+ def cleanup(self):
+ if AvatarPanel.currentAvatarPanel != self:
+ return
+ if self.avDisableName:
+ self.ignore(self.avDisableName)
+ if self.avGenerateName:
+ self.ignore(self.avGenerateName)
+ if self.avHpChangeName:
+ self.ignore(self.avHpChangeName)
+ AvatarPanel.currentAvatarPanel = None
+ return
+
+ def __handleClose(self):
+ self.cleanup()
+ AvatarPanel.currentAvatarPanel = None
+ if self.friendsListShown:
+ self.FriendsListPanel.showFriendsList()
+ return
+
+ def __handleDisableAvatar(self):
+ if AvatarPanel.currentAvatarPanel:
+ AvatarPanel.currentAvatarPanel.handleDisableAvatar()
+ else:
+ self.handleDisableAvatar()
+
+ def handleDisableAvatar(self):
+ self.cleanup()
+ AvatarPanel.currentAvatarPanel = None
+ return
+
+ def isHidden(self):
+ return 1
+
+ def getType(self):
+ return None
diff --git a/otp/avatar/DistributedAvatar.py b/otp/avatar/DistributedAvatar.py
new file mode 100644
index 0000000..8001717
--- /dev/null
+++ b/otp/avatar/DistributedAvatar.py
@@ -0,0 +1,262 @@
+import time
+import string
+from pandac.PandaModules import *
+from direct.distributed import DistributedNode
+from direct.actor.DistributedActor import DistributedActor
+from direct.task import Task
+from direct.showbase import PythonUtil
+from libotp import Nametag
+from otp.otpbase import OTPGlobals
+from otp.otpbase import OTPLocalizer
+from otp.speedchat import SCDecoders
+from otp.chat import ChatGarbler
+from otp.chat import ChatManager
+import random
+from Avatar import Avatar
+import AvatarDNA
+
+class DistributedAvatar(DistributedActor, Avatar):
+ HpTextGenerator = TextNode('HpTextGenerator')
+ HpTextEnabled = 1
+ ManagesNametagAmbientLightChanged = True
+
+ def __init__(self, cr):
+ try:
+ self.DistributedAvatar_initialized
+ return
+ except:
+ self.DistributedAvatar_initialized = 1
+
+ Avatar.__init__(self)
+ DistributedActor.__init__(self, cr)
+ self.hpText = None
+ self.hp = None
+ self.maxHp = None
+ return
+
+ def disable(self):
+ try:
+ del self.DistributedAvatar_announced
+ except:
+ return
+
+ self.reparentTo(hidden)
+ self.removeActive()
+ self.disableBodyCollisions()
+ self.hideHpText()
+ self.hp = None
+ self.ignore('nameTagShowAvId')
+ self.ignore('nameTagShowName')
+ DistributedActor.disable(self)
+ return
+
+ def delete(self):
+ try:
+ self.DistributedAvatar_deleted
+ except:
+ self.DistributedAvatar_deleted = 1
+ Avatar.delete(self)
+ DistributedActor.delete(self)
+
+ def generate(self):
+ DistributedActor.generate(self)
+ if not self.isLocal():
+ self.addActive()
+ self.considerUnderstandable()
+ self.setParent(OTPGlobals.SPHidden)
+ self.setTag('avatarDoId', str(self.doId))
+ self.accept('nameTagShowAvId', self.__nameTagShowAvId)
+ self.accept('nameTagShowName', self.__nameTagShowName)
+
+ def announceGenerate(self):
+ try:
+ self.DistributedAvatar_announced
+ return
+ except:
+ self.DistributedAvatar_announced = 1
+
+ if not self.isLocal():
+ self.initializeBodyCollisions('distAvatarCollNode-' + str(self.doId))
+ DistributedActor.announceGenerate(self)
+
+ def __setTags(self, extra = None):
+ if hasattr(base, 'idTags'):
+ if base.idTags:
+ self.__nameTagShowAvId()
+ else:
+ self.__nameTagShowName()
+
+ def do_setParent(self, parentToken):
+ if not self.isDisabled():
+ if parentToken == OTPGlobals.SPHidden:
+ self.nametag2dDist &= ~Nametag.CName
+ else:
+ self.nametag2dDist |= Nametag.CName
+ self.nametag.getNametag2d().setContents(self.nametag2dContents & self.nametag2dDist)
+ DistributedActor.do_setParent(self, parentToken)
+ self.__setTags()
+
+ def toonUp(self, hpGained):
+ if self.hp == None or hpGained < 0:
+ return
+ oldHp = self.hp
+ if self.hp + hpGained <= 0:
+ self.hp += hpGained
+ else:
+ self.hp = min(max(self.hp, 0) + hpGained, self.maxHp)
+ hpGained = self.hp - max(oldHp, 0)
+ if hpGained > 0:
+ self.showHpText(hpGained)
+ self.hpChange(quietly=0)
+ return
+
+ def takeDamage(self, hpLost, bonus = 0):
+ if self.hp == None or hpLost < 0:
+ return
+ oldHp = self.hp
+ self.hp = max(self.hp - hpLost, 0)
+ hpLost = oldHp - self.hp
+ if hpLost > 0:
+ self.showHpText(-hpLost, bonus)
+ self.hpChange(quietly=0)
+ if self.hp <= 0 and oldHp > 0:
+ self.died()
+ return
+
+ def setHp(self, hitPoints):
+ justRanOutOfHp = (hitPoints is not None and self.hp is not None and self.hp - hitPoints > 0) and (hitPoints <= 0)
+ self.hp = hitPoints
+ self.hpChange(quietly=1)
+ if justRanOutOfHp:
+ self.died()
+ return
+
+ def hpChange(self, quietly = 0):
+ if hasattr(self, 'doId'):
+ if self.hp != None and self.maxHp != None:
+ messenger.send(self.uniqueName('hpChange'), [self.hp, self.maxHp, quietly])
+ if self.hp != None and self.hp > 0:
+ messenger.send(self.uniqueName('positiveHP'))
+ return
+
+ def died(self):
+ pass
+
+ def getHp(self):
+ return self.hp
+
+ def setMaxHp(self, hitPoints):
+ self.maxHp = hitPoints
+ self.hpChange()
+
+ def getMaxHp(self):
+ return self.maxHp
+
+ def getName(self):
+ return Avatar.getName(self)
+
+ def setName(self, name):
+ try:
+ self.node().setName('%s-%d' % (name, self.doId))
+ self.gotName = 1
+ except:
+ pass
+
+ return Avatar.setName(self, name)
+
+ def showHpText(self, number, bonus = 0, scale = 1):
+ if self.HpTextEnabled and not self.ghostMode:
+ if number != 0:
+ if self.hpText:
+ self.hideHpText()
+ self.HpTextGenerator.setFont(OTPGlobals.getSignFont())
+ if number < 0:
+ self.HpTextGenerator.setText(str(number))
+ else:
+ self.HpTextGenerator.setText('+' + str(number))
+ self.HpTextGenerator.clearShadow()
+ self.HpTextGenerator.setAlign(TextNode.ACenter)
+ if bonus == 1:
+ r = 1.0
+ g = 1.0
+ b = 0
+ a = 1
+ elif bonus == 2:
+ r = 1.0
+ g = 0.5
+ b = 0
+ a = 1
+ elif number < 0:
+ r = 0.9
+ g = 0
+ b = 0
+ a = 1
+ else:
+ r = 0
+ g = 0.9
+ b = 0
+ a = 1
+ self.HpTextGenerator.setTextColor(r, g, b, a)
+ self.hpTextNode = self.HpTextGenerator.generate()
+ self.hpText = self.attachNewNode(self.hpTextNode)
+ self.hpText.setScale(scale)
+ self.hpText.setBillboardPointEye()
+ self.hpText.setBin('fixed', 100)
+ self.hpText.setPos(0, 0, self.height / 2)
+ seq = Task.sequence(self.hpText.lerpPos(Point3(0, 0, self.height + 1.5), 1.0, blendType='easeOut'), Task.pause(0.85), self.hpText.lerpColor(Vec4(r, g, b, a), Vec4(r, g, b, 0), 0.1), Task.Task(self.hideHpTextTask))
+ taskMgr.add(seq, self.uniqueName('hpText'))
+
+ def showHpString(self, text, duration = 0.85, scale = 0.7):
+ if self.HpTextEnabled and not self.ghostMode:
+ if text != '':
+ if self.hpText:
+ self.hideHpText()
+ self.HpTextGenerator.setFont(OTPGlobals.getSignFont())
+ self.HpTextGenerator.setText(text)
+ self.HpTextGenerator.clearShadow()
+ self.HpTextGenerator.setAlign(TextNode.ACenter)
+ r = a = 1.0
+ g = b = 0.0
+ self.HpTextGenerator.setTextColor(r, g, b, a)
+ self.hpTextNode = self.HpTextGenerator.generate()
+ self.hpText = self.attachNewNode(self.hpTextNode)
+ self.hpText.setScale(scale)
+ self.hpText.setBillboardAxis()
+ self.hpText.setPos(0, 0, self.height / 2)
+ seq = Task.sequence(self.hpText.lerpPos(Point3(0, 0, self.height + 1.5), 1.0, blendType='easeOut'), Task.pause(duration), self.hpText.lerpColor(Vec4(r, g, b, a), Vec4(r, g, b, 0), 0.1), Task.Task(self.hideHpTextTask))
+ taskMgr.add(seq, self.uniqueName('hpText'))
+
+ def hideHpTextTask(self, task):
+ self.hideHpText()
+ return Task.done
+
+ def hideHpText(self):
+ if self.hpText:
+ taskMgr.remove(self.uniqueName('hpText'))
+ self.hpText.removeNode()
+ self.hpText = None
+ return
+
+ def getStareAtNodeAndOffset(self):
+ return (self, Point3(0, 0, self.height))
+
+ def getAvIdName(self):
+ return '%s\n%s' % (self.getName(), self.doId)
+
+ def __nameTagShowAvId(self, extra = None):
+ self.setDisplayName(self.getAvIdName())
+
+ def __nameTagShowName(self, extra = None):
+ self.setDisplayName(self.getName())
+
+ def askAvOnShard(self, avId):
+ if base.cr.doId2do.get(avId):
+ messenger.send('AvOnShard%s' % avId, [True])
+ else:
+ self.sendUpdate('checkAvOnShard', [avId])
+
+ def confirmAvOnShard(self, avId, onShard = True):
+ messenger.send('AvOnShard%s' % avId, [onShard])
+
+ def getDialogueArray(self):
+ return None
diff --git a/otp/avatar/DistributedAvatarAI.py b/otp/avatar/DistributedAvatarAI.py
new file mode 100644
index 0000000..6e5bb17
--- /dev/null
+++ b/otp/avatar/DistributedAvatarAI.py
@@ -0,0 +1,101 @@
+from otp.ai.AIBaseGlobal import *
+from otp.otpbase import OTPGlobals
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from direct.distributed import DistributedNodeAI
+from direct.task import Task
+
+class DistributedAvatarAI(DistributedNodeAI.DistributedNodeAI):
+
+ def __init__(self, air):
+ DistributedNodeAI.DistributedNodeAI.__init__(self, air)
+ self.hp = 0
+ self.maxHp = 0
+
+ def b_setName(self, name):
+ self.setName(name)
+ self.d_setName(name)
+
+ def d_setName(self, name):
+ self.sendUpdate('setName', [name])
+
+ def setName(self, name):
+ self.name = name
+
+ def getName(self):
+ return self.name
+
+ def b_setMaxHp(self, maxHp):
+ self.d_setMaxHp(maxHp)
+ self.setMaxHp(maxHp)
+
+ def d_setMaxHp(self, maxHp):
+ self.sendUpdate('setMaxHp', [maxHp])
+
+ def setMaxHp(self, maxHp):
+ self.maxHp = maxHp
+
+ def getMaxHp(self):
+ return self.maxHp
+
+ def b_setHp(self, hp):
+ self.d_setHp(hp)
+ self.setHp(hp)
+
+ def d_setHp(self, hp):
+ self.sendUpdate('setHp', [hp])
+
+ def setHp(self, hp):
+ self.hp = hp
+
+ def getHp(self):
+ return self.hp
+
+ def b_setLocationName(self, locationName):
+ self.d_setLocationName(locationName)
+ self.setLocationName(locationName)
+
+ def d_setLocationName(self, locationName):
+ pass
+
+ def setLocationName(self, locationName):
+ self.locationName = locationName
+
+ def getLocationName(self):
+ return self.locationName
+
+ def b_setActivity(self, activity):
+ self.d_setActivity(activity)
+ self.setActivity(activity)
+
+ def d_setActivity(self, activity):
+ pass
+
+ def setActivity(self, activity):
+ self.activity = activity
+
+ def getActivity(self):
+ return self.activity
+
+ def toonUp(self, num):
+ if self.hp >= self.maxHp:
+ return
+ self.hp = min(self.hp + num, self.maxHp)
+ self.b_setHp(self.hp)
+
+ def getRadius(self):
+ return OTPGlobals.AvatarDefaultRadius
+
+ def checkAvOnShard(self, avId):
+ senderId = self.air.getAvatarIdFromSender()
+ onShard = False
+ if simbase.air.doId2do.get(avId):
+ onShard = True
+ self.sendUpdateToAvatarId(senderId, 'confirmAvOnShard', [avId, onShard])
+
+ def setParentStr(self, parentToken):
+ if parentToken:
+ senderId = self.air.getAvatarIdFromSender()
+ self.air.writeServerEvent('Admin chat warning', senderId, 'using setParentStr to send "%s"' % parentToken)
+ self.notify.warning('Admin chat warning: %s using setParentStr to send "%s"' % (senderId, parentToken))
+ DistributedNodeAI.DistributedNodeAI.setParentStr(self, parentToken)
diff --git a/otp/avatar/DistributedPlayer.py b/otp/avatar/DistributedPlayer.py
new file mode 100644
index 0000000..805ed1a
--- /dev/null
+++ b/otp/avatar/DistributedPlayer.py
@@ -0,0 +1,455 @@
+from pandac.PandaModules import *
+from libotp import WhisperPopup
+from libotp import CFQuicktalker, CFPageButton, CFQuitButton, CFSpeech, CFThought, CFTimeout
+from otp.chat import ChatGarbler
+import string
+from direct.task import Task
+from otp.otpbase import OTPLocalizer
+from otp.speedchat import SCDecoders
+from direct.showbase import PythonUtil
+from otp.avatar import DistributedAvatar
+import time
+from otp.avatar import Avatar, PlayerBase
+from otp.chat import TalkAssistant
+from otp.otpbase import OTPGlobals
+from otp.avatar.Avatar import teleportNotify
+from otp.distributed.TelemetryLimited import TelemetryLimited
+if base.config.GetBool('want-chatfilter-hacks', 0):
+ from otp.switchboard import badwordpy
+ import os
+ badwordpy.init(os.environ.get('OTP') + '\\src\\switchboard\\', '')
+
+class DistributedPlayer(DistributedAvatar.DistributedAvatar, PlayerBase.PlayerBase, TelemetryLimited):
+ TeleportFailureTimeout = 60.0
+ chatGarbler = ChatGarbler.ChatGarbler()
+
+ def __init__(self, cr):
+ try:
+ self.DistributedPlayer_initialized
+ except:
+ self.DistributedPlayer_initialized = 1
+ DistributedAvatar.DistributedAvatar.__init__(self, cr)
+ PlayerBase.PlayerBase.__init__(self)
+ TelemetryLimited.__init__(self)
+ self.__teleportAvailable = 0
+ self.inventory = None
+ self.experience = None
+ self.friendsList = []
+ self.oldFriendsList = None
+ self.timeFriendsListChanged = None
+ self.ignoreList = []
+ self.lastFailedTeleportMessage = {}
+ self._districtWeAreGeneratedOn = None
+ self.DISLname = ''
+ self.DISLid = 0
+ self.autoRun = 0
+ self.whiteListEnabled = base.config.GetBool('whitelist-chat-enabled', 1)
+
+ return
+
+ @staticmethod
+ def GetPlayerGenerateEvent():
+ return 'DistributedPlayerGenerateEvent'
+
+ @staticmethod
+ def GetPlayerNetworkDeleteEvent():
+ return 'DistributedPlayerNetworkDeleteEvent'
+
+ @staticmethod
+ def GetPlayerDeleteEvent():
+ return 'DistributedPlayerDeleteEvent'
+
+ def networkDelete(self):
+ DistributedAvatar.DistributedAvatar.networkDelete(self)
+ messenger.send(self.GetPlayerNetworkDeleteEvent(), [self])
+
+ def disable(self):
+ DistributedAvatar.DistributedAvatar.disable(self)
+ messenger.send(self.GetPlayerDeleteEvent(), [self])
+
+ def delete(self):
+ try:
+ self.DistributedPlayer_deleted
+ except:
+ self.DistributedPlayer_deleted = 1
+ del self.experience
+ if self.inventory:
+ self.inventory.unload()
+ del self.inventory
+ DistributedAvatar.DistributedAvatar.delete(self)
+
+ def generate(self):
+ DistributedAvatar.DistributedAvatar.generate(self)
+
+ def announceGenerate(self):
+ DistributedAvatar.DistributedAvatar.announceGenerate(self)
+ messenger.send(self.GetPlayerGenerateEvent(), [self])
+
+ def setLocation(self, parentId, zoneId):
+ DistributedAvatar.DistributedAvatar.setLocation(self, parentId, zoneId)
+ if not (parentId in (0, None) and zoneId in (0, None)):
+ if not self.cr._isValidPlayerLocation(parentId, zoneId):
+ self.cr.disableDoId(self.doId)
+ self.cr.deleteObject(self.doId)
+ return None
+
+ def isGeneratedOnDistrict(self, districtId = None):
+ if districtId is None:
+ return self._districtWeAreGeneratedOn is not None
+ else:
+ return self._districtWeAreGeneratedOn == districtId
+ return
+
+ def getArrivedOnDistrictEvent(self, districtId = None):
+ if districtId is None:
+ return 'arrivedOnDistrict'
+ else:
+ return 'arrivedOnDistrict-%s' % districtId
+ return
+
+ def arrivedOnDistrict(self, districtId):
+ curFrameTime = globalClock.getFrameTime()
+ if hasattr(self, 'frameTimeWeArrivedOnDistrict') and curFrameTime == self.frameTimeWeArrivedOnDistrict:
+ if districtId == 0 and self._districtWeAreGeneratedOn:
+ self.notify.warning('ignoring arrivedOnDistrict 0, since arrivedOnDistrict %d occured on the same frame' % self._districtWeAreGeneratedOn)
+ return
+ self._districtWeAreGeneratedOn = districtId
+ self.frameTimeWeArrivedOnDistrict = globalClock.getFrameTime()
+ messenger.send(self.getArrivedOnDistrictEvent(districtId))
+ messenger.send(self.getArrivedOnDistrictEvent())
+
+ def setLeftDistrict(self):
+ self._districtWeAreGeneratedOn = None
+ return
+
+ def hasParentingRules(self):
+ if self is localAvatar:
+ return True
+
+ def setAccountName(self, accountName):
+ self.accountName = accountName
+
+ def setSystemMessage(self, aboutId, chatString, whisperType = WhisperPopup.WTSystem):
+ self.displayWhisper(aboutId, chatString, whisperType)
+
+ def displayWhisper(self, fromId, chatString, whisperType):
+ print 'Whisper type %s from %s: %s' % (whisperType, fromId, chatString)
+
+ def displayWhisperPlayer(self, playerId, chatString, whisperType):
+ print 'WhisperPlayer type %s from %s: %s' % (whisperType, playerId, chatString)
+
+ def whisperSCTo(self, msgIndex, sendToId, toPlayer):
+ if toPlayer:
+ base.cr.playerFriendsManager.sendSCWhisper(sendToId, msgIndex)
+ else:
+ messenger.send('wakeup')
+ self.sendUpdate('setWhisperSCFrom', [self.doId, msgIndex], sendToId)
+
+ def setWhisperSCFrom(self, fromId, msgIndex):
+ handle = base.cr.identifyAvatar(fromId)
+ if handle == None:
+ return
+ if base.cr.avatarFriendsManager.checkIgnored(fromId):
+ self.d_setWhisperIgnored(fromId)
+ return
+ if fromId in self.ignoreList:
+ self.d_setWhisperIgnored(fromId)
+ return
+ chatString = SCDecoders.decodeSCStaticTextMsg(msgIndex)
+ if chatString:
+ self.displayWhisper(fromId, chatString, WhisperPopup.WTQuickTalker)
+ base.talkAssistant.receiveAvatarWhisperSpeedChat(TalkAssistant.SPEEDCHAT_NORMAL, msgIndex, fromId)
+ return
+
+ def whisperSCCustomTo(self, msgIndex, sendToId, toPlayer):
+ if toPlayer:
+ base.cr.playerFriendsManager.sendSCCustomWhisper(sendToId, msgIndex)
+ return
+ messenger.send('wakeup')
+ self.sendUpdate('setWhisperSCCustomFrom', [self.doId, msgIndex], sendToId)
+
+ def _isValidWhisperSource(self, source):
+ return True
+
+ def setWhisperSCCustomFrom(self, fromId, msgIndex):
+ handle = base.cr.identifyAvatar(fromId)
+ if handle == None:
+ return
+ if not self._isValidWhisperSource(handle):
+ self.notify.warning('displayWhisper from non-toon %s' % fromId)
+ return
+ if base.cr.avatarFriendsManager.checkIgnored(fromId):
+ self.d_setWhisperIgnored(fromId)
+ return
+ if fromId in self.ignoreList:
+ self.d_setWhisperIgnored(fromId)
+ return
+ chatString = SCDecoders.decodeSCCustomMsg(msgIndex)
+ if chatString:
+ self.displayWhisper(fromId, chatString, WhisperPopup.WTQuickTalker)
+ base.talkAssistant.receiveAvatarWhisperSpeedChat(TalkAssistant.SPEEDCHAT_CUSTOM, msgIndex, fromId)
+ return
+
+ def whisperSCEmoteTo(self, emoteId, sendToId, toPlayer):
+ print 'whisperSCEmoteTo %s %s %s' % (emoteId, sendToId, toPlayer)
+ if toPlayer:
+ base.cr.playerFriendsManager.sendSCEmoteWhisper(sendToId, emoteId)
+ return
+ messenger.send('wakeup')
+ self.sendUpdate('setWhisperSCEmoteFrom', [self.doId, emoteId], sendToId)
+
+ def setWhisperSCEmoteFrom(self, fromId, emoteId):
+ handle = base.cr.identifyAvatar(fromId)
+ if handle == None:
+ return
+ if base.cr.avatarFriendsManager.checkIgnored(fromId):
+ self.d_setWhisperIgnored(fromId)
+ return
+ chatString = SCDecoders.decodeSCEmoteWhisperMsg(emoteId, handle.getName())
+ if chatString:
+ self.displayWhisper(fromId, chatString, WhisperPopup.WTEmote)
+ base.talkAssistant.receiveAvatarWhisperSpeedChat(TalkAssistant.SPEEDCHAT_EMOTE, emoteId, fromId)
+ return
+
+ def d_setWhisperIgnored(self, sendToId):
+ pass
+
+ def setChatAbsolute(self, chatString, chatFlags, dialogue = None, interrupt = 1, quiet = 0):
+ DistributedAvatar.DistributedAvatar.setChatAbsolute(self, chatString, chatFlags, dialogue, interrupt)
+ if not quiet:
+ pass
+
+ def b_setChat(self, chatString, chatFlags):
+ if self.cr.wantMagicWords and len(chatString) > 0 and chatString[0] == '~':
+ messenger.send('magicWord', [chatString])
+ else:
+ if base.config.GetBool('want-chatfilter-hacks', 0):
+ if base.config.GetBool('want-chatfilter-drop-offending', 0):
+ if badwordpy.test(chatString):
+ return
+ else:
+ chatString = badwordpy.scrub(chatString)
+ messenger.send('wakeup')
+ self.setChatAbsolute(chatString, chatFlags)
+ self.d_setChat(chatString, chatFlags)
+
+ def d_setChat(self, chatString, chatFlags):
+ self.sendUpdate('setChat', [chatString, chatFlags, 0])
+
+ def setTalk(self, fromAV, fromAC, avatarName, chat, mods, flags):
+ newText, scrubbed = self.scrubTalk(chat, mods)
+ self.displayTalk(newText)
+ if base.talkAssistant.isThought(newText):
+ newText = base.talkAssistant.removeThoughtPrefix(newText)
+ base.talkAssistant.receiveThought(fromAV, avatarName, fromAC, None, newText, scrubbed)
+ else:
+ base.talkAssistant.receiveOpenTalk(fromAV, avatarName, fromAC, None, newText, scrubbed)
+ return
+
+ def setTalkWhisper(self, fromAV, fromAC, avatarName, chat, mods, flags):
+ newText, scrubbed = self.scrubTalk(chat, mods)
+ self.displayTalkWhisper(fromAV, avatarName, chat, mods)
+ base.talkAssistant.receiveWhisperTalk(fromAV, avatarName, fromAC, None, self.doId, self.getName(), newText, scrubbed)
+ return
+
+ def displayTalkWhisper(self, fromId, avatarName, chatString, mods):
+ print 'TalkWhisper from %s: %s' % (fromId, chatString)
+
+ def scrubTalk(self, chat, mods):
+ return chat
+
+ def setChat(self, chatString, chatFlags, DISLid):
+ self.notify.error('Should call setTalk')
+ chatString = base.talkAssistant.whiteListFilterMessage(chatString)
+ if base.cr.avatarFriendsManager.checkIgnored(self.doId):
+ return
+ if base.localAvatar.garbleChat and not self.isUnderstandable():
+ chatString = self.chatGarbler.garble(self, chatString)
+ chatFlags &= ~(CFQuicktalker | CFPageButton | CFQuitButton)
+ if chatFlags & CFThought:
+ chatFlags &= ~(CFSpeech | CFTimeout)
+ else:
+ chatFlags |= CFSpeech | CFTimeout
+ self.setChatAbsolute(chatString, chatFlags)
+
+ def b_setSC(self, msgIndex):
+ self.setSC(msgIndex)
+ self.d_setSC(msgIndex)
+
+ def d_setSC(self, msgIndex):
+ messenger.send('wakeup')
+ self.sendUpdate('setSC', [msgIndex])
+
+ def setSC(self, msgIndex):
+ if base.cr.avatarFriendsManager.checkIgnored(self.doId):
+ return
+ if self.doId in base.localAvatar.ignoreList:
+ return
+ chatString = SCDecoders.decodeSCStaticTextMsg(msgIndex)
+ if chatString:
+ self.setChatAbsolute(chatString, CFSpeech | CFQuicktalker | CFTimeout, quiet=1)
+ base.talkAssistant.receiveOpenSpeedChat(TalkAssistant.SPEEDCHAT_NORMAL, msgIndex, self.doId)
+
+ def b_setSCCustom(self, msgIndex):
+ self.setSCCustom(msgIndex)
+ self.d_setSCCustom(msgIndex)
+
+ def d_setSCCustom(self, msgIndex):
+ messenger.send('wakeup')
+ self.sendUpdate('setSCCustom', [msgIndex])
+
+ def setSCCustom(self, msgIndex):
+ if base.cr.avatarFriendsManager.checkIgnored(self.doId):
+ return
+ if self.doId in base.localAvatar.ignoreList:
+ return
+ chatString = SCDecoders.decodeSCCustomMsg(msgIndex)
+ if chatString:
+ self.setChatAbsolute(chatString, CFSpeech | CFQuicktalker | CFTimeout)
+ base.talkAssistant.receiveOpenSpeedChat(TalkAssistant.SPEEDCHAT_CUSTOM, msgIndex, self.doId)
+
+ def b_setSCEmote(self, emoteId):
+ self.b_setEmoteState(emoteId, animMultiplier=self.animMultiplier)
+
+ def d_friendsNotify(self, avId, status):
+ self.sendUpdate('friendsNotify', [avId, status])
+
+ def friendsNotify(self, avId, status):
+ avatar = base.cr.identifyFriend(avId)
+ if avatar != None:
+ if status == 1:
+ self.setSystemMessage(avId, OTPLocalizer.WhisperNoLongerFriend % avatar.getName())
+ elif status == 2:
+ self.setSystemMessage(avId, OTPLocalizer.WhisperNowSpecialFriend % avatar.getName())
+ return
+
+ def d_teleportQuery(self, requesterId, sendToId = None):
+ teleportNotify.debug('sending teleportQuery%s' % ((requesterId, sendToId),))
+ self.sendUpdate('teleportQuery', [requesterId], sendToId)
+
+ def teleportQuery(self, requesterId):
+ teleportNotify.debug('receieved teleportQuery(%s)' % requesterId)
+ avatar = base.cr.playerFriendsManager.identifyFriend(requesterId)
+ if avatar != None:
+ teleportNotify.debug('avatar is not None')
+ if base.cr.avatarFriendsManager.checkIgnored(requesterId):
+ teleportNotify.debug('avatar ignored via avatarFriendsManager')
+ self.d_teleportResponse(self.doId, 2, 0, 0, 0, sendToId=requesterId)
+ return
+ if requesterId in self.ignoreList:
+ teleportNotify.debug('avatar ignored via ignoreList')
+ self.d_teleportResponse(self.doId, 2, 0, 0, 0, sendToId=requesterId)
+ return
+ if hasattr(base, 'distributedParty'):
+ if base.distributedParty.partyInfo.isPrivate:
+ if requesterId not in base.distributedParty.inviteeIds:
+ teleportNotify.debug('avatar not in inviteeIds')
+ self.d_teleportResponse(self.doId, 0, 0, 0, 0, sendToId=requesterId)
+ return
+ if base.distributedParty.isPartyEnding:
+ teleportNotify.debug('party is ending')
+ self.d_teleportResponse(self.doId, 0, 0, 0, 0, sendToId=requesterId)
+ return
+ if self.__teleportAvailable and not self.ghostMode and base.config.GetBool('can-be-teleported-to', 1):
+ teleportNotify.debug('teleport initiation successful')
+ self.setSystemMessage(requesterId, OTPLocalizer.WhisperComingToVisit % avatar.getName())
+ messenger.send('teleportQuery', [avatar, self])
+ return
+ teleportNotify.debug('teleport initiation failed')
+ if self.failedTeleportMessageOk(requesterId):
+ self.setSystemMessage(requesterId, OTPLocalizer.WhisperFailedVisit % avatar.getName())
+ teleportNotify.debug('sending try-again-later message')
+ self.d_teleportResponse(self.doId, 0, 0, 0, 0, sendToId=requesterId)
+ return
+
+ def failedTeleportMessageOk(self, fromId):
+ now = globalClock.getFrameTime()
+ lastTime = self.lastFailedTeleportMessage.get(fromId, None)
+ if lastTime != None:
+ elapsed = now - lastTime
+ if elapsed < self.TeleportFailureTimeout:
+ return 0
+ self.lastFailedTeleportMessage[fromId] = now
+ return 1
+
+ def d_teleportResponse(self, avId, available, shardId, hoodId, zoneId, sendToId = None):
+ teleportNotify.debug('sending teleportResponse%s' % ((avId,
+ available,
+ shardId,
+ hoodId,
+ zoneId,
+ sendToId),))
+ self.sendUpdate('teleportResponse', [avId,
+ available,
+ shardId,
+ hoodId,
+ zoneId], sendToId)
+
+ def teleportResponse(self, avId, available, shardId, hoodId, zoneId):
+ teleportNotify.debug('received teleportResponse%s' % ((avId,
+ available,
+ shardId,
+ hoodId,
+ zoneId),))
+ messenger.send('teleportResponse', [avId,
+ available,
+ shardId,
+ hoodId,
+ zoneId])
+
+ def d_teleportGiveup(self, requesterId, sendToId = None):
+ teleportNotify.debug('sending teleportGiveup(%s) to %s' % (requesterId, sendToId))
+ self.sendUpdate('teleportGiveup', [requesterId], sendToId)
+
+ def teleportGiveup(self, requesterId):
+ teleportNotify.debug('received teleportGiveup(%s)' % (requesterId,))
+ avatar = base.cr.identifyAvatar(requesterId)
+ if not self._isValidWhisperSource(avatar):
+ self.notify.warning('teleportGiveup from non-toon %s' % requesterId)
+ return
+ if avatar != None:
+ self.setSystemMessage(requesterId, OTPLocalizer.WhisperGiveupVisit % avatar.getName())
+ return
+
+ def b_teleportGreeting(self, avId):
+ self.d_teleportGreeting(avId)
+ self.teleportGreeting(avId)
+
+ def d_teleportGreeting(self, avId):
+ self.sendUpdate('teleportGreeting', [avId])
+
+ def teleportGreeting(self, avId):
+ avatar = base.cr.getDo(avId)
+ if isinstance(avatar, Avatar.Avatar):
+ self.setChatAbsolute(OTPLocalizer.TeleportGreeting % avatar.getName(), CFSpeech | CFTimeout)
+ elif avatar is not None:
+ self.notify.warning('got teleportGreeting from %s referencing non-toon %s' % (self.doId, avId))
+ return
+
+ def setTeleportAvailable(self, available):
+ self.__teleportAvailable = available
+
+ def getTeleportAvailable(self):
+ return self.__teleportAvailable
+
+ def getFriendsList(self):
+ return self.friendsList
+
+ def setFriendsList(self, friendsList):
+ self.oldFriendsList = self.friendsList
+ self.friendsList = friendsList
+ self.timeFriendsListChanged = globalClock.getFrameTime()
+ messenger.send('friendsListChanged')
+ Avatar.reconsiderAllUnderstandable()
+
+ def setDISLname(self, name):
+ self.DISLname = name
+
+ def setDISLid(self, id):
+ self.DISLid = id
+
+ def setAutoRun(self, value):
+ self.autoRun = value
+
+ def getAutoRun(self):
+ return self.autoRun
diff --git a/otp/avatar/DistributedPlayerAI.py b/otp/avatar/DistributedPlayerAI.py
new file mode 100644
index 0000000..18dc08f
--- /dev/null
+++ b/otp/avatar/DistributedPlayerAI.py
@@ -0,0 +1,129 @@
+from direct.showbase import GarbageReport
+from otp.ai.AIBaseGlobal import *
+from otp.avatar import DistributedAvatarAI
+from otp.avatar import PlayerBase
+from otp.distributed.ClsendTracker import ClsendTracker
+from otp.otpbase import OTPGlobals
+
+class DistributedPlayerAI(DistributedAvatarAI.DistributedAvatarAI, PlayerBase.PlayerBase, ClsendTracker):
+
+ def __init__(self, air):
+ DistributedAvatarAI.DistributedAvatarAI.__init__(self, air)
+ PlayerBase.PlayerBase.__init__(self)
+ ClsendTracker.__init__(self)
+ self.friendsList = []
+ self.DISLname = ''
+ self.DISLid = 0
+
+ if __dev__:
+
+ def generate(self):
+ self._sentExitServerEvent = False
+ DistributedAvatarAI.DistributedAvatarAI.generate(self)
+
+ def announceGenerate(self):
+ DistributedAvatarAI.DistributedAvatarAI.announceGenerate(self)
+ ClsendTracker.announceGenerate(self)
+ self._doPlayerEnter()
+
+ def _announceArrival(self):
+ self.sendUpdate('arrivedOnDistrict', [self.air.districtId])
+
+ def _announceExit(self):
+ self.sendUpdate('arrivedOnDistrict', [0])
+
+ def _sendExitServerEvent(self):
+ self.air.writeServerEvent('avatarExit', self.doId, '')
+ if __dev__:
+ self._sentExitServerEvent = True
+
+ def delete(self):
+ if __dev__:
+ del self._sentExitServerEvent
+ self._doPlayerExit()
+ ClsendTracker.destroy(self)
+ if __dev__:
+ GarbageReport.checkForGarbageLeaks()
+ DistributedAvatarAI.DistributedAvatarAI.delete(self)
+
+ def isPlayerControlled(self):
+ return True
+
+ def setLocation(self, parentId, zoneId):
+ DistributedAvatarAI.DistributedAvatarAI.setLocation(self, parentId, zoneId)
+ if self.isPlayerControlled():
+ if not self.air._isValidPlayerLocation(parentId, zoneId):
+ self.notify.info('booting player %s for doing setLocation to (%s, %s)' % (self.doId, parentId, zoneId))
+ self.air.writeServerEvent('suspicious', self.doId, 'invalid setLocation: (%s, %s)' % (parentId, zoneId))
+ self.requestDelete()
+
+ def _doPlayerEnter(self):
+ self.incrementPopulation()
+ self._announceArrival()
+
+ def _doPlayerExit(self):
+ self._announceExit()
+ self.decrementPopulation()
+
+ def incrementPopulation(self):
+ self.air.incrementPopulation()
+
+ def decrementPopulation(self):
+ simbase.air.decrementPopulation()
+
+ def b_setChat(self, chatString, chatFlags):
+ self.setChat(chatString, chatFlags)
+ self.d_setChat(chatString, chatFlags)
+
+ def d_setChat(self, chatString, chatFlags):
+ self.sendUpdate('setChat', [chatString, chatFlags])
+
+ def setChat(self, chatString, chatFlags):
+ pass
+
+ def d_setMaxHp(self, maxHp):
+ DistributedAvatarAI.DistributedAvatarAI.d_setMaxHp(self, maxHp)
+ self.air.writeServerEvent('setMaxHp', self.doId, '%s' % maxHp)
+
+ def d_setSystemMessage(self, aboutId, chatString):
+ self.sendUpdate('setSystemMessage', [aboutId, chatString])
+
+ def d_setCommonChatFlags(self, flags):
+ self.sendUpdate('setCommonChatFlags', [flags])
+
+ def setCommonChatFlags(self, flags):
+ pass
+
+ def d_friendsNotify(self, avId, status):
+ self.sendUpdate('friendsNotify', [avId, status])
+
+ def friendsNotify(self, avId, status):
+ pass
+
+ def setAccountName(self, accountName):
+ self.accountName = accountName
+
+ def getAccountName(self):
+ return self.accountName
+
+ def setDISLid(self, id):
+ self.DISLid = id
+
+ def d_setFriendsList(self, friendsList):
+ self.sendUpdate('setFriendsList', [friendsList])
+
+ def setFriendsList(self, friendsList):
+ self.friendsList = friendsList
+ self.notify.debug('setting friends list to %s' % self.friendsList)
+
+ def getFriendsList(self):
+ return self.friendsList
+
+ def extendFriendsList(self, friendId, friendCode):
+ for i in range(len(self.friendsList)):
+ friendPair = self.friendsList[i]
+ if friendPair[0] == friendId:
+ self.friendsList[i] = (friendId, friendCode)
+ return
+
+ self.friendsList.append((friendId, friendCode))
diff --git a/otp/avatar/Emote.py b/otp/avatar/Emote.py
new file mode 100644
index 0000000..69e0301
--- /dev/null
+++ b/otp/avatar/Emote.py
@@ -0,0 +1,22 @@
+from otp.otpbase import OTPLocalizer
+import types
+
+class Emote:
+ EmoteClear = -1
+ EmoteEnableStateChanged = 'EmoteEnableStateChanged'
+
+ def __init__(self):
+ self.emoteFunc = None
+ return
+
+ def isEnabled(self, index):
+ if isinstance(index, types.StringType):
+ index = OTPLocalizer.EmoteFuncDict[index]
+ if self.emoteFunc == None:
+ return 0
+ elif self.emoteFunc[index][1] == 0:
+ return 1
+ return 0
+
+
+globalEmote = None
diff --git a/otp/avatar/LocalAvatar.py b/otp/avatar/LocalAvatar.py
new file mode 100644
index 0000000..d3960a7
--- /dev/null
+++ b/otp/avatar/LocalAvatar.py
@@ -0,0 +1,1215 @@
+from pandac.PandaModules import *
+from libotp import Nametag, WhisperPopup
+from direct.gui.DirectGui import *
+from direct.showbase.PythonUtil import *
+from direct.interval.IntervalGlobal import *
+from direct.showbase.InputStateGlobal import inputState
+from pandac.PandaModules import *
+import Avatar
+from direct.controls import ControlManager
+import DistributedAvatar
+from direct.task import Task
+import PositionExaminer
+from otp.otpbase import OTPGlobals
+from otp.otpbase import OTPRender
+import math
+import string
+import random
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedSmoothNode
+from direct.gui import DirectGuiGlobals
+from otp.otpbase import OTPLocalizer
+from direct.controls.GhostWalker import GhostWalker
+from direct.controls.GravityWalker import GravityWalker
+from direct.controls.ObserverWalker import ObserverWalker
+from direct.controls.PhysicsWalker import PhysicsWalker
+from direct.controls.SwimWalker import SwimWalker
+from direct.controls.TwoDWalker import TwoDWalker
+
+class LocalAvatar(DistributedAvatar.DistributedAvatar, DistributedSmoothNode.DistributedSmoothNode):
+ notify = DirectNotifyGlobal.directNotify.newCategory('LocalAvatar')
+ wantDevCameraPositions = base.config.GetBool('want-dev-camera-positions', 0)
+ wantMouse = base.config.GetBool('want-mouse', 0)
+ sleepTimeout = base.config.GetInt('sleep-timeout', 120)
+ swimTimeout = base.config.GetInt('afk-timeout', 600)
+ __enableMarkerPlacement = base.config.GetBool('place-markers', 0)
+ acceptingNewFriends = base.config.GetBool('accepting-new-friends', 1)
+ acceptingNonFriendWhispers = base.config.GetBool('accepting-non-friend-whispers', 0)
+
+ def __init__(self, cr, chatMgr, talkAssistant = None, passMessagesThrough = False):
+ try:
+ self.LocalAvatar_initialized
+ return
+ except:
+ pass
+
+ self.LocalAvatar_initialized = 1
+ DistributedAvatar.DistributedAvatar.__init__(self, cr)
+ DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr)
+ self.cTrav = CollisionTraverser('base.cTrav')
+ base.pushCTrav(self.cTrav)
+ self.cTrav.setRespectPrevTransform(1)
+ self.avatarControlsEnabled = 0
+ self.controlManager = ControlManager.ControlManager(True, passMessagesThrough)
+ self.initializeCollisions()
+ self.initializeSmartCamera()
+ self.cameraPositions = []
+ self.animMultiplier = 1.0
+ self.runTimeout = 2.5
+ self.customMessages = []
+ self.chatMgr = chatMgr
+ base.talkAssistant = talkAssistant
+ self.commonChatFlags = 0
+ self.garbleChat = 1
+ self.teleportAllowed = 1
+ self.lockedDown = 0
+ self.isPageUp = 0
+ self.isPageDown = 0
+ self.soundRun = None
+ self.soundWalk = None
+ self.sleepFlag = 0
+ self.isDisguised = 0
+ self.movingFlag = 0
+ self.swimmingFlag = 0
+ self.lastNeedH = None
+ self.accept('friendOnline', self.__friendOnline)
+ self.accept('friendOffline', self.__friendOffline)
+ self.accept('clickedWhisper', self.clickedWhisper)
+ self.accept('playerOnline', self.__playerOnline)
+ self.accept('playerOffline', self.__playerOffline)
+ self.sleepCallback = None
+ self.accept('wakeup', self.wakeUp)
+ self.jumpLandAnimFixTask = None
+ self.fov = OTPGlobals.DefaultCameraFov
+ self.accept('avatarMoving', self.clearPageUpDown)
+ self.nametag2dNormalContents = Nametag.CSpeech
+ self.showNametag2d()
+ self.setPickable(0)
+ return
+
+ def useSwimControls(self):
+ self.controlManager.use('swim', self)
+
+ def useGhostControls(self):
+ self.controlManager.use('ghost', self)
+
+ def useWalkControls(self):
+ self.controlManager.use('walk', self)
+
+ def useTwoDControls(self):
+ self.controlManager.use('twoD', self)
+
+ def isLockedDown(self):
+ return self.lockedDown
+
+ def lock(self):
+ if self.lockedDown == 1:
+ self.notify.debug('lock() - already locked!')
+ self.lockedDown = 1
+
+ def unlock(self):
+ if self.lockedDown == 0:
+ self.notify.debug('unlock() - already unlocked!')
+ self.lockedDown = 0
+
+ def isInWater(self):
+ return self.getZ(render) <= 0.0
+
+ def isTeleportAllowed(self):
+ return self.teleportAllowed and not self.isDisguised
+
+ def setTeleportAllowed(self, flag):
+ self.teleportAllowed = flag
+ self.refreshOnscreenButtons()
+
+ def sendFriendsListEvent(self):
+ self.wakeUp()
+ messenger.send('openFriendsList')
+
+ def delete(self):
+ try:
+ self.LocalAvatar_deleted
+ return
+ except:
+ self.LocalAvatar_deleted = 1
+
+ self.ignoreAll()
+ self.stopJumpLandTask()
+ taskMgr.remove('shadowReach')
+ base.popCTrav()
+ taskMgr.remove('posCamera')
+ self.disableAvatarControls()
+ self.stopTrackAnimToSpeed()
+ self.stopUpdateSmartCamera()
+ self.shutdownSmartCamera()
+ self.deleteCollisions()
+ self.controlManager.delete()
+ self.physControls = None
+ del self.controlManager
+ self.positionExaminer.delete()
+ del self.positionExaminer
+ taskMgr.remove(self.uniqueName('walkReturnTask'))
+ self.chatMgr.delete()
+ del self.chatMgr
+ del self.soundRun
+ del self.soundWalk
+ if hasattr(self, 'soundWhisper'):
+ del self.soundWhisper
+ DistributedAvatar.DistributedAvatar.delete(self)
+ return
+
+ def shadowReach(self, state):
+ if base.localAvatar.shadowPlacer:
+ base.localAvatar.shadowPlacer.lifter.setReach(base.localAvatar.getAirborneHeight() + 4.0)
+ return Task.cont
+
+ def wantLegacyLifter(self):
+ return False
+
+ def setupControls(self, avatarRadius = 1.4, floorOffset = OTPGlobals.FloorOffset, reach = 4.0, wallBitmask = OTPGlobals.WallBitmask, floorBitmask = OTPGlobals.FloorBitmask, ghostBitmask = OTPGlobals.GhostBitmask):
+ walkControls = GravityWalker(legacyLifter=self.wantLegacyLifter())
+ walkControls.setWallBitMask(wallBitmask)
+ walkControls.setFloorBitMask(floorBitmask)
+ walkControls.initializeCollisions(self.cTrav, self, avatarRadius, floorOffset, reach)
+ walkControls.setAirborneHeightFunc(self.getAirborneHeight)
+ self.controlManager.add(walkControls, 'walk')
+ self.physControls = walkControls
+ twoDControls = TwoDWalker()
+ twoDControls.setWallBitMask(wallBitmask)
+ twoDControls.setFloorBitMask(floorBitmask)
+ twoDControls.initializeCollisions(self.cTrav, self, avatarRadius, floorOffset, reach)
+ twoDControls.setAirborneHeightFunc(self.getAirborneHeight)
+ self.controlManager.add(twoDControls, 'twoD')
+ swimControls = SwimWalker()
+ swimControls.setWallBitMask(wallBitmask)
+ swimControls.setFloorBitMask(floorBitmask)
+ swimControls.initializeCollisions(self.cTrav, self, avatarRadius, floorOffset, reach)
+ swimControls.setAirborneHeightFunc(self.getAirborneHeight)
+ self.controlManager.add(swimControls, 'swim')
+ ghostControls = GhostWalker()
+ ghostControls.setWallBitMask(ghostBitmask)
+ ghostControls.setFloorBitMask(floorBitmask)
+ ghostControls.initializeCollisions(self.cTrav, self, avatarRadius, floorOffset, reach)
+ ghostControls.setAirborneHeightFunc(self.getAirborneHeight)
+ self.controlManager.add(ghostControls, 'ghost')
+ observerControls = ObserverWalker()
+ observerControls.setWallBitMask(ghostBitmask)
+ observerControls.setFloorBitMask(floorBitmask)
+ observerControls.initializeCollisions(self.cTrav, self, avatarRadius, floorOffset, reach)
+ observerControls.setAirborneHeightFunc(self.getAirborneHeight)
+ self.controlManager.add(observerControls, 'observer')
+ self.controlManager.use('walk', self)
+ self.controlManager.disable()
+
+ def initializeCollisions(self):
+ self.setupControls()
+
+ def deleteCollisions(self):
+ self.controlManager.deleteCollisions()
+ self.ignore('entero157')
+ del self.cTrav
+
+ def initializeSmartCameraCollisions(self):
+ self.ccTrav = CollisionTraverser('LocalAvatar.ccTrav')
+ self.ccLine = CollisionSegment(0.0, 0.0, 0.0, 1.0, 0.0, 0.0)
+ self.ccLineNode = CollisionNode('ccLineNode')
+ self.ccLineNode.addSolid(self.ccLine)
+ self.ccLineNodePath = self.attachNewNode(self.ccLineNode)
+ self.ccLineBitMask = OTPGlobals.CameraBitmask
+ self.ccLineNode.setFromCollideMask(self.ccLineBitMask)
+ self.ccLineNode.setIntoCollideMask(BitMask32.allOff())
+ self.camCollisionQueue = CollisionHandlerQueue()
+ self.ccTrav.addCollider(self.ccLineNodePath, self.camCollisionQueue)
+ self.ccSphere = CollisionSphere(0, 0, 0, 1)
+ self.ccSphereNode = CollisionNode('ccSphereNode')
+ self.ccSphereNode.addSolid(self.ccSphere)
+ self.ccSphereNodePath = base.camera.attachNewNode(self.ccSphereNode)
+ self.ccSphereNode.setFromCollideMask(OTPGlobals.CameraBitmask)
+ self.ccSphereNode.setIntoCollideMask(BitMask32.allOff())
+ self.camPusher = CollisionHandlerPusher()
+ self.camPusher.addCollider(self.ccSphereNodePath, base.camera)
+ self.camPusher.setCenter(self)
+ self.ccPusherTrav = CollisionTraverser('LocalAvatar.ccPusherTrav')
+ self.ccSphere2 = self.ccSphere
+ self.ccSphereNode2 = CollisionNode('ccSphereNode2')
+ self.ccSphereNode2.addSolid(self.ccSphere2)
+ self.ccSphereNodePath2 = base.camera.attachNewNode(self.ccSphereNode2)
+ self.ccSphereNode2.setFromCollideMask(OTPGlobals.CameraBitmask)
+ self.ccSphereNode2.setIntoCollideMask(BitMask32.allOff())
+ self.camPusher2 = CollisionHandlerPusher()
+ self.ccPusherTrav.addCollider(self.ccSphereNodePath2, self.camPusher2)
+ self.camPusher2.addCollider(self.ccSphereNodePath2, base.camera)
+ self.camPusher2.setCenter(self)
+ self.camFloorRayNode = self.attachNewNode('camFloorRayNode')
+ self.ccRay = CollisionRay(0.0, 0.0, 0.0, 0.0, 0.0, -1.0)
+ self.ccRayNode = CollisionNode('ccRayNode')
+ self.ccRayNode.addSolid(self.ccRay)
+ self.ccRayNodePath = self.camFloorRayNode.attachNewNode(self.ccRayNode)
+ self.ccRayBitMask = OTPGlobals.FloorBitmask
+ self.ccRayNode.setFromCollideMask(self.ccRayBitMask)
+ self.ccRayNode.setIntoCollideMask(BitMask32.allOff())
+ self.ccTravFloor = CollisionTraverser('LocalAvatar.ccTravFloor')
+ self.camFloorCollisionQueue = CollisionHandlerQueue()
+ self.ccTravFloor.addCollider(self.ccRayNodePath, self.camFloorCollisionQueue)
+ self.ccTravOnFloor = CollisionTraverser('LocalAvatar.ccTravOnFloor')
+ self.ccRay2 = CollisionRay(0.0, 0.0, 0.0, 0.0, 0.0, -1.0)
+ self.ccRay2Node = CollisionNode('ccRay2Node')
+ self.ccRay2Node.addSolid(self.ccRay2)
+ self.ccRay2NodePath = self.camFloorRayNode.attachNewNode(self.ccRay2Node)
+ self.ccRay2BitMask = OTPGlobals.FloorBitmask
+ self.ccRay2Node.setFromCollideMask(self.ccRay2BitMask)
+ self.ccRay2Node.setIntoCollideMask(BitMask32.allOff())
+ self.ccRay2MoveNodePath = hidden.attachNewNode('ccRay2MoveNode')
+ self.camFloorCollisionBroadcaster = CollisionHandlerFloor()
+ self.camFloorCollisionBroadcaster.setInPattern('on-floor')
+ self.camFloorCollisionBroadcaster.setOutPattern('off-floor')
+ self.camFloorCollisionBroadcaster.addCollider(self.ccRay2NodePath, self.ccRay2MoveNodePath)
+
+ def deleteSmartCameraCollisions(self):
+ del self.ccTrav
+ del self.ccLine
+ del self.ccLineNode
+ self.ccLineNodePath.removeNode()
+ del self.ccLineNodePath
+ del self.camCollisionQueue
+ del self.ccRay
+ del self.ccRayNode
+ self.ccRayNodePath.removeNode()
+ del self.ccRayNodePath
+ del self.ccRay2
+ del self.ccRay2Node
+ self.ccRay2NodePath.removeNode()
+ del self.ccRay2NodePath
+ self.ccRay2MoveNodePath.removeNode()
+ del self.ccRay2MoveNodePath
+ del self.ccTravOnFloor
+ del self.ccTravFloor
+ del self.camFloorCollisionQueue
+ del self.camFloorCollisionBroadcaster
+ del self.ccSphere
+ del self.ccSphereNode
+ self.ccSphereNodePath.removeNode()
+ del self.ccSphereNodePath
+ del self.camPusher
+ del self.ccPusherTrav
+ del self.ccSphere2
+ del self.ccSphereNode2
+ self.ccSphereNodePath2.removeNode()
+ del self.ccSphereNodePath2
+ del self.camPusher2
+
+ def collisionsOff(self):
+ self.controlManager.collisionsOff()
+
+ def collisionsOn(self):
+ self.controlManager.collisionsOn()
+
+ def recalcCameraSphere(self):
+ nearPlaneDist = base.camLens.getNear()
+ hFov = base.camLens.getHfov()
+ vFov = base.camLens.getVfov()
+ hOff = nearPlaneDist * math.tan(deg2Rad(hFov / 2.0))
+ vOff = nearPlaneDist * math.tan(deg2Rad(vFov / 2.0))
+ camPnts = [Point3(hOff, nearPlaneDist, vOff),
+ Point3(-hOff, nearPlaneDist, vOff),
+ Point3(hOff, nearPlaneDist, -vOff),
+ Point3(-hOff, nearPlaneDist, -vOff),
+ Point3(0.0, 0.0, 0.0)]
+ avgPnt = Point3(0.0, 0.0, 0.0)
+ for camPnt in camPnts:
+ avgPnt = avgPnt + camPnt
+
+ avgPnt = avgPnt / len(camPnts)
+ sphereRadius = 0.0
+ for camPnt in camPnts:
+ dist = Vec3(camPnt - avgPnt).length()
+ if dist > sphereRadius:
+ sphereRadius = dist
+
+ avgPnt = Point3(avgPnt)
+ self.ccSphereNodePath.setPos(avgPnt)
+ self.ccSphereNodePath2.setPos(avgPnt)
+ self.ccSphere.setRadius(sphereRadius)
+
+ def putCameraFloorRayOnAvatar(self):
+ self.camFloorRayNode.setPos(self, 0, 0, 5)
+
+ def putCameraFloorRayOnCamera(self):
+ self.camFloorRayNode.setPos(self.ccSphereNodePath, 0, 0, 0)
+
+ def attachCamera(self):
+ camera.reparentTo(self)
+ base.enableMouse()
+ base.setMouseOnNode(self.node())
+ self.ignoreMouse = not self.wantMouse
+ self.setWalkSpeedNormal()
+
+ def detachCamera(self):
+ base.disableMouse()
+
+ def stopJumpLandTask(self):
+ if self.jumpLandAnimFixTask:
+ self.jumpLandAnimFixTask.remove()
+ self.jumpLandAnimFixTask = None
+ return
+
+ def jumpStart(self):
+ if not self.sleepFlag and self.hp > 0:
+ self.b_setAnimState('jumpAirborne', 1.0)
+ self.stopJumpLandTask()
+
+ def returnToWalk(self, task):
+ if self.sleepFlag:
+ state = 'Sleep'
+ elif self.hp > 0:
+ state = 'Happy'
+ else:
+ state = 'Sad'
+ self.b_setAnimState(state, 1.0)
+ return Task.done
+
+ if 1:
+ def jumpLandAnimFix(self, jumpTime):
+ if self.playingAnim != 'run' and self.playingAnim != 'walk':
+ return taskMgr.doMethodLater(jumpTime, self.returnToWalk, self.uniqueName('walkReturnTask'))
+
+ def jumpHardLand(self):
+ if self.allowHardLand():
+ self.b_setAnimState('jumpLand', 1.0)
+ self.stopJumpLandTask()
+ self.jumpLandAnimFixTask = self.jumpLandAnimFix(1.0)
+ if self.d_broadcastPosHpr:
+ self.d_broadcastPosHpr()
+
+ def jumpLand(self):
+ self.jumpLandAnimFixTask = self.jumpLandAnimFix(0.01)
+ if self.d_broadcastPosHpr:
+ self.d_broadcastPosHpr()
+
+ def setupAnimationEvents(self):
+ self.accept('jumpStart', self.jumpStart, [])
+ self.accept('jumpHardLand', self.jumpHardLand, [])
+ self.accept('jumpLand', self.jumpLand, [])
+
+ def ignoreAnimationEvents(self):
+ self.ignore('jumpStart')
+ self.ignore('jumpHardLand')
+ self.ignore('jumpLand')
+
+ def allowHardLand(self):
+ return not self.sleepFlag and self.hp > 0
+
+ def enableSmartCameraViews(self):
+ self.accept('tab', self.nextCameraPos, [1])
+ self.accept('shift-tab', self.nextCameraPos, [0])
+ self.accept('page_up', self.pageUp)
+ self.accept('page_down', self.pageDown)
+
+ def disableSmartCameraViews(self):
+ self.ignore('tab')
+ self.ignore('shift-tab')
+ self.ignore('page_up')
+ self.ignore('page_down')
+ self.ignore('page_down-up')
+
+ def enableAvatarControls(self):
+ if self.avatarControlsEnabled:
+ return
+ self.avatarControlsEnabled = 1
+ self.setupAnimationEvents()
+ self.controlManager.enable()
+
+ def disableAvatarControls(self):
+ if not self.avatarControlsEnabled:
+ return
+ self.avatarControlsEnabled = 0
+ self.ignoreAnimationEvents()
+ self.controlManager.disable()
+ self.clearPageUpDown()
+
+ def setWalkSpeedNormal(self):
+ self.controlManager.setSpeeds(OTPGlobals.ToonForwardSpeed, OTPGlobals.ToonJumpForce, OTPGlobals.ToonReverseSpeed, OTPGlobals.ToonRotateSpeed)
+
+ def setWalkSpeedSlow(self):
+ self.controlManager.setSpeeds(OTPGlobals.ToonForwardSlowSpeed, OTPGlobals.ToonJumpSlowForce, OTPGlobals.ToonReverseSlowSpeed, OTPGlobals.ToonRotateSlowSpeed)
+
+ def pageUp(self):
+ if not self.avatarControlsEnabled:
+ return
+ self.wakeUp()
+ if not self.isPageUp:
+ self.isPageDown = 0
+ self.isPageUp = 1
+ self.lerpCameraFov(70, 0.6)
+ self.setCameraPositionByIndex(self.cameraIndex)
+ else:
+ self.clearPageUpDown()
+
+ def pageDown(self):
+ if not self.avatarControlsEnabled:
+ return
+ self.wakeUp()
+ if not self.isPageDown:
+ self.isPageUp = 0
+ self.isPageDown = 1
+ self.lerpCameraFov(70, 0.6)
+ self.setCameraPositionByIndex(self.cameraIndex)
+ else:
+ self.clearPageUpDown()
+
+ def clearPageUpDown(self):
+ if self.isPageDown or self.isPageUp:
+ self.lerpCameraFov(self.fov, 0.6)
+ self.isPageDown = 0
+ self.isPageUp = 0
+ self.setCameraPositionByIndex(self.cameraIndex)
+
+ def nextCameraPos(self, forward):
+ if not self.avatarControlsEnabled:
+ return
+ self.wakeUp()
+ self.__cameraHasBeenMoved = 1
+ if forward:
+ self.cameraIndex += 1
+ if self.cameraIndex > len(self.cameraPositions) - 1:
+ self.cameraIndex = 0
+ else:
+ self.cameraIndex -= 1
+ if self.cameraIndex < 0:
+ self.cameraIndex = len(self.cameraPositions) - 1
+ self.setCameraPositionByIndex(self.cameraIndex)
+
+ def initCameraPositions(self):
+ camHeight = self.getClampedAvatarHeight()
+ heightScaleFactor = camHeight * 0.3333333333
+ defLookAt = Point3(0.0, 1.5, camHeight)
+ scXoffset = 3.0
+ scPosition = (Point3(scXoffset - 1, -10.0, camHeight + 5.0), Point3(scXoffset, 2.0, camHeight))
+ self.cameraPositions = [(Point3(0.0, -9.0 * heightScaleFactor, camHeight),
+ defLookAt,
+ Point3(0.0, camHeight, camHeight * 4.0),
+ Point3(0.0, camHeight, camHeight * -1.0),
+ 0),
+ (Point3(0.0, 0.5, camHeight),
+ defLookAt,
+ Point3(0.0, camHeight, camHeight * 1.33),
+ Point3(0.0, camHeight, camHeight * 0.66),
+ 1),
+ (Point3(5.7 * heightScaleFactor, 7.65 * heightScaleFactor, camHeight + 2.0),
+ Point3(0.0, 1.0, camHeight),
+ Point3(0.0, 1.0, camHeight * 4.0),
+ Point3(0.0, 1.0, camHeight * -1.0),
+ 0),
+ (Point3(0.0, -24.0 * heightScaleFactor, camHeight + 4.0),
+ defLookAt,
+ Point3(0.0, 1.5, camHeight * 4.0),
+ Point3(0.0, 1.5, camHeight * -1.0),
+ 0),
+ (Point3(0.0, -12.0 * heightScaleFactor, camHeight + 4.0),
+ defLookAt,
+ Point3(0.0, 1.5, camHeight * 4.0),
+ Point3(0.0, 1.5, camHeight * -1.0),
+ 0)] + self.auxCameraPositions
+ if self.wantDevCameraPositions:
+ self.cameraPositions += [(Point3(0.0, 0.0, camHeight * 3),
+ Point3(0.0, 0.0, 0.0),
+ Point3(0.0, camHeight * 2, 0.0),
+ Point3(0.0, -camHeight * 2, 0.0),
+ 1),
+ (Point3(camHeight * 3, 0.0, camHeight),
+ Point3(0.0, 0.0, camHeight),
+ Point3(0.0, camHeight, camHeight * 1.1),
+ Point3(0.0, camHeight, camHeight * 0.9),
+ 1),
+ (Point3(camHeight * 3, 0.0, 0.0),
+ Point3(0.0, 0.0, camHeight),
+ Point3(0.0, camHeight, camHeight * 1.1),
+ Point3(0.0, camHeight, camHeight * 0.9),
+ 1),
+ (Point3(-camHeight * 3, 0.0, camHeight),
+ Point3(0.0, 0.0, camHeight),
+ Point3(0.0, camHeight, camHeight * 1.1),
+ Point3(0.0, camHeight, camHeight * 0.9),
+ 1),
+ (Point3(0.0, -60, 60),
+ defLookAt + Point3(0, 15, 0),
+ defLookAt + Point3(0, 15, 0),
+ defLookAt + Point3(0, 15, 0),
+ 1),
+ (Point3(0.0, -20, 20),
+ defLookAt + Point3(0, 5, 0),
+ defLookAt + Point3(0, 5, 0),
+ defLookAt + Point3(0, 5, 0),
+ 1)]
+
+ def addCameraPosition(self, camPos = None):
+ if camPos == None:
+ lookAtNP = self.attachNewNode('lookAt')
+ lookAtNP.setPos(base.cam, 0, 1, 0)
+ lookAtPos = lookAtNP.getPos()
+ camHeight = self.getClampedAvatarHeight()
+ camPos = (base.cam.getPos(self),
+ lookAtPos,
+ Point3(0.0, 1.5, camHeight * 4.0),
+ Point3(0.0, 1.5, camHeight * -1.0),
+ 1)
+ lookAtNP.removeNode()
+ self.auxCameraPositions.append(camPos)
+ self.cameraPositions.append(camPos)
+ return
+
+ def resetCameraPosition(self):
+ self.cameraIndex = 0
+ self.setCameraPositionByIndex(self.cameraIndex)
+
+ def removeCameraPosition(self):
+ if len(self.cameraPositions) > 1:
+ camPos = self.cameraPositions[self.cameraIndex]
+ if camPos in self.auxCameraPositions:
+ self.auxCameraPositions.remove(camPos)
+ if camPos in self.cameraPositions:
+ self.cameraPositions.remove(camPos)
+ self.nextCameraPos(1)
+
+ def printCameraPositions(self):
+ print '['
+ for i in range(len(self.cameraPositions)):
+ self.printCameraPosition(i)
+ print ','
+
+ print ']'
+
+ def printCameraPosition(self, index):
+ cp = self.cameraPositions[index]
+ print '(Point3(%0.2f, %0.2f, %0.2f),' % (cp[0][0], cp[0][1], cp[0][2])
+ print 'Point3(%0.2f, %0.2f, %0.2f),' % (cp[1][0], cp[1][1], cp[1][2])
+ print 'Point3(%0.2f, %0.2f, %0.2f),' % (cp[2][0], cp[2][1], cp[2][2])
+ print 'Point3(%0.2f, %0.2f, %0.2f),' % (cp[3][0], cp[3][1], cp[3][2])
+ print '%d,' % cp[4]
+ print ')',
+
+ def posCamera(self, lerp, time):
+ if not lerp:
+ self.positionCameraWithPusher(self.getCompromiseCameraPos(), self.getLookAtPoint())
+ else:
+ camPos = self.getCompromiseCameraPos()
+ savePos = camera.getPos()
+ saveHpr = camera.getHpr()
+ self.positionCameraWithPusher(camPos, self.getLookAtPoint())
+ x = camPos[0]
+ y = camPos[1]
+ z = camPos[2]
+ destHpr = camera.getHpr()
+ h = destHpr[0]
+ p = destHpr[1]
+ r = destHpr[2]
+ camera.setPos(savePos)
+ camera.setHpr(saveHpr)
+ taskMgr.remove('posCamera')
+ camera.lerpPosHpr(x, y, z, h, p, r, time, task='posCamera')
+
+ def getClampedAvatarHeight(self):
+ return max(self.getHeight(), 3.0)
+
+ def getVisibilityPoint(self):
+ return Point3(0.0, 0.0, self.getHeight())
+
+ def setLookAtPoint(self, la):
+ self.__curLookAt = Point3(la)
+
+ def getLookAtPoint(self):
+ return Point3(self.__curLookAt)
+
+ def setIdealCameraPos(self, pos):
+ self.__idealCameraPos = Point3(pos)
+ self.updateSmartCameraCollisionLineSegment()
+
+ def getIdealCameraPos(self):
+ return Point3(self.__idealCameraPos)
+
+ def setCameraPositionByIndex(self, index):
+ self.notify.debug('switching to camera position %s' % index)
+ self.setCameraSettings(self.cameraPositions[index])
+
+ def setCameraPosForPetInteraction(self):
+ height = self.getClampedAvatarHeight()
+ point = Point3(height * (7 / 3.0), height * (-7 / 3.0), height)
+ self.prevIdealPos = self.getIdealCameraPos()
+ self.setIdealCameraPos(point)
+ self.posCamera(1, 0.7)
+
+ def unsetCameraPosForPetInteraction(self):
+ self.setIdealCameraPos(self.prevIdealPos)
+ del self.prevIdealPos
+ self.posCamera(1, 0.7)
+
+ def setCameraSettings(self, camSettings):
+ self.setIdealCameraPos(camSettings[0])
+ if self.isPageUp and self.isPageDown or not self.isPageUp and not self.isPageDown:
+ self.__cameraHasBeenMoved = 1
+ self.setLookAtPoint(camSettings[1])
+ elif self.isPageUp:
+ self.__cameraHasBeenMoved = 1
+ self.setLookAtPoint(camSettings[2])
+ elif self.isPageDown:
+ self.__cameraHasBeenMoved = 1
+ self.setLookAtPoint(camSettings[3])
+ else:
+ self.notify.error('This case should be impossible.')
+ self.__disableSmartCam = camSettings[4]
+ if self.__disableSmartCam:
+ self.putCameraFloorRayOnAvatar()
+ self.cameraZOffset = 0.0
+
+ def getCompromiseCameraPos(self):
+ if self.__idealCameraObstructed == 0:
+ compromisePos = self.getIdealCameraPos()
+ else:
+ visPnt = self.getVisibilityPoint()
+ idealPos = self.getIdealCameraPos()
+ distance = Vec3(idealPos - visPnt).length()
+ ratio = self.closestObstructionDistance / distance
+ compromisePos = idealPos * ratio + visPnt * (1 - ratio)
+ liftMult = 1.0 - ratio * ratio
+ compromisePos = Point3(compromisePos[0], compromisePos[1], compromisePos[2] + self.getHeight() * 0.4 * liftMult)
+ compromisePos.setZ(compromisePos[2] + self.cameraZOffset)
+ return compromisePos
+
+ def updateSmartCameraCollisionLineSegment(self):
+ pointB = self.getIdealCameraPos()
+ pointA = self.getVisibilityPoint()
+ vectorAB = Vec3(pointB - pointA)
+ lengthAB = vectorAB.length()
+ if lengthAB > 0.001:
+ self.ccLine.setPointA(pointA)
+ self.ccLine.setPointB(pointB)
+
+ def initializeSmartCamera(self):
+ self.__idealCameraObstructed = 0
+ self.closestObstructionDistance = 0.0
+ self.cameraIndex = 0
+ self.auxCameraPositions = []
+ self.cameraZOffset = 0.0
+ self.__onLevelGround = 0
+ self.__camCollCanMove = 0
+ self.__geom = render
+ self.__disableSmartCam = 0
+ self.initializeSmartCameraCollisions()
+ self._smartCamEnabled = False
+
+ def shutdownSmartCamera(self):
+ self.deleteSmartCameraCollisions()
+
+ def setOnLevelGround(self, flag):
+ self.__onLevelGround = flag
+
+ def setCameraCollisionsCanMove(self, flag):
+ self.__camCollCanMove = flag
+
+ def setGeom(self, geom):
+ self.__geom = geom
+
+ def startUpdateSmartCamera(self, push = 1):
+ if self._smartCamEnabled:
+ LocalAvatar.notify.warning('redundant call to startUpdateSmartCamera')
+ return
+ self._smartCamEnabled = True
+ self.__floorDetected = 0
+ self.__cameraHasBeenMoved = 0
+ self.recalcCameraSphere()
+ self.initCameraPositions()
+ self.setCameraPositionByIndex(self.cameraIndex)
+ self.posCamera(0, 0.0)
+ self.__instantaneousCamPos = camera.getPos()
+ if push:
+ self.cTrav.addCollider(self.ccSphereNodePath, self.camPusher)
+ self.ccTravOnFloor.addCollider(self.ccRay2NodePath, self.camFloorCollisionBroadcaster)
+ self.__disableSmartCam = 0
+ else:
+ self.__disableSmartCam = 1
+ self.__lastPosWrtRender = camera.getPos(render)
+ self.__lastHprWrtRender = camera.getHpr(render)
+ taskName = self.taskName('updateSmartCamera')
+ taskMgr.remove(taskName)
+ taskMgr.add(self.updateSmartCamera, taskName, priority=47)
+ self.enableSmartCameraViews()
+
+ def stopUpdateSmartCamera(self):
+ if not self._smartCamEnabled:
+ LocalAvatar.notify.warning('redundant call to stopUpdateSmartCamera')
+ return
+ self.disableSmartCameraViews()
+ self.cTrav.removeCollider(self.ccSphereNodePath)
+ self.ccTravOnFloor.removeCollider(self.ccRay2NodePath)
+ if not base.localAvatar.isEmpty():
+ self.putCameraFloorRayOnAvatar()
+ taskName = self.taskName('updateSmartCamera')
+ taskMgr.remove(taskName)
+ self._smartCamEnabled = False
+
+ def updateSmartCamera(self, task):
+ if not self.__camCollCanMove and not self.__cameraHasBeenMoved:
+ if self.__lastPosWrtRender == camera.getPos(render):
+ if self.__lastHprWrtRender == camera.getHpr(render):
+ return Task.cont
+ self.__cameraHasBeenMoved = 0
+ self.__lastPosWrtRender = camera.getPos(render)
+ self.__lastHprWrtRender = camera.getHpr(render)
+ self.__idealCameraObstructed = 0
+ if not self.__disableSmartCam:
+ self.ccTrav.traverse(self.__geom)
+ if self.camCollisionQueue.getNumEntries() > 0:
+ self.camCollisionQueue.sortEntries()
+ self.handleCameraObstruction(self.camCollisionQueue.getEntry(0))
+ if not self.__onLevelGround:
+ self.handleCameraFloorInteraction()
+ if not self.__idealCameraObstructed:
+ self.nudgeCamera()
+ if not self.__disableSmartCam:
+ self.ccPusherTrav.traverse(self.__geom)
+ self.putCameraFloorRayOnCamera()
+ self.ccTravOnFloor.traverse(self.__geom)
+ return Task.cont
+
+ def positionCameraWithPusher(self, pos, lookAt):
+ camera.setPos(pos)
+ self.ccPusherTrav.traverse(self.__geom)
+ camera.lookAt(lookAt)
+
+ def nudgeCamera(self):
+ CLOSE_ENOUGH = 0.1
+ curCamPos = self.__instantaneousCamPos
+ curCamHpr = camera.getHpr()
+ targetCamPos = self.getCompromiseCameraPos()
+ targetCamLookAt = self.getLookAtPoint()
+ posDone = 0
+ if Vec3(curCamPos - targetCamPos).length() <= CLOSE_ENOUGH:
+ camera.setPos(targetCamPos)
+ posDone = 1
+ camera.setPos(targetCamPos)
+ camera.lookAt(targetCamLookAt)
+ targetCamHpr = camera.getHpr()
+ hprDone = 0
+ if Vec3(curCamHpr - targetCamHpr).length() <= CLOSE_ENOUGH:
+ hprDone = 1
+ if posDone and hprDone:
+ return
+ lerpRatio = 0.15
+ lerpRatio = 1 - pow(1 - lerpRatio, globalClock.getDt() * 30.0)
+ self.__instantaneousCamPos = targetCamPos * lerpRatio + curCamPos * (1 - lerpRatio)
+ if self.__disableSmartCam or not self.__idealCameraObstructed:
+ newHpr = targetCamHpr * lerpRatio + curCamHpr * (1 - lerpRatio)
+ else:
+ newHpr = targetCamHpr
+ camera.setPos(self.__instantaneousCamPos)
+ camera.setHpr(newHpr)
+
+ def popCameraToDest(self):
+ newCamPos = self.getCompromiseCameraPos()
+ newCamLookAt = self.getLookAtPoint()
+ self.positionCameraWithPusher(newCamPos, newCamLookAt)
+ self.__instantaneousCamPos = camera.getPos()
+
+ def handleCameraObstruction(self, camObstrCollisionEntry):
+ collisionPoint = camObstrCollisionEntry.getSurfacePoint(self.ccLineNodePath)
+ collisionVec = Vec3(collisionPoint - self.ccLine.getPointA())
+ distance = collisionVec.length()
+ self.__idealCameraObstructed = 1
+ self.closestObstructionDistance = distance
+ self.popCameraToDest()
+
+ def handleCameraFloorInteraction(self):
+ self.putCameraFloorRayOnCamera()
+ self.ccTravFloor.traverse(self.__geom)
+ if self.__onLevelGround:
+ return
+ if self.camFloorCollisionQueue.getNumEntries() == 0:
+ return
+ self.camFloorCollisionQueue.sortEntries()
+ camObstrCollisionEntry = self.camFloorCollisionQueue.getEntry(0)
+ camHeightFromFloor = camObstrCollisionEntry.getSurfacePoint(self.ccRayNodePath)[2]
+ self.cameraZOffset = camera.getPos()[2] + camHeightFromFloor
+ if self.cameraZOffset < 0:
+ self.cameraZOffset = 0
+ if self.__floorDetected == 0:
+ self.__floorDetected = 1
+ self.popCameraToDest()
+
+ def lerpCameraFov(self, fov, time):
+ taskMgr.remove('cam-fov-lerp-play')
+ oldFov = base.camLens.getHfov()
+ if abs(fov - oldFov) > 0.1:
+
+ def setCamFov(fov):
+ base.camLens.setFov(fov)
+
+ self.camLerpInterval = LerpFunctionInterval(setCamFov, fromData=oldFov, toData=fov, duration=time, name='cam-fov-lerp')
+ self.camLerpInterval.start()
+
+ def setCameraFov(self, fov):
+ self.fov = fov
+ if not (self.isPageDown or self.isPageUp):
+ base.camLens.setFov(self.fov)
+
+ def gotoNode(self, node, eyeHeight = 3):
+ possiblePoints = (Point3(3, 6, 0),
+ Point3(-3, 6, 0),
+ Point3(6, 6, 0),
+ Point3(-6, 6, 0),
+ Point3(3, 9, 0),
+ Point3(-3, 9, 0),
+ Point3(6, 9, 0),
+ Point3(-6, 9, 0),
+ Point3(9, 9, 0),
+ Point3(-9, 9, 0),
+ Point3(6, 0, 0),
+ Point3(-6, 0, 0),
+ Point3(6, 3, 0),
+ Point3(-6, 3, 0),
+ Point3(9, 9, 0),
+ Point3(-9, 9, 0),
+ Point3(0, 12, 0),
+ Point3(3, 12, 0),
+ Point3(-3, 12, 0),
+ Point3(6, 12, 0),
+ Point3(-6, 12, 0),
+ Point3(9, 12, 0),
+ Point3(-9, 12, 0),
+ Point3(0, -6, 0),
+ Point3(-3, -6, 0),
+ Point3(0, -9, 0),
+ Point3(-6, -9, 0))
+ for point in possiblePoints:
+ pos = self.positionExaminer.consider(node, point, eyeHeight)
+ if pos:
+ self.setPos(node, pos)
+ self.lookAt(node)
+ self.setHpr(self.getH() + random.choice((-10, 10)), 0, 0)
+ return
+
+ self.setPos(node, 0, 0, 0)
+
+ def setCustomMessages(self, customMessages):
+ self.customMessages = customMessages
+ messenger.send('customMessagesChanged')
+
+ def displayWhisper(self, fromId, chatString, whisperType):
+ sender = None
+ sfx = self.soundWhisper
+ if whisperType == WhisperPopup.WTNormal or whisperType == WhisperPopup.WTQuickTalker:
+ if sender == None:
+ return
+ chatString = sender.getName() + ': ' + chatString
+ whisper = WhisperPopup(chatString, OTPGlobals.getInterfaceFont(), whisperType)
+ if sender != None:
+ whisper.setClickable(sender.getName(), fromId)
+ whisper.manage(base.marginManager)
+ base.playSfx(sfx)
+ return
+
+ def displayWhisperPlayer(self, fromId, chatString, whisperType):
+ sender = None
+ playerInfo = None
+ sfx = self.soundWhisper
+ playerInfo = base.cr.playerFriendsManager.playerId2Info.get(fromId, None)
+ if playerInfo == None:
+ return
+ senderName = playerInfo.playerName
+ if whisperType == WhisperPopup.WTNormal or whisperType == WhisperPopup.WTQuickTalker:
+ chatString = senderName + ': ' + chatString
+ whisper = WhisperPopup(chatString, OTPGlobals.getInterfaceFont(), whisperType)
+ if sender != None:
+ whisper.setClickable(senderName, fromId)
+ whisper.manage(base.marginManager)
+ base.playSfx(sfx)
+ return
+
+ def setAnimMultiplier(self, value):
+ self.animMultiplier = value
+
+ def getAnimMultiplier(self):
+ return self.animMultiplier
+
+ def enableRun(self):
+ self.accept('arrow_up', self.startRunWatch)
+ self.accept('arrow_up-up', self.stopRunWatch)
+ self.accept('control-arrow_up', self.startRunWatch)
+ self.accept('control-arrow_up-up', self.stopRunWatch)
+ self.accept('alt-arrow_up', self.startRunWatch)
+ self.accept('alt-arrow_up-up', self.stopRunWatch)
+ self.accept('shift-arrow_up', self.startRunWatch)
+ self.accept('shift-arrow_up-up', self.stopRunWatch)
+
+ def disableRun(self):
+ self.ignore('arrow_up')
+ self.ignore('arrow_up-up')
+ self.ignore('control-arrow_up')
+ self.ignore('control-arrow_up-up')
+ self.ignore('alt-arrow_up')
+ self.ignore('alt-arrow_up-up')
+ self.ignore('shift-arrow_up')
+ self.ignore('shift-arrow_up-up')
+
+ def startRunWatch(self):
+
+ def setRun(ignored):
+ messenger.send('running-on')
+
+ taskMgr.doMethodLater(self.runTimeout, setRun, self.uniqueName('runWatch'))
+ return Task.cont
+
+ def stopRunWatch(self):
+ taskMgr.remove(self.uniqueName('runWatch'))
+ messenger.send('running-off')
+ return Task.cont
+
+ def runSound(self):
+ self.soundWalk.stop()
+ base.playSfx(self.soundRun, looping=1)
+
+ def walkSound(self):
+ self.soundRun.stop()
+ base.playSfx(self.soundWalk, looping=1)
+
+ def stopSound(self):
+ self.soundRun.stop()
+ self.soundWalk.stop()
+
+ def wakeUp(self):
+ if self.sleepCallback != None:
+ taskMgr.remove(self.uniqueName('sleepwatch'))
+ self.startSleepWatch(self.sleepCallback)
+ self.lastMoved = globalClock.getFrameTime()
+ if self.sleepFlag:
+ self.sleepFlag = 0
+ return
+
+ def gotoSleep(self):
+ if not self.sleepFlag:
+ self.b_setAnimState('Sleep', self.animMultiplier)
+ self.sleepFlag = 1
+
+ def forceGotoSleep(self):
+ if self.hp > 0:
+ self.sleepFlag = 0
+ self.gotoSleep()
+
+ def startSleepWatch(self, callback):
+ self.sleepCallback = callback
+ taskMgr.doMethodLater(self.sleepTimeout, callback, self.uniqueName('sleepwatch'))
+
+ def stopSleepWatch(self):
+ taskMgr.remove(self.uniqueName('sleepwatch'))
+ self.sleepCallback = None
+ return
+
+ def startSleepSwimTest(self):
+ taskName = self.taskName('sleepSwimTest')
+ taskMgr.remove(taskName)
+ task = Task.Task(self.sleepSwimTest)
+ self.lastMoved = globalClock.getFrameTime()
+ self.lastState = None
+ self.lastAction = None
+ self.sleepSwimTest(task)
+ taskMgr.add(self.sleepSwimTest, taskName, 35)
+ return
+
+ def stopSleepSwimTest(self):
+ taskName = self.taskName('sleepSwimTest')
+ taskMgr.remove(taskName)
+ self.stopSound()
+
+ def sleepSwimTest(self, task):
+ now = globalClock.getFrameTime()
+ speed, rotSpeed, slideSpeed = self.controlManager.getSpeeds()
+ if speed != 0.0 or rotSpeed != 0.0 or inputState.isSet('jump'):
+ if not self.swimmingFlag:
+ self.swimmingFlag = 1
+ elif self.swimmingFlag:
+ self.swimmingFlag = 0
+ if self.swimmingFlag or self.hp <= 0:
+ self.wakeUp()
+ elif not self.sleepFlag:
+ now = globalClock.getFrameTime()
+ if now - self.lastMoved > self.swimTimeout:
+ self.swimTimeoutAction()
+ return Task.done
+ return Task.cont
+
+ def swimTimeoutAction(self):
+ pass
+
+ def trackAnimToSpeed(self, task):
+ speed, rotSpeed, slideSpeed = self.controlManager.getSpeeds()
+ if speed != 0.0 or rotSpeed != 0.0 or inputState.isSet('jump'):
+ if not self.movingFlag:
+ self.movingFlag = 1
+ self.stopLookAround()
+ elif self.movingFlag:
+ self.movingFlag = 0
+ self.startLookAround()
+ if self.movingFlag or self.hp <= 0:
+ self.wakeUp()
+ elif not self.sleepFlag:
+ now = globalClock.getFrameTime()
+ if now - self.lastMoved > self.sleepTimeout:
+ self.gotoSleep()
+ state = None
+ if self.sleepFlag:
+ state = 'Sleep'
+ elif self.hp > 0:
+ state = 'Happy'
+ else:
+ state = 'Sad'
+ if state != self.lastState:
+ self.lastState = state
+ self.b_setAnimState(state, self.animMultiplier)
+ if state == 'Sad':
+ self.setWalkSpeedSlow()
+ else:
+ self.setWalkSpeedNormal()
+ if self.cheesyEffect == OTPGlobals.CEFlatProfile or self.cheesyEffect == OTPGlobals.CEFlatPortrait:
+ needH = None
+ if rotSpeed > 0.0:
+ needH = -10
+ elif rotSpeed < 0.0:
+ needH = 10
+ elif speed != 0.0:
+ needH = 0
+ if needH != None and self.lastNeedH != needH:
+ node = self.getGeomNode().getChild(0)
+ lerp = Sequence(LerpHprInterval(node, 0.5, Vec3(needH, 0, 0), blendType='easeInOut'), name='cheesy-lerp-hpr', autoPause=1)
+ lerp.start()
+ self.lastNeedH = needH
+ else:
+ self.lastNeedH = None
+ action = self.setSpeed(speed, rotSpeed)
+ if action != self.lastAction:
+ self.lastAction = action
+ if self.emoteTrack:
+ self.emoteTrack.finish()
+ self.emoteTrack = None
+ if action == OTPGlobals.WALK_INDEX or action == OTPGlobals.REVERSE_INDEX:
+ self.walkSound()
+ elif action == OTPGlobals.RUN_INDEX:
+ self.runSound()
+ else:
+ self.stopSound()
+ return Task.cont
+
+ def hasTrackAnimToSpeed(self):
+ taskName = self.taskName('trackAnimToSpeed')
+ return taskMgr.hasTaskNamed(taskName)
+
+ def startTrackAnimToSpeed(self):
+ taskName = self.taskName('trackAnimToSpeed')
+ taskMgr.remove(taskName)
+ task = Task.Task(self.trackAnimToSpeed)
+ self.lastMoved = globalClock.getFrameTime()
+ self.lastState = None
+ self.lastAction = None
+ self.trackAnimToSpeed(task)
+ taskMgr.add(self.trackAnimToSpeed, taskName, 35)
+ return
+
+ def stopTrackAnimToSpeed(self):
+ taskName = self.taskName('trackAnimToSpeed')
+ taskMgr.remove(taskName)
+ self.stopSound()
+
+ def startChat(self):
+ self.chatMgr.start()
+ self.accept(OTPGlobals.WhisperIncomingEvent, self.handlePlayerFriendWhisper)
+ self.accept(OTPGlobals.ThinkPosHotkey, self.thinkPos)
+ self.accept(OTPGlobals.PrintCamPosHotkey, self.printCamPos)
+ if self.__enableMarkerPlacement:
+ self.accept(OTPGlobals.PlaceMarkerHotkey, self.__placeMarker)
+
+ def stopChat(self):
+ self.chatMgr.stop()
+ self.ignore(OTPGlobals.WhisperIncomingEvent)
+ self.ignore(OTPGlobals.ThinkPosHotkey)
+ self.ignore(OTPGlobals.PrintCamPosHotkey)
+ if self.__enableMarkerPlacement:
+ self.ignore(OTPGlobals.PlaceMarkerHotkey)
+
+ def printCamPos(self):
+ node = base.camera.getParent()
+ pos = base.cam.getPos(node)
+ hpr = base.cam.getHpr(node)
+ print 'cam pos = ', `pos`, ', cam hpr = ', `hpr`
+
+ def d_broadcastPositionNow(self):
+ self.d_clearSmoothing()
+ self.d_broadcastPosHpr()
+
+ def travCollisionsLOS(self, n = None):
+ if n == None:
+ n = self.__geom
+ self.ccTrav.traverse(n)
+ return
+
+ def travCollisionsFloor(self, n = None):
+ if n == None:
+ n = self.__geom
+ self.ccTravFloor.traverse(n)
+ return
+
+ def travCollisionsPusher(self, n = None):
+ if n == None:
+ n = self.__geom
+ self.ccPusherTrav.traverse(n)
+ return
+
+ def __friendOnline(self, doId, commonChatFlags = 0, whitelistChatFlags = 0):
+ friend = base.cr.identifyFriend(doId)
+ if friend != None and hasattr(friend, 'setCommonAndWhitelistChatFlags'):
+ friend.setCommonAndWhitelistChatFlags(commonChatFlags, whitelistChatFlags)
+ if self.oldFriendsList != None:
+ now = globalClock.getFrameTime()
+ elapsed = now - self.timeFriendsListChanged
+ if elapsed < 10.0 and self.oldFriendsList.count(doId) == 0:
+ self.oldFriendsList.append(doId)
+ return
+ if friend != None:
+ self.setSystemMessage(doId, OTPLocalizer.WhisperFriendComingOnline % friend.getName())
+ return
+
+ def __friendOffline(self, doId):
+ friend = base.cr.identifyFriend(doId)
+ if friend != None:
+ self.setSystemMessage(0, OTPLocalizer.WhisperFriendLoggedOut % friend.getName())
+ return
+
+ def __playerOnline(self, playerId):
+ playerInfo = base.cr.playerFriendsManager.playerId2Info[playerId]
+ if playerInfo:
+ self.setSystemMessage(playerId, OTPLocalizer.WhisperPlayerOnline % (playerInfo.playerName, playerInfo.location))
+
+ def __playerOffline(self, playerId):
+ playerInfo = base.cr.playerFriendsManager.playerId2Info[playerId]
+ if playerInfo:
+ self.setSystemMessage(playerId, OTPLocalizer.WhisperPlayerOffline % playerInfo.playerName)
+
+ def clickedWhisper(self, doId, isPlayer = None):
+ if not isPlayer:
+ friend = base.cr.identifyFriend(doId)
+ if friend != None:
+ messenger.send('clickedNametag', [friend])
+ self.chatMgr.whisperTo(friend.getName(), doId)
+ else:
+ friend = base.cr.playerFriendsManager.getFriendInfo(doId)
+ if friend:
+ messenger.send('clickedNametagPlayer', [None, doId])
+ self.chatMgr.whisperTo(friend.getName(), None, doId)
+ return
+
+ def d_setParent(self, parentToken):
+ DistributedSmoothNode.DistributedSmoothNode.d_setParent(self, parentToken)
+
+ def handlePlayerFriendWhisper(self, playerId, charMessage):
+ print 'handlePlayerFriendWhisper'
+ self.displayWhisperPlayer(playerId, charMessage, WhisperPopup.WTNormal)
+
+ def canChat(self):
+ return 0
diff --git a/otp/avatar/PlayerBase.py b/otp/avatar/PlayerBase.py
new file mode 100644
index 0000000..fbb8f65
--- /dev/null
+++ b/otp/avatar/PlayerBase.py
@@ -0,0 +1,18 @@
+
+
+class PlayerBase:
+
+ def __init__(self):
+ self.gmState = False
+
+ def atLocation(self, locationId):
+ return True
+
+ def getLocation(self):
+ return []
+
+ def setAsGM(self, state):
+ self.gmState = state
+
+ def isGM(self):
+ return self.gmState
diff --git a/otp/avatar/PositionExaminer.py b/otp/avatar/PositionExaminer.py
new file mode 100644
index 0000000..bcddb03
--- /dev/null
+++ b/otp/avatar/PositionExaminer.py
@@ -0,0 +1,94 @@
+from pandac.PandaModules import *
+from direct.showbase.DirectObject import DirectObject
+from otp.otpbase import OTPGlobals
+
+class PositionExaminer(DirectObject, NodePath):
+
+ def __init__(self):
+ try:
+ self.__initialized
+ return
+ except:
+ self.__initialized = 1
+
+ NodePath.__init__(self, hidden.attachNewNode('PositionExaminer'))
+ self.cRay = CollisionRay(0.0, 0.0, 6.0, 0.0, 0.0, -1.0)
+ self.cRayNode = CollisionNode('cRayNode')
+ self.cRayNode.addSolid(self.cRay)
+ self.cRayNodePath = self.attachNewNode(self.cRayNode)
+ self.cRayNodePath.hide()
+ self.cRayBitMask = OTPGlobals.FloorBitmask
+ self.cRayNode.setFromCollideMask(self.cRayBitMask)
+ self.cRayNode.setIntoCollideMask(BitMask32.allOff())
+ self.cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.5)
+ self.cSphereNode = CollisionNode('cSphereNode')
+ self.cSphereNode.addSolid(self.cSphere)
+ self.cSphereNodePath = self.attachNewNode(self.cSphereNode)
+ self.cSphereNodePath.hide()
+ self.cSphereBitMask = OTPGlobals.WallBitmask
+ self.cSphereNode.setFromCollideMask(self.cSphereBitMask)
+ self.cSphereNode.setIntoCollideMask(BitMask32.allOff())
+ self.ccLine = CollisionSegment(0.0, 0.0, 0.0, 1.0, 0.0, 0.0)
+ self.ccLineNode = CollisionNode('ccLineNode')
+ self.ccLineNode.addSolid(self.ccLine)
+ self.ccLineNodePath = self.attachNewNode(self.ccLineNode)
+ self.ccLineNodePath.hide()
+ self.ccLineBitMask = OTPGlobals.CameraBitmask
+ self.ccLineNode.setFromCollideMask(self.ccLineBitMask)
+ self.ccLineNode.setIntoCollideMask(BitMask32.allOff())
+ self.cRayTrav = CollisionTraverser('PositionExaminer.cRayTrav')
+ self.cRayTrav.setRespectPrevTransform(False)
+ self.cRayQueue = CollisionHandlerQueue()
+ self.cRayTrav.addCollider(self.cRayNodePath, self.cRayQueue)
+ self.cSphereTrav = CollisionTraverser('PositionExaminer.cSphereTrav')
+ self.cSphereTrav.setRespectPrevTransform(False)
+ self.cSphereQueue = CollisionHandlerQueue()
+ self.cSphereTrav.addCollider(self.cSphereNodePath, self.cSphereQueue)
+ self.ccLineTrav = CollisionTraverser('PositionExaminer.ccLineTrav')
+ self.ccLineTrav.setRespectPrevTransform(False)
+ self.ccLineQueue = CollisionHandlerQueue()
+ self.ccLineTrav.addCollider(self.ccLineNodePath, self.ccLineQueue)
+
+ def delete(self):
+ del self.cRay
+ del self.cRayNode
+ self.cRayNodePath.removeNode()
+ del self.cRayNodePath
+ del self.cSphere
+ del self.cSphereNode
+ self.cSphereNodePath.removeNode()
+ del self.cSphereNodePath
+ del self.ccLine
+ del self.ccLineNode
+ self.ccLineNodePath.removeNode()
+ del self.ccLineNodePath
+ del self.cRayTrav
+ del self.cRayQueue
+ del self.cSphereTrav
+ del self.cSphereQueue
+ del self.ccLineTrav
+ del self.ccLineQueue
+
+ def consider(self, node, pos, eyeHeight):
+ self.reparentTo(node)
+ self.setPos(pos)
+ result = None
+ self.cRayTrav.traverse(render)
+ if self.cRayQueue.getNumEntries() != 0:
+ self.cRayQueue.sortEntries()
+ floorPoint = self.cRayQueue.getEntry(0).getSurfacePoint(self.cRayNodePath)
+ if abs(floorPoint[2]) <= 4.0:
+ pos += floorPoint
+ self.setPos(pos)
+ self.cSphereTrav.traverse(render)
+ if self.cSphereQueue.getNumEntries() == 0:
+ self.ccLine.setPointA(0, 0, eyeHeight)
+ self.ccLine.setPointB(-pos[0], -pos[1], eyeHeight)
+ self.ccLineTrav.traverse(render)
+ if self.ccLineQueue.getNumEntries() == 0:
+ result = pos
+ self.reparentTo(hidden)
+ self.cRayQueue.clearEntries()
+ self.cSphereQueue.clearEntries()
+ self.ccLineQueue.clearEntries()
+ return result
diff --git a/otp/avatar/ShadowCaster.py b/otp/avatar/ShadowCaster.py
new file mode 100644
index 0000000..f3e964c
--- /dev/null
+++ b/otp/avatar/ShadowCaster.py
@@ -0,0 +1,134 @@
+from pandac.PandaModules import *
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.showbase.ShadowPlacer import ShadowPlacer
+from otp.otpbase import OTPGlobals
+globalDropShadowFlag = 1
+
+def setGlobalDropShadowFlag(flag):
+ global globalDropShadowFlag
+ if flag != globalDropShadowFlag:
+ globalDropShadowFlag = flag
+ messenger.send('globalDropShadowFlagChanged')
+
+
+globalDropShadowGrayLevel = 0.5
+
+def setGlobalDropShadowGrayLevel(grayLevel):
+ global globalDropShadowGrayLevel
+ if grayLevel != globalDropShadowGrayLevel:
+ globalDropShadowGrayLevel = grayLevel
+ messenger.send('globalDropShadowGrayLevelChanged')
+
+
+class ShadowCaster:
+ notify = DirectNotifyGlobal.directNotify.newCategory('ShadowCaster')
+
+ def __init__(self, squareShadow = False):
+ if squareShadow:
+ self.shadowFileName = 'phase_3/models/props/square_drop_shadow'
+ else:
+ self.shadowFileName = 'phase_3/models/props/drop_shadow'
+ self.dropShadow = None
+ self.shadowPlacer = None
+ self.activeShadow = 0
+ self.wantsActive = 1
+ self.storedActiveState = 0
+ if hasattr(base, 'wantDynamicShadows') and base.wantDynamicShadows:
+ messenger.accept('globalDropShadowFlagChanged', self, self.__globalDropShadowFlagChanged)
+ messenger.accept('globalDropShadowGrayLevelChanged', self, self.__globalDropShadowGrayLevelChanged)
+ return
+
+ def delete(self):
+ if hasattr(base, 'wantDynamicShadows') and base.wantDynamicShadows:
+ messenger.ignore('globalDropShadowFlagChanged', self)
+ messenger.ignore('globalDropShadowGrayLevelChanged', self)
+ self.deleteDropShadow()
+ self.shadowJoint = None
+ return
+
+ def initializeDropShadow(self, hasGeomNode = True):
+ self.deleteDropShadow()
+ if hasGeomNode:
+ self.getGeomNode().setTag('cam', 'caster')
+ dropShadow = loader.loadModel(self.shadowFileName)
+ dropShadow.setScale(0.4)
+ dropShadow.flattenMedium()
+ dropShadow.setBillboardAxis(2)
+ dropShadow.setColor(0.0, 0.0, 0.0, globalDropShadowGrayLevel, 1)
+ self.shadowPlacer = ShadowPlacer(base.shadowTrav, dropShadow, OTPGlobals.WallBitmask, OTPGlobals.FloorBitmask)
+ self.dropShadow = dropShadow
+ if not globalDropShadowFlag:
+ self.dropShadow.hide()
+ if self.getShadowJoint():
+ dropShadow.reparentTo(self.getShadowJoint())
+ else:
+ self.dropShadow.hide()
+ self.setActiveShadow(self.wantsActive)
+ self.__globalDropShadowFlagChanged()
+ self.__globalDropShadowGrayLevelChanged()
+
+ def update(self):
+ pass
+
+ def deleteDropShadow(self):
+ if self.shadowPlacer:
+ self.shadowPlacer.delete()
+ self.shadowPlacer = None
+ if self.dropShadow:
+ self.dropShadow.removeNode()
+ self.dropShadow = None
+ return
+
+ def setActiveShadow(self, isActive = 1):
+ isActive = isActive and self.wantsActive
+ if not globalDropShadowFlag:
+ self.storedActiveState = isActive
+ if self.shadowPlacer != None:
+ isActive = isActive and globalDropShadowFlag
+ if self.activeShadow != isActive:
+ self.activeShadow = isActive
+ if isActive:
+ self.shadowPlacer.on()
+ else:
+ self.shadowPlacer.off()
+ return
+
+ def setShadowHeight(self, shadowHeight):
+ if self.dropShadow:
+ self.dropShadow.setZ(-shadowHeight)
+
+ def getShadowJoint(self):
+ if hasattr(self, 'shadowJoint'):
+ return self.shadowJoint
+ shadowJoint = self.find('**/attachShadow')
+ if shadowJoint.isEmpty():
+ self.shadowJoint = NodePath(self)
+ else:
+ self.shadowJoint = shadowJoint
+ return self.shadowJoint
+
+ def hideShadow(self):
+ self.dropShadow.hide()
+
+ def showShadow(self):
+ if not globalDropShadowFlag:
+ self.dropShadow.hide()
+ else:
+ self.dropShadow.show()
+
+ def __globalDropShadowFlagChanged(self):
+ if self.dropShadow != None:
+ if globalDropShadowFlag == 0:
+ if self.activeShadow == 1:
+ self.storedActiveState = 1
+ self.setActiveShadow(0)
+ elif self.activeShadow == 0:
+ self.setActiveShadow(1)
+ self.showShadow()
+ return
+
+ def __globalDropShadowGrayLevelChanged(self):
+ if self.dropShadow != None:
+ self.dropShadow.setColor(0.0, 0.0, 0.0, globalDropShadowGrayLevel, 1)
+ return
diff --git a/otp/avatar/SpeedMonitor.py b/otp/avatar/SpeedMonitor.py
new file mode 100644
index 0000000..532edfe
--- /dev/null
+++ b/otp/avatar/SpeedMonitor.py
@@ -0,0 +1,91 @@
+from direct.showbase.PythonUtil import SerialNumGen
+from direct.task import Task
+
+class SpeedMonitor:
+ notify = directNotify.newCategory('SpeedMonitor')
+ SerialGen = SerialNumGen()
+ TrackingPeriod = 30.0
+
+ def __init__(self, name):
+ self._name = name
+ self._nodepaths = {}
+ self._maxSpeeds = {}
+ self._prevPosQueue = {}
+ self._speedLimits = {}
+ self._trackTask = taskMgr.add(self._trackSpeedsTask, 'speedMonitorTask-%s-%s' % (self._name, id(self)))
+
+ def destroy(self):
+ taskMgr.remove(self._trackTask)
+
+ def _allocToken(self):
+ return 'speedMonitorToken-%s-%s-%s' % (self._name, id(self), SpeedMonitor.SerialGen.next())
+
+ def addNodepath(self, nodepath):
+ token = self._allocToken()
+ self._nodepaths[token] = nodepath
+ self.resetMaxSpeed(token)
+ return token
+
+ def setSpeedLimit(self, token, limit, callback):
+ self._speedLimits[token] = (limit, callback)
+
+ def removeNodepath(self, token):
+ del self._nodepaths[token]
+ del self._maxSpeeds[token]
+ del self._prevPosQueue[token]
+ if token in self._speedLimits:
+ self._speedLimits.pop(token)
+
+ def getMaxSpeed(self, token):
+ return self._maxSpeeds[token]
+
+ def resetMaxSpeed(self, token):
+ self._maxSpeeds[token] = 0.0
+ nodepath = self._nodepaths[token]
+ self._prevPosQueue[token] = [
+ (nodepath.getPos(), globalClock.getFrameTime() - SpeedMonitor.TrackingPeriod, 0.0)]
+
+ def _trackSpeedsTask(self, task = None):
+ for (token, nodepath) in self._nodepaths.iteritems():
+ curT = globalClock.getFrameTime()
+ curPos = nodepath.getPos()
+ while len(self._prevPosQueue[token]) > 1:
+ (oldestPos, oldestT, oldestDistance) = self._prevPosQueue[token][1]
+ if curT - oldestT > SpeedMonitor.TrackingPeriod:
+ self._prevPosQueue[token] = self._prevPosQueue[token][1:]
+ else:
+ break
+ storeCurPos = False
+ if len(self._prevPosQueue[token]) == 0:
+ storeCurPos = True
+ curDistance = 0.0
+ else:
+ (prevPos, prevT, prevDistance) = self._prevPosQueue[token][-1]
+ if curPos != prevPos:
+ storeCurPos = True
+ curDistance = (curPos - prevPos).length()
+
+ if storeCurPos:
+ self._prevPosQueue[token].append((curPos, curT, curDistance))
+
+ if len(self._prevPosQueue[token]) > 1:
+ (oldestPos, oldestT, oldestDistance) = self._prevPosQueue[token][0]
+ (newestPos, newestT, newestDistance) = self._prevPosQueue[token][-1]
+ tDelta = newestT - oldestT
+ if tDelta >= SpeedMonitor.TrackingPeriod:
+ totalDistance = 0.0
+ for (pos, t, distance) in self._prevPosQueue[token][1:]:
+ totalDistance += distance
+
+ speed = totalDistance / tDelta
+ if speed > self._maxSpeeds[token]:
+ if self.notify.getDebug():
+ self.notify.debug('new max speed(%s): %s' % (nodepath, speed))
+
+ self._maxSpeeds[token] = speed
+ (limit, callback) = self._speedLimits[token]
+ if speed > limit:
+ self.notify.warning('%s over speed limit (%s, cur speed=%s)' % (nodepath, limit, speed))
+ callback(speed)
+
+ return Task.cont
diff --git a/otp/avatar/__init__.py b/otp/avatar/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/chat/ChatGarbler.py b/otp/chat/ChatGarbler.py
new file mode 100644
index 0000000..1503131
--- /dev/null
+++ b/otp/chat/ChatGarbler.py
@@ -0,0 +1,29 @@
+import string
+import random
+from otp.otpbase import OTPLocalizer
+
+class ChatGarbler:
+
+ def garble(self, avatar, message):
+ newMessage = ''
+ numWords = random.randint(1, 7)
+ wordlist = OTPLocalizer.ChatGarblerDefault
+ for i in range(1, numWords + 1):
+ wordIndex = random.randint(0, len(wordlist) - 1)
+ newMessage = newMessage + wordlist[wordIndex]
+ if i < numWords:
+ newMessage = newMessage + ' '
+
+ return newMessage
+
+ def garbleSingle(self, avatar, message):
+ newMessage = ''
+ numWords = 1
+ wordlist = OTPLocalizer.ChatGarblerDefault
+ for i in range(1, numWords + 1):
+ wordIndex = random.randint(0, len(wordlist) - 1)
+ newMessage = newMessage + wordlist[wordIndex]
+ if i < numWords:
+ newMessage = newMessage + ' '
+
+ return newMessage
diff --git a/otp/chat/ChatGlobals.py b/otp/chat/ChatGlobals.py
new file mode 100644
index 0000000..b7248b1
--- /dev/null
+++ b/otp/chat/ChatGlobals.py
@@ -0,0 +1,53 @@
+import string
+NORMAL_CHAT = 1
+WHISPER_CHAT = 2
+GUILD_CHAT = 3
+CREW_CHAT = 4
+SHIPPVP_CHAT = 5
+ERROR_NONE = None
+ERROR_NO_OPEN_CHAT = 1
+ERROR_NOT_FRIENDS = 2
+ERROR_NO_RECEIVER = 3
+ERROR_NO_GUILD_CHAT = 4
+ERROR_NO_CREW_CHAT = 5
+ERROR_NO_SHIPPVP_CHAT = 6
+TYPEDCHAT = 0
+SPEEDCHAT_NORMAL = 1
+SPEEDCHAT_EMOTE = 2
+SPEEDCHAT_CUSTOM = 3
+SYSTEMCHAT = 4
+GAMECHAT = 5
+GUILDCHAT = 6
+PARTYCHAT = 7
+SPEEDCHAT_QUEST = 8
+FRIEND_UPDATE = 9
+CREW_UPDATE = 10
+GUILD_UPDATE = 11
+AVATAR_UNAVAILABLE = 12
+SHIPPVPCHAT = 13
+GMCHAT = 14
+ChatEvent = 'ChatEvent'
+NormalChatEvent = 'NormalChatEvent'
+SCChatEvent = 'SCChatEvent'
+SCCustomChatEvent = 'SCCustomChatEvent'
+SCEmoteChatEvent = 'SCEmoteChatEvent'
+SCQuestEvent = 'SCQuestEvent'
+OnScreen = 0
+OffScreen = 1
+Thought = 2
+ThoughtPrefix = '.'
+
+def isThought(message):
+ if len(message) == 0:
+ return 0
+ elif string.find(message, ThoughtPrefix, 0, len(ThoughtPrefix)) >= 0:
+ return 1
+ else:
+ return 0
+
+
+def removeThoughtPrefix(message):
+ if isThought(message):
+ return message[len(ThoughtPrefix):]
+ else:
+ return message
diff --git a/otp/chat/ChatInputNormal.py b/otp/chat/ChatInputNormal.py
new file mode 100644
index 0000000..0d922d6
--- /dev/null
+++ b/otp/chat/ChatInputNormal.py
@@ -0,0 +1,161 @@
+from direct.showbase import DirectObject
+from otp.otpbase import OTPGlobals
+import sys
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from otp.otpbase import OTPLocalizer
+
+class ChatInputNormal(DirectObject.DirectObject):
+ ExecNamespace = None
+
+ def __init__(self, chatMgr):
+ self.chatMgr = chatMgr
+ self.normalPos = Vec3(-1.083, 0, 0.804)
+ self.whisperPos = Vec3(0.0, 0, 0.71)
+ self.whisperAvatarName = None
+ self.whisperAvatarId = None
+ self.toPlayer = 0
+ wantHistory = 0
+ if __dev__:
+ wantHistory = 1
+ self.wantHistory = base.config.GetBool('want-chat-history', wantHistory)
+ self.history = ['']
+ self.historySize = base.config.GetInt('chat-history-size', 10)
+ self.historyIndex = 0
+ return
+
+ def typeCallback(self, extraArgs):
+ messenger.send('enterNormalChat')
+
+ def delete(self):
+ self.ignore('arrow_up-up')
+ self.ignore('arrow_down-up')
+ self.chatFrame.destroy()
+ del self.chatFrame
+ del self.chatButton
+ del self.cancelButton
+ del self.chatEntry
+ del self.whisperLabel
+ del self.chatMgr
+
+ def activateByData(self, whisperAvatarId = None, toPlayer = 0):
+ self.toPlayer = toPlayer
+ self.whisperAvatarId = whisperAvatarId
+ self.whisperAvatarName = base.talkAssistant.findName(self.whisperAvatarId, self.toPlayer)
+ if self.whisperAvatarId:
+ self.chatFrame.setPos(self.whisperPos)
+ self.whisperLabel['text'] = OTPLocalizer.ChatInputWhisperLabel % self.whisperAvatarName
+ self.whisperLabel.show()
+ else:
+ self.chatFrame.setPos(self.normalPos)
+ self.whisperLabel.hide()
+ self.chatEntry['focus'] = 1
+ self.chatFrame.show()
+ if self.wantHistory:
+ self.accept('arrow_up-up', self.getPrevHistory)
+ self.accept('arrow_down-up', self.getNextHistory)
+
+ def deactivate(self):
+ self.chatEntry.set('')
+ self.chatEntry['focus'] = 0
+ self.chatFrame.hide()
+ self.whisperLabel.hide()
+ base.win.closeIme()
+ self.ignore('arrow_up-up')
+ self.ignore('arrow_down-up')
+
+ def checkForOverRide(self):
+ return False
+
+ def sendChat(self, text):
+ if self.checkForOverRide():
+ self.chatEntry.enterText('')
+ return
+ self.deactivate()
+ self.chatMgr.fsm.request('mainMenu')
+ if text:
+ if self.toPlayer:
+ if self.whisperAvatarId:
+ self.whisperAvatarName = None
+ self.whisperAvatarId = None
+ self.toPlayer = 0
+ elif self.whisperAvatarId:
+ self.chatMgr.sendWhisperString(text, self.whisperAvatarId)
+ self.whisperAvatarName = None
+ self.whisperAvatarId = None
+ else:
+ if self.chatMgr.execChat:
+ if text[0] == '>':
+ text = self.__execMessage(text[1:])
+ base.localAvatar.setChatAbsolute(text, CFSpeech | CFTimeout)
+ return
+ base.talkAssistant.sendOpenTalk(text)
+ if self.wantHistory:
+ self.addToHistory(text)
+ return
+
+ def chatOverflow(self, overflowText):
+ self.sendChat(self.chatEntry.get())
+
+ def __execMessage(self, message):
+ if not ChatInputNormal.ExecNamespace:
+ ChatInputNormal.ExecNamespace = {}
+ exec 'from pandac.PandaModules import *' in globals(), self.ExecNamespace
+ self.importExecNamespace()
+ try:
+ if not isClient():
+ print 'EXECWARNING ChatInputNormal eval: %s' % message
+ printStack()
+ return str(eval(message, globals(), ChatInputNormal.ExecNamespace))
+ except SyntaxError:
+ try:
+ if not isClient():
+ print 'EXECWARNING ChatInputNormal exec: %s' % message
+ printStack()
+ exec message in globals(), ChatInputNormal.ExecNamespace
+ return 'ok'
+ except:
+ exception = sys.exc_info()[0]
+ extraInfo = sys.exc_info()[1]
+ if extraInfo:
+ return str(extraInfo)
+ else:
+ return str(exception)
+
+ except:
+ exception = sys.exc_info()[0]
+ extraInfo = sys.exc_info()[1]
+ if extraInfo:
+ return str(extraInfo)
+ else:
+ return str(exception)
+
+ def cancelButtonPressed(self):
+ self.chatEntry.set('')
+ self.chatMgr.fsm.request('mainMenu')
+
+ def chatButtonPressed(self):
+ self.sendChat(self.chatEntry.get())
+
+ def importExecNamespace(self):
+ pass
+
+ def addToHistory(self, text):
+ self.history = [text] + self.history[:self.historySize - 1]
+ self.historyIndex = 0
+
+ def getPrevHistory(self):
+ self.chatEntry.set(self.history[self.historyIndex])
+ self.historyIndex += 1
+ self.historyIndex %= len(self.history)
+
+ def getNextHistory(self):
+ self.chatEntry.set(self.history[self.historyIndex])
+ self.historyIndex -= 1
+ self.historyIndex %= len(self.history)
+
+ def setPos(self, posX, posY = None, posZ = None):
+ if posX and posY and posZ:
+ self.chatFrame.setPos(posX, posY, posZ)
+ else:
+ self.chatFrame.setPos(posX)
diff --git a/otp/chat/ChatInputTyped.py b/otp/chat/ChatInputTyped.py
new file mode 100644
index 0000000..e8ee4ea
--- /dev/null
+++ b/otp/chat/ChatInputTyped.py
@@ -0,0 +1,182 @@
+from direct.showbase import DirectObject
+from otp.otpbase import OTPGlobals
+import sys
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from otp.otpbase import OTPLocalizer
+
+class ChatInputTyped(DirectObject.DirectObject):
+ ExecNamespace = None
+
+ def __init__(self, mainEntry = 0):
+ self.whisperName = None
+ self.whisperId = None
+ self.toPlayer = 0
+ self.mainEntry = mainEntry
+ wantHistory = 0
+ if __dev__:
+ wantHistory = 1
+ self.wantHistory = base.config.GetBool('want-chat-history', wantHistory)
+ self.history = ['']
+ self.historySize = base.config.GetInt('chat-history-size', 10)
+ self.historyIndex = 0
+ return
+
+ def typeCallback(self, extraArgs):
+ self.activate()
+
+ def delete(self):
+ self.ignore('arrow_up-up')
+ self.ignore('arrow_down-up')
+ self.chatFrame.destroy()
+ del self.chatFrame
+ del self.chatButton
+ del self.cancelButton
+ del self.chatEntry
+ del self.whisperLabel
+ del self.chatMgr
+
+ def show(self, whisperId = None, toPlayer = 0):
+ self.toPlayer = toPlayer
+ self.whisperId = whisperId
+ self.whisperName = None
+ if self.whisperId:
+ self.whisperName = base.talkAssistant.findName(whisperId, toPlayer)
+ if hasattr(self, 'whisperPos'):
+ self.chatFrame.setPos(self.whisperPos)
+ self.whisperLabel['text'] = OTPLocalizer.ChatInputWhisperLabel % self.whisperName
+ self.whisperLabel.show()
+ else:
+ if hasattr(self, 'normalPos'):
+ self.chatFrame.setPos(self.normalPos)
+ self.whisperLabel.hide()
+ self.chatEntry['focus'] = 1
+ self.chatEntry.set('')
+ self.chatFrame.show()
+ self.chatEntry.show()
+ self.cancelButton.show()
+ self.typedChatButton.hide()
+ self.typedChatBar.hide()
+ if self.wantHistory:
+ self.accept('arrow_up-up', self.getPrevHistory)
+ self.accept('arrow_down-up', self.getNextHistory)
+ return
+
+ def hide(self):
+ self.chatEntry.set('')
+ self.chatEntry['focus'] = 0
+ self.chatFrame.hide()
+ self.chatEntry.hide()
+ self.cancelButton.hide()
+ self.typedChatButton.show()
+ self.typedChatBar.show()
+ self.ignore('arrow_up-up')
+ self.ignore('arrow_down-up')
+
+ def activate(self):
+ self.chatEntry.set('')
+ self.chatEntry['focus'] = 1
+ self.chatFrame.show()
+ self.chatEntry.show()
+ self.cancelButton.show()
+ self.typedChatButton.hide()
+ self.typedChatBar.hide()
+ if self.whisperId:
+ print 'have id'
+ if self.toPlayer:
+ if not base.talkAssistant.checkWhisperTypedChatPlayer(self.whisperId):
+ messenger.send('Chat-Failed player typed chat test')
+ self.deactivate()
+ elif not base.talkAssistant.checkWhisperTypedChatAvatar(self.whisperId):
+ messenger.send('Chat-Failed avatar typed chat test')
+ self.deactivate()
+ elif not base.talkAssistant.checkOpenTypedChat():
+ messenger.send('Chat-Failed open typed chat test')
+ self.deactivate()
+
+ def deactivate(self):
+ self.chatEntry.set('')
+ self.chatEntry['focus'] = 0
+ self.chatFrame.show()
+ self.chatEntry.hide()
+ self.cancelButton.hide()
+ self.typedChatButton.show()
+ self.typedChatBar.show()
+
+ def sendChat(self, text):
+ self.deactivate()
+ if text:
+ if self.toPlayer:
+ if self.whisperId:
+ pass
+ elif self.whisperId:
+ pass
+ elif base.config.GetBool('exec-chat', 0) and text[0] == '>':
+ text = self.__execMessage(text[1:])
+ base.localAvatar.setChatAbsolute(text, CFSpeech | CFTimeout)
+ return
+ else:
+ base.talkAssistant.sendOpenTalk(text)
+ if self.wantHistory:
+ self.addToHistory(text)
+ self.chatEntry.set('')
+
+ def chatOverflow(self, overflowText):
+ self.sendChat(self.chatEntry.get())
+
+ def __execMessage(self, message):
+ if not ChatInputTyped.ExecNamespace:
+ ChatInputTyped.ExecNamespace = {}
+ exec 'from pandac.PandaModules import *' in globals(), self.ExecNamespace
+ self.importExecNamespace()
+ try:
+ if not isClient():
+ print 'EXECWARNING ChatInputNormal eval: %s' % message
+ printStack()
+ return str(eval(message, globals(), ChatInputTyped.ExecNamespace))
+ except SyntaxError:
+ try:
+ if not isClient():
+ print 'EXECWARNING ChatInputNormal exec: %s' % message
+ printStack()
+ exec message in globals(), ChatInputTyped.ExecNamespace
+ return 'ok'
+ except:
+ exception = sys.exc_info()[0]
+ extraInfo = sys.exc_info()[1]
+ if extraInfo:
+ return str(extraInfo)
+ else:
+ return str(exception)
+
+ except:
+ exception = sys.exc_info()[0]
+ extraInfo = sys.exc_info()[1]
+ if extraInfo:
+ return str(extraInfo)
+ else:
+ return str(exception)
+
+ def cancelButtonPressed(self):
+ self.chatEntry.set('')
+ self.deactivate()
+
+ def chatButtonPressed(self):
+ self.sendChat(self.chatEntry.get())
+
+ def importExecNamespace(self):
+ pass
+
+ def addToHistory(self, text):
+ self.history = [text] + self.history[:self.historySize - 1]
+ self.historyIndex = 0
+
+ def getPrevHistory(self):
+ self.chatEntry.set(self.history[self.historyIndex])
+ self.historyIndex += 1
+ self.historyIndex %= len(self.history)
+
+ def getNextHistory(self):
+ self.chatEntry.set(self.history[self.historyIndex])
+ self.historyIndex -= 1
+ self.historyIndex %= len(self.history)
diff --git a/otp/chat/ChatInputWhiteListFrame.py b/otp/chat/ChatInputWhiteListFrame.py
new file mode 100644
index 0000000..c5bb317
--- /dev/null
+++ b/otp/chat/ChatInputWhiteListFrame.py
@@ -0,0 +1,345 @@
+from direct.fsm import FSM
+from otp.otpbase import OTPGlobals
+import sys
+from direct.directnotify import DirectNotifyGlobal
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from otp.otpbase import OTPLocalizer
+from direct.task import Task
+from otp.chat.ChatInputTyped import ChatInputTyped
+
+class ChatInputWhiteListFrame(FSM.FSM, DirectFrame):
+ notify = DirectNotifyGlobal.directNotify.newCategory('ChatInputWhiteList')
+ ExecNamespace = None
+
+ def __init__(self, entryOptions, parent = None, **kw):
+ FSM.FSM.__init__(self, 'ChatInputWhiteListFrame')
+ self.okayToSubmit = True
+ self.receiverId = None
+ DirectFrame.__init__(self, parent=aspect2dp, pos=(0, 0, 0.3), relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(1.6, 1, 1.4), image_pos=(0, 0, -0.05), image_color=OTPGlobals.GlobalDialogColor, borderWidth=(0.01, 0.01))
+ optiondefs = {'parent': self,
+ 'relief': DGG.SUNKEN,
+ 'scale': 0.05,
+ 'frameSize': (-0.2,
+ 25.3,
+ -0.5,
+ 1.2),
+ 'borderWidth': (0.1, 0.1),
+ 'frameColor': (0.9, 0.9, 0.85, 0.8),
+ 'pos': (-0.2, 0, 0.11),
+ 'entryFont': OTPGlobals.getInterfaceFont(),
+ 'width': 8.6,
+ 'numLines': 3,
+ 'cursorKeys': 1,
+ 'backgroundFocus': 0,
+ 'suppressKeys': 1,
+ 'suppressMouse': 1,
+ 'command': self.sendChat,
+ 'failedCommand': self.sendFailed,
+ 'focus': 0,
+ 'text': '',
+ 'sortOrder': DGG.FOREGROUND_SORT_INDEX}
+ entryOptions['parent'] = self
+ self.chatEntry = DirectEntry(**entryOptions)
+ self.whisperId = None
+ self.chatEntry.bind(DGG.OVERFLOW, self.chatOverflow)
+ wantHistory = 0
+ if __dev__:
+ wantHistory = 1
+ self.wantHistory = base.config.GetBool('want-chat-history', wantHistory)
+ self.history = ['']
+ self.historySize = base.config.GetInt('chat-history-size', 10)
+ self.historyIndex = 0
+ self.promoteWhiteList = 0
+ self.checkBeforeSend = base.config.GetBool('white-list-check-before-send', 0)
+ self.whiteList = None
+ self.active = 0
+ self.autoOff = 0
+ self.sendBy = 'Mode'
+ self.prefilter = 1
+ from direct.gui import DirectGuiGlobals
+ self.chatEntry.bind(DirectGuiGlobals.TYPE, self.applyFilter)
+ self.chatEntry.bind(DirectGuiGlobals.ERASE, self.applyFilter)
+ tpMgr = TextPropertiesManager.getGlobalPtr()
+ Red = tpMgr.getProperties('red')
+ Red.setTextColor(1.0, 0.0, 0.0, 1)
+ tpMgr.setProperties('WLRed', Red)
+ del tpMgr
+ self.origFrameColor = self.chatEntry['frameColor']
+ return
+
+ def destroy(self):
+ from direct.gui import DirectGuiGlobals
+ self.chatEntry.unbind(DGG.OVERFLOW)
+ self.chatEntry.unbind(DirectGuiGlobals.TYPE)
+ self.chatEntry.unbind(DirectGuiGlobals.ERASE)
+ self.chatEntry.ignoreAll()
+ DirectFrame.destroy(self)
+
+ def delete(self):
+ self.ignore('arrow_up-up')
+ self.ignore('arrow_down-up')
+
+ def requestMode(self, mode, *args):
+ return self.request(mode, *args)
+
+ def defaultFilter(self, request, *args):
+ if request == 'AllChat':
+ if not base.talkAssistant.checkAnyTypedChat():
+ messenger.send('Chat-Failed open typed chat test')
+ self.notify.warning('Chat-Failed open typed chat test')
+ return None
+ elif request == 'PlayerWhisper':
+ if not base.talkAssistant.checkWhisperTypedChatPlayer(self.whisperId):
+ messenger.send('Chat-Failed player typed chat test')
+ self.notify.warning('Chat-Failed player typed chat test')
+ return None
+ elif request == 'AvatarWhisper':
+ if not base.talkAssistant.checkWhisperTypedChatAvatar(self.whisperId):
+ messenger.send('Chat-Failed avatar typed chat test')
+ self.notify.warning('Chat-Failed avatar typed chat test')
+ return None
+ return FSM.FSM.defaultFilter(self, request, *args)
+
+ def enterOff(self):
+ self.deactivate()
+ localAvatar.chatMgr.fsm.request('mainMenu')
+
+ def exitOff(self):
+ self.activate()
+
+ def enterAllChat(self):
+ self.chatEntry['focus'] = 1
+ self.show()
+
+ def exitAllChat(self):
+ pass
+
+ def enterGuildChat(self):
+ self['focus'] = 1
+ self.show()
+
+ def exitGuildChat(self):
+ pass
+
+ def enterCrewChat(self):
+ self['focus'] = 1
+ self.show()
+
+ def exitCrewChat(self):
+ pass
+
+ def enterPlayerWhisper(self):
+ self.tempText = self.chatEntry.get()
+ self.activate()
+
+ def exitPlayerWhisper(self):
+ self.chatEntry.set(self.tempText)
+ self.whisperId = None
+ return
+
+ def enterAvatarWhisper(self):
+ self.tempText = self.chatEntry.get()
+ self.activate()
+
+ def exitAvatarWhisper(self):
+ self.chatEntry.set(self.tempText)
+ self.whisperId = None
+ return
+
+ def activateByData(self, receiverId = None, toPlayer = 0):
+ self.receiverId = receiverId
+ self.toPlayer = toPlayer
+ result = None
+ if not self.receiverId:
+ result = self.requestMode('AllChat')
+ elif self.receiverId and not self.toPlayer:
+ self.whisperId = receiverId
+ result = self.requestMode('AvatarWhisper')
+ elif self.receiverId and self.toPlayer:
+ self.whisperId = receiverId
+ result = self.requestMode('PlayerWhisper')
+ return result
+
+ def activate(self):
+ self.chatEntry['focus'] = 1
+ self.show()
+ self.active = 1
+ self.chatEntry.guiItem.setAcceptEnabled(False)
+
+ def deactivate(self):
+ self.chatEntry.set('')
+ self.chatEntry['focus'] = 0
+ self.hide()
+ self.active = 0
+
+ def isActive(self):
+ return self.active
+
+ def sendChat(self, text, overflow = False):
+ if not (len(text) > 0 and text[0] in ['~', '>']):
+ if self.prefilter:
+ words = text.split(' ')
+ newwords = []
+ for word in words:
+ if word == '' or self.whiteList.isWord(word) or self.promoteWhiteList:
+ newwords.append(word)
+ else:
+ newwords.append(base.whiteList.defaultWord)
+
+ text = ' '.join(newwords)
+ else:
+ text = self.chatEntry.get(plain=True)
+
+ if text:
+ self.chatEntry.set('')
+ if base.config.GetBool('exec-chat', 0) and text[0] == '>':
+ text = self.__execMessage(text[1:])
+ base.localAvatar.setChatAbsolute(text, CFSpeech | CFTimeout)
+ return
+ else:
+ self.sendChatBySwitch(text)
+ if self.wantHistory:
+ self.addToHistory(text)
+ else:
+ localAvatar.chatMgr.deactivateChat()
+
+ if not overflow:
+ self.hide()
+ if self.autoOff:
+ self.requestMode('Off')
+
+ localAvatar.chatMgr.messageSent()
+
+ def sendChatBySwitch(self, text):
+ if len(text) > 0 and text[0] == '~':
+ base.talkAssistant.sendOpenTalk(text)
+ elif self.sendBy == 'Mode':
+ self.sendChatByMode(text)
+ elif self.sendBy == 'Data':
+ self.sendChatByData(text)
+ else:
+ self.sendChatByMode(text)
+
+ def sendChatByData(self, text):
+ if not self.receiverId:
+ base.talkAssistant.sendOpenTalk(text)
+ elif self.receiverId and not self.toPlayer:
+ base.talkAssistant.sendWhisperTalk(text, self.receiverId)
+ elif self.receiverId and self.toPlayer:
+ base.talkAssistant.sendAccountTalk(text, self.receiverId)
+
+ def sendChatByMode(self, text):
+ state = self.getCurrentOrNextState()
+ messenger.send('sentRegularChat')
+ if state == 'PlayerWhisper':
+ base.talkAssistant.sendPlayerWhisperWLChat(text, self.whisperId)
+ elif state == 'AvatarWhisper':
+ base.talkAssistant.sendAvatarWhisperWLChat(text, self.whisperId)
+ elif state == 'GuildChat':
+ base.talkAssistant.sendAvatarGuildWLChat(text)
+ elif state == 'CrewChat':
+ base.talkAssistant.sendAvatarCrewWLChat(text)
+ elif len(text) > 0 and text[0] == '~':
+ base.talkAssistant.sendOpenTalk(text)
+ else:
+ base.talkAssistant.sendOpenTalk(text)
+
+ def sendFailed(self, text):
+ if not self.checkBeforeSend:
+ self.sendChat(text)
+ return
+ self.chatEntry['frameColor'] = (0.9, 0.0, 0.0, 0.8)
+
+ def resetFrameColor(task = None):
+ self.chatEntry['frameColor'] = self.origFrameColor
+ return Task.done
+
+ taskMgr.doMethodLater(0.1, resetFrameColor, 'resetFrameColor')
+ self.applyFilter(keyArgs=None, strict=True)
+ self.okayToSubmit = True
+ self.chatEntry.guiItem.setAcceptEnabled(True)
+ return
+
+ def chatOverflow(self, overflowText):
+ self.notify.debug('chatOverflow')
+ self.sendChat(self.chatEntry.get(plain=True), overflow=True)
+
+ def addToHistory(self, text):
+ self.history = [text] + self.history[:self.historySize - 1]
+ self.historyIndex = 0
+
+ def getPrevHistory(self):
+ self.chatEntry.set(self.history[self.historyIndex])
+ self.historyIndex += 1
+ self.historyIndex %= len(self.history)
+
+ def getNextHistory(self):
+ self.chatEntry.set(self.history[self.historyIndex])
+ self.historyIndex -= 1
+ self.historyIndex %= len(self.history)
+
+ def importExecNamespace(self):
+ pass
+
+ def __execMessage(self, message):
+ if not ChatInputTyped.ExecNamespace:
+ ChatInputTyped.ExecNamespace = {}
+ exec 'from pandac.PandaModules import *' in globals(), self.ExecNamespace
+ self.importExecNamespace()
+ try:
+ if not isClient():
+ print 'EXECWARNING ChatInputWhiteListFrame eval: %s' % message
+ printStack()
+ return str(eval(message, globals(), ChatInputTyped.ExecNamespace))
+ except SyntaxError:
+ try:
+ if not isClient():
+ print 'EXECWARNING ChatInputWhiteListFrame exec: %s' % message
+ printStack()
+ exec message in globals(), ChatInputTyped.ExecNamespace
+ return 'ok'
+ except:
+ exception = sys.exc_info()[0]
+ extraInfo = sys.exc_info()[1]
+ if extraInfo:
+ return str(extraInfo)
+ else:
+ return str(exception)
+
+ except:
+ exception = sys.exc_info()[0]
+ extraInfo = sys.exc_info()[1]
+ if extraInfo:
+ return str(extraInfo)
+ else:
+ return str(exception)
+
+ def applyFilter(self, keyArgs, strict = False):
+ text = self.chatEntry.get(plain=True)
+ if len(text) > 0 and text[0] in ['~', '>']:
+ self.okayToSubmit = True
+ else:
+ words = text.split(' ')
+ newwords = []
+ self.notify.debug('%s' % words)
+ self.okayToSubmit = True
+ for word in words:
+ if word == '' or self.whiteList.isWord(word):
+ newwords.append(word)
+ else:
+ if self.checkBeforeSend:
+ self.okayToSubmit = False
+ else:
+ self.okayToSubmit = True
+ newwords.append('\x01WLEnter\x01' + word + '\x02')
+
+ if not strict:
+ lastword = words[-1]
+ if lastword == '' or self.whiteList.isPrefix(lastword):
+ newwords[-1] = lastword
+ else:
+ newwords[-1] = '\x01WLEnter\x01' + lastword + '\x02'
+ newtext = ' '.join(newwords)
+ self.chatEntry.set(newtext)
+ self.chatEntry.guiItem.setAcceptEnabled(self.okayToSubmit)
diff --git a/otp/chat/ChatManager.py b/otp/chat/ChatManager.py
new file mode 100644
index 0000000..c212b9b
--- /dev/null
+++ b/otp/chat/ChatManager.py
@@ -0,0 +1,517 @@
+import string
+import sys
+from direct.showbase import DirectObject
+from otp.otpbase import OTPGlobals
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from otp.login import SecretFriendsInfoPanel
+from otp.login import PrivacyPolicyPanel
+from otp.otpbase import OTPLocalizer
+from direct.directnotify import DirectNotifyGlobal
+from otp.login import LeaveToPayDialog
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+ChatEvent = 'ChatEvent'
+NormalChatEvent = 'NormalChatEvent'
+SCChatEvent = 'SCChatEvent'
+SCCustomChatEvent = 'SCCustomChatEvent'
+SCEmoteChatEvent = 'SCEmoteChatEvent'
+OnScreen = 0
+OffScreen = 1
+Thought = 2
+ThoughtPrefix = '.'
+
+def isThought(message):
+ if len(message) == 0:
+ return 0
+ elif string.find(message, ThoughtPrefix, 0, len(ThoughtPrefix)) >= 0:
+ return 1
+ else:
+ return 0
+
+
+def removeThoughtPrefix(message):
+ if isThought(message):
+ return message[len(ThoughtPrefix):]
+ else:
+ return message
+
+
+class ChatManager(DirectObject.DirectObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('ChatManager')
+ execChat = base.config.GetBool('exec-chat', 0)
+
+ def __init__(self, cr, localAvatar):
+ self.cr = cr
+ self.localAvatar = localAvatar
+ self.wantBackgroundFocus = 1
+ self.__scObscured = 0
+ self.__normalObscured = 0
+ self.openChatWarning = None
+ self.unpaidChatWarning = None
+ self.teaser = None
+ self.paidNoParentPassword = None
+ self.noSecretChatAtAll = None
+ self.noSecretChatAtAllAndNoWhitelist = None
+ self.noSecretChatWarning = None
+ self.activateChatGui = None
+ self.chatMoreInfo = None
+ self.chatPrivacyPolicy = None
+ self.secretChatActivated = None
+ self.problemActivatingChat = None
+ self.leaveToPayDialog = None
+ self.fsm = ClassicFSM.ClassicFSM('chatManager', [State.State('off', self.enterOff, self.exitOff),
+ State.State('mainMenu', self.enterMainMenu, self.exitMainMenu),
+ State.State('speedChat', self.enterSpeedChat, self.exitSpeedChat),
+ State.State('normalChat', self.enterNormalChat, self.exitNormalChat),
+ State.State('whisper', self.enterWhisper, self.exitWhisper),
+ State.State('whisperChat', self.enterWhisperChat, self.exitWhisperChat),
+ State.State('whisperChatPlayer', self.enterWhisperChatPlayer, self.exitWhisperChatPlayer),
+ State.State('whisperSpeedChat', self.enterWhisperSpeedChat, self.exitWhisperSpeedChat),
+ State.State('whisperSpeedChatPlayer', self.enterWhisperSpeedChatPlayer, self.exitWhisperSpeedChatPlayer),
+ State.State('openChatWarning', self.enterOpenChatWarning, self.exitOpenChatWarning),
+ State.State('leaveToPayDialog', self.enterLeaveToPayDialog, self.exitLeaveToPayDialog),
+ State.State('unpaidChatWarning', self.enterUnpaidChatWarning, self.exitUnpaidChatWarning),
+ State.State('noSecretChatAtAll', self.enterNoSecretChatAtAll, self.exitNoSecretChatAtAll),
+ State.State('noSecretChatAtAllAndNoWhitelist', self.enterNoSecretChatAtAllAndNoWhitelist, self.exitNoSecretChatAtAllAndNoWhitelist),
+ State.State('noSecretChatWarning', self.enterNoSecretChatWarning, self.exitNoSecretChatWarning),
+ State.State('noFriendsWarning', self.enterNoFriendsWarning, self.exitNoFriendsWarning),
+ State.State('otherDialog', self.enterOtherDialog, self.exitOtherDialog),
+ State.State('activateChat', self.enterActivateChat, self.exitActivateChat),
+ State.State('chatMoreInfo', self.enterChatMoreInfo, self.exitChatMoreInfo),
+ State.State('chatPrivacyPolicy', self.enterChatPrivacyPolicy, self.exitChatPrivacyPolicy),
+ State.State('secretChatActivated', self.enterSecretChatActivated, self.exitSecretChatActivated),
+ State.State('problemActivatingChat', self.enterProblemActivatingChat, self.exitProblemActivatingChat),
+ State.State('whiteListOpenChat', self.enterWhiteListOpenChat, self.exitWhiteListOpenChat),
+ State.State('whiteListAvatarChat', self.enterWhiteListAvatarChat, self.exitWhiteListAvatarChat),
+ State.State('whiteListPlayerChat', self.enterWhiteListPlayerChat, self.exitWhiteListPlayerChat),
+ State.State('trueFriendTeaserPanel', self.enterTrueFriendTeaserPanel, self.exitTrueFriendTeaserPanel)], 'off', 'off')
+ self.fsm.enterInitialState()
+ return
+
+ def delete(self):
+ self.ignoreAll()
+ del self.fsm
+ if hasattr(self.chatInputNormal, 'destroy'):
+ self.chatInputNormal.destroy()
+ self.chatInputNormal.delete()
+ del self.chatInputNormal
+ self.chatInputSpeedChat.delete()
+ del self.chatInputSpeedChat
+ if self.openChatWarning:
+ self.openChatWarning.destroy()
+ self.openChatWarning = None
+ if self.unpaidChatWarning:
+ self.payButton = None
+ self.unpaidChatWarning.destroy()
+ self.unpaidChatWarning = None
+ if self.teaser:
+ self.teaser.cleanup()
+ self.teaser.unload()
+ self.teaser = None
+ if self.noSecretChatAtAll:
+ self.noSecretChatAtAll.destroy()
+ self.noSecretChatAtAll = None
+ if self.noSecretChatAtAllAndNoWhitelist:
+ self.noSecretChatAtAllAndNoWhitelist.destroy()
+ self.noSecretChatAtAllAndNoWhitelist = None
+ if self.noSecretChatWarning:
+ self.noSecretChatWarning.destroy()
+ self.noSecretChatWarning = None
+ if self.activateChatGui:
+ self.activateChatGui.destroy()
+ self.activateChatGui = None
+ if self.chatMoreInfo:
+ self.chatMoreInfo.destroy()
+ self.chatMoreInfo = None
+ if self.chatPrivacyPolicy:
+ self.chatPrivacyPolicy.destroy()
+ self.chatPrivacyPolicy = None
+ if self.secretChatActivated:
+ self.secretChatActivated.destroy()
+ self.secretChatActivated = None
+ if self.problemActivatingChat:
+ self.problemActivatingChat.destroy()
+ self.problemActivatingChat = None
+ del self.localAvatar
+ del self.cr
+ return
+
+ def obscure(self, normal, sc):
+ self.__scObscured = sc
+ if self.__scObscured:
+ self.scButton.hide()
+ self.__normalObscured = normal
+ if self.__normalObscured:
+ self.normalButton.hide()
+
+ def isObscured(self):
+ return (self.__normalObscured, self.__scObscured)
+
+ def stop(self):
+ self.fsm.request('off')
+ self.ignoreAll()
+
+ def start(self):
+ self.fsm.request('mainMenu')
+
+ def announceChat(self):
+ messenger.send(ChatEvent)
+
+ def announceSCChat(self):
+ messenger.send(SCChatEvent)
+ self.announceChat()
+
+ def sendChatString(self, message):
+ chatFlags = CFSpeech | CFTimeout
+ if base.cr.wantSwitchboardHacks:
+ from otp.switchboard import badwordpy
+ badwordpy.init('', '')
+ message = badwordpy.scrub(message)
+ if isThought(message):
+ message = removeThoughtPrefix(message)
+ chatFlags = CFThought
+ messenger.send(NormalChatEvent)
+ self.announceChat()
+
+ def sendWhisperString(self, message, whisperAvatarId):
+ pass
+
+ def sendSCChatMessage(self, msgIndex):
+ base.talkAssistant.sendOpenSpeedChat(1, msgIndex)
+
+ def sendSCWhisperMessage(self, msgIndex, whisperAvatarId, toPlayer):
+ if toPlayer:
+ base.talkAssistant.sendPlayerWhisperSpeedChat(1, msgIndex, whisperAvatarId)
+ else:
+ base.talkAssistant.sendAvatarWhisperSpeedChat(1, msgIndex, whisperAvatarId)
+
+ def sendSCCustomChatMessage(self, msgIndex):
+ base.talkAssistant.sendOpenSpeedChat(3, msgIndex)
+
+ def sendSCCustomWhisperMessage(self, msgIndex, whisperAvatarId, toPlayer):
+ if toPlayer:
+ base.talkAssistant.sendPlayerWhisperSpeedChat(3, msgIndex, whisperAvatarId)
+ else:
+ base.talkAssistant.sendAvatarWhisperSpeedChat(3, msgIndex, whisperAvatarId)
+
+ def sendSCEmoteChatMessage(self, emoteId):
+ base.talkAssistant.sendOpenSpeedChat(2, emoteId)
+
+ def sendSCEmoteWhisperMessage(self, emoteId, whisperAvatarId, toPlayer):
+ if toPlayer:
+ base.talkAssistant.sendPlayerWhisperSpeedChat(2, emoteId, whisperAvatarId)
+ else:
+ base.talkAssistant.sendAvatarWhisperSpeedChat(2, emoteId, whisperAvatarId)
+
+ def enterOff(self):
+ self.scButton.hide()
+ self.normalButton.hide()
+ self.ignoreAll()
+
+ def exitOff(self):
+ pass
+
+ def enterMainMenu(self):
+ self.checkObscurred()
+ if self.localAvatar.canChat() or self.cr.wantMagicWords:
+ if self.wantBackgroundFocus:
+ self.chatInputNormal.chatEntry['backgroundFocus'] = 1
+ self.acceptOnce('enterNormalChat', self.fsm.request, ['normalChat'])
+
+ def checkObscurred(self):
+ if not self.__scObscured:
+ self.scButton.show()
+ if not self.__normalObscured:
+ self.normalButton.show()
+
+ def exitMainMenu(self):
+ self.scButton.hide()
+ self.normalButton.hide()
+ self.ignore('enterNormalChat')
+ if self.wantBackgroundFocus:
+ self.chatInputNormal.chatEntry['backgroundFocus'] = 0
+
+ def whisperTo(self, avatarName, avatarId, playerId = None):
+ self.fsm.request('whisper', [avatarName, avatarId, playerId])
+
+ def noWhisper(self):
+ self.fsm.request('mainMenu')
+
+ def handleWhiteListSelect(self):
+ self.fsm.request('whiteListOpenChat')
+
+ def enterWhiteListOpenChat(self):
+ self.checkObscurred()
+ if self.wantBackgroundFocus:
+ self.chatInputNormal.chatEntry['backgroundFocus'] = 0
+ base.localAvatar.chatMgr.chatInputWhiteList.activateByData()
+
+ def exitWhiteListOpenChat(self):
+ pass
+
+ def enterWhiteListAvatarChat(self, receiverId):
+ if self.wantBackgroundFocus:
+ self.chatInputNormal.chatEntry['backgroundFocus'] = 0
+ base.localAvatar.chatMgr.chatInputWhiteList.activateByData(receiverId, 0)
+
+ def exitWhiteListAvatarChat(self):
+ pass
+
+ def enterWhiteListPlayerChat(self, receiverId):
+ if self.wantBackgroundFocus:
+ self.chatInputNormal.chatEntry['backgroundFocus'] = 0
+ base.localAvatar.chatMgr.chatInputWhiteList.activateByData(receiverId, 1)
+
+ def exitWhiteListPlayerChat(self):
+ pass
+
+ def enterWhisper(self, avatarName, avatarId, playerId = None):
+ self.whisperScButton['extraArgs'] = [avatarName, avatarId, playerId]
+ self.whisperButton['extraArgs'] = [avatarName, avatarId, playerId]
+ playerName = None
+ chatToToon = 1
+ online = 0
+ if self.cr.doId2do.has_key(avatarId):
+ online = 1
+ elif self.cr.isFriend(avatarId):
+ online = self.cr.isFriendOnline(avatarId)
+ hasManager = hasattr(base.cr, 'playerFriendsManager')
+ if hasManager:
+ if base.cr.playerFriendsManager.askAvatarOnline(avatarId):
+ online = 1
+ avatarUnderstandable = 0
+ playerUnderstandable = 0
+ av = None
+ if avatarId:
+ av = self.cr.identifyAvatar(avatarId)
+ if av != None:
+ avatarUnderstandable = av.isUnderstandable()
+ if playerId:
+ if base.cr.playerFriendsManager.playerId2Info.has_key(playerId):
+ playerInfo = base.cr.playerFriendsManager.playerId2Info.get(playerId)
+ playerName = playerInfo.playerName
+ online = 1
+ playerUnderstandable = playerInfo.understandableYesNo
+ if playerUnderstandable or not avatarId:
+ chatToToon = 0
+ if chatToToon:
+ chatName = avatarName
+ else:
+ chatName = playerName
+ normalButtonObscured, scButtonObscured = self.isObscured()
+ if (avatarUnderstandable or playerUnderstandable) and online and not normalButtonObscured:
+ self.whisperButton['state'] = 'normal'
+ self.enablewhisperButton()
+ else:
+ self.whisperButton['state'] = 'inactive'
+ self.disablewhisperButton()
+ if online:
+ self.whisperScButton['state'] = 'normal'
+ self.changeFrameText(OTPLocalizer.ChatManagerWhisperToName % chatName)
+ else:
+ self.whisperScButton['state'] = 'inactive'
+ self.changeFrameText(OTPLocalizer.ChatManagerWhisperOffline % chatName)
+ self.whisperFrame.show()
+ self.refreshWhisperFrame()
+ if avatarUnderstandable or playerUnderstandable:
+ if playerId and not chatToToon:
+ if self.wantBackgroundFocus:
+ self.chatInputNormal.chatEntry['backgroundFocus'] = 1
+ self.acceptOnce('enterNormalChat', self.fsm.request, ['whisperChatPlayer', [avatarName, playerId]])
+ elif online and chatToToon:
+ if self.wantBackgroundFocus:
+ self.chatInputNormal.chatEntry['backgroundFocus'] = 1
+ self.acceptOnce('enterNormalChat', self.fsm.request, ['whisperChat', [avatarName, avatarId]])
+ if base.cr.config.GetBool('force-typed-whisper-enabled', 0):
+ self.whisperButton['state'] = 'normal'
+ self.enablewhisperButton()
+ return
+
+ def disablewhisperButton(self):
+ pass
+
+ def enablewhisperButton(self):
+ pass
+
+ def refreshWhisperFrame(self):
+ pass
+
+ def changeFrameText(self, newText):
+ self.whisperFrame['text'] = newText
+
+ def exitWhisper(self):
+ self.whisperFrame.hide()
+ self.ignore('enterNormalChat')
+ self.chatInputNormal.chatEntry['backgroundFocus'] = 0
+
+ def enterWhisperSpeedChat(self, avatarId):
+ self.whisperFrame.show()
+ if self.wantBackgroundFocus:
+ self.chatInputNormal.chatEntry['backgroundFocus'] = 0
+ self.chatInputSpeedChat.show(avatarId)
+
+ def exitWhisperSpeedChat(self):
+ self.whisperFrame.hide()
+ self.chatInputSpeedChat.hide()
+
+ def enterWhisperSpeedChatPlayer(self, playerId):
+ self.whisperFrame.show()
+ if self.wantBackgroundFocus:
+ self.chatInputNormal.chatEntry['backgroundFocus'] = 0
+ self.chatInputSpeedChat.show(playerId, 1)
+
+ def exitWhisperSpeedChatPlayer(self):
+ self.whisperFrame.hide()
+ self.chatInputSpeedChat.hide()
+
+ def enterWhisperChat(self, avatarName, avatarId):
+ result = self.chatInputNormal.activateByData(avatarId)
+ return result
+
+ def exitWhisperChat(self):
+ self.chatInputNormal.deactivate()
+
+ def enterWhisperChatPlayer(self, avatarName, playerId):
+ playerInfo = base.cr.playerFriendsManager.getFriendInfo(playerId)
+ if playerInfo:
+ avatarName = playerInfo.playerName
+ result = self.chatInputNormal.activateByData(playerId, 1)
+ return result
+
+ def exitWhisperChatPlayer(self):
+ self.chatInputNormal.deactivate()
+
+ def enterSpeedChat(self):
+ messenger.send('enterSpeedChat')
+ if not self.__scObscured:
+ self.scButton.show()
+ if not self.__normalObscured:
+ self.normalButton.show()
+ if self.wantBackgroundFocus:
+ self.chatInputNormal.chatEntry['backgroundFocus'] = 0
+ self.chatInputSpeedChat.show()
+
+ def exitSpeedChat(self):
+ self.scButton.hide()
+ self.normalButton.hide()
+ self.chatInputSpeedChat.hide()
+
+ def enterNormalChat(self):
+ result = self.chatInputNormal.activateByData()
+ return result
+
+ def exitNormalChat(self):
+ self.chatInputNormal.deactivate()
+
+ def enterOpenChatWarning(self):
+ self.notify.error('called enterOpenChatWarning() on parent class')
+
+ def exitOpenChatWarning(self):
+ self.notify.error('called exitOpenChatWarning() on parent class')
+
+ def enterLeaveToPayDialog(self):
+ if self.leaveToPayDialog == None:
+ self.leaveToPayDialog = LeaveToPayDialog.LeaveToPayDialog(self.paidNoParentPassword)
+ self.leaveToPayDialog.setCancel(self.__handleLeaveToPayCancel)
+ self.leaveToPayDialog.show()
+ return
+
+ def exitLeaveToPayDialog(self):
+ if self.leaveToPayDialog:
+ self.leaveToPayDialog.destroy()
+ self.leaveToPayDialog = None
+ return
+
+ def enterUnpaidChatWarning(self):
+ self.notify.error('called enterUnpaidChatWarning() on parent class')
+
+ def exitUnpaidChatWarning(self):
+ self.notify.error('called exitUnpaidChatWarning() on parent class')
+
+ def enterNoSecretChatAtAll(self):
+ self.notify.error('called enterNoSecretChatAtAll() on parent class')
+
+ def exitNoSecretChatAtAll(self):
+ self.notify.error('called exitNoSecretChatAtAll() on parent class')
+
+ def enterNoSecretChatAtAllAndNoWhitelist(self):
+ self.notify.error('called enterNoSecretChatAtAllAndNoWhitelist() on parent class')
+
+ def exitNoSecretChatAtAllAndNoWhitelist(self):
+ self.notify.error('called exitNoSecretChatAtAllAndNoWhitelist() on parent class')
+
+ def enterNoSecretChatWarning(self):
+ self.notify.error('called enterNoSecretChatWarning() on parent class')
+
+ def exitNoSecretChatWarning(self):
+ self.notify.error('called exitNoSecretChatWarning() on parent class')
+
+ def enterNoFriendsWarning(self):
+ self.notify.error('called enterNoFriendsWarning() on parent class')
+
+ def exitNoFriendsWarning(self):
+ self.notify.error('called exitNoFriendsWarning() on parent class')
+
+ def enterActivateChat(self):
+ self.notify.error('called enterActivateChat() on parent class')
+
+ def exitActivateChat(self):
+ self.notify.error('called exitActivateChat() on parent class')
+
+ def enterOtherDialog(self):
+ pass
+
+ def exitOtherDialog(self):
+ pass
+
+ def enterChatMoreInfo(self):
+ if self.chatMoreInfo == None:
+ self.chatMoreInfo = SecretFriendsInfoPanel.SecretFriendsInfoPanel('secretFriendsInfoDone')
+ self.chatMoreInfo.show()
+ self.accept('secretFriendsInfoDone', self.__secretFriendsInfoDone)
+ return
+
+ def exitChatMoreInfo(self):
+ self.chatMoreInfo.hide()
+ self.ignore('secretFriendsInfoDone')
+
+ def enterChatPrivacyPolicy(self):
+ if self.chatPrivacyPolicy == None:
+ self.chatPrivacyPolicy = PrivacyPolicyPanel.PrivacyPolicyPanel('privacyPolicyDone')
+ self.chatPrivacyPolicy.show()
+ self.accept('privacyPolicyDone', self.__privacyPolicyDone)
+ return
+
+ def exitChatPrivacyPolicy(self):
+ cleanupDialog('privacyPolicyDialog')
+ self.chatPrivacyPolicy = None
+ self.ignore('privacyPolicyDone')
+ return
+
+ def enterSecretChatActivated(self):
+ self.notify.error('called enterSecretChatActivated() on parent class')
+
+ def exitSecretChatActivated(self):
+ self.notify.error('called exitSecretChatActivated() on parent class')
+
+ def enterProblemActivatingChat(self):
+ self.notify.error('called enterProblemActivatingChat() on parent class')
+
+ def exitProblemActivatingChat(self):
+ self.notify.error('called exitProblemActivatingChat() on parent class')
+
+ def enterTrueFriendTeaserPanel(self):
+ self.notify.error('called enterTrueFriendTeaserPanel () on parent class')
+
+ def exitTrueFriendTeaserPanel(self):
+ self.notify.error('called exitTrueFriendTeaserPanel () on parent class')
+
+ def __handleLeaveToPayCancel(self):
+ self.fsm.request('mainMenu')
+
+ def __secretFriendsInfoDone(self):
+ self.fsm.request('activateChat')
+
+ def __privacyPolicyDone(self):
+ self.fsm.request('activateChat')
diff --git a/otp/chat/TalkAssistant.py b/otp/chat/TalkAssistant.py
new file mode 100644
index 0000000..f2e622b
--- /dev/null
+++ b/otp/chat/TalkAssistant.py
@@ -0,0 +1,753 @@
+import string
+import sys
+from direct.showbase import DirectObject
+from otp.otpbase import OTPLocalizer
+from direct.directnotify import DirectNotifyGlobal
+from otp.otpbase import OTPGlobals
+from otp.speedchat import SCDecoders
+from pandac.PandaModules import *
+from otp.chat.TalkMessage import TalkMessage
+from otp.chat.TalkHandle import TalkHandle
+import time
+from otp.chat.TalkGlobals import *
+from otp.chat.ChatGlobals import *
+from libotp import CFSpeech, CFTimeout, CFThought
+ThoughtPrefix = '.'
+
+class TalkAssistant(DirectObject.DirectObject):
+ ExecNamespace = None
+ notify = DirectNotifyGlobal.directNotify.newCategory('TalkAssistant')
+ execChat = base.config.GetBool('exec-chat', 0)
+
+ def __init__(self):
+ self.logWhispers = 1
+ self.whiteList = None
+ self.clearHistory()
+ self.zeroTimeDay = time.time()
+ self.zeroTimeGame = globalClock.getRealTime()
+ self.floodThreshold = 10.0
+ self.useWhiteListFilter = base.config.GetBool('white-list-filter-openchat', 0)
+ self.lastWhisperDoId = None
+ self.lastWhisperPlayerId = None
+ self.lastWhisper = None
+ self.SCDecoder = SCDecoders
+ return
+
+ def clearHistory(self):
+ self.historyComplete = []
+ self.historyOpen = []
+ self.historyUpdates = []
+ self.historyGuild = []
+ self.historyByDoId = {}
+ self.historyByDISLId = {}
+ self.floodDataByDoId = {}
+ self.spamDictByDoId = {}
+ self.labelGuild = OTPLocalizer.TalkGuild
+ self.handleDict = {}
+ self.messageCount = 0
+ self.shownWhiteListWarning = 0
+
+ def delete(self):
+ self.ignoreAll()
+ self.clearHistory()
+
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
+
+ def countMessage(self):
+ self.messageCount += 1
+ return self.messageCount - 1
+
+ def getOpenText(self, numLines, startPoint = 0):
+ return self.historyOpen[startPoint:startPoint + numLines]
+
+ def getSizeOpenText(self):
+ return len(self.historyOpen)
+
+ def getCompleteText(self, numLines, startPoint = 0):
+ return self.historyComplete[startPoint:startPoint + numLines]
+
+ def getCompleteTextFromRecent(self, numLines, startPoint = 0):
+ start = len(self.historyComplete) - startPoint
+ if start < 0:
+ start = 0
+ backStart = max(start - numLines, 0)
+ text = self.historyComplete[backStart:start]
+ text.reverse()
+ return text
+
+ def getAllCompleteText(self):
+ return self.historyComplete
+
+ def getAllHistory(self):
+ return self.historyComplete
+
+ def getSizeCompleteText(self):
+ return len(self.historyComplete)
+
+ def getHandle(self, doId):
+ return self.handleDict.get(doId)
+
+ def doWhiteListWarning(self):
+ pass
+
+ def addToHistoryDoId(self, message, doId, scrubbed = 0):
+ if message.getTalkType() == TALK_WHISPER and doId != localAvatar.doId:
+ self.lastWhisperDoId = doId
+ self.lastWhisper = self.lastWhisperDoId
+ if not self.historyByDoId.has_key(doId):
+ self.historyByDoId[doId] = []
+ self.historyByDoId[doId].append(message)
+ if not self.shownWhiteListWarning and scrubbed and doId == localAvatar.doId:
+ self.doWhiteListWarning()
+ self.shownWhiteListWarning = 1
+ if not self.floodDataByDoId.has_key(doId):
+ self.floodDataByDoId[doId] = [0.0, self.stampTime(), message]
+ else:
+ oldTime = self.floodDataByDoId[doId][1]
+ newTime = self.stampTime()
+ timeDiff = newTime - oldTime
+ oldRating = self.floodDataByDoId[doId][0]
+ contentMult = 1.0
+ if len(message.getBody()) < 6:
+ contentMult += 0.2 * float(6 - len(message.getBody()))
+ if self.floodDataByDoId[doId][2].getBody() == message.getBody():
+ contentMult += 1.0
+ floodRating = max(0, 3.0 * contentMult + oldRating - timeDiff)
+ self.floodDataByDoId[doId] = [floodRating, self.stampTime(), message]
+ if floodRating > self.floodThreshold:
+ if oldRating < self.floodThreshold:
+ self.floodDataByDoId[doId] = [floodRating + 3.0, self.stampTime(), message]
+ return 1
+ else:
+ self.floodDataByDoId[doId] = [oldRating - timeDiff, self.stampTime(), message]
+ return 2
+ return 0
+
+ def addToHistoryDISLId(self, message, dISLId, scrubbed = 0):
+ if message.getTalkType() == TALK_ACCOUNT and dISLId != base.cr.accountDetailRecord.playerAccountId:
+ self.lastWhisperPlayerId = dISLId
+ self.lastWhisper = self.lastWhisperPlayerId
+ if not self.historyByDISLId.has_key(dISLId):
+ self.historyByDISLId[dISLId] = []
+ self.historyByDISLId[dISLId].append(message)
+
+ def addHandle(self, doId, message):
+ if doId == localAvatar.doId:
+ return
+ handle = self.handleDict.get(doId)
+ if not handle:
+ handle = TalkHandle(doId, message)
+ self.handleDict[doId] = handle
+ else:
+ handle.addMessageInfo(message)
+
+ def stampTime(self):
+ return globalClock.getRealTime() - self.zeroTimeGame
+
+ def findName(self, id, isPlayer = 0):
+ if isPlayer:
+ return self.findPlayerName(id)
+ else:
+ return self.findAvatarName(id)
+
+ def findAvatarName(self, id):
+ info = base.cr.identifyAvatar(id)
+ if info:
+ return info.getName()
+ else:
+ return ''
+
+ def findPlayerName(self, id):
+ info = base.cr.playerFriendsManager.getFriendInfo(id)
+ if info:
+ return info.playerName
+ else:
+ return ''
+
+ def whiteListFilterMessage(self, text):
+ if not self.useWhiteListFilter:
+ return text
+ elif not base.whiteList:
+ return 'no list'
+ words = text.split(' ')
+ newwords = []
+ for word in words:
+ if word == '' or base.whiteList.isWord(word):
+ newwords.append(word)
+ else:
+ newwords.append(base.whiteList.defaultWord)
+
+ newText = ' '.join(newwords)
+ return newText
+
+ def colorMessageByWhiteListFilter(self, text):
+ if not base.whiteList:
+ return text
+ words = text.split(' ')
+ newwords = []
+ for word in words:
+ if word == '' or base.whiteList.isWord(word):
+ newwords.append(word)
+ else:
+ newwords.append('\x01WLRed\x01' + word + '\x02')
+
+ newText = ' '.join(newwords)
+ return newText
+
+ def executeSlashCommand(self, text):
+ pass
+
+ def executeGMCommand(self, text):
+ pass
+
+ def isThought(self, message):
+ if not message:
+ return 0
+ elif len(message) == 0:
+ return 0
+ elif string.find(message, ThoughtPrefix, 0, len(ThoughtPrefix)) >= 0:
+ return 1
+ else:
+ return 0
+
+ def removeThoughtPrefix(self, message):
+ if self.isThought(message):
+ return message[len(ThoughtPrefix):]
+ else:
+ return message
+
+ def fillWithTestText(self):
+ hold = self.floodThreshold
+ self.floodThreshold = 1000.0
+ self.receiveOpenTalk(1001, 'Bob the Ghost', None, None, 'Hello from the machine')
+ self.receiveOpenTalk(1001, 'Bob the Ghost', None, None, 'More text for ya!')
+ self.receiveOpenTalk(1001, 'Bob the Ghost', None, None, 'Hope this makes life easier')
+ self.receiveOpenTalk(1002, 'Doug the Spirit', None, None, 'Now we need some longer text that will spill over onto two lines')
+ self.receiveOpenTalk(1002, 'Doug the Spirit', None, None, 'Maybe I will tell you')
+ self.receiveOpenTalk(1001, 'Bob the Ghost', None, None, 'If you are seeing this text it is because you are cool')
+ self.receiveOpenTalk(1002, 'Doug the Spirit', None, None, "That's right, there is no need to call tech support")
+ self.receiveOpenTalk(localAvatar.doId, localAvatar.getName, None, None, "Okay I won't call tech support, because I am cool")
+ self.receiveGMTalk(1003, 'God of Text', None, None, 'Good because I have seen it already')
+ self.floodThreshold = hold
+ return
+
+ def printHistoryComplete(self):
+ print 'HISTORY COMPLETE'
+ for message in self.historyComplete:
+ print '%s %s %s\n%s\n' % (message.getTimeStamp(),
+ message.getSenderAvatarName(),
+ message.getSenderAccountName(),
+ message.getBody())
+
+ def importExecNamespace(self):
+ pass
+
+ def execMessage(self, message):
+ print 'execMessage %s' % message
+ if not TalkAssistant.ExecNamespace:
+ TalkAssistant.ExecNamespace = {}
+ exec 'from pandac.PandaModules import *' in globals(), self.ExecNamespace
+ self.importExecNamespace()
+ try:
+ if not isClient():
+ print 'EXECWARNING TalkAssistant eval: %s' % message
+ printStack()
+ return str(eval(message, globals(), TalkAssistant.ExecNamespace))
+ except SyntaxError:
+ try:
+ if not isClient():
+ print 'EXECWARNING TalkAssistant exec: %s' % message
+ printStack()
+ exec message in globals(), TalkAssistant.ExecNamespace
+ return 'ok'
+ except:
+ exception = sys.exc_info()[0]
+ extraInfo = sys.exc_info()[1]
+ if extraInfo:
+ return str(extraInfo)
+ else:
+ return str(exception)
+
+ except:
+ exception = sys.exc_info()[0]
+ extraInfo = sys.exc_info()[1]
+ if extraInfo:
+ return str(extraInfo)
+ else:
+ return str(exception)
+
+ def checkOpenTypedChat(self):
+ if base.localAvatar.commonChatFlags & OTPGlobals.CommonChat:
+ return True
+ return False
+
+ def checkAnyTypedChat(self):
+ if base.localAvatar.commonChatFlags & OTPGlobals.CommonChat:
+ return True
+ if base.localAvatar.canChat():
+ return True
+ return False
+
+ def checkOpenSpeedChat(self):
+ return True
+
+ def checkWhisperTypedChatAvatar(self, avatarId):
+ remoteAvatar = base.cr.doId2do.get(avatarId)
+ if remoteAvatar:
+ if remoteAvatar.isUnderstandable():
+ return True
+ if base.localAvatar.commonChatFlags & OTPGlobals.SuperChat:
+ return True
+ remoteAvatarOrHandleOrInfo = base.cr.identifyAvatar(avatarId)
+ if remoteAvatarOrHandleOrInfo and hasattr(remoteAvatarOrHandleOrInfo, 'isUnderstandable'):
+ if remoteAvatarOrHandleOrInfo.isUnderstandable():
+ return True
+ info = base.cr.playerFriendsManager.findPlayerInfoFromAvId(avatarId)
+ if info:
+ if info.understandableYesNo:
+ return True
+ info = base.cr.avatarFriendsManager.getFriendInfo(avatarId)
+ if info:
+ if info.understandableYesNo:
+ return True
+ if base.cr.getFriendFlags(avatarId) & OTPGlobals.FriendChat:
+ return True
+ return False
+
+ def checkWhisperSpeedChatAvatar(self, avatarId):
+ return True
+
+ def checkWhisperTypedChatPlayer(self, playerId):
+ info = base.cr.playerFriendsManager.getFriendInfo(playerId)
+ if info:
+ if info.understandableYesNo:
+ return True
+ return False
+
+ def checkWhisperSpeedChatPlayer(self, playerId):
+ if base.cr.playerFriendsManager.isPlayerFriend(playerId):
+ return True
+ return False
+
+ def checkOpenSpeedChat(self):
+ return True
+
+ def checkWhisperSpeedChatAvatar(self, avatarId):
+ return True
+
+ def checkWhisperSpeedChatPlayer(self, playerId):
+ if base.cr.playerFriendsManager.isPlayerFriend(playerId):
+ return True
+ return False
+
+ def checkGuildTypedChat(self):
+ if localAvatar.guildId:
+ return True
+ return False
+
+ def checkGuildSpeedChat(self):
+ if localAvatar.guildId:
+ return True
+ return False
+
+ def receiveOpenTalk(self, senderAvId, avatarName, accountId, accountName, message, scrubbed = 0):
+ error = None
+ if not avatarName and senderAvId:
+ localAvatar.sendUpdate('logSuspiciousEvent', ['receiveOpenTalk: invalid avatar name (%s)' % senderAvId])
+ avatarName = self.findAvatarName(senderAvId)
+ if not accountName and accountId:
+ accountName = self.findPlayerName(accountId)
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, senderAvId, avatarName, accountId, accountName, None, None, None, None, TALK_OPEN, None)
+ if senderAvId != localAvatar.doId:
+ self.addHandle(senderAvId, newMessage)
+ reject = 0
+ if senderAvId:
+ reject = self.addToHistoryDoId(newMessage, senderAvId, scrubbed)
+ if accountId:
+ self.addToHistoryDISLId(newMessage, accountId)
+ if reject == 1:
+ newMessage.setBody(OTPLocalizer.AntiSpamInChat)
+ if reject != 2:
+ isSpam = self.spamDictByDoId.get(senderAvId) and reject
+ if not isSpam:
+ self.historyComplete.append(newMessage)
+ self.historyOpen.append(newMessage)
+ messenger.send('NewOpenMessage', [newMessage])
+ if newMessage.getBody() == OTPLocalizer.AntiSpamInChat:
+ self.spamDictByDoId[senderAvId] = 1
+ else:
+ self.spamDictByDoId[senderAvId] = 0
+ return error
+
+ def receiveWhisperTalk(self, avatarId, avatarName, accountId, accountName, toId, toName, message, scrubbed = 0):
+ error = None
+ print 'receiveWhisperTalk %s %s %s %s %s' % (avatarId,
+ avatarName,
+ accountId,
+ accountName,
+ message)
+ if not avatarName and avatarId:
+ avatarName = self.findAvatarName(avatarId)
+ if not accountName and accountId:
+ accountName = self.findPlayerName(accountId)
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, avatarId, avatarName, accountId, accountName, toId, toName, None, None, TALK_WHISPER, None)
+ if avatarId == localAvatar.doId:
+ self.addHandle(toId, newMessage)
+ else:
+ self.addHandle(avatarId, newMessage)
+ self.historyComplete.append(newMessage)
+ if avatarId:
+ self.addToHistoryDoId(newMessage, avatarId, scrubbed)
+ if accountId:
+ self.addToHistoryDISLId(newMessage, accountId)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def receiveAccountTalk(self, avatarId, avatarName, accountId, accountName, toId, toName, message, scrubbed = 0):
+ if not accountName and base.cr.playerFriendsManager.playerId2Info.get(accountId):
+ accountName = base.cr.playerFriendsManager.playerId2Info.get(accountId).playerName
+ error = None
+ if not avatarName and avatarId:
+ avatarName = self.findAvatarName(avatarId)
+ if not accountName and accountId:
+ accountName = self.findPlayerName(accountId)
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, avatarId, avatarName, accountId, accountName, None, None, toId, toName, TALK_ACCOUNT, None)
+ self.historyComplete.append(newMessage)
+ if avatarId:
+ self.addToHistoryDoId(newMessage, avatarId, scrubbed)
+ if accountId:
+ self.addToHistoryDISLId(newMessage, accountId, scrubbed)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def receiveGuildTalk(self, senderAvId, fromAC, avatarName, message, scrubbed = 0):
+ error = None
+ if not self.isThought(message):
+ accountName = self.findName(fromAC, 1)
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, senderAvId, avatarName, fromAC, accountName, None, None, None, None, TALK_GUILD, None)
+ reject = self.addToHistoryDoId(newMessage, senderAvId)
+ if reject == 1:
+ newMessage.setBody(OTPLocalizer.AntiSpamInChat)
+ if reject != 2:
+ isSpam = self.spamDictByDoId.get(senderAvId) and reject
+ if not isSpam:
+ self.historyComplete.append(newMessage)
+ self.historyGuild.append(newMessage)
+ messenger.send('NewOpenMessage', [newMessage])
+ if newMessage.getBody() == OTPLocalizer.AntiSpamInChat:
+ self.spamDictByDoId[senderAvId] = 1
+ else:
+ self.spamDictByDoId[senderAvId] = 0
+ return error
+
+ def receiveGMTalk(self, avatarId, avatarName, accountId, accountName, message, scrubbed = 0):
+ error = None
+ if not avatarName and avatarId:
+ avatarName = self.findAvatarName(avatarId)
+ if not accountName and accountId:
+ accountName = self.findPlayerName(accountId)
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, avatarId, avatarName, accountId, accountName, None, None, None, None, TALK_GM, None)
+ self.historyComplete.append(newMessage)
+ self.historyOpen.append(newMessage)
+ if avatarId:
+ self.addToHistoryDoId(newMessage, avatarId)
+ if accountId:
+ self.addToHistoryDISLId(newMessage, accountId)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def receiveThought(self, avatarId, avatarName, accountId, accountName, message, scrubbed = 0):
+ error = None
+ if not avatarName and avatarId:
+ avatarName = self.findAvatarName(avatarId)
+ if not accountName and accountId:
+ accountName = self.findPlayerName(accountId)
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, avatarId, avatarName, accountId, accountName, None, None, None, None, AVATAR_THOUGHT, None)
+ if avatarId != localAvatar.doId:
+ self.addHandle(avatarId, newMessage)
+ reject = 0
+ if avatarId:
+ reject = self.addToHistoryDoId(newMessage, avatarId, scrubbed)
+ if accountId:
+ self.addToHistoryDISLId(newMessage, accountId)
+ if reject == 1:
+ newMessage.setBody(OTPLocalizer.AntiSpamInChat)
+ if reject != 2:
+ self.historyComplete.append(newMessage)
+ self.historyOpen.append(newMessage)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def receiveGameMessage(self, message):
+ error = None
+ if not self.isThought(message):
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, None, None, None, None, localAvatar.doId, localAvatar.getName(), localAvatar.DISLid, localAvatar.DISLname, INFO_GAME, None)
+ self.historyComplete.append(newMessage)
+ self.historyUpdates.append(newMessage)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def receiveSystemMessage(self, message):
+ error = None
+ if not self.isThought(message):
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, None, None, None, None, localAvatar.doId, localAvatar.getName(), localAvatar.DISLid, localAvatar.DISLname, INFO_SYSTEM, None)
+ self.historyComplete.append(newMessage)
+ self.historyUpdates.append(newMessage)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def receiveDeveloperMessage(self, message):
+ error = None
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, None, None, None, None, localAvatar.doId, localAvatar.getName(), localAvatar.DISLid, localAvatar.DISLname, INFO_DEV, None)
+ self.historyComplete.append(newMessage)
+ self.historyUpdates.append(newMessage)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def receiveGuildMessage(self, message, senderAvId, senderName):
+ error = None
+ if not self.isThought(message):
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, senderAvId, senderName, None, None, None, None, None, None, TALK_GUILD, None)
+ self.historyComplete.append(newMessage)
+ self.historyGuild.append(newMessage)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def receiveGuildUpdateMessage(self, message, senderId, senderName, receiverId, receiverName, extraInfo = None):
+ error = None
+ if not self.isThought(message):
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, senderId, senderName, None, None, receiverId, receiverName, None, None, INFO_GUILD, extraInfo)
+ self.historyComplete.append(newMessage)
+ self.historyGuild.append(newMessage)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def receiveFriendUpdate(self, friendId, friendName, isOnline):
+ if isOnline:
+ onlineMessage = OTPLocalizer.FriendOnline
+ else:
+ onlineMessage = OTPLocalizer.FriendOffline
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), onlineMessage, friendId, friendName, None, None, localAvatar.doId, localAvatar.getName(), localAvatar.DISLid, localAvatar.DISLname, UPDATE_FRIEND, None)
+ self.addHandle(friendId, newMessage)
+ self.historyComplete.append(newMessage)
+ self.historyUpdates.append(newMessage)
+ messenger.send('NewOpenMessage', [newMessage])
+ return
+
+ def receiveFriendAccountUpdate(self, friendId, friendName, isOnline):
+ if isOnline:
+ onlineMessage = OTPLocalizer.FriendOnline
+ else:
+ onlineMessage = OTPLocalizer.FriendOffline
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), onlineMessage, None, None, friendId, friendName, localAvatar.doId, localAvatar.getName(), localAvatar.DISLid, localAvatar.DISLname, UPDATE_FRIEND, None)
+ self.historyComplete.append(newMessage)
+ self.historyUpdates.append(newMessage)
+ messenger.send('NewOpenMessage', [newMessage])
+ return
+
+ def receiveGuildUpdate(self, memberId, memberName, isOnline):
+ if base.cr.identifyFriend(memberId) is None:
+ if isOnline:
+ onlineMessage = OTPLocalizer.GuildMemberOnline
+ else:
+ onlineMessage = OTPLocalizer.GuildMemberOffline
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), onlineMessage, memberId, memberName, None, None, None, None, None, None, UPDATE_GUILD, None)
+ self.addHandle(memberId, newMessage)
+ self.historyComplete.append(newMessage)
+ self.historyUpdates.append(newMessage)
+ self.historyGuild.append(newMessage)
+ messenger.send('NewOpenMessage', [newMessage])
+ return
+
+ def receiveOpenSpeedChat(self, type, messageIndex, senderAvId, name = None):
+ error = None
+ if not name and senderAvId:
+ name = self.findName(senderAvId, 0)
+ if type == SPEEDCHAT_NORMAL:
+ message = self.SCDecoder.decodeSCStaticTextMsg(messageIndex)
+ elif type == SPEEDCHAT_EMOTE:
+ message = self.SCDecoder.decodeSCEmoteWhisperMsg(messageIndex, name)
+ elif type == SPEEDCHAT_CUSTOM:
+ message = self.SCDecoder.decodeSCCustomMsg(messageIndex)
+ if message in (None, ''):
+ return
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, senderAvId, name, None, None, None, None, None, None, TALK_OPEN, None)
+ self.historyComplete.append(newMessage)
+ self.historyOpen.append(newMessage)
+ self.addToHistoryDoId(newMessage, senderAvId)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def receiveAvatarWhisperSpeedChat(self, type, messageIndex, senderAvId, name = None):
+ error = None
+ if not name and senderAvId:
+ name = self.findName(senderAvId, 0)
+ if type == SPEEDCHAT_NORMAL:
+ message = self.SCDecoder.decodeSCStaticTextMsg(messageIndex)
+ elif type == SPEEDCHAT_EMOTE:
+ message = self.SCDecoder.decodeSCEmoteWhisperMsg(messageIndex, name)
+ elif type == SPEEDCHAT_CUSTOM:
+ message = self.SCDecoder.decodeSCCustomMsg(messageIndex)
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, senderAvId, name, None, None, localAvatar.doId, localAvatar.getName(), localAvatar.DISLid, localAvatar.DISLname, TALK_WHISPER, None)
+ self.historyComplete.append(newMessage)
+ self.historyOpen.append(newMessage)
+ self.addToHistoryDoId(newMessage, senderAvId)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def receivePlayerWhisperSpeedChat(self, type, messageIndex, senderAvId, name = None):
+ error = None
+ if not name and senderAvId:
+ name = self.findName(senderAvId, 1)
+ if type == SPEEDCHAT_NORMAL:
+ message = self.SCDecoder.decodeSCStaticTextMsg(messageIndex)
+ elif type == SPEEDCHAT_EMOTE:
+ message = self.SCDecoder.decodeSCEmoteWhisperMsg(messageIndex, name)
+ elif type == SPEEDCHAT_CUSTOM:
+ message = self.SCDecoder.decodeSCCustomMsg(messageIndex)
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, None, None, senderAvId, name, localAvatar.doId, localAvatar.getName(), localAvatar.DISLid, localAvatar.DISLname, TALK_WHISPER, None)
+ self.historyComplete.append(newMessage)
+ self.historyOpen.append(newMessage)
+ self.addToHistoryDISLId(newMessage, senderAvId)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def sendOpenTalk(self, message):
+ error = None
+ if base.cr.wantMagicWords and len(message) > 0 and message[0] == '~':
+ messenger.send('magicWord', [message])
+ self.receiveDeveloperMessage(message)
+ else:
+ chatFlags = CFSpeech | CFTimeout
+ if self.isThought(message):
+ chatFlags = CFThought
+ base.localAvatar.sendUpdate('setTalk', [0,
+ 0,
+ '',
+ message,
+ [],
+ 0])
+ messenger.send('chatUpdate', [message, chatFlags])
+ return error
+
+ def sendWhisperTalk(self, message, receiverAvId):
+ error = None
+ receiver = base.cr.doId2do.get(receiverAvId)
+ if receiver:
+ receiver.sendUpdate('setTalkWhisper', [0,
+ 0,
+ '',
+ message,
+ [],
+ 0])
+ else:
+ receiver = base.cr.identifyAvatar(receiverAvId)
+ if receiver:
+ base.localAvatar.sendUpdate('setTalkWhisper', [0,
+ 0,
+ '',
+ message,
+ [],
+ 0], sendToId=receiverAvId)
+ return error
+
+ def sendAccountTalk(self, message, receiverAccount):
+ error = None
+ base.cr.playerFriendsManager.sendUpdate('setTalkAccount', [receiverAccount,
+ 0,
+ '',
+ message,
+ [],
+ 0])
+ return error
+
+ def sendGuildTalk(self, message):
+ error = None
+ if self.checkGuildTypedChat():
+ base.cr.guildManager.sendTalk(message)
+ else:
+ print 'Guild chat error'
+ error = ERROR_NO_GUILD_CHAT
+ return error
+
+ def sendOpenSpeedChat(self, type, messageIndex):
+ error = None
+ if type == SPEEDCHAT_NORMAL:
+ messenger.send(SCChatEvent)
+ messenger.send('chatUpdateSC', [messageIndex])
+ base.localAvatar.b_setSC(messageIndex)
+ elif type == SPEEDCHAT_EMOTE:
+ messenger.send('chatUpdateSCEmote', [messageIndex])
+ messenger.send(SCEmoteChatEvent)
+ base.localAvatar.b_setSCEmote(messageIndex)
+ elif type == SPEEDCHAT_CUSTOM:
+ messenger.send('chatUpdateSCCustom', [messageIndex])
+ messenger.send(SCCustomChatEvent)
+ base.localAvatar.b_setSCCustom(messageIndex)
+ return error
+
+ def sendAvatarWhisperSpeedChat(self, type, messageIndex, receiverId):
+ error = None
+ if type == SPEEDCHAT_NORMAL:
+ base.localAvatar.whisperSCTo(messageIndex, receiverId, 0)
+ message = self.SCDecoder.decodeSCStaticTextMsg(messageIndex)
+ elif type == SPEEDCHAT_EMOTE:
+ base.localAvatar.whisperSCEmoteTo(messageIndex, receiverId, 0)
+ message = self.SCDecoder.decodeSCEmoteWhisperMsg(messageIndex, localAvatar.getName())
+ elif type == SPEEDCHAT_CUSTOM:
+ base.localAvatar.whisperSCCustomTo(messageIndex, receiverId, 0)
+ message = self.SCDecoder.decodeSCCustomMsg(messageIndex)
+ if self.logWhispers:
+ avatarName = None
+ accountId = None
+ avatar = base.cr.identifyAvatar(receiverId)
+ if avatar:
+ avatarName = avatar.getName()
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, localAvatar.doId, localAvatar.getName(), localAvatar.DISLid, localAvatar.DISLname, receiverId, avatarName, None, None, TALK_WHISPER, None)
+ self.historyComplete.append(newMessage)
+ self.addToHistoryDoId(newMessage, localAvatar.doId)
+ self.addToHistoryDISLId(newMessage, base.cr.accountDetailRecord.playerAccountId)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def sendPlayerWhisperSpeedChat(self, type, messageIndex, receiverId):
+ error = None
+ if type == SPEEDCHAT_NORMAL:
+ base.cr.speedchatRelay.sendSpeedchat(receiverId, messageIndex)
+ message = self.SCDecoder.decodeSCStaticTextMsg(messageIndex)
+ elif type == SPEEDCHAT_EMOTE:
+ base.cr.speedchatRelay.sendSpeedchatEmote(receiverId, messageIndex)
+ message = self.SCDecoder.decodeSCEmoteWhisperMsg(messageIndex, localAvatar.getName())
+ return
+ elif type == SPEEDCHAT_CUSTOM:
+ base.cr.speedchatRelay.sendSpeedchatCustom(receiverId, messageIndex)
+ message = self.SCDecoder.decodeSCCustomMsg(messageIndex)
+ if self.logWhispers:
+ receiverName = self.findName(receiverId, 1)
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, localAvatar.doId, localAvatar.getName(), localAvatar.DISLid, localAvatar.DISLname, None, None, receiverId, receiverName, TALK_ACCOUNT, None)
+ self.historyComplete.append(newMessage)
+ self.addToHistoryDoId(newMessage, localAvatar.doId)
+ self.addToHistoryDISLId(newMessage, base.cr.accountDetailRecord.playerAccountId)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def sendGuildSpeedChat(self, type, msgIndex):
+ error = None
+ if self.checkGuildSpeedChat():
+ base.cr.guildManager.sendSC(msgIndex)
+ else:
+ print 'Guild Speedchat error'
+ error = ERROR_NO_GUILD_CHAT
+ return error
+
+ def getWhisperReplyId(self):
+ if self.lastWhisper:
+ toPlayer = 0
+ if self.lastWhisper == self.lastWhisperPlayerId:
+ toPlayer = 1
+ return (self.lastWhisper, toPlayer)
+ return (0, 0)
diff --git a/otp/chat/TalkGlobals.py b/otp/chat/TalkGlobals.py
new file mode 100644
index 0000000..d994f91
--- /dev/null
+++ b/otp/chat/TalkGlobals.py
@@ -0,0 +1,19 @@
+TALK_NONE = 0
+TALK_OPEN = 1
+TALK_WHISPER = 2
+TALK_ACCOUNT = 13
+TALK_GM = 14
+AVATAR_THOUGHT = 16
+TALK_GUILD = 3
+TALK_PARTY = 4
+TALK_PVP = 5
+UPDATE_GUILD = 6
+UPDATE_FRIEND = 7
+UPDATE_PARTY = 8
+UPDATE_PVP = 9
+INFO_SYSTEM = 10
+INFO_GAME = 11
+INFO_AVATAR_UNAVAILABLE = 12
+INFO_OPEN = 15
+INFO_DEV = 17
+INFO_GUILD = 18
diff --git a/otp/chat/TalkHandle.py b/otp/chat/TalkHandle.py
new file mode 100644
index 0000000..527cd67
--- /dev/null
+++ b/otp/chat/TalkHandle.py
@@ -0,0 +1,41 @@
+from otp.avatar.AvatarHandle import AvatarHandle
+
+class TalkHandle(AvatarHandle):
+
+ def __init__(self, doId, message):
+ self.avatarId = doId
+ self.avatarName = None
+ self.accountId = None
+ self.accountName = None
+ self.addMessageInfo(message)
+ return
+
+ def getName(self):
+ return self.avatarName
+
+ def isUnderstandable(self):
+ return False
+
+ def isOnline(self):
+ return False
+
+ def addMessageInfo(self, message):
+ if self.avatarId == message.getSenderAvatarId():
+ if not self.avatarName and message.getSenderAvatarName():
+ self.avatarName = message.getSenderAvatarName()
+ if not self.accountId and message.getSenderAccountId():
+ self.accountId = message.getSenderAccountId()
+ if not self.accountName and message.getSenderAccountName():
+ self.accountName = message.getSenderAccountName()
+ elif self.avatarId == message.getReceiverAvatarId():
+ if not self.avatarName and message.getReceiverAvatarName():
+ self.avatarName = message.getReceiverAvatarName()
+ if not self.accountId and message.getReceiverAccountId():
+ self.accountId = message.getReceiverAccountId()
+ if not self.accountName and message.getReceiverAccountName():
+ self.accountName = message.getReceiverAccountName()
+
+ def setTalkWhisper(self, fromAV, fromAC, avatarName, chat, mods, flags):
+ newText, scrubbed = localAvatar.scrubTalk(chat, mods)
+ base.talkAssistant.receiveWhisperTalk(fromAV, avatarName, fromAC, None, self.avatarId, self.getName(), newText, scrubbed)
+ return
diff --git a/otp/chat/TalkMessage.py b/otp/chat/TalkMessage.py
new file mode 100644
index 0000000..0beaa4f
--- /dev/null
+++ b/otp/chat/TalkMessage.py
@@ -0,0 +1,96 @@
+
+
+class TalkMessage:
+
+ def __init__(self, messageId, timeStamp, body, senderAvatarId, senderAvatarName, senderAccountId, senderAccountName, receiverAvatarId, receiverAvatarName, receiverAccountId, receiverAccountName, talkType, extraInfo = None):
+ self.timeStamp = timeStamp
+ self.body = body
+ self.senderAvatarId = senderAvatarId
+ self.senderAvatarName = senderAvatarName
+ self.senderAccountId = senderAccountId
+ self.senderAccountName = senderAccountName
+ self.receiverAvatarId = receiverAvatarId
+ self.receiverAvatarName = receiverAvatarName
+ self.receiverAccountId = receiverAccountId
+ self.receiverAccountName = receiverAccountName
+ self.talkType = talkType
+ self.extraInfo = extraInfo
+ self.messageId = messageId
+
+ def getMessageId(self):
+ return self.messageId
+
+ def setMessageId(self, id):
+ self.messageId = id
+
+ def getTimeStamp(self):
+ return self.timeStamp
+
+ def setTimeStamp(self, timeStamp):
+ self.timeStamp = timeStamp
+
+ def getBody(self):
+ return self.body
+
+ def setBody(self, body):
+ self.body = body
+
+ def getSenderAvatarId(self):
+ return self.senderAvatarId
+
+ def setSenderAvatarId(self, senderAvatarId):
+ self.senderAvatarId = senderAvatarId
+
+ def getSenderAvatarName(self):
+ return self.senderAvatarName
+
+ def setSenderAvatarName(self, senderAvatarName):
+ self.senderAvatarName = senderAvatarName
+
+ def getSenderAccountId(self):
+ return self.senderAccountId
+
+ def setSenderAccountId(self, senderAccountId):
+ self.senderAccountId = senderAccountId
+
+ def getSenderAccountName(self):
+ return self.senderAccountName
+
+ def setSenderAccountName(self, senderAccountName):
+ self.senderAccountName = senderAccountName
+
+ def getReceiverAvatarId(self):
+ return self.receiverAvatarId
+
+ def setReceiverAvatarId(self, receiverAvatarId):
+ self.receiverAvatarId = receiverAvatarId
+
+ def getReceiverAvatarName(self):
+ return self.receiverAvatarName
+
+ def setReceiverAvatarName(self, receiverAvatarName):
+ self.receiverAvatarName = receiverAvatarName
+
+ def getReceiverAccountId(self):
+ return self.receiverAccountId
+
+ def setReceiverAccountId(self, receiverAccountId):
+ self.receiverAccountId = receiverAccountId
+
+ def getReceiverAccountName(self):
+ return self.receiverAccountName
+
+ def setReceiverAccountName(self, receiverAccountName):
+ self.receiverAccountName = receiverAccountName
+
+ def getTalkType(self):
+ return self.talkType
+
+ def setTalkType(self, talkType):
+ self.talkType = talkType
+
+ def getExtraInfo(self):
+ return self.extraInfo
+
+ def setExtraInfo(self, extraInfo):
+ self.extraInfo = extraInfo
diff --git a/otp/chat/WhiteList.py b/otp/chat/WhiteList.py
new file mode 100644
index 0000000..14f9b76
--- /dev/null
+++ b/otp/chat/WhiteList.py
@@ -0,0 +1,51 @@
+from bisect import bisect_left
+import string
+import sys
+import os
+
+class WhiteList:
+
+ def __init__(self, wordlist):
+ self.words = []
+ for line in wordlist:
+ self.words.append(line.strip('\n\r').lower())
+
+ self.words.sort()
+ self.numWords = len(self.words)
+
+ def cleanText(self, text):
+ text = text.strip('.,?!')
+ text = text.lower()
+ return text
+
+ def isWord(self, text):
+ text = self.cleanText(text)
+ i = bisect_left(self.words, text)
+ if i == self.numWords:
+ return False
+ return self.words[i] == text
+
+ def isPrefix(self, text):
+ text = self.cleanText(text)
+ i = bisect_left(self.words, text)
+ if i == self.numWords:
+ return False
+ return self.words[i].startswith(text)
+
+ def prefixCount(self, text):
+ text = self.cleanText(text)
+ i = bisect_left(self.words, text)
+ j = i
+ while j < self.numWords and self.words[j].startswith(text):
+ j += 1
+
+ return j - i
+
+ def prefixList(self, text):
+ text = self.cleanText(text)
+ i = bisect_left(self.words, text)
+ j = i
+ while j < self.numWords and self.words[j].startswith(text):
+ j += 1
+
+ return self.words[i:j]
diff --git a/otp/chat/__init__.py b/otp/chat/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/distributed/Account.py b/otp/distributed/Account.py
new file mode 100644
index 0000000..6f0648b
--- /dev/null
+++ b/otp/distributed/Account.py
@@ -0,0 +1,6 @@
+from direct.distributed import DistributedObject
+
+class Account(DistributedObject.DistributedObject):
+
+ def __init__(self, cr):
+ pass
diff --git a/otp/distributed/CentralLogger.py b/otp/distributed/CentralLogger.py
new file mode 100644
index 0000000..747bde9
--- /dev/null
+++ b/otp/distributed/CentralLogger.py
@@ -0,0 +1,29 @@
+from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
+REPORT_PLAYER = 'REPORT_PLAYER'
+ReportFoulLanguage = 'MODERATION_FOUL_LANGUAGE'
+ReportPersonalInfo = 'MODERATION_PERSONAL_INFO'
+ReportRudeBehavior = 'MODERATION_RUDE_BEHAVIOR'
+ReportBadName = 'MODERATION_BAD_NAME'
+ReportHacking = 'MODERATION_HACKING'
+
+class CentralLogger(DistributedObjectGlobal):
+ PlayersReportedThisSession = {}
+
+ def hasReportedPlayer(self, targetDISLId, targetAvId):
+ return self.PlayersReportedThisSession.has_key((targetDISLId, targetAvId))
+
+ def reportPlayer(self, category, targetDISLId, targetAvId, description = 'None'):
+ if self.hasReportedPlayer(targetDISLId, targetAvId):
+ return False
+ self.PlayersReportedThisSession[targetDISLId, targetAvId] = 1
+ self.sendUpdate('sendMessage', [category,
+ REPORT_PLAYER,
+ targetDISLId,
+ targetAvId])
+ return True
+
+ def writeClientEvent(self, eventString):
+ self.sendUpdate('sendMessage', ['ClientEvent',
+ eventString,
+ 0,
+ 0])
diff --git a/otp/distributed/ClsendTracker.py b/otp/distributed/ClsendTracker.py
new file mode 100644
index 0000000..85cb1ef
--- /dev/null
+++ b/otp/distributed/ClsendTracker.py
@@ -0,0 +1,58 @@
+from pandac.PandaModules import StringStream
+from direct.distributed.PyDatagram import PyDatagram
+import random
+
+class ClsendTracker:
+ clsendNotify = directNotify.newCategory('clsend')
+ NumTrackersLoggingOverflow = 0
+ MaxTrackersLoggingOverflow = config.GetInt('max-clsend-loggers', 5)
+
+ def __init__(self):
+ self._logClsendOverflow = False
+ if self.isPlayerControlled():
+ if simbase.air.getTrackClsends():
+ if ClsendTracker.NumTrackersLoggingOverflow < ClsendTracker.MaxTrackersLoggingOverflow:
+ self._logClsendOverflow = random.random() < 1.0 / config.GetFloat('clsend-log-one-av-in-every', choice(__dev__, 4, 50))
+ if self._logClsendOverflow:
+ ClsendTracker.NumTrackersLoggingOverflow += 1
+ self._clsendMsgs = []
+ self._clsendBufLimit = 100
+ self._clsendFlushNum = 20
+ self._clsendCounter = 0
+
+ def announceGenerate(self):
+ if self._logClsendOverflow:
+ self.clsendNotify.info('logging all clsends for %s' % self.doId)
+
+ def destroy(self):
+ if self._logClsendOverflow:
+ ClsendTracker.NumTrackersLoggingOverflow -= 1
+
+ def trackClientSendMsg(self, dataStr):
+ self._clsendMsgs.append((self.air.getAvatarIdFromSender(), dataStr))
+ if len(self._clsendMsgs) >= self._clsendBufLimit:
+ self._trimClsend()
+
+ def _trimClsend(self):
+ for i in xrange(self._clsendFlushNum):
+ if self._logClsendOverflow:
+ self._logClsend(*self._clsendMsgs[0])
+ self._clsendMsgs = self._clsendMsgs[1:]
+ self._clsendCounter += 1
+
+ def _logClsend(self, senderId, dataStr):
+ msgStream = StringStream()
+ simbase.air.describeMessage(msgStream, '', dataStr)
+ readableStr = msgStream.getData()
+ sstream = StringStream()
+ PyDatagram(dataStr).dumpHex(sstream)
+ hexDump = sstream.getData()
+ self.clsendNotify.info('%s [%s]: %s%s' % (self.doId,
+ self._clsendCounter,
+ readableStr,
+ hexDump))
+
+ def dumpClientSentMsgs(self):
+ for msg in self._clsendMsgs:
+ self._logClsend(*msg)
+ self._clsendCounter += 1
diff --git a/otp/distributed/DistributedDirectory.py b/otp/distributed/DistributedDirectory.py
new file mode 100644
index 0000000..4863db7
--- /dev/null
+++ b/otp/distributed/DistributedDirectory.py
@@ -0,0 +1,4 @@
+from direct.distributed.DistributedObject import DistributedObject
+
+class DistributedDirectory(DistributedObject):
+ pass
diff --git a/otp/distributed/DistributedDistrict.py b/otp/distributed/DistributedDistrict.py
new file mode 100644
index 0000000..4971f9b
--- /dev/null
+++ b/otp/distributed/DistributedDistrict.py
@@ -0,0 +1,35 @@
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from direct.distributed.DistributedObject import DistributedObject
+
+class DistributedDistrict(DistributedObject):
+ notify = directNotify.newCategory('DistributedDistrict')
+ neverDisable = 1
+
+ def __init__(self, cr):
+ DistributedObject.__init__(self, cr)
+ self.name = 'NotGiven'
+ self.available = 0
+ self.avatarCount = 0
+ self.newAvatarCount = 0
+
+ def announceGenerate(self):
+ DistributedObject.announceGenerate(self)
+ self.cr.activeDistrictMap[self.doId] = self
+ messenger.send('shardInfoUpdated')
+
+ def delete(self):
+ if base.cr.distributedDistrict is self:
+ base.cr.distributedDistrict = None
+ if self.cr.activeDistrictMap.has_key(self.doId):
+ del self.cr.activeDistrictMap[self.doId]
+ DistributedObject.delete(self)
+ messenger.send('shardInfoUpdated')
+ return
+
+ def setAvailable(self, available):
+ self.available = available
+ messenger.send('shardInfoUpdated')
+
+ def setName(self, name):
+ self.name = name
+ messenger.send('shardInfoUpdated')
diff --git a/otp/distributed/DistributedTestObject.py b/otp/distributed/DistributedTestObject.py
new file mode 100644
index 0000000..0adf004
--- /dev/null
+++ b/otp/distributed/DistributedTestObject.py
@@ -0,0 +1,34 @@
+from direct.distributed import DistributedObject
+
+class DistributedTestObject(DistributedObject.DistributedObject):
+
+ def setRequiredField(self, r):
+ self.requiredField = r
+
+ def setB(self, B):
+ self.B = B
+
+ def setBA(self, BA):
+ self.BA = BA
+
+ def setBO(self, BO):
+ self.BO = BO
+
+ def setBR(self, BR):
+ self.BR = BR
+
+ def setBRA(self, BRA):
+ self.BRA = BRA
+
+ def setBRO(self, BRO):
+ self.BRO = BRO
+
+ def setBROA(self, BROA):
+ self.BROA = BROA
+
+ def gotNonReqThatWasntSet(self):
+ for field in ('B', 'BA', 'BO', 'BR', 'BRA', 'BRO', 'BROA'):
+ if hasattr(self, field):
+ return True
+
+ return False
diff --git a/otp/distributed/GameServerTestSuite.py b/otp/distributed/GameServerTestSuite.py
new file mode 100644
index 0000000..b555155
--- /dev/null
+++ b/otp/distributed/GameServerTestSuite.py
@@ -0,0 +1,133 @@
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from direct.showbase import DirectObject, TaskThreaded
+
+class GameServerTestSuite(DirectObject.DirectObject, TaskThreaded.TaskThreaded):
+ notify = directNotify.newCategory('GarbageReport')
+
+ def __init__(self, cr):
+ self.cr = cr
+ TaskThreaded.TaskThreaded.__init__(self, self.__class__.__name__)
+
+ class TimeoutTest(DirectObject.DirectObject):
+ Timeout = 10
+
+ def _getTaskName(self, name):
+ return '%s-timeout-%s' % (self.__class__.__name__, name)
+
+ def startTimeout(self, name):
+ self.stopTimeout(name)
+ _taskName = self._getTaskName(name)
+ taskMgr.doMethodLater(self.Timeout, Functor(self._timeout, _taskName), _taskName)
+
+ def stopTimeout(self, name):
+ _taskName = self._getTaskName(name)
+ taskMgr.remove(_taskName)
+
+ def _timeout(self, taskName, task = None):
+ self.parent.notify.warning('TEST TIMED OUT: %s' % taskName)
+ import pdb
+ pdb.set_trace()
+
+ class MsgHandlerTest:
+
+ def installMsgHandler(self):
+ self.oldHandler = self.parent.handler
+ self.parent.handler = self.handleMsg
+
+ def removeMsgHandler(self):
+ self.parent.handler = self.oldHandler
+ del self.oldHandler
+
+ def handleMsg(self, msgType, di):
+ self.parent.cr.handler(msgType, di)
+
+ class TestGetAvatars(TaskThreaded.TaskThread, TimeoutTest, MsgHandlerTest):
+
+ def setUp(self):
+ self.state = 'request'
+ self.installMsgHandler()
+
+ def handleMsg(self, msgType, di):
+ if msgType == CLIENT_GET_AVATARS_RESP:
+ self.finished()
+ else:
+ MsgHandlerTest.handleMsg(self, msgType, di)
+
+ def run(self):
+ if self.state == 'request':
+ self.parent.cr.sendGetAvatarsMsg()
+ self.startTimeout('getAvatarList')
+ self.state = 'waitForList'
+
+ def tearDown(self):
+ self.stopTimeout('getAvatarList')
+ self.removeMsgHandler()
+
+ class TestInterestOpenAndClose(TaskThreaded.TaskThread, TimeoutTest):
+
+ def setUp(self):
+ self.state = 'open'
+
+ def run(self):
+ if self.state == 'open':
+
+ def openInterestDone():
+ self.stopTimeout(self.timeoutName)
+ self.state = 'modify'
+
+ doneEvent = uniqueName('openInterest')
+ self.acceptOnce(doneEvent, openInterestDone)
+ openInterestDone = None
+ self.timeoutName = 'openInterest'
+ self.startTimeout(self.timeoutName)
+ self.handle = self.parent.cr.addInterest(self.parent.cr.GameGlobalsId, 91504, 'testInterest', doneEvent)
+ self.state = 'waitOpenComplete'
+ elif self.state == 'modify':
+
+ def modifyInterestDone():
+ self.stopTimeout(self.timeoutName)
+ self.state = 'close'
+
+ doneEvent = uniqueName('openInterest')
+ self.acceptOnce(doneEvent, modifyInterestDone)
+ modifyInterestDone = None
+ self.timeoutName = 'modifyInterest'
+ self.startTimeout(self.timeoutName)
+ self.parent.cr.alterInterest(self.handle, self.parent.cr.GameGlobalsId, 91506, 'testInterest', doneEvent)
+ self.state = 'waitModifyComplete'
+ elif self.state == 'close':
+
+ def closeInterestDone():
+ self.stopTimeout(self.timeoutName)
+ self.state = 'done'
+
+ doneEvent = uniqueName('closeInterest')
+ self.acceptOnce(doneEvent, closeInterestDone)
+ closeInterestDone = None
+ self.timeoutName = 'closeInterest'
+ self.startTimeout(self.timeoutName)
+ self.handle = self.parent.cr.removeInterest(self.handle, doneEvent)
+ self.state = 'waitCloseComplete'
+ elif self.state == 'done':
+ self.finished()
+ return
+
+ class TestNonRequiredNonSetFields(TaskThreaded.TaskThread, TimeoutTest):
+ Timeout = 60
+
+ def setUp(self):
+ self.timeoutName = 'lookForObj'
+ self.startTimeout(self.timeoutName)
+
+ def run(self):
+ testObj = self.parent.cr.doFind('DistributedTestObject')
+ if testObj is not None:
+ self.finished()
+ return
+
+ def tearDown(self):
+ self.stopTimeout(self.timeoutName)
+ del self.timeoutName
+
+ self.scheduleThread(TestInterestOpenAndClose())
+ self.scheduleThread(TestNonRequiredNonSetFields())
diff --git a/otp/distributed/OTPClientRepository.py b/otp/distributed/OTPClientRepository.py
new file mode 100644
index 0000000..87bf2fe
--- /dev/null
+++ b/otp/distributed/OTPClientRepository.py
@@ -0,0 +1,2219 @@
+import sys
+import time
+import string
+import types
+import random
+import gc
+import os
+from pandac.PandaModules import *
+from pandac.PandaModules import *
+from direct.gui.DirectGui import *
+from otp.distributed.OtpDoGlobals import *
+from direct.interval.IntervalGlobal import ivalMgr
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from direct.distributed.ClientRepositoryBase import ClientRepositoryBase
+from direct.fsm.ClassicFSM import ClassicFSM
+from direct.fsm.State import State
+from direct.task import Task
+from direct.distributed import DistributedSmoothNode
+from direct.showbase import PythonUtil, GarbageReport, BulletinBoardWatcher
+from direct.showbase.ContainerLeakDetector import ContainerLeakDetector
+from direct.showbase import MessengerLeakDetector
+from direct.showbase.GarbageReportScheduler import GarbageReportScheduler
+from direct.showbase import LeakDetectors
+from direct.distributed.PyDatagram import PyDatagram
+from direct.distributed.PyDatagramIterator import PyDatagramIterator
+from otp.avatar import Avatar
+from otp.avatar.DistributedPlayer import DistributedPlayer
+from otp.login import TTAccount
+from otp.login import LoginTTSpecificDevAccount
+from otp.login import AccountServerConstants
+from otp.login.CreateAccountScreen import CreateAccountScreen
+from otp.login import LoginScreen
+from otp.otpgui import OTPDialog
+from otp.avatar import DistributedAvatar
+from otp.otpbase import OTPLocalizer
+from otp.login import LoginGSAccount
+from otp.login import LoginGoAccount
+from otp.login.LoginWebPlayTokenAccount import LoginWebPlayTokenAccount
+from otp.login.LoginDISLTokenAccount import LoginDISLTokenAccount
+from otp.login import LoginTTAccount
+from otp.login import HTTPUtil
+from otp.otpbase import OTPGlobals
+from otp.otpbase import OTPLauncherGlobals
+from otp.uberdog import OtpAvatarManager
+from otp.distributed import OtpDoGlobals
+from otp.distributed.TelemetryLimiter import TelemetryLimiter
+from otp.ai.GarbageLeakServerEventAggregator import GarbageLeakServerEventAggregator
+from PotentialAvatar import PotentialAvatar
+
+class OTPClientRepository(ClientRepositoryBase):
+ notify = directNotify.newCategory('OTPClientRepository')
+ avatarLimit = 6
+ WishNameResult = Enum(['Failure',
+ 'PendingApproval',
+ 'Approved',
+ 'Rejected'])
+
+ def __init__(self, serverVersion, launcher = None, playGame = None):
+ ClientRepositoryBase.__init__(self)
+ self.handler = None
+ self.launcher = launcher
+ base.launcher = launcher
+ self.__currentAvId = 0
+ self.productName = config.GetString('product-name', 'DisneyOnline-US')
+ self.createAvatarClass = None
+ self.systemMessageSfx = None
+ reg_deployment = ''
+ if self.productName == 'DisneyOnline-US':
+ if self.launcher:
+ if self.launcher.isDummy():
+ reg_deployment = self.launcher.getDeployment()
+ else:
+ reg_deployment = self.launcher.getRegistry('DEPLOYMENT')
+ if reg_deployment != 'UK' and reg_deployment != 'AP':
+ reg_deployment = self.launcher.getRegistry('GAME_DEPLOYMENT')
+
+ self.notify.info('reg_deployment=%s' % reg_deployment)
+
+ if reg_deployment == 'UK':
+ self.productName = 'DisneyOnline-UK'
+ elif reg_deployment == 'AP':
+ self.productName = 'DisneyOnline-AP'
+
+ self.blue = None
+ if self.launcher:
+ self.blue = self.launcher.getBlue()
+
+ fakeBlue = config.GetString('fake-blue', '')
+ if fakeBlue:
+ self.blue = fakeBlue
+
+ self.playToken = None
+ if self.launcher:
+ self.playToken = self.launcher.getPlayToken()
+
+ fakePlayToken = config.GetString('fake-playtoken', '')
+ if fakePlayToken:
+ self.playToken = fakePlayToken
+
+ self.DISLToken = None
+ if self.launcher:
+ self.DISLToken = self.launcher.getDISLToken()
+
+ fakeDISLToken = config.GetString('fake-DISLToken', '')
+ fakeDISLPlayerName = config.GetString('fake-DISL-PlayerName', '')
+ if fakeDISLToken:
+ self.DISLToken = fakeDISLToken
+ elif fakeDISLPlayerName:
+ defaultId = 42
+ defaultNumAvatars = 4
+ defaultNumAvatarSlots = 4
+ defaultNumConcur = 1
+ subCount = config.GetInt('fake-DISL-NumSubscriptions', 1)
+ playerAccountId = config.GetInt('fake-DISL-PlayerAccountId', defaultId)
+ self.DISLToken = ('ACCOUNT_NAME=%s' % fakeDISLPlayerName +
+ '&ACCOUNT_NUMBER=%s' % playerAccountId +
+ '&ACCOUNT_NAME_APPROVAL=%s' % config.GetString('fake-DISL-PlayerNameApproved', 'YES') +
+ '&SWID=%s' % config.GetString('fake-DISL-SWID', '{1763AC36-D73F-41C2-A54A-B579E58B69C8}') +
+ '&FAMILY_NUMBER=%s' % config.GetString('fake-DISL-FamilyAccountId', '-1') +
+ '&familyAdmin=%s' % config.GetString('fake-DISL-FamilyAdmin', '1') +
+ '&PIRATES_ACCESS=%s' % config.GetString('fake-DISL-PiratesAccess', 'FULL') +
+ '&PIRATES_MAX_NUM_AVATARS=%s' % config.GetInt('fake-DISL-MaxAvatars', defaultNumAvatars) +
+ '&PIRATES_NUM_AVATAR_SLOTS=%s' % config.GetInt('fake-DISL-MaxAvatarSlots', defaultNumAvatarSlots) +
+ '&expires=%s' % config.GetString('fake-DISL-expire', '1577898000') +
+ '&OPEN_CHAT_ENABLED=%s' % config.GetString('fake-DISL-OpenChatEnabled', 'YES') +
+ '&CREATE_FRIENDS_WITH_CHAT=%s' % config.GetString('fake-DISL-CreateFriendsWithChat', 'YES') +
+ '&CHAT_CODE_CREATION_RULE=%s' % config.GetString('fake-DISL-ChatCodeCreation', 'YES') +
+ '&FAMILY_MEMBERS=%s' % config.GetString('fake-DISL-FamilyMembers') + '&PIRATES_SUB_COUNT=%s' % subCount)
+ for i in range(subCount):
+ self.DISLToken += ('&PIRATES_SUB_%s_ACCESS=%s' % (i, config.GetString('fake-DISL-Sub-%s-Access' % i, 'FULL')) +
+ '&PIRATES_SUB_%s_ACTIVE=%s' % (i, config.GetString('fake-DISL-Sub-%s-Active' % i, 'YES')) +
+ '&PIRATES_SUB_%s_ID=%s' % (i, config.GetInt('fake-DISL-Sub-%s-Id' % i, playerAccountId) + config.GetInt('fake-DISL-Sub-Id-Offset', 0)) +
+ '&PIRATES_SUB_%s_LEVEL=%s' % (i, config.GetInt('fake-DISL-Sub-%s-Level' % i, 3)) +
+ '&PIRATES_SUB_%s_NAME=%s' % (i, config.GetString('fake-DISL-Sub-%s-Name' % i, fakeDISLPlayerName)) +
+ '&PIRATES_SUB_%s_NUM_AVATARS=%s' % (i, config.GetInt('fake-DISL-Sub-%s-NumAvatars' % i, defaultNumAvatars)) +
+ '&PIRATES_SUB_%s_NUM_CONCUR=%s' % (i, config.GetInt('fake-DISL-Sub-%s-NumConcur' % i, defaultNumConcur)) +
+ '&PIRATES_SUB_%s_OWNERID=%s' % (i, config.GetInt('fake-DISL-Sub-%s-OwnerId' % i, playerAccountId)) +
+ '&PIRATES_SUB_%s_FOUNDER=%s' % (i, config.GetString('fake-DISL-Sub-%s-Founder' % i, 'YES')))
+
+ self.DISLToken += ('&WL_CHAT_ENABLED=%s' % config.GetString('fake-DISL-WLChatEnabled', 'YES') +
+ '&valid=true')
+ if base.logPrivateInfo:
+ print self.DISLToken
+
+ self.requiredLogin = config.GetString('required-login', 'auto')
+ if self.requiredLogin == 'auto':
+ self.notify.info('required-login auto.')
+ elif self.requiredLogin == 'green':
+ self.notify.error('The green code is out of date')
+ elif self.requiredLogin == 'blue':
+ if not self.blue:
+ self.notify.error('The tcr does not have the required blue login')
+ elif self.requiredLogin == 'playToken':
+ if not self.playToken:
+ self.notify.error('The tcr does not have the required playToken login')
+ elif self.requiredLogin == 'DISLToken':
+ if not self.DISLToken:
+ self.notify.error('The tcr does not have the required DISL token login')
+ elif self.requiredLogin == 'gameServer':
+ self.notify.info('Using game server name/password.')
+ self.DISLToken = None
+ else:
+ self.notify.error('The required-login was not recognized.')
+
+ self.computeValidateDownload()
+ self.wantMagicWords = base.config.GetString('want-magic-words', '')
+ if self.launcher and hasattr(self.launcher, 'http'):
+ self.http = self.launcher.http
+ else:
+ self.http = HTTPClient()
+
+ self.allocateDcFile()
+ self.accountOldAuth = config.GetBool('account-old-auth', 0)
+ self.accountOldAuth = config.GetBool('%s-account-old-auth' % game.name,
+ self.accountOldAuth)
+ self.useNewTTDevLogin = base.config.GetBool('use-tt-specific-dev-login', False)
+ if self.useNewTTDevLogin:
+ self.loginInterface = LoginTTSpecificDevAccount.LoginTTSpecificDevAccount(self)
+ self.notify.info('loginInterface: LoginTTSpecificDevAccount')
+ elif self.accountOldAuth:
+ self.loginInterface = LoginGSAccount.LoginGSAccount(self)
+ self.notify.info('loginInterface: LoginGSAccount')
+ elif self.blue:
+ self.loginInterface = LoginGoAccount.LoginGoAccount(self)
+ self.notify.info('loginInterface: LoginGoAccount')
+ elif self.playToken:
+ self.loginInterface = LoginWebPlayTokenAccount(self)
+ self.notify.info('loginInterface: LoginWebPlayTokenAccount')
+ elif self.DISLToken:
+ self.loginInterface = LoginDISLTokenAccount(self)
+ self.notify.info('loginInterface: LoginDISLTokenAccount')
+ else:
+ self.loginInterface = LoginTTAccount.LoginTTAccount(self)
+ self.notify.info('loginInterface: LoginTTAccount')
+
+ self.secretChatAllowed = base.config.GetBool('allow-secret-chat', 0)
+ self.openChatAllowed = base.config.GetBool('allow-open-chat', 0)
+ self.secretChatNeedsParentPassword = base.config.GetBool('secret-chat-needs-parent-password', 0) or (self.launcher and self.launcher.getNeedPwForSecretKey())
+ self.parentPasswordSet = base.config.GetBool('parent-password-set', 0) or (self.launcher and self.launcher.getParentPasswordSet())
+ self.userSignature = base.config.GetString('signature', 'none')
+ self.freeTimeExpiresAt = -1
+ self.__isPaid = 0
+ self.periodTimerExpired = 0
+ self.periodTimerStarted = None
+ self.periodTimerSecondsRemaining = None
+ self.parentMgr.registerParent(OTPGlobals.SPRender, base.render)
+ self.parentMgr.registerParent(OTPGlobals.SPHidden, NodePath())
+ self.timeManager = None
+ if config.GetBool('detect-leaks', 0) or config.GetBool('client-detect-leaks', 0):
+ self.startLeakDetector()
+
+ if config.GetBool('detect-messenger-leaks', 0) or config.GetBool('ai-detect-messenger-leaks', 0):
+ self.messengerLeakDetector = MessengerLeakDetector.MessengerLeakDetector('client messenger leak detector')
+ if config.GetBool('leak-messages', 0):
+ MessengerLeakDetector._leakMessengerObject()
+
+ if config.GetBool('run-garbage-reports', 0) or config.GetBool('client-run-garbage-reports', 0):
+ noneValue = -1.0
+ reportWait = config.GetFloat('garbage-report-wait', noneValue)
+ reportWaitScale = config.GetFloat('garbage-report-wait-scale', noneValue)
+ if reportWait == noneValue:
+ reportWait = 60.0 * 2.0
+ if reportWaitScale == noneValue:
+ reportWaitScale = None
+ self.garbageReportScheduler = GarbageReportScheduler(waitBetween=reportWait,
+ waitScale=reportWaitScale)
+
+ self._proactiveLeakChecks = config.GetBool('proactive-leak-checks', 1) or config.GetBool('client-proactive-leak-checks', 1)
+ self._crashOnProactiveLeakDetect = config.GetBool('crash-on-proactive-leak-detect', 1)
+ self.activeDistrictMap = {}
+ self.telemetryLimiter = TelemetryLimiter()
+ self.serverVersion = serverVersion
+ self.waitingForDatabase = None
+ self.loginFSM = ClassicFSM('loginFSM', [
+ State('loginOff',
+ self.enterLoginOff,
+ self.exitLoginOff, [
+ 'connect']),
+ State('connect',
+ self.enterConnect,
+ self.exitConnect, [
+ 'login',
+ 'failedToConnect',
+ 'failedToGetServerConstants']),
+ State('login',
+ self.enterLogin,
+ self.exitLogin, [
+ 'noConnection',
+ 'waitForGameList',
+ 'createAccount',
+ 'reject',
+ 'failedToConnect',
+ 'shutdown']),
+ State('createAccount',
+ self.enterCreateAccount,
+ self.exitCreateAccount, [
+ 'noConnection',
+ 'waitForGameList',
+ 'login',
+ 'reject',
+ 'failedToConnect',
+ 'shutdown']),
+ State('failedToConnect',
+ self.enterFailedToConnect,
+ self.exitFailedToConnect, [
+ 'connect',
+ 'shutdown']),
+ State('failedToGetServerConstants',
+ self.enterFailedToGetServerConstants,
+ self.exitFailedToGetServerConstants, [
+ 'connect',
+ 'shutdown',
+ 'noConnection']),
+ State('shutdown',
+ self.enterShutdown,
+ self.exitShutdown, [
+ 'loginOff']),
+ State('waitForGameList',
+ self.enterWaitForGameList,
+ self.exitWaitForGameList, [
+ 'noConnection',
+ 'waitForShardList',
+ 'missingGameRootObject']),
+ State('missingGameRootObject',
+ self.enterMissingGameRootObject,
+ self.exitMissingGameRootObject, [
+ 'waitForGameList',
+ 'shutdown']),
+ State('waitForShardList',
+ self.enterWaitForShardList,
+ self.exitWaitForShardList, [
+ 'noConnection',
+ 'waitForAvatarList',
+ 'noShards']),
+ State('noShards',
+ self.enterNoShards,
+ self.exitNoShards, [
+ 'noConnection',
+ 'noShardsWait',
+ 'shutdown']),
+ State('noShardsWait',
+ self.enterNoShardsWait,
+ self.exitNoShardsWait, [
+ 'noConnection',
+ 'waitForShardList',
+ 'shutdown']),
+ State('reject',
+ self.enterReject,
+ self.exitReject, []),
+ State('noConnection',
+ self.enterNoConnection,
+ self.exitNoConnection, [
+ 'login',
+ 'connect',
+ 'shutdown']),
+ State('afkTimeout',
+ self.enterAfkTimeout,
+ self.exitAfkTimeout, [
+ 'waitForAvatarList',
+ 'shutdown']),
+ State('periodTimeout',
+ self.enterPeriodTimeout,
+ self.exitPeriodTimeout, [
+ 'shutdown']),
+ State('waitForAvatarList',
+ self.enterWaitForAvatarList,
+ self.exitWaitForAvatarList, [
+ 'noConnection',
+ 'chooseAvatar',
+ 'shutdown']),
+ State('chooseAvatar',
+ self.enterChooseAvatar,
+ self.exitChooseAvatar, [
+ 'noConnection',
+ 'createAvatar',
+ 'waitForAvatarList',
+ 'waitForSetAvatarResponse',
+ 'waitForDeleteAvatarResponse',
+ 'shutdown',
+ 'login']),
+ State('createAvatar',
+ self.enterCreateAvatar,
+ self.exitCreateAvatar, [
+ 'noConnection',
+ 'chooseAvatar',
+ 'waitForSetAvatarResponse',
+ 'shutdown']),
+ State('waitForDeleteAvatarResponse',
+ self.enterWaitForDeleteAvatarResponse,
+ self.exitWaitForDeleteAvatarResponse, [
+ 'noConnection',
+ 'chooseAvatar',
+ 'shutdown']),
+ State('rejectRemoveAvatar',
+ self.enterRejectRemoveAvatar,
+ self.exitRejectRemoveAvatar, [
+ 'noConnection',
+ 'chooseAvatar',
+ 'shutdown']),
+ State('waitForSetAvatarResponse',
+ self.enterWaitForSetAvatarResponse,
+ self.exitWaitForSetAvatarResponse, [
+ 'noConnection',
+ 'playingGame',
+ 'shutdown']),
+ State('playingGame',
+ self.enterPlayingGame,
+ self.exitPlayingGame, [
+ 'noConnection',
+ 'waitForAvatarList',
+ 'login',
+ 'shutdown',
+ 'afkTimeout',
+ 'periodTimeout',
+ 'noShards'])],
+ 'loginOff', 'loginOff')
+ self.gameFSM = ClassicFSM('gameFSM', [
+ State('gameOff',
+ self.enterGameOff,
+ self.exitGameOff, [
+ 'waitOnEnterResponses']),
+ State('waitOnEnterResponses',
+ self.enterWaitOnEnterResponses,
+ self.exitWaitOnEnterResponses, [
+ 'playGame',
+ 'tutorialQuestion',
+ 'gameOff']),
+ State('tutorialQuestion',
+ self.enterTutorialQuestion,
+ self.exitTutorialQuestion, [
+ 'playGame',
+ 'gameOff']),
+ State('playGame',
+ self.enterPlayGame,
+ self.exitPlayGame, [
+ 'gameOff',
+ 'closeShard',
+ 'switchShards']),
+ State('switchShards',
+ self.enterSwitchShards,
+ self.exitSwitchShards, [
+ 'gameOff',
+ 'waitOnEnterResponses']),
+ State('closeShard',
+ self.enterCloseShard,
+ self.exitCloseShard, [
+ 'gameOff',
+ 'waitOnEnterResponses'])],
+ 'gameOff', 'gameOff')
+ self.loginFSM.getStateNamed('playingGame').addChild(self.gameFSM)
+ self.loginFSM.enterInitialState()
+ self.loginScreen = None
+ self.music = None
+ self.gameDoneEvent = 'playGameDone'
+ self.playGame = playGame(self.gameFSM, self.gameDoneEvent)
+ self.shardListHandle = None
+ self.uberZoneInterest = None
+ self.wantSwitchboard = config.GetBool('want-switchboard', 0)
+ self.wantSwitchboardHacks = base.config.GetBool('want-switchboard-hacks', 0)
+ self.centralLogger = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_CENTRAL_LOGGER, 'CentralLogger')
+
+ def startLeakDetector(self):
+ if hasattr(self, 'leakDetector'):
+ return False
+ firstCheckDelay = config.GetFloat('leak-detector-first-check-delay', 2 * 60.0)
+ self.leakDetector = ContainerLeakDetector('client container leak detector', firstCheckDelay=firstCheckDelay)
+ self.objectTypesLeakDetector = LeakDetectors.ObjectTypesLeakDetector()
+ self.garbageLeakDetector = LeakDetectors.GarbageLeakDetector()
+ self.renderLeakDetector = LeakDetectors.SceneGraphLeakDetector(render)
+ self.hiddenLeakDetector = LeakDetectors.SceneGraphLeakDetector(hidden)
+ self.cppMemoryUsageLeakDetector = LeakDetectors.CppMemoryUsage()
+ self.taskLeakDetector = LeakDetectors.TaskLeakDetector()
+ self.messageListenerTypesLeakDetector = LeakDetectors.MessageListenerTypesLeakDetector()
+ return True
+
+ def getGameDoId(self):
+ return self.GameGlobalsId
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterLoginOff(self):
+ self.handler = self.handleMessageType
+ self.shardListHandle = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitLoginOff(self):
+ self.handler = None
+ return
+
+ def computeValidateDownload(self):
+ if self.launcher:
+ hash = HashVal()
+ hash.mergeWith(launcher.launcherFileDbHash)
+ hash.mergeWith(launcher.serverDbFileHash)
+ self.validateDownload = hash.asHex()
+ else:
+ self.validateDownload = ''
+ basePath = os.path.expandvars('$TOONTOWN') or './toontown'
+ downloadParFilename = Filename.expandFrom(basePath + '/src/configfiles/download.par')
+ if downloadParFilename.exists():
+ downloadPar = open(downloadParFilename.toOsSpecific())
+ for line in downloadPar.readlines():
+ i = string.find(line, 'VALIDATE_DOWNLOAD=')
+ if i != -1:
+ self.validateDownload = string.strip(line[i + 18:])
+ break
+
+ def getServerVersion(self):
+ return self.serverVersion
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterConnect(self, serverList):
+ self.serverList = serverList
+ dialogClass = OTPGlobals.getGlobalDialogClass()
+ self.connectingBox = dialogClass(message=OTPLocalizer.CRConnecting)
+ self.connectingBox.show()
+ self.renderFrame()
+ self.handler = self.handleMessageType
+ self.connect(self.serverList, successCallback=self._handleConnected, failureCallback=self.failedToConnect)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def failedToConnect(self, statusCode, statusString):
+ self.loginFSM.request('failedToConnect', [statusCode, statusString])
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitConnect(self):
+ self.connectingBox.cleanup()
+ del self.connectingBox
+
+ def handleSystemMessage(self, di):
+ message = ClientRepositoryBase.handleSystemMessage(self, di)
+ whisper = WhisperPopup(message, OTPGlobals.getInterfaceFont(), WhisperPopup.WTSystem)
+ whisper.manage(base.marginManager)
+ if not self.systemMessageSfx:
+ self.systemMessageSfx = base.loadSfx('phase_3/audio/sfx/clock03.mp3')
+ if self.systemMessageSfx:
+ base.playSfx(self.systemMessageSfx)
+
+ def getConnectedEvent(self):
+ return 'OTPClientRepository-connected'
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def _handleConnected(self):
+ self.launcher.setDisconnectDetailsNormal()
+ messenger.send(self.getConnectedEvent())
+ self.gotoFirstScreen()
+
+ def gotoFirstScreen(self):
+ try:
+ self.accountServerConstants = AccountServerConstants.AccountServerConstants(self)
+ except TTAccount.TTAccountException, e:
+ self.notify.debug(str(e))
+ self.loginFSM.request('failedToGetServerConstants', [e])
+ return
+
+ self.startReaderPollTask()
+ self.startHeartbeat()
+ newInstall = launcher.getIsNewInstallation()
+ newInstall = base.config.GetBool('new-installation', newInstall)
+ if newInstall:
+ self.notify.warning('new installation')
+ self.loginFSM.request('login')
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterLogin(self):
+ self.sendSetAvatarIdMsg(0)
+ self.loginDoneEvent = 'loginDone'
+ self.loginScreen = LoginScreen.LoginScreen(self, self.loginDoneEvent)
+ self.accept(self.loginDoneEvent, self.__handleLoginDone)
+ self.loginScreen.load()
+ self.loginScreen.enter()
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def __handleLoginDone(self, doneStatus):
+ mode = doneStatus['mode']
+ if mode == 'success':
+ self.setIsNotNewInstallation()
+ self.loginFSM.request('waitForGameList')
+ elif mode == 'getChatPassword':
+ self.loginFSM.request('parentPassword')
+ elif mode == 'freeTimeExpired':
+ self.loginFSM.request('freeTimeInform')
+ elif mode == 'createAccount':
+ self.loginFSM.request('createAccount', [{'back': 'login',
+ 'backArgs': []}])
+ elif mode == 'reject':
+ self.loginFSM.request('reject')
+ elif mode == 'quit':
+ self.loginFSM.request('shutdown')
+ elif mode == 'failure':
+ self.loginFSM.request('failedToConnect', [-1, '?'])
+ else:
+ self.notify.error('Invalid doneStatus mode from loginScreen: ' + str(mode))
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitLogin(self):
+ if self.loginScreen:
+ self.loginScreen.exit()
+ self.loginScreen.unload()
+ self.loginScreen = None
+ self.renderFrame()
+ self.ignore(self.loginDoneEvent)
+ del self.loginDoneEvent
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterCreateAccount(self, createAccountDoneData = {'back': 'login',
+ 'backArgs': []}):
+ self.createAccountDoneData = createAccountDoneData
+ self.createAccountDoneEvent = 'createAccountDone'
+ self.createAccountScreen = None
+ self.createAccountScreen = CreateAccountScreen(self, self.createAccountDoneEvent)
+ self.accept(self.createAccountDoneEvent, self.__handleCreateAccountDone)
+ self.createAccountScreen.load()
+ self.createAccountScreen.enter()
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def __handleCreateAccountDone(self, doneStatus):
+ mode = doneStatus['mode']
+ if mode == 'success':
+ self.setIsNotNewInstallation()
+ self.loginFSM.request('waitForGameList')
+ elif mode == 'reject':
+ self.loginFSM.request('reject')
+ elif mode == 'cancel':
+ self.loginFSM.request(self.createAccountDoneData['back'], self.createAccountDoneData['backArgs'])
+ elif mode == 'failure':
+ self.loginFSM.request(self.createAccountDoneData['back'], self.createAccountDoneData['backArgs'])
+ elif mode == 'quit':
+ self.loginFSM.request('shutdown')
+ else:
+ self.notify.error('Invalid doneStatus mode from CreateAccountScreen: ' + str(mode))
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitCreateAccount(self):
+ if self.createAccountScreen:
+ self.createAccountScreen.exit()
+ self.createAccountScreen.unload()
+ self.createAccountScreen = None
+ self.renderFrame()
+ self.ignore(self.createAccountDoneEvent)
+ del self.createAccountDoneEvent
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterFailedToConnect(self, statusCode, statusString):
+ self.handler = self.handleMessageType
+ messenger.send('connectionIssue')
+ url = self.serverList[0]
+ self.notify.warning('Failed to connect to %s (%s %s). Notifying user.' % (url.cStr(), statusCode, statusString))
+ if statusCode == 1403 or statusCode == 1405 or statusCode == 1400:
+ message = OTPLocalizer.CRNoConnectProxyNoPort % (url.getServer(), url.getPort(), url.getPort())
+ style = OTPDialog.CancelOnly
+ else:
+ message = OTPLocalizer.CRNoConnectTryAgain % (url.getServer(), url.getPort())
+ style = OTPDialog.TwoChoice
+ dialogClass = OTPGlobals.getGlobalDialogClass()
+ self.failedToConnectBox = dialogClass(message=message, doneEvent='failedToConnectAck', text_wordwrap=18, style=style)
+ self.failedToConnectBox.show()
+ self.notify.info(message)
+ self.accept('failedToConnectAck', self.__handleFailedToConnectAck)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def __handleFailedToConnectAck(self):
+ doneStatus = self.failedToConnectBox.doneStatus
+ if doneStatus == 'ok':
+ self.loginFSM.request('connect', [self.serverList])
+ messenger.send('connectionRetrying')
+ elif doneStatus == 'cancel':
+ self.loginFSM.request('shutdown')
+ else:
+ self.notify.error('Unrecognized doneStatus: ' + str(doneStatus))
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitFailedToConnect(self):
+ self.handler = None
+ self.ignore('failedToConnectAck')
+ self.failedToConnectBox.cleanup()
+ del self.failedToConnectBox
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterFailedToGetServerConstants(self, e):
+ self.handler = self.handleMessageType
+ messenger.send('connectionIssue')
+ url = AccountServerConstants.AccountServerConstants.getServerURL()
+ statusCode = 0
+ if isinstance(e, HTTPUtil.ConnectionError):
+ statusCode = e.statusCode
+ self.notify.warning('Got status code %s from connection to %s.' % (statusCode, url.cStr()))
+ else:
+ self.notify.warning("Didn't get status code from connection to %s." % url.cStr())
+ if statusCode == 1403 or statusCode == 1400:
+ message = OTPLocalizer.CRServerConstantsProxyNoPort % (url.cStr(), url.getPort())
+ style = OTPDialog.CancelOnly
+ elif statusCode == 1405:
+ message = OTPLocalizer.CRServerConstantsProxyNoCONNECT % url.cStr()
+ style = OTPDialog.CancelOnly
+ else:
+ message = OTPLocalizer.CRServerConstantsTryAgain % url.cStr()
+ style = OTPDialog.TwoChoice
+ dialogClass = OTPGlobals.getGlobalDialogClass()
+ self.failedToGetConstantsBox = dialogClass(message=message, doneEvent='failedToGetConstantsAck', text_wordwrap=18, style=style)
+ self.failedToGetConstantsBox.show()
+ self.accept('failedToGetConstantsAck', self.__handleFailedToGetConstantsAck)
+ self.notify.warning('Failed to get account server constants. Notifying user.')
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def __handleFailedToGetConstantsAck(self):
+ doneStatus = self.failedToGetConstantsBox.doneStatus
+ if doneStatus == 'ok':
+ self.loginFSM.request('connect', [self.serverList])
+ messenger.send('connectionRetrying')
+ elif doneStatus == 'cancel':
+ self.loginFSM.request('shutdown')
+ else:
+ self.notify.error('Unrecognized doneStatus: ' + str(doneStatus))
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitFailedToGetServerConstants(self):
+ self.handler = None
+ self.ignore('failedToGetConstantsAck')
+ self.failedToGetConstantsBox.cleanup()
+ del self.failedToGetConstantsBox
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterShutdown(self, errorCode = None):
+ self.handler = self.handleMessageType
+ self.sendDisconnect()
+ self.notify.info('Exiting cleanly')
+ base.exitShow(errorCode)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitShutdown(self):
+ if hasattr(self, 'garbageWatcher'):
+ self.garbageWatcher.destroy()
+ del self.garbageWatcher
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterWaitForGameList(self):
+ self.gameDoDirectory = self.addTaggedInterest(self.GameGlobalsId, OTP_ZONE_ID_MANAGEMENT, self.ITAG_PERM, 'game directory', event='GameList_Complete')
+ self.acceptOnce('GameList_Complete', self.waitForGetGameListResponse)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def waitForGetGameListResponse(self):
+ if self.isGameListCorrect():
+ if base.config.GetBool('game-server-tests', 0):
+ from otp.distributed import GameServerTestSuite
+ GameServerTestSuite.GameServerTestSuite(self)
+ self.loginFSM.request('waitForShardList')
+ else:
+ self.loginFSM.request('missingGameRootObject')
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def isGameListCorrect(self):
+ return 1
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitWaitForGameList(self):
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterMissingGameRootObject(self):
+ self.notify.warning('missing some game root objects.')
+ self.handler = self.handleMessageType
+ dialogClass = OTPGlobals.getGlobalDialogClass()
+ self.missingGameRootObjectBox = dialogClass(message=OTPLocalizer.CRMissingGameRootObject, doneEvent='missingGameRootObjectBoxAck', style=OTPDialog.TwoChoice)
+ self.missingGameRootObjectBox.show()
+ self.accept('missingGameRootObjectBoxAck', self.__handleMissingGameRootObjectAck)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def __handleMissingGameRootObjectAck(self):
+ doneStatus = self.missingGameRootObjectBox.doneStatus
+ if doneStatus == 'ok':
+ self.loginFSM.request('waitForGameList')
+ elif doneStatus == 'cancel':
+ self.loginFSM.request('shutdown')
+ else:
+ self.notify.error('Unrecognized doneStatus: ' + str(doneStatus))
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitMissingGameRootObject(self):
+ self.handler = None
+ self.ignore('missingGameRootObjectBoxAck')
+ self.missingGameRootObjectBox.cleanup()
+ del self.missingGameRootObjectBox
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterWaitForShardList(self):
+ if not self.isValidInterestHandle(self.shardListHandle):
+ self.shardListHandle = self.addTaggedInterest(self.GameGlobalsId, OTP_ZONE_ID_DISTRICTS, self.ITAG_PERM, 'LocalShardList', event='ShardList_Complete')
+ self.acceptOnce('ShardList_Complete', self._wantShardListComplete)
+ else:
+ self._wantShardListComplete()
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def _wantShardListComplete(self):
+ if self._shardsAreReady():
+ self.loginFSM.request('waitForAvatarList')
+ else:
+ self.loginFSM.request('noShards')
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def _shardsAreReady(self):
+ for shard in self.activeDistrictMap.values():
+ if shard.available:
+ return True
+ else:
+ return False
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitWaitForShardList(self):
+ self.ignore('ShardList_Complete')
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterNoShards(self):
+ messenger.send('connectionIssue')
+ self.handler = self.handleMessageType
+ dialogClass = OTPGlobals.getGlobalDialogClass()
+ self.noShardsBox = dialogClass(message=OTPLocalizer.CRNoDistrictsTryAgain, doneEvent='noShardsAck', style=OTPDialog.TwoChoice)
+ self.noShardsBox.show()
+ self.accept('noShardsAck', self.__handleNoShardsAck)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def __handleNoShardsAck(self):
+ doneStatus = self.noShardsBox.doneStatus
+ if doneStatus == 'ok':
+ messenger.send('connectionRetrying')
+ self.loginFSM.request('noShardsWait')
+ elif doneStatus == 'cancel':
+ self.loginFSM.request('shutdown')
+ else:
+ self.notify.error('Unrecognized doneStatus: ' + str(doneStatus))
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitNoShards(self):
+ self.handler = None
+ self.ignore('noShardsAck')
+ self.noShardsBox.cleanup()
+ del self.noShardsBox
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterNoShardsWait(self):
+ dialogClass = OTPGlobals.getGlobalDialogClass()
+ self.connectingBox = dialogClass(message=OTPLocalizer.CRConnecting)
+ self.connectingBox.show()
+ self.renderFrame()
+ self.noShardsWaitTaskName = 'noShardsWait'
+
+ def doneWait(task, self = self):
+ self.loginFSM.request('waitForShardList')
+
+ if __dev__:
+ delay = 0.0
+ else:
+ delay = 6.5 + random.random() * 2.0
+ taskMgr.doMethodLater(delay, doneWait, self.noShardsWaitTaskName)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitNoShardsWait(self):
+ taskMgr.remove(self.noShardsWaitTaskName)
+ del self.noShardsWaitTaskName
+ self.connectingBox.cleanup()
+ del self.connectingBox
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterReject(self):
+ self.handler = self.handleMessageType
+ self.notify.warning('Connection Rejected')
+ launcher.setPandaErrorCode(13)
+ sys.exit()
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitReject(self):
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterNoConnection(self):
+ messenger.send('connectionIssue')
+ self.resetInterestStateForConnectionLoss()
+ self.shardListHandle = None
+ self.handler = self.handleMessageType
+ self.__currentAvId = 0
+ self.stopHeartbeat()
+ self.stopReaderPollTask()
+ gameUsername = launcher.getValue('GAME_USERNAME', base.cr.userName)
+ if self.bootedIndex != None and OTPLocalizer.CRBootedReasons.has_key(self.bootedIndex):
+ message = OTPLocalizer.CRBootedReasons[self.bootedIndex] % {'name': gameUsername}
+ elif self.bootedText != None:
+ message = OTPLocalizer.CRBootedReasonUnknownCode % self.bootedIndex
+ else:
+ message = OTPLocalizer.CRLostConnection
+ reconnect = 1
+ if self.bootedIndex in (152, 127):
+ reconnect = 0
+ self.launcher.setDisconnectDetails(self.bootedIndex, message)
+ style = OTPDialog.Acknowledge
+ if reconnect and self.loginInterface.supportsRelogin():
+ message += OTPLocalizer.CRTryConnectAgain
+ style = OTPDialog.TwoChoice
+ dialogClass = OTPGlobals.getGlobalDialogClass()
+ self.lostConnectionBox = dialogClass(doneEvent='lostConnectionAck', message=message, text_wordwrap=18, style=style)
+ self.lostConnectionBox.show()
+ self.accept('lostConnectionAck', self.__handleLostConnectionAck)
+ self.notify.warning('Lost connection to server. Notifying user.')
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def __handleLostConnectionAck(self):
+ if self.lostConnectionBox.doneStatus == 'ok' and self.loginInterface.supportsRelogin():
+ self.loginFSM.request('connect', [self.serverList])
+ else:
+ self.loginFSM.request('shutdown')
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitNoConnection(self):
+ self.handler = None
+ self.ignore('lostConnectionAck')
+ self.lostConnectionBox.cleanup()
+ messenger.send('connectionRetrying')
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterAfkTimeout(self):
+ self.sendSetAvatarIdMsg(0)
+ msg = OTPLocalizer.AfkForceAcknowledgeMessage
+ dialogClass = OTPGlobals.getDialogClass()
+ self.afkDialog = dialogClass(text=msg, command=self.__handleAfkOk, style=OTPDialog.Acknowledge)
+ self.handler = self.handleMessageType
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def __handleAfkOk(self, value):
+ self.loginFSM.request('waitForAvatarList')
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitAfkTimeout(self):
+ if self.afkDialog:
+ self.afkDialog.cleanup()
+ self.afkDialog = None
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterPeriodTimeout(self):
+ self.sendSetAvatarIdMsg(0)
+ self.sendDisconnect()
+ msg = OTPLocalizer.PeriodForceAcknowledgeMessage
+ dialogClass = OTPGlobals.getDialogClass()
+ self.periodDialog = dialogClass(text=msg, command=self.__handlePeriodOk, style=OTPDialog.Acknowledge)
+ self.handler = self.handleMessageType
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def __handlePeriodOk(self, value):
+ base.exitShow()
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitPeriodTimeout(self):
+ if self.periodDialog:
+ self.periodDialog.cleanup()
+ self.periodDialog = None
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterWaitForAvatarList(self):
+ self.handler = self.handleWaitForAvatarList
+ self._requestAvatarList()
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def _requestAvatarList(self):
+ self.sendGetAvatarsMsg()
+ self.waitForDatabaseTimeout(requestName='WaitForAvatarList')
+ self.acceptOnce(OtpAvatarManager.OtpAvatarManager.OnlineEvent, self._requestAvatarList)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def sendGetAvatarsMsg(self):
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_GET_AVATARS)
+ self.send(datagram)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitWaitForAvatarList(self):
+ self.cleanupWaitingForDatabase()
+ self.ignore(OtpAvatarManager.OtpAvatarManager.OnlineEvent)
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def handleWaitForAvatarList(self, msgType, di):
+ if msgType == CLIENT_GET_AVATARS_RESP:
+ self.handleGetAvatarsRespMsg(di)
+ elif msgType == CLIENT_GET_AVATARS_RESP2:
+ pass
+ else:
+ self.handleMessageType(msgType, di)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def handleGetAvatarsRespMsg(self, di):
+ returnCode = di.getUint8()
+ if returnCode == 0:
+ avatarTotal = di.getUint16()
+ avList = []
+ for i in range(0, avatarTotal):
+ avNum = di.getUint32()
+ avNames = ['',
+ '',
+ '',
+ '']
+ avNames[0] = di.getString()
+ avNames[1] = di.getString()
+ avNames[2] = di.getString()
+ avNames[3] = di.getString()
+ avDNA = di.getString()
+ avPosition = di.getUint8()
+ aname = di.getUint8()
+ potAv = PotentialAvatar(avNum, avNames, avDNA, avPosition, aname)
+ avList.append(potAv)
+
+ self.avList = avList
+ self.loginFSM.request('chooseAvatar', [self.avList])
+ else:
+ self.notify.error('Bad avatar list return code: ' + str(returnCode))
+ self.loginFSM.request('shutdown')
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def handleGetAvatarsResp2Msg(self, di):
+ returnCode = di.getUint8()
+ if returnCode == 0:
+ avatarTotal = di.getUint16()
+ avList = []
+ for i in range(0, avatarTotal):
+ avNum = di.getUint32()
+ avNames = ['',
+ '',
+ '',
+ '']
+ avNames[0] = di.getString()
+ avDNA = None
+ avPosition = di.getUint8()
+ aname = None
+ potAv = PotentialAvatar(avNum, avNames, avDNA, avPosition, aname)
+ avList.append(potAv)
+
+ self.avList = avList
+ self.loginFSM.request('chooseAvatar', [self.avList])
+ else:
+ self.notify.error('Bad avatar list return code: ' + str(returnCode))
+ self.loginFSM.request('shutdown')
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterChooseAvatar(self, avList):
+ pass
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitChooseAvatar(self):
+ pass
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterCreateAvatar(self, avList, index, newDNA = None):
+ pass
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitCreateAvatar(self):
+ pass
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def sendCreateAvatarMsg(self, avDNA, avName, avPosition):
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_CREATE_AVATAR)
+ datagram.addUint16(0)
+ datagram.addString(avDNA.makeNetString())
+ datagram.addUint8(avPosition)
+ self.newName = avName
+ self.newDNA = avDNA
+ self.newPosition = avPosition
+ self.send(datagram)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def sendCreateAvatar2Msg(self, avClass, avDNA, avName, avPosition):
+ className = avClass.__name__
+ dclass = self.dclassesByName[className]
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_CREATE_AVATAR2)
+ datagram.addUint16(0)
+ datagram.addUint8(avPosition)
+ datagram.addUint16(dclass.getNumber())
+ self.newName = avName
+ self.newDNA = avDNA
+ self.newPosition = avPosition
+ self.send(datagram)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterWaitForDeleteAvatarResponse(self, potAv):
+ self.handler = self.handleWaitForDeleteAvatarResponse
+ self.sendDeleteAvatarMsg(potAv.id)
+ self.waitForDatabaseTimeout(requestName='WaitForDeleteAvatarResponse')
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def sendDeleteAvatarMsg(self, avId):
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_DELETE_AVATAR)
+ datagram.addUint32(avId)
+ self.send(datagram)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitWaitForDeleteAvatarResponse(self):
+ self.cleanupWaitingForDatabase()
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def handleWaitForDeleteAvatarResponse(self, msgType, di):
+ if msgType == CLIENT_DELETE_AVATAR_RESP:
+ self.handleGetAvatarsRespMsg(di)
+ else:
+ self.handleMessageType(msgType, di)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterRejectRemoveAvatar(self, reasonCode):
+ self.notify.warning('Rejected removed avatar. (%s)' % (reasonCode,))
+ self.handler = self.handleMessageType
+ dialogClass = OTPGlobals.getGlobalDialogClass()
+ self.rejectRemoveAvatarBox = dialogClass(message='%s\n(%s)' % (OTPLocalizer.CRRejectRemoveAvatar, reasonCode), doneEvent='rejectRemoveAvatarAck', style=OTPDialog.Acknowledge)
+ self.rejectRemoveAvatarBox.show()
+ self.accept('rejectRemoveAvatarAck', self.__handleRejectRemoveAvatar)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def __handleRejectRemoveAvatar(self):
+ self.loginFSM.request('chooseAvatar')
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitRejectRemoveAvatar(self):
+ self.handler = None
+ self.ignore('rejectRemoveAvatarAck')
+ self.rejectRemoveAvatarBox.cleanup()
+ del self.rejectRemoveAvatarBox
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterWaitForSetAvatarResponse(self, potAv):
+ self.handler = self.handleWaitForSetAvatarResponse
+ self.sendSetAvatarMsg(potAv)
+ self.waitForDatabaseTimeout(requestName='WaitForSetAvatarResponse')
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitWaitForSetAvatarResponse(self):
+ self.cleanupWaitingForDatabase()
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def sendSetAvatarMsg(self, potAv):
+ self.sendSetAvatarIdMsg(potAv.id)
+ self.avData = potAv
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def sendSetAvatarIdMsg(self, avId):
+ if avId != self.__currentAvId:
+ self.__currentAvId = avId
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_SET_AVATAR)
+ datagram.addUint32(avId)
+ self.send(datagram)
+ if avId == 0:
+ self.stopPeriodTimer()
+ else:
+ self.startPeriodTimer()
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def handleAvatarResponseMsg(self, di):
+ pass
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def handleWaitForSetAvatarResponse(self, msgType, di):
+ if msgType == CLIENT_GET_AVATAR_DETAILS_RESP:
+ self.handleAvatarResponseMsg(di)
+ elif msgType == CLIENT_GET_PET_DETAILS_RESP:
+ self.handleAvatarResponseMsg(di)
+ elif msgType == CLIENT_GET_FRIEND_LIST_RESP:
+ self.handleGetFriendsList(di)
+ elif msgType == CLIENT_GET_FRIEND_LIST_EXTENDED_RESP:
+ self.handleGetFriendsListExtended(di)
+ elif msgType == CLIENT_FRIEND_ONLINE:
+ self.handleFriendOnline(di)
+ elif msgType == CLIENT_FRIEND_OFFLINE:
+ self.handleFriendOffline(di)
+ else:
+ self.handleMessageType(msgType, di)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterPlayingGame(self):
+ pass
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitPlayingGame(self):
+ self.notify.info('sending clientLogout')
+ messenger.send('clientLogout')
+
+ @report(types=['args'], dConfigParam='teleport')
+ def detectLeaks(self, okTasks = None, okEvents = None):
+ if not __dev__ or configIsToday('allow-unclean-exit'):
+ return
+ leakedTasks = self.detectLeakedTasks(okTasks)
+ leakedEvents = self.detectLeakedEvents(okEvents)
+ leakedIvals = self.detectLeakedIntervals()
+ leakedGarbage = self.detectLeakedGarbage()
+ if leakedTasks or leakedEvents or leakedIvals or leakedGarbage:
+ errorCode = base.getExitErrorCode()
+ if errorCode >= OTPLauncherGlobals.NonErrorExitStateStart and errorCode <= OTPLauncherGlobals.NonErrorExitStateEnd:
+ logFunc = self.notify.warning
+ allowExit = True
+ elif __debug__ and not PythonUtil.configIsToday('temp-disable-leak-detection'):
+ logFunc = self.notify.error
+ allowExit = False
+ else:
+ logFunc = self.notify.warning
+ allowExit = False
+ if base.config.GetBool('direct-gui-edit', 0):
+ logFunc('There are leaks: %s tasks, %s events, %s ivals, %s garbage cycles\nLeaked Events may be due to direct gui editing' % (leakedTasks,
+ leakedEvents,
+ leakedIvals,
+ leakedGarbage))
+ else:
+ logFunc('There are leaks: %s tasks, %s events, %s ivals, %s garbage cycles' % (leakedTasks,
+ leakedEvents,
+ leakedIvals,
+ leakedGarbage))
+ if allowExit:
+ self.notify.info('Allowing client to leave, panda error code %s' % errorCode)
+ else:
+ base.userExit()
+ else:
+ self.notify.info('There are no leaks detected.')
+
+ def detectLeakedGarbage(self, callback = None):
+ if not __debug__:
+ return 0
+ self.notify.info('checking for leaked garbage...')
+ if gc.garbage:
+ self.notify.warning('garbage already contains %d items' % len(gc.garbage))
+ report = GarbageReport.GarbageReport('logout', verbose=True)
+ numCycles = report.getNumCycles()
+ if numCycles:
+ msg = "You can't leave until you take out your garbage. See report above & base.garbage"
+ self.notify.info(msg)
+ report.destroy()
+ return numCycles
+
+ def detectLeakedTasks(self, extraTasks = None):
+ allowedTasks = ['dataLoop',
+ 'resetPrevTransform',
+ 'doLaterProcessor',
+ 'eventManager',
+ 'readerPollTask',
+ 'heartBeat',
+ 'gridZoneLoop',
+ 'igLoop',
+ 'audioLoop',
+ 'asyncLoad',
+ 'collisionLoop',
+ 'shadowCollisionLoop',
+ 'ivalLoop',
+ 'downloadSequence',
+ 'patchAndHash',
+ 'launcher-download',
+ 'launcher-download-multifile',
+ 'launcher-decompressFile',
+ 'launcher-decompressMultifile',
+ 'launcher-extract',
+ 'launcher-patch',
+ 'slowCloseShardCallback',
+ 'tkLoop',
+ 'manager-update',
+ 'downloadStallTask',
+ 'clientSleep',
+ jobMgr.TaskName,
+ self.GarbageCollectTaskName,
+ 'RedownloadNewsTask',
+ TelemetryLimiter.TaskName]
+ if extraTasks is not None:
+ allowedTasks.extend(extraTasks)
+ problems = []
+ for task in taskMgr.getTasks():
+ if not hasattr(task, 'name'):
+ continue
+ if task.name in allowedTasks:
+ continue
+ else:
+ if hasattr(task, 'debugInitTraceback'):
+ print task.debugInitTraceback
+ problems.append(task.name)
+
+ if problems:
+ print taskMgr
+ msg = "You can't leave until you clean up your tasks: {"
+ for task in problems:
+ msg += '\n ' + task
+
+ msg += '}\n'
+ self.notify.info(msg)
+ return len(problems)
+ else:
+ return 0
+ return
+
+ def detectLeakedEvents(self, extraHooks = None):
+ allowedHooks = ['destroy-DownloadWatcherBar',
+ 'destroy-DownloadWatcherText',
+ 'destroy-fade',
+ 'f9',
+ 'control-f9',
+ 'launcherAllPhasesComplete',
+ 'launcherPercentPhaseComplete',
+ 'newDistributedDirectory',
+ 'page_down',
+ 'page_up',
+ 'panda3d-render-error',
+ 'PandaPaused',
+ 'PandaRestarted',
+ 'phaseComplete-3',
+ 'press-mouse2-fade',
+ 'print-fade',
+ 'release-mouse2-fade',
+ 'resetClock',
+ 'window-event',
+ 'TCRSetZoneDone',
+ 'aspectRatioChanged',
+ 'newDistributedDirectory',
+ CConnectionRepository.getOverflowEventName(),
+ self._getLostConnectionEvent(),
+ 'render-texture-targets-changed',
+ 'gotExtraFriendHandles']
+ if hasattr(loader, 'hook'):
+ allowedHooks.append(loader.hook)
+ if extraHooks is not None:
+ allowedHooks.extend(extraHooks)
+ problems = []
+ for hook in messenger.getEvents():
+ if hook not in allowedHooks:
+ problems.append(hook)
+
+ if problems:
+ msg = "You can't leave until you clean up your messenger hooks: {"
+ for hook in problems:
+ whoAccepts = messenger.whoAccepts(hook)
+ msg += '\n %s' % hook
+ for obj in whoAccepts:
+ msg += '\n OBJECT:%s, %s %s' % (obj, obj.__class__, whoAccepts[obj])
+ if hasattr(obj, 'getCreationStackTraceCompactStr'):
+ msg += '\n CREATIONSTACKTRACE:%s' % obj.getCreationStackTraceCompactStr()
+ else:
+ try:
+ value = whoAccepts[obj]
+ callback = value[0]
+ guiObj = callback.im_self
+ if hasattr(guiObj, 'getCreationStackTraceCompactStr'):
+ msg += '\n CREATIONSTACKTRACE:%s' % guiObj.getCreationStackTraceCompactStr()
+ except:
+ pass
+
+ msg += '\n}\n'
+ self.notify.warning(msg)
+ return len(problems)
+ else:
+ return 0
+ return
+
+ def detectLeakedIntervals(self):
+ numIvals = ivalMgr.getNumIntervals()
+ if numIvals > 0:
+ print "You can't leave until you clean up your intervals: {"
+ for i in range(ivalMgr.getMaxIndex()):
+ ival = None
+ if i < len(ivalMgr.ivals):
+ ival = ivalMgr.ivals[i]
+ if ival == None:
+ ival = ivalMgr.getCInterval(i)
+ if ival:
+ print ival
+ if hasattr(ival, 'debugName'):
+ print ival.debugName
+ if hasattr(ival, 'debugInitTraceback'):
+ print ival.debugInitTraceback
+
+ print '}'
+ self.notify.info("You can't leave until you clean up your intervals.")
+ return numIvals
+ else:
+ return 0
+ return
+
+ def _abandonShard(self):
+ self.notify.error('%s must override _abandonShard' % self.__class__.__name__)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterGameOff(self):
+ self.uberZoneInterest = None
+ if not hasattr(self, 'cleanGameExit'):
+ self.cleanGameExit = True
+ if self.cleanGameExit:
+ if self.isShardInterestOpen():
+ self.notify.error('enterGameOff: shard interest is still open')
+ elif self.isShardInterestOpen():
+ self.notify.warning('unclean exit, abandoning shard')
+ self._abandonShard()
+ self.cleanupWaitAllInterestsComplete()
+ del self.cleanGameExit
+ self.cache.flush()
+ self.doDataCache.flush()
+ self.handler = self.handleMessageType
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitGameOff(self):
+ self.handler = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterWaitOnEnterResponses(self, shardId, hoodId, zoneId, avId):
+ self.cleanGameExit = False
+ self.handler = self.handleWaitOnEnterResponses
+ self.handlerArgs = {'hoodId': hoodId,
+ 'zoneId': zoneId,
+ 'avId': avId}
+ if shardId is not None:
+ district = self.activeDistrictMap.get(shardId)
+ else:
+ district = None
+ if not district:
+ self.distributedDistrict = self.getStartingDistrict()
+ if self.distributedDistrict is None:
+ self.loginFSM.request('noShards')
+ return
+ shardId = self.distributedDistrict.doId
+ else:
+ self.distributedDistrict = district
+ self.notify.info('Entering shard %s' % shardId)
+ localAvatar.setLocation(shardId, zoneId)
+ base.localAvatar.defaultShard = shardId
+ self.waitForDatabaseTimeout(requestName='WaitOnEnterResponses')
+ self.handleSetShardComplete()
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def handleWaitOnEnterResponses(self, msgType, di):
+ if msgType == CLIENT_GET_FRIEND_LIST_RESP:
+ self.handleGetFriendsList(di)
+ elif msgType == CLIENT_GET_FRIEND_LIST_EXTENDED_RESP:
+ self.handleGetFriendsListExtended(di)
+ elif msgType == CLIENT_FRIEND_ONLINE:
+ self.handleFriendOnline(di)
+ elif msgType == CLIENT_FRIEND_OFFLINE:
+ self.handleFriendOffline(di)
+ elif msgType == CLIENT_GET_PET_DETAILS_RESP:
+ self.handleGetAvatarDetailsResp(di)
+ else:
+ self.handleMessageType(msgType, di)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def handleSetShardComplete(self):
+ hoodId = self.handlerArgs['hoodId']
+ zoneId = self.handlerArgs['zoneId']
+ avId = self.handlerArgs['avId']
+ self.uberZoneInterest = self.addInterest(base.localAvatar.defaultShard, OTPGlobals.UberZone, 'uberZone', 'uberZoneInterestComplete')
+ self.acceptOnce('uberZoneInterestComplete', self.uberZoneInterestComplete)
+ self.waitForDatabaseTimeout(20, requestName='waitingForUberZone')
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def uberZoneInterestComplete(self):
+ self.__gotTimeSync = 0
+ self.cleanupWaitingForDatabase()
+ if self.timeManager == None:
+ self.notify.info('TimeManager is not present.')
+ DistributedSmoothNode.globalActivateSmoothing(0, 0)
+ self.gotTimeSync()
+ else:
+ DistributedSmoothNode.globalActivateSmoothing(1, 0)
+ h = HashVal()
+ hashPrcVariables(h)
+ pyc = HashVal()
+ if not __dev__:
+ self.hashFiles(pyc)
+ self.timeManager.d_setSignature(self.userSignature, h.asBin(), pyc.asBin())
+ self.timeManager.sendCpuInfo()
+ if self.timeManager.synchronize('startup'):
+ self.accept('gotTimeSync', self.gotTimeSync)
+ self.waitForDatabaseTimeout(requestName='uberZoneInterest-timeSync')
+ else:
+ self.notify.info('No sync from TimeManager.')
+ self.gotTimeSync()
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitWaitOnEnterResponses(self):
+ self.ignore('uberZoneInterestComplete')
+ self.cleanupWaitingForDatabase()
+ self.handler = None
+ self.handlerArgs = None
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterCloseShard(self, loginState = None):
+ self.notify.info('Exiting shard')
+ if loginState is None:
+ loginState = 'waitForAvatarList'
+ self._closeShardLoginState = loginState
+ base.cr.setNoNewInterests(True)
+ return
+
+ def _removeLocalAvFromStateServer(self):
+ self.sendSetAvatarIdMsg(0)
+ self._removeAllOV()
+ callback = Functor(self.loginFSM.request, self._closeShardLoginState)
+ if base.slowCloseShard:
+ taskMgr.doMethodLater(base.slowCloseShardDelay * 0.5, Functor(self.removeShardInterest, callback), 'slowCloseShard')
+ else:
+ self.removeShardInterest(callback)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def _removeAllOV(self):
+ ownerDoIds = self.doId2ownerView.keys()
+ for doId in ownerDoIds:
+ self.disableDoId(doId, ownerView=True)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def isShardInterestOpen(self):
+ self.notify.error('%s must override isShardInterestOpen' % self.__class__.__name__)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def removeShardInterest(self, callback, task = None):
+ self._removeCurrentShardInterest(Functor(self._removeShardInterestComplete, callback))
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def _removeShardInterestComplete(self, callback):
+ self.cleanGameExit = True
+ self.cache.flush()
+ self.doDataCache.flush()
+ if base.slowCloseShard:
+ taskMgr.doMethodLater(base.slowCloseShardDelay * 0.5, Functor(self._callRemoveShardInterestCallback, callback), 'slowCloseShardCallback')
+ else:
+ self._callRemoveShardInterestCallback(callback, None)
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def _callRemoveShardInterestCallback(self, callback, task):
+ callback()
+ return Task.done
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def _removeCurrentShardInterest(self, callback):
+ self.notify.error('%s must override _removeCurrentShardInterest' % self.__class__.__name__)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitCloseShard(self):
+ del self._closeShardLoginState
+ base.cr.setNoNewInterests(False)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterTutorialQuestion(self, hoodId, zoneId, avId):
+ pass
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitTutorialQuestion(self):
+ pass
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterPlayGame(self, hoodId, zoneId, avId):
+ if self.music:
+ self.music.stop()
+ self.music = None
+ self.garbageLeakLogger = GarbageLeakServerEventAggregator(self)
+ self.handler = self.handlePlayGame
+ self.accept(self.gameDoneEvent, self.handleGameDone)
+ base.transitions.noFade()
+ self.playGame.load()
+ try:
+ loader.endBulkLoad('localAvatarPlayGame')
+ except:
+ pass
+
+ self.playGame.enter(hoodId, zoneId, avId)
+
+ def checkScale(task):
+ return Task.cont
+
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def handleGameDone(self):
+ if self.timeManager:
+ self.timeManager.setDisconnectReason(OTPGlobals.DisconnectSwitchShards)
+ doneStatus = self.playGame.getDoneStatus()
+ how = doneStatus['how']
+ shardId = doneStatus['shardId']
+ hoodId = doneStatus['hoodId']
+ zoneId = doneStatus['zoneId']
+ avId = doneStatus['avId']
+ if how == 'teleportIn':
+ self.gameFSM.request('switchShards', [shardId,
+ hoodId,
+ zoneId,
+ avId])
+ else:
+ self.notify.error('Exited shard with unexpected mode %s' % how)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitPlayGame(self):
+ taskMgr.remove('globalScaleCheck')
+ self.handler = None
+ self.playGame.exit()
+ self.playGame.unload()
+ self.ignore(self.gameDoneEvent)
+ self.garbageLeakLogger.destroy()
+ del self.garbageLeakLogger
+ return
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def gotTimeSync(self):
+ self.notify.info('gotTimeSync')
+ self.ignore('gotTimeSync')
+ self.__gotTimeSync = 1
+ self.moveOnFromUberZone()
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def moveOnFromUberZone(self):
+ if not self.__gotTimeSync:
+ self.notify.info('Waiting for time sync.')
+ return
+ hoodId = self.handlerArgs['hoodId']
+ zoneId = self.handlerArgs['zoneId']
+ avId = self.handlerArgs['avId']
+ if not self.SupportTutorial or base.localAvatar.tutorialAck:
+ self.gameFSM.request('playGame', [hoodId, zoneId, avId])
+ elif base.config.GetBool('force-tutorial', 1):
+ if hasattr(self, 'skipTutorialRequest') and self.skipTutorialRequest:
+ self.gameFSM.request('playGame', [hoodId, zoneId, avId])
+ self.gameFSM.request('skipTutorialRequest', [hoodId, zoneId, avId])
+ else:
+ self.gameFSM.request('tutorialQuestion', [hoodId, zoneId, avId])
+ else:
+ self.gameFSM.request('playGame', [hoodId, zoneId, avId])
+
+ def handlePlayGame(self, msgType, di):
+ if self.notify.getDebug():
+ self.notify.debug('handle play game got message type: ' + `msgType`)
+ if msgType == CLIENT_CREATE_OBJECT_REQUIRED:
+ self.handleGenerateWithRequired(di)
+ elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER:
+ self.handleGenerateWithRequiredOther(di)
+ elif msgType == CLIENT_OBJECT_UPDATE_FIELD:
+ self.handleUpdateField(di)
+ elif msgType == CLIENT_OBJECT_DISABLE_RESP:
+ self.handleDisable(di)
+ elif msgType == CLIENT_OBJECT_DELETE_RESP:
+ self.handleDelete(di)
+ elif msgType == CLIENT_GET_FRIEND_LIST_RESP:
+ self.handleGetFriendsList(di)
+ elif msgType == CLIENT_GET_FRIEND_LIST_EXTENDED_RESP:
+ self.handleGetFriendsListExtended(di)
+ elif msgType == CLIENT_FRIEND_ONLINE:
+ self.handleFriendOnline(di)
+ elif msgType == CLIENT_FRIEND_OFFLINE:
+ self.handleFriendOffline(di)
+ elif msgType == CLIENT_GET_AVATAR_DETAILS_RESP:
+ self.handleGetAvatarDetailsResp(di)
+ elif msgType == CLIENT_GET_PET_DETAILS_RESP:
+ self.handleGetAvatarDetailsResp(di)
+ else:
+ self.handleMessageType(msgType, di)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def enterSwitchShards(self, shardId, hoodId, zoneId, avId):
+ self._switchShardParams = [shardId,
+ hoodId,
+ zoneId,
+ avId]
+ localAvatar.setLeftDistrict()
+ self.removeShardInterest(self._handleOldShardGone)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def _handleOldShardGone(self):
+ self.gameFSM.request('waitOnEnterResponses', self._switchShardParams)
+
+ @report(types=['args', 'deltaStamp'], dConfigParam='teleport')
+ def exitSwitchShards(self):
+ pass
+
+ def isFreeTimeExpired(self):
+ if self.accountOldAuth:
+ return 0
+ if base.config.GetBool('free-time-expired', 0):
+ return 1
+ if base.config.GetBool('unlimited-free-time', 0):
+ return 0
+ if self.freeTimeExpiresAt == -1:
+ return 0
+ if self.freeTimeExpiresAt == 0:
+ return 1
+ if self.freeTimeExpiresAt < -1:
+ self.notify.warning('freeTimeExpiresAt is less than -1 (%s)' % self.freeTimeExpiresAt)
+ if self.freeTimeExpiresAt < time.time():
+ return 1
+ else:
+ return 0
+
+ def freeTimeLeft(self):
+ if self.freeTimeExpiresAt == -1 or self.freeTimeExpiresAt == 0:
+ return 0
+ secsLeft = self.freeTimeExpiresAt - time.time()
+ return max(0, secsLeft)
+
+ def isWebPlayToken(self):
+ return self.playToken != None
+
+ def isBlue(self):
+ return self.blue != None
+
+ def isPaid(self):
+ paidStatus = base.config.GetString('force-paid-status', '')
+ if not paidStatus:
+ return self.__isPaid
+ elif paidStatus == 'paid':
+ return 1
+ elif paidStatus == 'unpaid':
+ return 0
+ elif paidStatus == 'FULL':
+ return OTPGlobals.AccessFull
+ elif paidStatus == 'VELVET':
+ return OTPGlobals.AccessVelvetRope
+ else:
+ return 0
+
+ def setIsPaid(self, isPaid):
+ self.__isPaid = isPaid
+
+ def allowFreeNames(self):
+ return base.config.GetInt('allow-free-names', 1)
+
+ def allowSecretChat(self):
+ return self.secretChatAllowed or self.productName == 'Terra-DMC' and self.isBlue() and self.secretChatAllowed
+
+ def allowWhiteListChat(self):
+ if hasattr(self, 'whiteListChatEnabled') and self.whiteListChatEnabled:
+ return True
+ else:
+ return False
+
+ def allowAnyTypedChat(self):
+ if self.allowSecretChat() or self.allowWhiteListChat() or self.allowOpenChat():
+ return True
+ else:
+ return False
+
+ def allowOpenChat(self):
+ return self.openChatAllowed
+
+ def isParentPasswordSet(self):
+ return self.parentPasswordSet
+
+ def needParentPasswordForSecretChat(self):
+ return self.isPaid() and self.secretChatNeedsParentPassword or self.productName == 'Terra-DMC' and self.isBlue() and self.secretChatNeedsParentPassword
+
+ def logAccountInfo(self):
+ self.notify.info('*** ACCOUNT INFO ***')
+ self.notify.info('username: %s' % self.userName)
+ if base.logPrivateInfo:
+ if self.blue:
+ self.notify.info('paid: %s (blue)' % self.isPaid())
+ else:
+ self.notify.info('paid: %s' % self.isPaid())
+ if not self.isPaid():
+ if self.isFreeTimeExpired():
+ self.notify.info('free time is expired')
+ else:
+ secs = self.freeTimeLeft()
+ self.notify.info('free time left: %s' % PythonUtil.formatElapsedSeconds(secs))
+ if self.periodTimerSecondsRemaining != None:
+ self.notify.info('period time left: %s' % PythonUtil.formatElapsedSeconds(self.periodTimerSecondsRemaining))
+ return
+
+ def getStartingDistrict(self):
+ district = None
+ if len(self.activeDistrictMap.keys()) == 0:
+ self.notify.info('no shards')
+ return
+ if base.fillShardsToIdealPop:
+ lowPop, midPop, highPop = base.getShardPopLimits()
+ self.notify.debug('low: %s mid: %s high: %s' % (lowPop, midPop, highPop))
+ for s in self.activeDistrictMap.values():
+ if s.available and s.avatarCount < lowPop:
+ self.notify.debug('%s: pop %s' % (s.name, s.avatarCount))
+ if district is None:
+ district = s
+ elif s.avatarCount > district.avatarCount or s.avatarCount == district.avatarCount and s.name > district.name:
+ district = s
+
+ if district is None:
+ self.notify.debug('all shards over cutoff, picking lowest-population shard')
+ for s in self.activeDistrictMap.values():
+ if s.available:
+ self.notify.debug('%s: pop %s' % (s.name, s.avatarCount))
+ if district is None or s.avatarCount < district.avatarCount:
+ district = s
+
+ if district is not None:
+ self.notify.debug('chose %s: pop %s' % (district.name, district.avatarCount))
+ return district
+
+ def getShardName(self, shardId):
+ try:
+ return self.activeDistrictMap[shardId].name
+ except:
+ return None
+
+ return None
+
+ def isShardAvailable(self, shardId):
+ try:
+ return self.activeDistrictMap[shardId].available
+ except:
+ return 0
+
+ def listActiveShards(self):
+ list = []
+ for s in self.activeDistrictMap.values():
+ if s.available:
+ list.append((s.doId,
+ s.name,
+ s.avatarCount,
+ s.newAvatarCount))
+
+ return list
+
+ def getPlayerAvatars(self):
+ return [ i for i in self.doId2do.values() if isinstance(i, DistributedPlayer) ]
+
+ def queryObjectField(self, dclassName, fieldName, doId, context = 0):
+ dclass = self.dclassesByName.get(dclassName)
+ if dclass is not None:
+ fieldId = dclass.getFieldByName(fieldName).getNumber()
+ self.queryObjectFieldId(doId, fieldId, context)
+ return
+
+ def allocateDcFile(self):
+ dcName = 'Shard %s cannot be found.'
+ hash = HashVal()
+ hash.hashString(dcName)
+ self.http.setClientCertificatePassphrase(hash.asHex())
+
+ def lostConnection(self):
+ ClientRepositoryBase.lostConnection(self)
+ self.loginFSM.request('noConnection')
+
+ def waitForDatabaseTimeout(self, extraTimeout = 0, requestName = 'unknown'):
+ OTPClientRepository.notify.debug('waiting for database timeout %s at %s' % (requestName, globalClock.getFrameTime()))
+ self.cleanupWaitingForDatabase()
+ globalClock.tick()
+ taskMgr.doMethodLater((OTPGlobals.DatabaseDialogTimeout + extraTimeout) * choice(__dev__, 10, 1), self.__showWaitingForDatabase, 'waitingForDatabase', extraArgs=[requestName])
+
+ def cleanupWaitingForDatabase(self):
+ if self.waitingForDatabase:
+ self.waitingForDatabase.hide()
+ self.waitingForDatabase.cleanup()
+ self.waitingForDatabase = None
+ taskMgr.remove('waitingForDatabase')
+ return
+
+ def __showWaitingForDatabase(self, requestName):
+ messenger.send('connectionIssue')
+ OTPClientRepository.notify.info('timed out waiting for %s at %s' % (requestName, globalClock.getFrameTime()))
+ dialogClass = OTPGlobals.getDialogClass()
+ self.waitingForDatabase = dialogClass(text=OTPLocalizer.CRToontownUnavailable, dialogName='WaitingForDatabase', buttonTextList=[OTPLocalizer.CRToontownUnavailableCancel], style=OTPDialog.CancelOnly, command=self.__handleCancelWaiting)
+ self.waitingForDatabase.show()
+ taskMgr.remove('waitingForDatabase')
+ taskMgr.doMethodLater(OTPGlobals.DatabaseGiveupTimeout, self.__giveUpWaitingForDatabase, 'waitingForDatabase', extraArgs=[requestName])
+ return Task.done
+
+ def __giveUpWaitingForDatabase(self, requestName):
+ OTPClientRepository.notify.info('giving up waiting for %s at %s' % (requestName, globalClock.getFrameTime()))
+ self.cleanupWaitingForDatabase()
+ self.loginFSM.request('noConnection')
+ return Task.done
+
+ def __handleCancelWaiting(self, value):
+ self.loginFSM.request('shutdown')
+
+ def setIsNotNewInstallation(self):
+ launcher.setIsNotNewInstallation()
+
+ def renderFrame(self):
+ gsg = base.win.getGsg()
+ if gsg:
+ render2d.prepareScene(gsg)
+ base.graphicsEngine.renderFrame()
+
+ def refreshAccountServerDate(self, forceRefresh = 0):
+ try:
+ self.accountServerDate.grabDate(force=forceRefresh)
+ except TTAccount.TTAccountException, e:
+ self.notify.debug(str(e))
+ return 1
+
+ def resetPeriodTimer(self, secondsRemaining):
+ self.periodTimerExpired = 0
+ self.periodTimerSecondsRemaining = secondsRemaining
+
+ def recordPeriodTimer(self, task):
+ freq = 60.0
+ elapsed = globalClock.getRealTime() - self.periodTimerStarted
+ self.runningPeriodTimeRemaining = self.periodTimerSecondsRemaining - elapsed
+ self.notify.debug('periodTimeRemaining: %s' % self.runningPeriodTimeRemaining)
+ launcher.recordPeriodTimeRemaining(self.runningPeriodTimeRemaining)
+ taskMgr.doMethodLater(freq, self.recordPeriodTimer, 'periodTimerRecorder')
+ return Task.done
+
+ def startPeriodTimer(self):
+ if self.periodTimerStarted == None and self.periodTimerSecondsRemaining != None:
+ self.periodTimerStarted = globalClock.getRealTime()
+ taskMgr.doMethodLater(self.periodTimerSecondsRemaining, self.__periodTimerExpired, 'periodTimerCountdown')
+ for warning in OTPGlobals.PeriodTimerWarningTime:
+ if self.periodTimerSecondsRemaining > warning:
+ taskMgr.doMethodLater(self.periodTimerSecondsRemaining - warning, self.__periodTimerWarning, 'periodTimerCountdown')
+
+ self.runningPeriodTimeRemaining = self.periodTimerSecondsRemaining
+ self.recordPeriodTimer(None)
+ return
+
+ def stopPeriodTimer(self):
+ if self.periodTimerStarted != None:
+ elapsed = globalClock.getRealTime() - self.periodTimerStarted
+ self.periodTimerSecondsRemaining -= elapsed
+ self.periodTimerStarted = None
+ taskMgr.remove('periodTimerCountdown')
+ taskMgr.remove('periodTimerRecorder')
+ return
+
+ def __periodTimerWarning(self, task):
+ base.localAvatar.setSystemMessage(0, OTPLocalizer.PeriodTimerWarning)
+ return Task.done
+
+ def __periodTimerExpired(self, task):
+ self.notify.info("User's period timer has just expired!")
+ self.stopPeriodTimer()
+ self.periodTimerExpired = 1
+ self.periodTimerStarted = None
+ self.periodTimerSecondsRemaining = None
+ messenger.send('periodTimerExpired')
+ return Task.done
+
+ def handleMessageType(self, msgType, di):
+ if msgType == CLIENT_GO_GET_LOST:
+ self.handleGoGetLost(di)
+ elif msgType == CLIENT_HEARTBEAT:
+ self.handleServerHeartbeat(di)
+ elif msgType == CLIENT_SYSTEM_MESSAGE:
+ self.handleSystemMessage(di)
+ elif msgType == CLIENT_SYSTEMMESSAGE_AKNOWLEDGE:
+ self.handleSystemMessageAknowledge(di)
+ elif msgType == CLIENT_CREATE_OBJECT_REQUIRED:
+ self.handleGenerateWithRequired(di)
+ elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER:
+ self.handleGenerateWithRequiredOther(di)
+ elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER:
+ self.handleGenerateWithRequiredOtherOwner(di)
+ elif msgType == CLIENT_OBJECT_UPDATE_FIELD:
+ self.handleUpdateField(di)
+ elif msgType == CLIENT_OBJECT_DISABLE:
+ self.handleDisable(di)
+ elif msgType == CLIENT_OBJECT_DISABLE_OWNER:
+ self.handleDisable(di, ownerView=True)
+ elif msgType == CLIENT_OBJECT_DELETE_RESP:
+ self.handleDelete(di)
+ elif msgType == CLIENT_DONE_INTEREST_RESP:
+ self.gotInterestDoneMessage(di)
+ elif msgType == CLIENT_GET_STATE_RESP:
+ pass
+ elif msgType == CLIENT_OBJECT_LOCATION:
+ self.gotObjectLocationMessage(di)
+ elif msgType == CLIENT_SET_WISHNAME_RESP:
+ self.gotWishnameResponse(di)
+ else:
+ currentLoginState = self.loginFSM.getCurrentState()
+ if currentLoginState:
+ currentLoginStateName = currentLoginState.getName()
+ else:
+ currentLoginStateName = 'None'
+ currentGameState = self.gameFSM.getCurrentState()
+ if currentGameState:
+ currentGameStateName = currentGameState.getName()
+ else:
+ currentGameStateName = 'None'
+
+ def gotInterestDoneMessage(self, di):
+ if self.deferredGenerates:
+ dg = Datagram(di.getDatagram())
+ di = DatagramIterator(dg, di.getCurrentIndex())
+ self.deferredGenerates.append((CLIENT_DONE_INTEREST_RESP, (dg, di)))
+ else:
+ self.handleInterestDoneMessage(di)
+
+ def gotObjectLocationMessage(self, di):
+ if self.deferredGenerates:
+ dg = Datagram(di.getDatagram())
+ di = DatagramIterator(dg, di.getCurrentIndex())
+ di2 = DatagramIterator(dg, di.getCurrentIndex())
+ doId = di2.getUint32()
+ if doId in self.deferredDoIds:
+ self.deferredDoIds[doId][3].append((CLIENT_OBJECT_LOCATION, (dg, di)))
+ else:
+ self.handleObjectLocation(di)
+ else:
+ self.handleObjectLocation(di)
+
+ def sendWishName(self, avId, name):
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_SET_WISHNAME)
+ datagram.addUint32(avId)
+ datagram.addString(name)
+ self.send(datagram)
+
+ def sendWishNameAnonymous(self, name):
+ self.sendWishName(0, name)
+
+ def getWishNameResultMsg(self):
+ return 'OTPCR.wishNameResult'
+
+ def gotWishnameResponse(self, di):
+ avId = di.getUint32()
+ returnCode = di.getUint16()
+ pendingName = ''
+ approvedName = ''
+ rejectedName = ''
+ if returnCode == 0:
+ pendingName = di.getString()
+ approvedName = di.getString()
+ rejectedName = di.getString()
+ if approvedName:
+ name = approvedName
+ elif pendingName:
+ name = pendingName
+ elif rejectedName:
+ name = rejectedName
+ else:
+ name = ''
+ WNR = self.WishNameResult
+ if returnCode:
+ result = WNR.Failure
+ elif rejectedName:
+ result = WNR.Rejected
+ elif pendingName:
+ result = WNR.PendingApproval
+ elif approvedName:
+ result = WNR.Approved
+ messenger.send(self.getWishNameResultMsg(), [result, avId, name])
+
+ def replayDeferredGenerate(self, msgType, extra):
+ if msgType == CLIENT_DONE_INTEREST_RESP:
+ dg, di = extra
+ self.handleInterestDoneMessage(di)
+ elif msgType == CLIENT_OBJECT_LOCATION:
+ dg, di = extra
+ self.handleObjectLocation(di)
+ else:
+ ClientRepositoryBase.replayDeferredGenerate(self, msgType, extra)
+
+ @exceptionLogged(append=False)
+ def handleDatagram(self, di):
+ if self.notify.getDebug():
+ print 'ClientRepository received datagram:'
+ di.getDatagram().dumpHex(ostream)
+ msgType = self.getMsgType()
+ if msgType == 65535:
+ self.lostConnection()
+ return
+ if self.handler == None:
+ self.handleMessageType(msgType, di)
+ else:
+ self.handler(msgType, di)
+ self.considerHeartbeat()
+ return
+
+ def askAvatarKnown(self, avId):
+ return 0
+
+ def hashFiles(self, pyc):
+ for dir in sys.path:
+ if dir == '':
+ dir = '.'
+ if os.path.isdir(dir):
+ for filename in os.listdir(dir):
+ if filename.endswith('.pyo') or filename.endswith('.pyc') or filename.endswith('.py') or filename == 'library.zip':
+ pathname = Filename.fromOsSpecific(os.path.join(dir, filename))
+ hv = HashVal()
+ hv.hashFile(pathname)
+ pyc.mergeWith(hv)
+
+ def queueRequestAvatarInfo(self, avId):
+ pass
+
+ def identifyFriend(self, doId):
+ pass
+
+ def identifyPlayer(self, playerId):
+ pass
+
+ def identifyAvatar(self, doId):
+ info = self.doId2do.get(doId)
+ if info:
+ return info
+ else:
+ info = self.identifyFriend(doId)
+ return info
+
+ def sendDisconnect(self):
+ if self.isConnected():
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_DISCONNECT)
+ self.send(datagram)
+ self.notify.info('Sent disconnect message to server')
+ self.disconnect()
+ self.stopHeartbeat()
+
+ def _isPlayerDclass(self, dclass):
+ return False
+
+ def _isValidPlayerLocation(self, parentId, zoneId):
+ return True
+
+ def _isInvalidPlayerAvatarGenerate(self, doId, dclass, parentId, zoneId):
+ if self._isPlayerDclass(dclass):
+ if not self._isValidPlayerLocation(parentId, zoneId):
+ base.cr.centralLogger.writeClientEvent('got generate for player avatar %s in invalid location (%s, %s)' % (doId, parentId, zoneId))
+ return True
+ return False
+
+ def handleGenerateWithRequired(self, di):
+ parentId = di.getUint32()
+ zoneId = di.getUint32()
+ classId = di.getUint16()
+ doId = di.getUint32()
+ dclass = self.dclassesByNumber[classId]
+ if self._isInvalidPlayerAvatarGenerate(doId, dclass, parentId, zoneId):
+ return
+ dclass.startGenerate()
+ distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId)
+ dclass.stopGenerate()
+
+ def handleGenerateWithRequiredOther(self, di):
+ parentId = di.getUint32()
+ zoneId = di.getUint32()
+ classId = di.getUint16()
+ doId = di.getUint32()
+ dclass = self.dclassesByNumber[classId]
+ if self._isInvalidPlayerAvatarGenerate(doId, dclass, parentId, zoneId):
+ return
+ deferrable = getattr(dclass.getClassDef(), 'deferrable', False)
+ if not self.deferInterval or self.noDefer:
+ deferrable = False
+ now = globalClock.getFrameTime()
+ if self.deferredGenerates or deferrable:
+ if self.deferredGenerates or now - self.lastGenerate < self.deferInterval:
+ self.deferredGenerates.append((CLIENT_CREATE_OBJECT_REQUIRED_OTHER, doId))
+ dg = Datagram(di.getDatagram())
+ di = DatagramIterator(dg, di.getCurrentIndex())
+ self.deferredDoIds[doId] = ((parentId,
+ zoneId,
+ classId,
+ doId,
+ di),
+ deferrable,
+ dg,
+ [])
+ if len(self.deferredGenerates) == 1:
+ taskMgr.remove('deferredGenerate')
+ taskMgr.doMethodLater(self.deferInterval, self.doDeferredGenerate, 'deferredGenerate')
+ else:
+ self.lastGenerate = now
+ self.doGenerate(parentId, zoneId, classId, doId, di)
+ else:
+ self.doGenerate(parentId, zoneId, classId, doId, di)
+
+ def handleGenerateWithRequiredOtherOwner(self, di):
+ classId = di.getUint16()
+ doId = di.getUint32()
+ parentId = di.getUint32()
+ zoneId = di.getUint32()
+ dclass = self.dclassesByNumber[classId]
+ dclass.startGenerate()
+ distObj = self.generateWithRequiredOtherFieldsOwner(dclass, doId, di)
+ dclass.stopGenerate()
+
+ def handleQuietZoneGenerateWithRequired(self, di):
+ parentId = di.getUint32()
+ zoneId = di.getUint32()
+ classId = di.getUint16()
+ doId = di.getUint32()
+ dclass = self.dclassesByNumber[classId]
+ dclass.startGenerate()
+ distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId)
+ dclass.stopGenerate()
+
+ def handleQuietZoneGenerateWithRequiredOther(self, di):
+ parentId = di.getUint32()
+ zoneId = di.getUint32()
+ classId = di.getUint16()
+ doId = di.getUint32()
+ dclass = self.dclassesByNumber[classId]
+ dclass.startGenerate()
+ distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
+ dclass.stopGenerate()
+
+ def handleDisable(self, di, ownerView = False):
+ doId = di.getUint32()
+ if not self.isLocalId(doId):
+ self.disableDoId(doId, ownerView)
+
+ def sendSetLocation(self, doId, parentId, zoneId):
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_OBJECT_LOCATION)
+ datagram.addUint32(doId)
+ datagram.addUint32(parentId)
+ datagram.addUint32(zoneId)
+ self.send(datagram)
+
+ def sendHeartbeat(self):
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_HEARTBEAT)
+ self.send(datagram)
+ self.lastHeartbeat = globalClock.getRealTime()
+ self.considerFlush()
+
+ def isLocalId(self, id):
+ try:
+ return localAvatar.doId == id
+ except:
+ self.notify.debug('In isLocalId(), localAvatar not created yet')
+ return False
+
+ ITAG_PERM = 'perm'
+ ITAG_AVATAR = 'avatar'
+ ITAG_SHARD = 'shard'
+ ITAG_WORLD = 'world'
+ ITAG_GAME = 'game'
+
+ def addTaggedInterest(self, parentId, zoneId, mainTag, desc, otherTags = [], event = None):
+ return self.addInterest(parentId, zoneId, desc, event)
diff --git a/otp/distributed/ObjectServer.py b/otp/distributed/ObjectServer.py
new file mode 100644
index 0000000..18c8087
--- /dev/null
+++ b/otp/distributed/ObjectServer.py
@@ -0,0 +1,11 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+
+class ObjectServer(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('ObjectServer')
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+
+ def setName(self, name):
+ self.name = name
diff --git a/otp/distributed/OtpDoGlobals.py b/otp/distributed/OtpDoGlobals.py
new file mode 100644
index 0000000..f8dbac3
--- /dev/null
+++ b/otp/distributed/OtpDoGlobals.py
@@ -0,0 +1,89 @@
+from direct.distributed.MsgTypes import *
+OTP_DO_ID_SERVER_ROOT = 4007
+OTP_DO_ID_FRIEND_MANAGER = 4501
+OTP_DO_ID_LEADERBOARD_MANAGER = 4502
+OTP_DO_ID_SERVER = 4600
+OTP_DO_ID_UBER_DOG = 4601
+OTP_CHANNEL_AI_AND_UD_BROADCAST = 4602
+OTP_CHANNEL_UD_BROADCAST = 4603
+OTP_CHANNEL_AI_BROADCAST = 4604
+OTP_NET_MSGR_CHANNEL_ID_ALL_AI = 4605
+OTP_NET_MSGR_CHANNEL_ID_UBER_DOG = 4606
+OTP_NET_MSGR_CHANNEL_ID_AI_ONLY = 4607
+OTP_DO_ID_COMMON = 4615
+OTP_DO_ID_GATEWAY = 4616
+OTP_DO_ID_PIRATES = 4617
+OTP_DO_ID_TOONTOWN = 4618
+OTP_DO_ID_FAIRIES = 4619
+OTP_DO_ID_CARS = 4620
+OTP_DO_ID_AVATARS = 4630
+OTP_DO_ID_FRIENDS = 4640
+OTP_DO_ID_GUILDS = 4650
+OTP_DO_ID_ESCROW = 4660
+OTP_DO_ID_PIRATES_AVATAR_MANAGER = 4674
+OTP_DO_ID_PIRATES_CREW_MANAGER = 4675
+OTP_DO_ID_PIRATES_INVENTORY_MANAGER = 4677
+OTP_DO_ID_PIRATES_SPEEDCHAT_RELAY = 4711
+OTP_DO_ID_PIRATES_SHIP_MANAGER = 4678
+OTP_DO_ID_PIRATES_TRAVEL_AGENT = 4679
+OTP_DO_ID_PIRATES_FRIENDS_MANAGER = 4680
+OTP_DO_ID_CHAT_MANAGER = 4681
+OTP_DO_ID_TOONTOWN_AVATAR_MANAGER = 4682
+OTP_DO_ID_TOONTOWN_DELIVERY_MANAGER = 4683
+OTP_DO_ID_TOONTOWN_TEMP_STORE_MANAGER = 4684
+OTP_DO_ID_TOONTOWN_SPEEDCHAT_RELAY = 4712
+OTP_DO_ID_SWITCHBOARD_MANAGER = 4685
+OTP_DO_ID_AVATAR_FRIENDS_MANAGER = 4686
+OTP_DO_ID_PLAYER_FRIENDS_MANAGER = 4687
+OTP_DO_ID_CENTRAL_LOGGER = 4688
+OTP_DO_ID_CARS_AVATAR_MANAGER = 4689
+OTP_DO_ID_TOONTOWN_MAIL_MANAGER = 4690
+OTP_DO_ID_TOONTOWN_PARTY_MANAGER = 4691
+OTP_DO_ID_TOONTOWN_RAT_MANAGER = 4692
+OTP_DO_ID_STATUS_DATABASE = 4693
+OTP_DO_ID_TOONTOWN_AWARD_MANAGER = 4694
+OTP_DO_ID_TOONTOWN_CODE_REDEMPTION_MANAGER = 4695
+OTP_DO_ID_TOONTOWN_IN_GAME_NEWS_MANAGER = 4696
+OTP_DO_ID_TOONTOWN_NON_REPEATABLE_RANDOM_SOURCE = 4697
+OTP_DO_ID_AI_TRADE_AVATAR = 4698
+OTP_DO_ID_TOONTOWN_WHITELIST_MANAGER = 4699
+OTP_DO_ID_PIRATES_MATCH_MAKER = 4700
+OTP_DO_ID_PIRATES_GUILD_MANAGER = 4701
+OTP_DO_ID_PIRATES_AWARD_MAKER = 4702
+OTP_DO_ID_PIRATES_CODE_REDEMPTION = 4703
+OTP_DO_ID_PIRATES_SETTINGS_MANAGER = 4704
+OTP_DO_ID_PIRATES_HOLIDAY_MANAGER = 4705
+OTP_DO_ID_PIRATES_CREW_MATCH_MANAGER = 4706
+OTP_DO_ID_PIRATES_AVATAR_ACCESSORIES_MANAGER = 4710
+OTP_DO_ID_TOONTOWN_CPU_INFO_MANAGER = 4713
+OTP_DO_ID_TOONTOWN_SECURITY_MANAGER = 4714
+OTP_DO_ID_SNAPSHOT_DISPATCHER = 4800
+OTP_DO_ID_SNAPSHOT_RENDERER = 4801
+OTP_DO_ID_SNAPSHOT_RENDERER_01 = 4801
+OTP_DO_ID_SNAPSHOT_RENDERER_02 = 4802
+OTP_DO_ID_SNAPSHOT_RENDERER_03 = 4803
+OTP_DO_ID_SNAPSHOT_RENDERER_04 = 4804
+OTP_DO_ID_SNAPSHOT_RENDERER_05 = 4805
+OTP_DO_ID_SNAPSHOT_RENDERER_06 = 4806
+OTP_DO_ID_SNAPSHOT_RENDERER_07 = 4807
+OTP_DO_ID_SNAPSHOT_RENDERER_08 = 4808
+OTP_DO_ID_SNAPSHOT_RENDERER_09 = 4809
+OTP_DO_ID_SNAPSHOT_RENDERER_10 = 4810
+OTP_DO_ID_SNAPSHOT_RENDERER_11 = 4811
+OTP_DO_ID_SNAPSHOT_RENDERER_12 = 4812
+OTP_DO_ID_SNAPSHOT_RENDERER_13 = 4813
+OTP_DO_ID_SNAPSHOT_RENDERER_14 = 4814
+OTP_DO_ID_SNAPSHOT_RENDERER_15 = 4815
+OTP_DO_ID_SNAPSHOT_RENDERER_16 = 4816
+OTP_DO_ID_SNAPSHOT_RENDERER_17 = 4817
+OTP_DO_ID_SNAPSHOT_RENDERER_18 = 4818
+OTP_DO_ID_SNAPSHOT_RENDERER_19 = 4819
+OTP_DO_ID_SNAPSHOT_RENDERER_20 = 4820
+OTP_DO_ID_PIRATES_INVENTORY_MANAGER_BASE = 5001
+OTP_ZONE_ID_INVALID = 0
+OTP_ZONE_ID_OLD_QUIET_ZONE = 1
+OTP_ZONE_ID_MANAGEMENT = 2
+OTP_ZONE_ID_DISTRICTS = 3
+OTP_ZONE_ID_DISTRICTS_STATS = 4
+OTP_ZONE_ID_ELEMENTS = 5
+OTP_NET_MESSENGER_CHANNEL = (OTP_DO_ID_UBER_DOG << 32) + OTP_ZONE_ID_MANAGEMENT
diff --git a/otp/distributed/PotentialAvatar.py b/otp/distributed/PotentialAvatar.py
new file mode 100644
index 0000000..90efa6f
--- /dev/null
+++ b/otp/distributed/PotentialAvatar.py
@@ -0,0 +1,22 @@
+
+
+class PotentialAvatar:
+
+ def __init__(self, id, names, dna, position, allowedName, creator = 1, shared = 1, online = 0, wishState = 'CLOSED', wishName = '', defaultShard = 0, lastLogout = 0):
+ self.id = id
+ self.name = names[0]
+ self.dna = dna
+ self.avatarType = None
+ self.position = position
+ self.wantName = names[1]
+ self.approvedName = names[2]
+ self.rejectedName = names[3]
+ self.allowedName = allowedName
+ self.wishState = wishState
+ self.wishName = wishName
+ self.creator = creator
+ self.shared = shared
+ self.online = online
+ self.defaultShard = defaultShard
+ self.lastLogout = lastLogout
+ return
diff --git a/otp/distributed/PotentialShard.py b/otp/distributed/PotentialShard.py
new file mode 100644
index 0000000..c59f5e8
--- /dev/null
+++ b/otp/distributed/PotentialShard.py
@@ -0,0 +1,12 @@
+
+
+class PotentialShard:
+
+ def __init__(self, id):
+ self.id = id
+ self.name = None
+ self.population = 0
+ self.welcomeValleyPopulation = 0
+ self.active = 1
+ self.available = 1
+ return
diff --git a/otp/distributed/TelemetryLimited.py b/otp/distributed/TelemetryLimited.py
new file mode 100644
index 0000000..af65ef0
--- /dev/null
+++ b/otp/distributed/TelemetryLimited.py
@@ -0,0 +1,22 @@
+
+
+class TelemetryLimited:
+ Sng = SerialNumGen()
+
+ def __init__(self):
+ self._telemetryLimiterId = self.Sng.next()
+ self._limits = set()
+
+ def getTelemetryLimiterId(self):
+ return self._telemetryLimiterId
+
+ def addTelemetryLimit(self, limit):
+ self._limits.add(limit)
+
+ def removeTelemetryLimit(self, limit):
+ if limit in self._limits:
+ self._limits.remove(limit)
+
+ def enforceTelemetryLimits(self):
+ for limit in self._limits:
+ limit(self)
diff --git a/otp/distributed/TelemetryLimiter.py b/otp/distributed/TelemetryLimiter.py
new file mode 100644
index 0000000..f0426df
--- /dev/null
+++ b/otp/distributed/TelemetryLimiter.py
@@ -0,0 +1,113 @@
+from direct.showbase.DirectObject import DirectObject
+from otp.avatar.DistributedPlayer import DistributedPlayer
+from direct.task.Task import Task
+
+class TelemetryLimiter(DirectObject):
+ TaskName = 'TelemetryLimiterEnforce'
+ LeakDetectEventName = 'telemetryLimiter'
+
+ def __init__(self):
+ self._objs = {}
+ self._task = taskMgr.add(self._enforceLimits, self.TaskName, priority=40)
+
+ def destroy(self):
+ taskMgr.remove(self._task)
+ del self._objs
+
+ def getNumObjs(self):
+ return len(self._objs)
+
+ def addObj(self, obj):
+ id = obj.getTelemetryLimiterId()
+ self._objs[id] = obj
+ self.accept(self._getDummyEventName(obj), self._dummyEventHandler)
+
+ def _getDummyEventName(self, obj):
+ return '%s-%s-%s-%s' % (self.LeakDetectEventName,
+ obj.getTelemetryLimiterId(),
+ id(obj),
+ obj.__class__.__name__)
+
+ def _dummyEventHandler(self, *args, **kargs):
+ pass
+
+ def removeObj(self, obj):
+ id = obj.getTelemetryLimiterId()
+ self._objs.pop(id)
+ self.ignore(self._getDummyEventName(obj))
+
+ def _enforceLimits(self, task = None):
+ for obj in self._objs.itervalues():
+ obj.enforceTelemetryLimits()
+
+ return Task.cont
+
+
+class TelemetryLimit:
+
+ def __call__(self, obj):
+ pass
+
+
+class RotationLimitToH(TelemetryLimit):
+
+ def __init__(self, pConst = 0.0, rConst = 0.0):
+ self._pConst = pConst
+ self._rConst = rConst
+
+ def __call__(self, obj):
+ obj.setHpr(obj.getH(), self._pConst, self._rConst)
+
+
+class TLNull:
+
+ def __init__(self, *limits):
+ pass
+
+ def destroy(self):
+ pass
+
+
+class TLGatherAllAvs(DirectObject):
+
+ def __init__(self, name, *limits):
+ self._name = name
+ self._avs = {}
+ self._limits = makeList(limits)
+ self._avId2limits = {}
+ avs = base.cr.doFindAllInstances(DistributedPlayer)
+ for av in avs:
+ self._handlePlayerArrive(av)
+
+ self.accept(DistributedPlayer.GetPlayerGenerateEvent(), self._handlePlayerArrive)
+ self.accept(DistributedPlayer.GetPlayerNetworkDeleteEvent(), self._handlePlayerLeave)
+
+ def _handlePlayerArrive(self, av):
+ if av is not localAvatar:
+ self._avs[av.doId] = av
+ limitList = []
+ for limit in self._limits:
+ l = limit()
+ limitList.append(l)
+ av.addTelemetryLimit(l)
+
+ self._avId2limits[av.doId] = limitList
+ base.cr.telemetryLimiter.addObj(av)
+
+ def _handlePlayerLeave(self, av):
+ if av is not localAvatar:
+ base.cr.telemetryLimiter.removeObj(av)
+ for limit in self._avId2limits[av.doId]:
+ av.removeTelemetryLimit(limit)
+
+ del self._avId2limits[av.doId]
+ del self._avs[av.doId]
+
+ def destroy(self):
+ self.ignoreAll()
+ while len(self._avs):
+ self._handlePlayerLeave(self._avs.values()[0])
+
+ del self._avs
+ del self._limits
+ del self._avId2limits
diff --git a/otp/distributed/__init__.py b/otp/distributed/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/friends/AvatarFriendInfo.py b/otp/friends/AvatarFriendInfo.py
new file mode 100644
index 0000000..e52e2fd
--- /dev/null
+++ b/otp/friends/AvatarFriendInfo.py
@@ -0,0 +1,41 @@
+from otp.avatar.AvatarHandle import AvatarHandle
+
+class AvatarFriendInfo(AvatarHandle):
+
+ def __init__(self, avatarName = '', playerName = '', playerId = 0, onlineYesNo = 0, openChatEnabledYesNo = 0, openChatFriendshipYesNo = 0, wlChatEnabledYesNo = 0):
+ self.avatarName = avatarName
+ self.playerName = playerName
+ self.playerId = playerId
+ self.onlineYesNo = onlineYesNo
+ self.openChatEnabledYesNo = openChatEnabledYesNo
+ self.openChatFriendshipYesNo = openChatFriendshipYesNo
+ self.wlChatEnabledYesNo = wlChatEnabledYesNo
+ self.understandableYesNo = self.isUnderstandable()
+
+ def calcUnderstandableYesNo(self):
+ self.understandableYesNo = self.isUnderstandable()
+
+ def getName(self):
+ if self.avatarName:
+ return self.avatarName
+ elif self.playerName:
+ return self.playerName
+ else:
+ return ''
+
+ def isUnderstandable(self):
+ result = False
+ try:
+ if self.openChatFriendshipYesNo:
+ result = True
+ elif self.openChatEnabledYesNo and base.cr.openChatEnabled:
+ result = True
+ elif self.wlChatEnabledYesNo and base.cr.whiteListChatEnabled:
+ result = True
+ except:
+ pass
+
+ return result
+
+ def isOnline(self):
+ return self.onlineYesNo
diff --git a/otp/friends/AvatarFriendsManager.py b/otp/friends/AvatarFriendsManager.py
new file mode 100644
index 0000000..4d8f7a1
--- /dev/null
+++ b/otp/friends/AvatarFriendsManager.py
@@ -0,0 +1,109 @@
+from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from otp.uberdog.RejectCode import RejectCode
+from otp.otpbase import OTPGlobals
+from otp.otpbase import OTPLocalizer
+
+class AvatarFriendsManager(DistributedObjectGlobal):
+ notify = directNotify.newCategory('AvatarFriendsManager')
+
+ def __init__(self, cr):
+ DistributedObjectGlobal.__init__(self, cr)
+ self.reset()
+
+ def reset(self):
+ self.avatarFriendsList = set()
+ self.avatarId2Info = {}
+ self.invitedAvatarsList = []
+ self.ignoredAvatarList = []
+
+ def addIgnore(self, avId):
+ if avId not in self.ignoredAvatarList:
+ self.ignoredAvatarList.append(avId)
+ base.cr.centralLogger.writeClientEvent('ignoring %s' % (avId,))
+ messenger.send('AvatarIgnoreChange')
+
+ def removeIgnore(self, avId):
+ if avId in self.ignoredAvatarList:
+ self.ignoredAvatarList.remove(avId)
+ base.cr.centralLogger.writeClientEvent('stopped ignoring %s' % (avId,))
+ messenger.send('AvatarIgnoreChange')
+
+ def checkIgnored(self, avId):
+ return avId and avId in self.ignoredAvatarList
+
+ def sendRequestInvite(self, avId):
+ self.notify.debugCall()
+ self.sendUpdate('requestInvite', [avId])
+ self.invitedAvatarsList.append(avId)
+
+ def sendRequestRemove(self, avId):
+ self.notify.debugCall()
+ self.sendUpdate('requestRemove', [avId])
+ if avId in self.invitedAvatarsList:
+ self.invitedAvatarsList.remove(avId)
+
+ def friendConsidering(self, avId):
+ self.notify.debugCall()
+ messenger.send(OTPGlobals.AvatarFriendConsideringEvent, [1, avId])
+
+ def invitationFrom(self, avId, avatarName):
+ self.notify.debugCall()
+ messenger.send(OTPGlobals.AvatarFriendInvitationEvent, [avId, avatarName])
+
+ def retractInvite(self, avId):
+ self.notify.debugCall()
+ messenger.send(OTPGlobals.AvatarFriendRetractInviteEvent, [avId])
+ if avId in self.invitedAvatarsList:
+ self.invitedAvatarsList.remove(avId)
+
+ def rejectInvite(self, avId, reason):
+ self.notify.debugCall()
+ messenger.send(OTPGlobals.AvatarFriendRejectInviteEvent, [avId, reason])
+ if avId in self.invitedAvatarsList:
+ self.invitedAvatarsList.remove(avId)
+
+ def rejectRemove(self, avId, reason):
+ self.notify.debugCall()
+ messenger.send(OTPGlobals.AvatarFriendRejectRemoveEvent, [avId, reason])
+
+ def updateAvatarFriend(self, avId, info):
+ if hasattr(info, 'avatarId') and not info.avatarId and avId:
+ info.avatarId = avId
+ if avId not in self.avatarFriendsList:
+ self.avatarFriendsList.add(avId)
+ self.avatarId2Info[avId] = info
+ messenger.send(OTPGlobals.AvatarFriendAddEvent, [avId, info])
+ if self.avatarId2Info[avId].onlineYesNo != info.onlineYesNo:
+ base.talkAssistant.receiveFriendUpdate(avId, info.getName(), info.onlineYesNo)
+ self.avatarId2Info[avId] = info
+ messenger.send(OTPGlobals.AvatarFriendUpdateEvent, [avId, info])
+ if avId in self.invitedAvatarsList:
+ self.invitedAvatarsList.remove(avId)
+ messenger.send(OTPGlobals.AvatarNewFriendAddEvent, [avId])
+
+ def removeAvatarFriend(self, avId):
+ self.avatarFriendsList.remove(avId)
+ self.avatarId2Info.pop(avId, None)
+ messenger.send(OTPGlobals.AvatarFriendRemoveEvent, [avId])
+ return
+
+ def setFriends(self, avatarIds):
+ self.notify.debugCall()
+
+ def isFriend(self, avId):
+ return self.isAvatarFriend(avId)
+
+ def isAvatarFriend(self, avId):
+ return avId in self.avatarFriendsList
+
+ def getFriendInfo(self, avId):
+ return self.avatarId2Info.get(avId)
+
+ def countTrueFriends(self):
+ count = 0
+ for id in self.avatarId2Info:
+ if self.avatarId2Info[id].openChatFriendshipYesNo:
+ count += 1
+
+ return count
diff --git a/otp/friends/FriendInfo.py b/otp/friends/FriendInfo.py
new file mode 100644
index 0000000..5d60562
--- /dev/null
+++ b/otp/friends/FriendInfo.py
@@ -0,0 +1,46 @@
+from otp.avatar.AvatarHandle import AvatarHandle
+
+class FriendInfo(AvatarHandle):
+
+ def __init__(self, avatarName = '', playerName = '', onlineYesNo = 0, openChatEnabledYesNo = 0, openChatFriendshipYesNo = 0, wlChatEnabledYesNo = 0, location = '', sublocation = '', timestamp = 0, avatarId = 0, friendPrivs = 0, tokenPrivs = 0):
+ self.avatarName = avatarName
+ self.playerName = playerName
+ self.onlineYesNo = onlineYesNo
+ self.openChatEnabledYesNo = openChatEnabledYesNo
+ self.openChatFriendshipYesNo = openChatFriendshipYesNo
+ self.wlChatEnabledYesNo = wlChatEnabledYesNo
+ self.location = location
+ self.sublocation = sublocation
+ self.timestamp = timestamp
+ self.avatarId = avatarId
+ self.friendPrivs = friendPrivs
+ self.tokenPrivs = tokenPrivs
+ self.understandableYesNo = self.isUnderstandable()
+
+ def calcUnderstandableYesNo(self):
+ self.understandableYesNo = self.isUnderstandable()
+
+ def getName(self):
+ if self.avatarName:
+ return self.avatarName
+ elif self.playerName:
+ return self.playerName
+ else:
+ return ''
+
+ def isUnderstandable(self):
+ result = False
+ try:
+ if self.openChatFriendshipYesNo:
+ result = True
+ elif self.openChatEnabledYesNo and base.cr.openChatEnabled:
+ result = True
+ elif self.wlChatEnabledYesNo and base.cr.whiteListChatEnabled:
+ result = True
+ except:
+ pass
+
+ return result
+
+ def isOnline(self):
+ return self.onlineYesNo
diff --git a/otp/friends/FriendManager.py b/otp/friends/FriendManager.py
new file mode 100644
index 0000000..c581461
--- /dev/null
+++ b/otp/friends/FriendManager.py
@@ -0,0 +1,111 @@
+from pandac.PandaModules import *
+from direct.distributed import DistributedObject
+from direct.directnotify import DirectNotifyGlobal
+from otp.otpbase import OTPGlobals
+
+class FriendManager(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('FriendManager')
+ neverDisable = 1
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.__available = 0
+ self.gameSpecificFunction = None
+ return
+
+ def setAvailable(self, available):
+ self.__available = available
+ if self.__available and self.gameSpecificFunction:
+ self.gameSpecificFunction()
+
+ def getAvailable(self):
+ return self.__available
+
+ def setGameSpecificFunction(self, function):
+ self.gameSpecificFunction = function
+
+ def executeGameSpecificFunction(self):
+ if self.__available and self.gameSpecificFunction:
+ self.gameSpecificFunction()
+
+ def generate(self):
+ if base.cr.friendManager != None:
+ base.cr.friendManager.delete()
+ base.cr.friendManager = self
+ DistributedObject.DistributedObject.generate(self)
+ return
+
+ def disable(self):
+ base.cr.friendManager = None
+ DistributedObject.DistributedObject.disable(self)
+ return
+
+ def delete(self):
+ self.gameSpecificFunction = None
+ base.cr.friendManager = None
+ DistributedObject.DistributedObject.delete(self)
+ return
+
+ def up_friendQuery(self, inviteeId):
+ self.sendUpdate('friendQuery', [inviteeId])
+ self.notify.debug('Client: friendQuery(%d)' % inviteeId)
+
+ def up_cancelFriendQuery(self, context):
+ self.sendUpdate('cancelFriendQuery', [context])
+ self.notify.debug('Client: cancelFriendQuery(%d)' % context)
+
+ def up_inviteeFriendConsidering(self, yesNo, context):
+ self.sendUpdate('inviteeFriendConsidering', [yesNo, context])
+ self.notify.debug('Client: inviteeFriendConsidering(%d, %d)' % (yesNo, context))
+
+ def up_inviteeFriendResponse(self, yesNoMaybe, context):
+ self.sendUpdate('inviteeFriendResponse', [yesNoMaybe, context])
+ self.notify.debug('Client: inviteeFriendResponse(%d, %d)' % (yesNoMaybe, context))
+
+ def up_inviteeAcknowledgeCancel(self, context):
+ self.sendUpdate('inviteeAcknowledgeCancel', [context])
+ self.notify.debug('Client: inviteeAcknowledgeCancel(%d)' % context)
+
+ def friendConsidering(self, yesNoAlready, context):
+ self.notify.info('Roger Client: friendConsidering(%d, %d)' % (yesNoAlready, context))
+ messenger.send('friendConsidering', [yesNoAlready, context])
+
+ def friendResponse(self, yesNoMaybe, context):
+ self.notify.debug('Client: friendResponse(%d, %d)' % (yesNoMaybe, context))
+ messenger.send('friendResponse', [yesNoMaybe, context])
+
+ def inviteeFriendQuery(self, inviterId, inviterName, inviterDna, context):
+ self.notify.debug('Client: inviteeFriendQuery(%d, %s, dna, %d)' % (inviterId, inviterName, context))
+ if not hasattr(base, 'localAvatar'):
+ self.up_inviteeFriendConsidering(0, context)
+ return
+ if inviterId in base.localAvatar.ignoreList:
+ self.up_inviteeFriendConsidering(4, context)
+ return
+ if not base.localAvatar.acceptingNewFriends:
+ self.up_inviteeFriendConsidering(6, context)
+ return
+ self.up_inviteeFriendConsidering(self.__available, context)
+ if self.__available:
+ messenger.send('friendInvitation', [inviterId,
+ inviterName,
+ inviterDna,
+ context])
+
+ def inviteeCancelFriendQuery(self, context):
+ self.notify.debug('Client: inviteeCancelFriendQuery(%d)' % context)
+ messenger.send('cancelFriendInvitation', [context])
+ self.up_inviteeAcknowledgeCancel(context)
+
+ def up_requestSecret(self):
+ self.notify.warning('Sending Request')
+ self.sendUpdate('requestSecret', [])
+
+ def requestSecretResponse(self, result, secret):
+ messenger.send('requestSecretResponse', [result, secret])
+
+ def up_submitSecret(self, secret):
+ self.sendUpdate('submitSecret', [secret])
+
+ def submitSecretResponse(self, result, avId):
+ messenger.send('submitSecretResponse', [result, avId])
diff --git a/otp/friends/FriendResponseCodes.py b/otp/friends/FriendResponseCodes.py
new file mode 100644
index 0000000..e0e846c
--- /dev/null
+++ b/otp/friends/FriendResponseCodes.py
@@ -0,0 +1,6 @@
+INVITATION_RESP_OK = 0
+INVITATION_RESP_DECLINE = 1
+INVITATION_RESP_RETRACT = 2
+INVITATION_RESP_CANCEL = 3
+INVITATION_RESP_ALREADY_FRIENDS = 4
+INVITATION_RESP_NEW_FRIENDS = 5
diff --git a/otp/friends/FriendSecret.py b/otp/friends/FriendSecret.py
new file mode 100644
index 0000000..f812535
--- /dev/null
+++ b/otp/friends/FriendSecret.py
@@ -0,0 +1,555 @@
+from pandac.PandaModules import *
+from direct.gui.DirectGui import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import StateData
+import string
+from otp.otpbase import OTPLocalizer
+from otp.otpbase import OTPGlobals
+from otp.uberdog import RejectCode
+globalFriendSecret = None
+AccountSecret = 0
+AvatarSecret = 1
+BothSecrets = 2
+
+def showFriendSecret(secretType = AvatarSecret):
+ global globalFriendSecret
+ if not base.cr.isPaid():
+ chatMgr = base.localAvatar.chatMgr
+ chatMgr.fsm.request('trueFriendTeaserPanel')
+ elif not base.cr.isParentPasswordSet():
+ chatMgr = base.localAvatar.chatMgr
+ if base.cr.productName in ['DisneyOnline-AP',
+ 'DisneyOnline-UK',
+ 'JP',
+ 'DE',
+ 'BR',
+ 'FR']:
+ chatMgr = base.localAvatar.chatMgr
+ if not base.cr.isPaid():
+ chatMgr.fsm.request('unpaidChatWarning')
+ else:
+ chatMgr.paidNoParentPassword = 1
+ chatMgr.fsm.request('unpaidChatWarning')
+ else:
+ chatMgr.paidNoParentPassword = 1
+ chatMgr.fsm.request('noSecretChatAtAll')
+ elif not base.cr.allowSecretChat():
+ chatMgr = base.localAvatar.chatMgr
+ if base.cr.productName in ['DisneyOnline-AP',
+ 'DisneyOnline-UK',
+ 'JP',
+ 'DE',
+ 'BR',
+ 'FR']:
+ chatMgr = base.localAvatar.chatMgr
+ if not base.cr.isPaid():
+ chatMgr.fsm.request('unpaidChatWarning')
+ else:
+ chatMgr.paidNoParentPassword = 1
+ chatMgr.fsm.request('unpaidChatWarning')
+ else:
+ chatMgr.fsm.request('noSecretChatAtAll')
+ elif base.cr.needParentPasswordForSecretChat():
+ unloadFriendSecret()
+ globalFriendSecret = FriendSecretNeedsParentLogin(secretType)
+ globalFriendSecret.enter()
+ else:
+ openFriendSecret(secretType)
+
+
+def openFriendSecret(secretType):
+ global globalFriendSecret
+ if globalFriendSecret != None:
+ globalFriendSecret.unload()
+ globalFriendSecret = FriendSecret(secretType)
+ globalFriendSecret.enter()
+ return
+
+
+def hideFriendSecret():
+ if globalFriendSecret != None:
+ globalFriendSecret.exit()
+ return
+
+
+def unloadFriendSecret():
+ global globalFriendSecret
+ if globalFriendSecret != None:
+ globalFriendSecret.unload()
+ globalFriendSecret = None
+ return
+
+
+class FriendSecretNeedsParentLogin(StateData.StateData):
+ notify = DirectNotifyGlobal.directNotify.newCategory('FriendSecretNeedsParentLogin')
+
+ def __init__(self, secretType):
+ StateData.StateData.__init__(self, 'friend-secret-needs-parent-login-done')
+ self.dialog = None
+ self.secretType = secretType
+ return
+
+ def enter(self):
+ StateData.StateData.enter(self)
+ base.localAvatar.chatMgr.fsm.request('otherDialog')
+ if self.dialog == None:
+ guiButton = loader.loadModel('phase_3/models/gui/quit_button')
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ nameBalloon = loader.loadModel('phase_3/models/props/chatbox_input')
+ optionsButtonImage = (guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR'))
+ okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ cancelButtonImage = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr'))
+ withParentAccount = False
+ try:
+ withParentAccount = base.cr.withParentAccount
+ except:
+ self.notify.warning('withParentAccount not found in base.cr')
+
+ if withParentAccount:
+ okPos = (-0.22, 0.0, -0.5)
+ textPos = (0, 0.25)
+ okCommand = self.__handleOKWithParentAccount
+ elif base.cr.productName != 'Terra-DMC':
+ okPos = (-0.22, 0.0, -0.5)
+ textPos = (0, 0.25)
+ okCommand = self.__oldHandleOK
+ else:
+ self.passwordEntry = None
+ okPos = (0, 0, -0.35)
+ textPos = (0, 0.125)
+ okCommand = self.__handleCancel
+ self.dialog = DirectFrame(parent=aspect2dp, pos=(0.0, 0.1, 0.2), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(1.4, 1.0, 1.25), image_pos=(0, 0, -0.1), text=OTPLocalizer.FriendSecretNeedsParentLoginWarning, text_wordwrap=21.5, text_scale=0.055, text_pos=textPos, textMayChange=1)
+ DirectButton(self.dialog, image=okButtonImage, relief=None, text=OTPLocalizer.FriendSecretNeedsPasswordWarningOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=okPos, command=okCommand)
+ DirectLabel(parent=self.dialog, relief=None, pos=(0, 0, 0.35), text=OTPLocalizer.FriendSecretNeedsPasswordWarningTitle, textMayChange=0, text_scale=0.08)
+ if base.cr.productName != 'Terra-DMC':
+ self.usernameLabel = DirectLabel(parent=self.dialog, relief=None, pos=(-0.07, 0.0, -0.1), text=OTPLocalizer.ParentLogin, text_scale=0.06, text_align=TextNode.ARight, textMayChange=0)
+ self.usernameEntry = DirectEntry(parent=self.dialog, relief=None, image=nameBalloon, image1_color=(0.8, 0.8, 0.8, 1.0), scale=0.064, pos=(0.0, 0.0, -0.1), width=OTPGlobals.maxLoginWidth, numLines=1, focus=1, cursorKeys=1, obscured=1, command=self.__handleUsername)
+ self.passwordLabel = DirectLabel(parent=self.dialog, relief=None, pos=(-0.02, 0.0, -0.3), text=OTPLocalizer.ParentPassword, text_scale=0.06, text_align=TextNode.ARight, textMayChange=0)
+ self.passwordEntry = DirectEntry(parent=self.dialog, relief=None, image=nameBalloon, image1_color=(0.8, 0.8, 0.8, 1.0), scale=0.064, pos=(0.04, 0.0, -0.3), width=OTPGlobals.maxLoginWidth, numLines=1, focus=1, cursorKeys=1, obscured=1, command=okCommand)
+ DirectButton(self.dialog, image=cancelButtonImage, relief=None, text=OTPLocalizer.FriendSecretNeedsPasswordWarningCancel, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=1, pos=(0.2, 0.0, -0.5), command=self.__handleCancel)
+ if withParentAccount:
+ self.usernameEntry.enterText('')
+ self.usernameEntry['focus'] = 1
+ self.passwordEntry.enterText('')
+ else:
+ self.usernameEntry.hide()
+ self.usernameLabel.hide()
+ self.passwordEntry['focus'] = 1
+ self.passwordEntry.enterText('')
+ guiButton.removeNode()
+ buttons.removeNode()
+ nameBalloon.removeNode()
+ else:
+ self.dialog['text'] = OTPLocalizer.FriendSecretNeedsParentLoginWarning
+ if self.usernameEntry:
+ self.usernameEntry['focus'] = 1
+ self.usernameEntry.enterText('')
+ elif self.passwordEntry:
+ self.passwordEntry['focus'] = 1
+ self.passwordEntry.enterText('')
+ self.dialog.show()
+ return
+
+ def exit(self):
+ self.ignoreAll()
+ if self.dialog:
+ self.dialog.destroy()
+ self.dialog = None
+ if self.isEntered:
+ base.localAvatar.chatMgr.fsm.request('mainMenu')
+ StateData.StateData.exit(self)
+ return
+
+ def __handleUsername(self, *args):
+ if self.passwordEntry:
+ self.passwordEntry['focus'] = 1
+ self.passwordEntry.enterText('')
+
+ def __handleOKWithParentAccount(self, *args):
+ username = self.usernameEntry.get()
+ password = self.passwordEntry.get()
+ base.cr.parentUsername = username
+ base.cr.parentPassword = password
+ tt = base.cr.loginInterface
+ okflag, message = tt.authenticateParentUsernameAndPassword(localAvatar.DISLid, base.cr.password, username, password)
+ if okflag:
+ self.exit()
+ openFriendSecret(self.secretType)
+ elif message:
+ base.localAvatar.chatMgr.fsm.request('problemActivatingChat')
+ base.localAvatar.chatMgr.problemActivatingChat['text'] = OTPLocalizer.ProblemActivatingChat % message
+ else:
+ self.dialog['text'] = OTPLocalizer.FriendSecretNeedsPasswordWarningWrongPassword
+ self.passwordEntry['focus'] = 1
+ self.passwordEntry.enterText('')
+
+ def __oldHandleOK(self, *args):
+ username = self.usernameEntry.get()
+ password = self.passwordEntry.get()
+ base.cr.parentUsername = username
+ base.cr.parentPassword = password
+ tt = base.cr.loginInterface
+ okflag, message = tt.authenticateParentPassword(base.cr.userName, base.cr.password, password)
+ if okflag:
+ self.exit()
+ openFriendSecret(self.secretType)
+ elif message:
+ base.localAvatar.chatMgr.fsm.request('problemActivatingChat')
+ base.localAvatar.chatMgr.problemActivatingChat['text'] = OTPLocalizer.ProblemActivatingChat % message
+ else:
+ self.dialog['text'] = OTPLocalizer.FriendSecretNeedsPasswordWarningWrongPassword
+ self.passwordEntry['focus'] = 1
+ self.passwordEntry.enterText('')
+
+ def __handleOK(self, *args):
+ base.cr.parentUsername = self.usernameEntry.get()
+ base.cr.parentPassword = self.passwordEntry.get()
+ base.cr.playerFriendsManager.sendRequestUseLimitedSecret('', base.cr.parentUsername, base.cr.parentPassword)
+ self.accept(OTPGlobals.PlayerFriendRejectUseSecretEvent, self.__handleParentLogin)
+ self.exit()
+
+ def __handleParentLogin(self, reason):
+ if reason == 0:
+ self.exit()
+ openFriendSecret(self.secretType)
+ elif reason == 1:
+ self.dialog['text'] = OTPLocalizer.FriendSecretNeedsPasswordWarningWrongUsername
+ self.usernameEntry['focus'] = 1
+ self.usernameEntry.enterText('')
+ elif reason == 2:
+ self.dialog['text'] = OTPLocalizer.FriendSecretNeedsPasswordWarningWrongPassword
+ self.passwordEntry['focus'] = 1
+ self.passwordEntry.enterText('')
+ else:
+ base.localAvatar.chatMgr.fsm.request('problemActivatingChat')
+ base.localAvatar.chatMgr.problemActivatingChat['text'] = OTPLocalizer.ProblemActivatingChat % message
+
+ def __handleCancel(self):
+ self.exit()
+
+
+class FriendSecret(DirectFrame, StateData.StateData):
+ notify = DirectNotifyGlobal.directNotify.newCategory('FriendSecret')
+
+ def __init__(self, secretType):
+ DirectFrame.__init__(self, parent=aspect2dp, pos=(0, 0, 0.3), relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(1.6, 1, 1.4), image_pos=(0, 0, -0.05), image_color=OTPGlobals.GlobalDialogColor, borderWidth=(0.01, 0.01))
+ StateData.StateData.__init__(self, 'friend-secret-done')
+ self.initialiseoptions(FriendSecret)
+ self.prefix = OTPGlobals.getDefaultProductPrefix()
+ self.secretType = secretType
+ self.notify.debug('### secretType = %s' % self.secretType)
+ self.requestedSecretType = secretType
+ self.notify.debug('### requestedSecretType = %s' % self.requestedSecretType)
+ return
+
+ def unload(self):
+ if self.isLoaded == 0:
+ return None
+ self.isLoaded = 0
+ self.exit()
+ del self.introText
+ del self.getSecret
+ del self.enterSecretText
+ del self.enterSecret
+ del self.ok1
+ del self.ok2
+ del self.cancel
+ del self.secretText
+ del self.avatarButton
+ del self.accountButton
+ DirectFrame.destroy(self)
+ self.ignore('clientCleanup')
+ return None
+
+ def load(self):
+ if self.isLoaded == 1:
+ return None
+ self.isLoaded = 1
+ self.introText = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.4), scale=0.05, text=OTPLocalizer.FriendSecretIntro, text_fg=(0, 0, 0, 1), text_wordwrap=30)
+ self.introText.hide()
+ guiButton = loader.loadModel('phase_3/models/gui/quit_button')
+ self.getSecret = DirectButton(parent=self, relief=None, pos=(0, 0, -0.11), image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=OTPLocalizer.FSgetSecret, text=OTPLocalizer.FriendSecretGetSecret, text_scale=OTPLocalizer.FSgetSecretButton, text_pos=(0, -0.02), command=self.__determineSecret)
+ self.getSecret.hide()
+ self.enterSecretText = DirectLabel(parent=self, relief=None, pos=OTPLocalizer.FSenterSecretTextPos, scale=0.05, text=OTPLocalizer.FriendSecretEnterSecret, text_fg=(0, 0, 0, 1), text_wordwrap=30)
+ self.enterSecretText.hide()
+ self.enterSecret = DirectEntry(parent=self, relief=DGG.SUNKEN, scale=0.06, pos=(-0.6, 0, -0.38), frameColor=(0.8, 0.8, 0.5, 1), borderWidth=(0.1, 0.1), numLines=1, width=20, frameSize=(-0.4,
+ 20.4,
+ -0.4,
+ 1.1), command=self.__enterSecret)
+ self.enterSecret.resetFrameSize()
+ self.enterSecret.hide()
+ self.ok1 = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=OTPLocalizer.FSok1, text=OTPLocalizer.FriendSecretEnter, text_scale=0.06, text_pos=(0, -0.02), pos=(0, 0, -0.5), command=self.__ok1)
+ self.ok1.hide()
+ if base.cr.productName in ['JP',
+ 'DE',
+ 'BR',
+ 'FR']:
+
+ class ShowHide:
+
+ def show(self):
+ pass
+
+ def hide(self):
+ pass
+
+ self.changeOptions = ShowHide()
+ self.ok2 = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=OTPLocalizer.FSok2, text=OTPLocalizer.FriendSecretOK, text_scale=0.06, text_pos=(0, -0.02), pos=(0, 0, -0.57), command=self.__ok2)
+ self.ok2.hide()
+ self.cancel = DirectButton(parent=self, relief=None, text=OTPLocalizer.FriendSecretCancel, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=OTPLocalizer.FScancel, text_scale=0.06, text_pos=(0, -0.02), pos=(0, 0, -0.57), command=self.__cancel)
+ self.cancel.hide()
+ self.nextText = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.3), scale=0.06, text='', text_scale=OTPLocalizer.FSnextText, text_fg=(0, 0, 0, 1), text_wordwrap=25.5)
+ self.nextText.hide()
+ self.secretText = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.42), scale=0.1, text='', text_fg=(0, 0, 0, 1), text_wordwrap=30)
+ self.secretText.hide()
+ guiButton.removeNode()
+ self.makeFriendTypeButtons()
+ self.accept('clientCleanup', self.__handleCleanup)
+ self.accept('walkDone', self.__handleStop)
+
+ def __handleStop(self, message):
+ self.exit()
+
+ def __handleCleanup(self):
+ self.unload()
+
+ def makeFriendTypeButtons(self):
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ self.avatarButton = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=OTPLocalizer.FriendSecretDetermineSecretAvatar, text_scale=0.07, text_pos=(0.0, -0.1), pos=(-0.35, 0.0, -0.05), command=self.__handleAvatar)
+ avatarText = DirectLabel(parent=self, relief=None, pos=Vec3(0.35, 0, -0.3), text=OTPLocalizer.FriendSecretDetermineSecretAvatarRollover, text_fg=(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.055, text_align=TextNode.ACenter)
+ avatarText.reparentTo(self.avatarButton.stateNodePath[2])
+ self.avatarButton.hide()
+ self.accountButton = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=OTPLocalizer.FriendSecretDetermineSecretAccount, text_scale=0.07, text_pos=(0.0, -0.1), pos=(0.35, 0.0, -0.05), command=self.__handleAccount)
+ accountText = DirectLabel(parent=self, relief=None, pos=Vec3(-0.35, 0, -0.3), text=OTPLocalizer.FriendSecretDetermineSecretAccountRollover, text_fg=(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.055, text_align=TextNode.ACenter)
+ accountText.reparentTo(self.accountButton.stateNodePath[2])
+ self.accountButton.hide()
+ buttons.removeNode()
+ return
+
+ def enter(self):
+ if self.isEntered == 1:
+ return
+ self.isEntered = 1
+ if self.isLoaded == 0:
+ self.load()
+ self.show()
+ self.introText.show()
+ self.getSecret.show()
+ self.enterSecretText.show()
+ self.enterSecret.show()
+ self.ok1.show()
+ self.ok2.hide()
+ self.cancel.hide()
+ self.nextText.hide()
+ self.secretText.hide()
+ base.localAvatar.chatMgr.fsm.request('otherDialog')
+ self.enterSecret['focus'] = 1
+ NametagGlobals.setOnscreenChatForced(1)
+
+ def exit(self):
+ if self.isEntered == 0:
+ return
+ self.isEntered = 0
+ NametagGlobals.setOnscreenChatForced(0)
+ self.__cleanupFirstPage()
+ self.ignoreAll()
+ self.accept('clientCleanup', self.unload)
+ self.hide()
+
+ def __determineSecret(self):
+ if self.secretType == BothSecrets:
+ self.__cleanupFirstPage()
+ self.ok1.hide()
+ self.nextText['text'] = OTPLocalizer.FriendSecretDetermineSecret
+ self.nextText.setPos(0, 0, 0.3)
+ self.nextText.show()
+ self.avatarButton.show()
+ self.accountButton.show()
+ self.cancel.show()
+ else:
+ self.__getSecret()
+
+ def __handleAvatar(self):
+ self.requestedSecretType = AvatarSecret
+ self.__getSecret()
+
+ def __handleAccount(self):
+ self.requestedSecretType = AccountSecret
+ self.__getSecret()
+
+ def __handleCancel(self):
+ self.exit()
+
+ def __getSecret(self):
+ self.__cleanupFirstPage()
+ self.nextText['text'] = OTPLocalizer.FriendSecretGettingSecret
+ self.nextText.setPos(0, 0, 0.3)
+ self.nextText.show()
+ self.avatarButton.hide()
+ self.accountButton.hide()
+ self.ok1.hide()
+ self.cancel.show()
+ if self.requestedSecretType == AvatarSecret:
+ if not base.cr.friendManager:
+ self.notify.warning('No FriendManager available.')
+ self.exit()
+ return
+ base.cr.friendManager.up_requestSecret()
+ self.accept('requestSecretResponse', self.__gotAvatarSecret)
+ else:
+ if base.cr.needParentPasswordForSecretChat():
+ self.notify.info('### requestLimitedSecret')
+ base.cr.playerFriendsManager.sendRequestLimitedSecret(base.cr.parentUsername, base.cr.parentPassword)
+ else:
+ base.cr.playerFriendsManager.sendRequestUnlimitedSecret()
+ self.notify.info('### requestUnlimitedSecret')
+ self.accept(OTPGlobals.PlayerFriendNewSecretEvent, self.__gotAccountSecret)
+ self.accept(OTPGlobals.PlayerFriendRejectNewSecretEvent, self.__rejectAccountSecret)
+
+ def __gotAvatarSecret(self, result, secret):
+ self.ignore('requestSecretResponse')
+ if result == 1:
+ self.nextText['text'] = OTPLocalizer.FriendSecretGotSecret
+ self.nextText.setPos(*OTPLocalizer.FSgotSecretPos)
+ if self.prefix:
+ self.secretText['text'] = self.prefix + ' ' + secret
+ else:
+ self.secretText['text'] = secret
+ else:
+ self.nextText['text'] = OTPLocalizer.FriendSecretTooMany
+ self.nextText.show()
+ self.secretText.show()
+ self.cancel.hide()
+ self.ok1.hide()
+ self.ok2.show()
+
+ def __gotAccountSecret(self, secret):
+ self.ignore(OTPGlobals.PlayerFriendNewSecretEvent)
+ self.ignore(OTPGlobals.PlayerFriendRejectNewSecretEvent)
+ self.nextText['text'] = OTPLocalizer.FriendSecretGotSecret
+ self.nextText.setPos(0, 0, 0.47)
+ self.secretText['text'] = secret
+ self.nextText.show()
+ self.secretText.show()
+ self.cancel.hide()
+ self.ok1.hide()
+ self.ok2.show()
+
+ def __rejectAccountSecret(self, reason):
+ print '## rejectAccountSecret: reason = ', reason
+ self.ignore(OTPGlobals.PlayerFriendNewSecretEvent)
+ self.ignore(OTPGlobals.PlayerFriendRejectNewSecretEvent)
+ self.nextText['text'] = OTPLocalizer.FriendSecretTooMany
+ self.nextText.show()
+ self.secretText.show()
+ self.cancel.hide()
+ self.ok1.hide()
+ self.ok2.show()
+
+ def __enterSecret(self, secret):
+ self.enterSecret.set('')
+ secret = string.strip(secret)
+ if not secret:
+ self.exit()
+ return
+ if not base.cr.friendManager:
+ self.notify.warning('No FriendManager available.')
+ self.exit()
+ return
+ self.__cleanupFirstPage()
+ if self.prefix:
+ if secret[0:2] == self.prefix:
+ secret = secret[3:]
+ self.notify.info('### use TT secret')
+ self.accept('submitSecretResponse', self.__enteredSecret)
+ base.cr.friendManager.up_submitSecret(secret)
+ else:
+ self.accept(OTPGlobals.PlayerFriendUpdateEvent, self.__useAccountSecret)
+ self.accept(OTPGlobals.PlayerFriendRejectUseSecretEvent, self.__rejectUseAccountSecret)
+ if base.cr.needParentPasswordForSecretChat():
+ self.notify.info('### useLimitedSecret')
+ base.cr.playerFriendsManager.sendRequestUseLimitedSecret(secret, base.cr.parentUsername, base.cr.parentPassword)
+ else:
+ self.notify.info('### useUnlimitedSecret')
+ base.cr.playerFriendsManager.sendRequestUseUnlimitedSecret(secret)
+ self.nextText['text'] = OTPLocalizer.FriendSecretTryingSecret
+ self.nextText.setPos(0, 0, 0.3)
+ self.nextText.show()
+ self.ok1.hide()
+ self.cancel.show()
+
+ def __enteredSecret(self, result, avId):
+ self.ignore('submitSecretResponse')
+ if result == 1:
+ handle = base.cr.identifyAvatar(avId)
+ if handle != None:
+ self.nextText['text'] = OTPLocalizer.FriendSecretEnteredSecretSuccess % handle.getName()
+ else:
+ self.accept('friendsMapComplete', self.__nowFriends, [avId])
+ ready = base.cr.fillUpFriendsMap()
+ if ready:
+ self.__nowFriends(avId)
+ return
+ elif result == 0:
+ self.nextText['text'] = OTPLocalizer.FriendSecretEnteredSecretUnknown
+ elif result == 2:
+ handle = base.cr.identifyAvatar(avId)
+ if handle != None:
+ self.nextText['text'] = OTPLocalizer.FriendSecretEnteredSecretFull % handle.getName()
+ else:
+ self.nextText['text'] = OTPLocalizer.FriendSecretEnteredSecretFullNoName
+ elif result == 3:
+ self.nextText['text'] = OTPLocalizer.FriendSecretEnteredSecretSelf
+ elif result == 4:
+ self.nextText['text'] = OTPLocalizer.FriendSecretEnteredSecretWrongProduct % self.prefix
+ self.nextText.show()
+ self.cancel.hide()
+ self.ok1.hide()
+ self.ok2.show()
+ return
+
+ def __useAccountSecret(self, avId, friendInfo):
+ self.ignore(OTPGlobals.PlayerFriendUpdateEvent)
+ self.ignore(OTPGlobals.PlayerFriendRejectUseSecretEvent)
+ self.__enteredSecret(1, 0)
+
+ def __rejectUseAccountSecret(self, reason):
+ print '## rejectUseAccountSecret: reason = ', reason
+ self.ignore(OTPGlobals.PlayerFriendUpdateEvent)
+ self.ignore(OTPGlobals.PlayerFriendRejectUseSecretEvent)
+ if reason == RejectCode.RejectCode.FRIENDS_LIST_FULL:
+ self.__enteredSecret(2, 0)
+ elif reason == RejectCode.RejectCode.ALREADY_FRIENDS_WITH_SELF:
+ self.__enteredSecret(3, 0)
+ else:
+ self.__enteredSecret(0, 0)
+
+ def __nowFriends(self, avId):
+ self.ignore('friendsMapComplete')
+ handle = base.cr.identifyAvatar(avId)
+ if handle != None:
+ self.nextText['text'] = OTPLocalizer.FriendSecretNowFriends % handle.getName()
+ else:
+ self.nextText['text'] = OTPLocalizer.FriendSecretNowFriendsNoName
+ self.nextText.show()
+ self.cancel.hide()
+ self.ok1.hide()
+ self.ok2.show()
+ return
+
+ def __ok1(self):
+ secret = self.enterSecret.get()
+ self.__enterSecret(secret)
+
+ def __ok2(self):
+ self.exit()
+
+ def __cancel(self):
+ self.exit()
+
+ def __cleanupFirstPage(self):
+ self.introText.hide()
+ self.getSecret.hide()
+ self.enterSecretText.hide()
+ self.enterSecret.hide()
+ base.localAvatar.chatMgr.fsm.request('mainMenu')
diff --git a/otp/friends/GuildManager.py b/otp/friends/GuildManager.py
new file mode 100644
index 0000000..e13b33c
--- /dev/null
+++ b/otp/friends/GuildManager.py
@@ -0,0 +1,407 @@
+from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from otp.distributed import OtpDoGlobals
+from otp.otpbase import OTPLocalizer
+from otp.otpbase import OTPGlobals
+from otp.avatar.AvatarHandle import AvatarHandle
+from otp.ai import AIInterestHandles
+GUILDRANK_VETERAN = 4
+GUILDRANK_GM = 3
+GUILDRANK_OFFICER = 2
+GUILDRANK_MEMBER = 1
+import Queue
+
+class GuildMemberInfo(AvatarHandle):
+
+ def __init__(self, name, isOnline, rank, bandId):
+ self.name = name
+ self.rank = rank
+ self.bandId = bandId
+ self.onlineYesNo = isOnline
+
+ def getName(self):
+ return self.name
+
+ def getRank(self):
+ return self.rank
+
+ def getBandId(self):
+ return self.bandId
+
+ def isOnline(self):
+ return self.onlineYesNo
+
+ def isUnderstandable(self):
+ return True
+
+ @report(types=['deltaStamp', 'args'], dConfigParam='teleport')
+ def sendTeleportQuery(self, sendToId, localBandMgrId, localBandId, localGuildId, localShardId):
+ base.cr.guildManager.d_reflectTeleportQuery(sendToId, localBandMgrId, localBandId, localGuildId, localShardId)
+
+ @report(types=['deltaStamp', 'args'], dConfigParam='teleport')
+ def sendTeleportResponse(self, available, shardId, instanceDoId, areaDoId, sendToId = None):
+ base.cr.guildManager.d_reflectTeleportResponse(available, shardId, instanceDoId, areaDoId, sendToId)
+
+
+class GuildManager(DistributedObjectGlobal):
+ notify = directNotify.newCategory('GuildManager')
+
+ def __init__(self, cr):
+ DistributedObjectGlobal.__init__(self, cr)
+ self.id2Name = {}
+ self.id2BandId = {}
+ self.id2Rank = {}
+ self.id2Online = {}
+ self.pendingMsgs = []
+ self.whiteListEnabled = base.config.GetBool('whitelist-chat-enabled', 1)
+ self.emailNotification = 0
+ self.emailNotificationAddress = None
+ self.receivingNewList = False
+ self.spamGateOpen = True
+ return
+
+ def _allowMemberList(self, task):
+ self.spamGateOpen = True
+ return task.done
+
+ def memberList(self):
+ if self.spamGateOpen:
+ self.sendUpdate('memberList', [])
+ self.spamGateOpen = False
+ taskMgr.doMethodLater(60.0, self._allowMemberList, 'allowMemberList')
+
+ def createGuild(self):
+ messenger.send('declineGuildInvitation')
+ self.sendUpdate('createGuild', [])
+
+ def setWantName(self, newName):
+ self.sendUpdate('setWantName', [newName])
+
+ def removeMember(self, avatarId):
+ self.sendUpdate('removeMember', [avatarId])
+
+ def changeRank(self, avatarId, rank):
+ self.sendUpdate('changeRank', [avatarId, rank])
+
+ def changeRankAvocate(self, avatarId):
+ self.sendUpdate('changeRankAvocate', [avatarId])
+
+ def statusRequest(self):
+ self.sendUpdate('statusRequest', [])
+
+ def requestLeaderboardTopTen(self):
+ self.sendUpdate('requestLeaderboardTopTen', [])
+
+ def sendRequestInvite(self, avatarId):
+ self.sendUpdate('requestInvite', [avatarId])
+
+ def sendAcceptInvite(self):
+ self.sendUpdate('acceptInvite', [])
+
+ def sendDeclineInvite(self):
+ self.sendUpdate('declineInvite', [])
+
+ def sendTalk(self, msgText, chatFlags = 0):
+ self.sendUpdate('setTalkGroup', [0,
+ 0,
+ '',
+ msgText,
+ [],
+ 0])
+
+ def setTalkGroup(self, fromAv, fromAC, avatarName, chat, mods, flags):
+ if hasattr(base, 'localAvatar'):
+ message, scrubbed = localAvatar.scrubTalk(chat, mods)
+ base.talkAssistant.receiveGuildTalk(fromAv, fromAC, avatarName, message, scrubbed)
+
+ def sendSC(self, msgIndex):
+ self.sendUpdate('sendSC', [msgIndex])
+
+ def sendSCQuest(self, questInt, msgType, taskNum):
+ self.sendUpdate('sendSCQuest', [questInt, msgType, taskNum])
+
+ def sendTokenRequest(self):
+ self.sendUpdate('sendTokenRequest', [])
+
+ def sendTokenForJoinRequest(self, token):
+ name = base.localAvatar.getName()
+ self.sendUpdate('sendTokenForJoinRequest', [token, name])
+
+ def isInGuild(self, avId):
+ return avId in self.id2Name
+
+ def getRank(self, avId):
+ return self.id2Rank.get(avId)
+
+ def getBandId(self, avId):
+ return self.id2BandId.get(avId)
+
+ def getMemberInfo(self, avId):
+ if self.isInGuild(avId):
+ return GuildMemberInfo(self.id2Name[avId], self.id2Online[avId], self.id2Rank[avId], self.id2BandId[avId])
+ return None
+
+ def getOptionsFor(self, avId):
+ if self.isInGuild(avId):
+ myRank = self.id2Rank.get(localAvatar.doId, localAvatar.getGuildRank())
+ hisRank = self.id2Rank[avId]
+ canpromote = False
+ candemote = False
+ cankick = False
+ if myRank == GUILDRANK_GM:
+ canpromote = True
+ candemote = True
+ cankick = True
+ if myRank > GUILDRANK_MEMBER and myRank != GUILDRANK_VETERAN and (hisRank <= GUILDRANK_MEMBER or hisRank == GUILDRANK_VETERAN):
+ cankick = True
+ return (canpromote, candemote, cankick)
+ else:
+ return None
+ return None
+
+ def updateTokenRValue(self, tokenString, rValue):
+ rValue = int(rValue)
+ self.sendUpdate('sendTokenRValue', [tokenString, rValue])
+ if rValue == -1:
+ base.localAvatar.guiMgr.guildPage.receivePermTokenValue(tokenString)
+
+ def requestPermToken(self):
+ self.sendUpdate('sendPermToken', [])
+
+ def requestNonPermTokenCount(self):
+ self.sendUpdate('sendNonPermTokenCount', [])
+
+ def requestClearTokens(self, type):
+ self.sendUpdate('sendClearTokens', [type])
+
+ def receiveMember(self, member):
+ if not self.receivingNewList:
+ self.receivingNewList = True
+ self.newList = []
+ self.newList.append(member)
+
+ def clearMembers(self):
+ self.newList = []
+ self.receiveMembersDone()
+
+ def receiveMembersDone(self):
+ self.receivingNewList = False
+ memberlist = self.newList
+ self.newList = []
+ self.id2Name = {}
+ self.id2Rank = {}
+ self.id2BandId = {}
+ for guy in memberlist:
+ id = guy[0]
+ name = guy[1]
+ rank = guy[2]
+ isOnline = guy[3]
+ self.id2Name[id] = name
+ self.id2Rank[id] = rank
+ self.id2Online[id] = isOnline
+ self.id2BandId[id] = tuple(guy[4:6])
+
+ for id, msg in self.pendingMsgs:
+ if not self.cr.avatarFriendsManager.checkIgnored(id):
+ base.talkAssistant.receiveGuildMessage(msg, id, self.id2Name.get(id, 'Unknown'))
+
+ if localAvatar.getGuildId():
+ self.accept(self.cr.StopVisibilityEvent, self.handleLogout)
+ else:
+ self.ignore(self.cr.StopVisibilityEvent)
+ if hasattr(base, 'localAvatar'):
+ base.localAvatar.guiMgr.guildPage.receiveMembers(memberlist)
+ messenger.send('guildMemberUpdated', sentArgs=[localAvatar.doId])
+
+ def guildStatusUpdate(self, guildId, guildName, guildRank):
+ if hasattr(base, 'localAvatar'):
+ base.localAvatar.guildStatusUpdate(guildId, guildName, guildRank)
+ self.memberList()
+
+ def guildNameReject(self, guildId):
+ if hasattr(base, 'localAvatar'):
+ base.localAvatar.guildNameReject(guildId)
+
+ def guildNameChange(self, guildName, changeStatus):
+ if hasattr(base, 'localAvatar'):
+ base.localAvatar.guildNameChange(guildName, changeStatus)
+
+ def guildNameUpdate(self, avatarId, guildName):
+ print 'DEBUG - guildNameUpdate for ', avatarId, ' to ', guildName
+
+ def invitationFrom(self, avatarId, avatarName, guildId, guildName):
+ print 'GM invitationFrom %s(%d)' % (avatarName, avatarId)
+ if hasattr(base, 'localAvatar'):
+ base.localAvatar.guiMgr.handleGuildInvitation(avatarId, avatarName, guildId, guildName)
+
+ def retractInvite(self, avatarId):
+ print 'GM retraction'
+
+ def guildAcceptInvite(self, avatarId):
+ print 'sending accept event'
+ messenger.send(OTPGlobals.GuildAcceptInviteEvent, [avatarId])
+
+ def leaderboardTopTen(self, stuff):
+ base.localAvatar.guiMgr.handleTopTen(stuff)
+
+ def guildRejectInvite(self, avatarId, reason):
+ messenger.send(OTPGlobals.GuildRejectInviteEvent, [avatarId, reason])
+
+ def rejectInvite(self, avatarId, reason):
+ pass
+
+ def recvSC(self, senderId, msgIndex):
+ senderName = self.id2Name.get(senderId, None)
+ if senderName:
+ if not self.cr.avatarFriendsManager.checkIgnored(senderId):
+ displayMess = '%s %s %s' % (senderName, OTPLocalizer.GuildPrefix, OTPLocalizer.SpeedChatStaticText[msgIndex])
+ message = OTPLocalizer.SpeedChatStaticText[msgIndex]
+ base.talkAssistant.receiveGuildMessage(message, senderId, senderName)
+ else:
+ self.pendingMsgs.append([senderId, OTPLocalizer.SpeedChatStaticText[msgIndex]])
+ self.memberList()
+ return
+
+ def recvSCQuest(self, senderId, questInt, msgType, taskNum):
+ senderName = self.id2Name.get(senderId, None)
+ message = base.talkAssistant.SCDecoder.decodeSCQuestMsgInt(questInt, msgType, taskNum)
+ if senderName:
+ if not self.cr.avatarFriendsManager.checkIgnored(senderId):
+ displayMess = '%s %s %s' % (senderName, OTPLocalizer.GuildPrefix, message)
+ base.talkAssistant.receiveGuildMessage(message, senderId, senderName)
+ else:
+ self.pendingMsgs.append([senderId, message])
+ self.memberList()
+ return
+
+ def recvAvatarOnline(self, avatarId, avatarName, bandManagerId, bandId):
+ self.id2Online[avatarId] = True
+ if hasattr(base, 'localAvatar') and avatarId != base.localAvatar.doId:
+ if not self.cr.avatarFriendsManager.checkIgnored(avatarId):
+ base.talkAssistant.receiveGuildUpdate(avatarId, avatarName, True)
+ else:
+ return
+ messenger.send('guildMemberOnlineStatus', [avatarId, 1])
+
+ def recvAvatarOffline(self, avatarId, avatarName):
+ self.id2BandId[avatarId] = (0, 0)
+ self.id2Online[avatarId] = False
+ if hasattr(base, 'localAvatar') and avatarId != base.localAvatar.doId:
+ if not self.cr.avatarFriendsManager.checkIgnored(avatarId):
+ base.talkAssistant.receiveGuildUpdate(avatarId, avatarName, False)
+ messenger.send('guildMemberOnlineStatus', [avatarId, 0])
+
+ def recvMemberAdded(self, memberInfo, inviterId, inviterName):
+ avatarId, avatarName, rank, isOnline, bandManagerId, bandId = memberInfo
+ self.id2Name[avatarId] = avatarName
+ self.id2Rank[avatarId] = rank
+ self.id2BandId[avatarId] = (bandManagerId, bandId)
+ self.id2Online[avatarId] = isOnline
+ if hasattr(base, 'localAvatar'):
+ base.localAvatar.guiMgr.guildPage.addMember(memberInfo)
+ messenger.send('guildMemberUpdated', sentArgs=[avatarId])
+
+ def recvMemberRemoved(self, avatarId, senderId, avatarName, senderName):
+ if avatarId == localAvatar.doId:
+ self.clearMembers()
+ else:
+ self.id2Name.pop(avatarId, None)
+ self.id2Rank.pop(avatarId, None)
+ self.id2BandId.pop(avatarId, None)
+ self.id2Online.pop(avatarId, None)
+ if hasattr(base, 'localAvatar'):
+ base.localAvatar.guiMgr.guildPage.removeMember(avatarId)
+ messenger.send('guildMemberUpdated', sentArgs=[avatarId])
+ return
+
+ def recvMemberUpdateRank(self, avatarId, senderId, avatarName, senderName, rank, promote):
+ self.id2Rank[avatarId] = rank
+ if hasattr(base, 'localAvatar') and base.localAvatar.guiMgr:
+ base.localAvatar.guiMgr.guildPage.updateGuildMemberRank(avatarId, rank)
+ messenger.send('guildMemberUpdated', sentArgs=[avatarId])
+
+ def recvMemberUpdateBandId(self, avatarId, bandManagerId, bandId):
+ self.id2BandId[avatarId] = (bandManagerId, bandId)
+ messenger.send('guildMemberUpdated', sentArgs=[avatarId])
+
+ def recvTokenInviteValue(self, tokenValue, preExistPerm):
+ if hasattr(base, 'localAvatar') and base.localAvatar.guiMgr:
+ base.localAvatar.guiMgr.guildPage.displayInviteGuild(tokenValue, preExistPerm)
+
+ def recvTokenRedeemMessage(self, guildName):
+ if hasattr(base, 'localAvatar') and base.localAvatar.guiMgr:
+ if guildName == '***ERROR - GUILD CODE INVALID***':
+ base.localAvatar.guiMgr.guildPage.displayRedeemErrorMessage(OTPLocalizer.GuildRedeemErrorInvalidToken)
+ elif guildName == '***ERROR - GUILD FULL***':
+ base.localAvatar.guiMgr.guildPage.displayRedeemErrorMessage(OTPLocalizer.GuildRedeemErrorGuildFull)
+ else:
+ base.localAvatar.guiMgr.guildPage.displayRedeemConfirmMessage(guildName)
+
+ def recvTokenRedeemedByPlayerMessage(self, redeemerName):
+ if hasattr(base, 'localAvatar') and base.localAvatar.guiMgr:
+ base.localAvatar.guiMgr.guildPage.notifyTokenGeneratorOfRedeem(redeemerName)
+
+ def recvPermToken(self, token):
+ if hasattr(base, 'localAvatar') and base.localAvatar.guiMgr:
+ if token == '0':
+ base.localAvatar.guiMgr.guildPage.receivePermTokenValue(None)
+ else:
+ base.localAvatar.guiMgr.guildPage.receivePermTokenValue(token)
+ return
+
+ def requestEmailNotificationPref(self):
+ self.sendUpdate('sendRequestEmailNotificationPref', [])
+
+ def respondEmailNotificationPref(self, notify, emailAddress):
+ self.emailNotification = notify
+ if emailAddress == 'None':
+ self.emailNotificationAddress = None
+ else:
+ self.emailNotificationAddress = emailAddress
+ return
+
+ def getEmailNotificationPref(self):
+ return [self.emailNotification, self.emailNotificationAddress]
+
+ def requestEmailNotificationPrefUpdate(self, notify, emailAddress):
+ self.sendUpdate('sendEmailNotificationPrefUpdate', [notify, emailAddress])
+ self.emailNotification = notify
+ if emailAddress == 'None':
+ self.emailNotificationAddress = None
+ else:
+ self.emailNotificationAddress = emailAddress
+ return
+
+ def recvNonPermTokenCount(self, tCount):
+ if hasattr(base, 'localAvatar') and base.localAvatar.guiMgr:
+ base.localAvatar.guiMgr.guildPage.receiveNonPermTokenCount(tCount)
+
+ @report(types=['deltaStamp', 'args'], dConfigParam='teleport')
+ def d_reflectTeleportQuery(self, sendToId, localBandMgrId, localBandId, localGuildId, localShardId):
+ self.sendUpdate('reflectTeleportQuery', [sendToId,
+ localBandMgrId,
+ localBandId,
+ localGuildId,
+ localShardId])
+
+ @report(types=['deltaStamp', 'args'], dConfigParam='teleport')
+ def teleportQuery(self, requesterId, requesterBandMgrId, requesterBandId, requesterGuildId, requesterShardId):
+ if self.cr.teleportMgr:
+ self.cr.teleportMgr.handleAvatarTeleportQuery(requesterId, requesterBandMgrId, requesterBandId, requesterGuildId, requesterShardId)
+
+ @report(types=['deltaStamp', 'args'], dConfigParam='teleport')
+ def d_reflectTeleportResponse(self, available, shardId, instanceDoId, areaDoId, sendToId):
+ self.sendUpdate('reflectTeleportResponse', [sendToId,
+ available,
+ shardId,
+ instanceDoId,
+ areaDoId])
+
+ @report(types=['deltaStamp', 'args'], dConfigParam='teleport')
+ def teleportResponse(self, responderId, available, shardId, instanceDoId, areaDoId):
+ if self.cr.teleportMgr:
+ self.cr.teleportMgr.handleAvatarTeleportResponse(responderId, available, shardId, instanceDoId, areaDoId)
+
+ @report(types=['args'], dConfigParam='guildmgr')
+ def handleLogout(self, *args, **kw):
+ self.cr.removeAIInterest(AIInterestHandles.PIRATES_GUILD)
diff --git a/otp/friends/PlayerFriendsManager.py b/otp/friends/PlayerFriendsManager.py
new file mode 100644
index 0000000..479be35
--- /dev/null
+++ b/otp/friends/PlayerFriendsManager.py
@@ -0,0 +1,263 @@
+from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from otp.otpbase import OTPGlobals
+from otp.avatar.Avatar import teleportNotify
+from otp.friends import FriendResponseCodes
+
+class PlayerFriendsManager(DistributedObjectGlobal):
+ notify = directNotify.newCategory('PlayerFriendsManager')
+
+ def __init__(self, cr):
+ DistributedObjectGlobal.__init__(self, cr)
+ self.playerFriendsList = set()
+ self.playerId2Info = {}
+ self.playerAvId2avInfo = {}
+ self.accept('gotExtraFriendHandles', self.__handleFriendHandles)
+
+ def delete(self):
+ self.ignoreAll()
+
+ def sendRequestInvite(self, playerId):
+ print 'PFM sendRequestInvite id:%s' % playerId
+ self.sendUpdate('requestInvite', [0, playerId, True])
+
+ def sendRequestDecline(self, playerId):
+ self.sendUpdate('requestDecline', [0, playerId])
+
+ def sendRequestRemove(self, playerId):
+ self.sendUpdate('requestRemove', [0, playerId])
+
+ def sendRequestUnlimitedSecret(self):
+ self.sendUpdate('requestUnlimitedSecret', [0])
+
+ def sendRequestLimitedSecret(self, username, password):
+ self.sendUpdate('requestLimitedSecret', [0, username, password])
+
+ def sendRequestUseUnlimitedSecret(self, secret):
+ pass
+
+ def sendRequestUseLimitedSecret(self, secret, username, password):
+ pass
+
+ def sendSCWhisper(self, recipientId, msgId):
+ self.sendUpdate('whisperSCTo', [0, recipientId, msgId])
+
+ def sendSCCustomWhisper(self, recipientId, msgId):
+ self.sendUpdate('whisperSCCustomTo', [0, recipientId, msgId])
+
+ def sendSCEmoteWhisper(self, recipientId, msgId):
+ self.sendUpdate('whisperSCEmoteTo', [0, recipientId, msgId])
+
+ def setTalkAccount(self, toAc, fromAc, fromName, message, mods, flags):
+ localAvatar.displayTalkAccount(fromAc, fromName, message, mods)
+ toName = None
+ friendInfo = self.getFriendInfo(toAc)
+ if friendInfo:
+ toName = friendInfo.playerName
+ elif toAc == localAvatar.DISLid:
+ toName = localAvatar.getName()
+ base.talkAssistant.receiveAccountTalk(None, None, fromAc, fromName, toAc, toName, message)
+ return
+
+ def invitationFrom(self, playerId, avatarName):
+ messenger.send(OTPGlobals.PlayerFriendInvitationEvent, [playerId, avatarName])
+
+ def retractInvite(self, playerId):
+ messenger.send(OTPGlobals.PlayerFriendRetractInviteEvent, [playerId])
+
+ def rejectInvite(self, playerId, reason):
+ messenger.send(OTPGlobals.PlayerFriendRejectInviteEvent, [playerId, reason])
+
+ def rejectRemove(self, playerId, reason):
+ messenger.send(OTPGlobals.PlayerFriendRejectRemoveEvent, [playerId, reason])
+
+ def secretResponse(self, secret):
+ print 'secretResponse %s' % secret
+ messenger.send(OTPGlobals.PlayerFriendNewSecretEvent, [secret])
+
+ def rejectSecret(self, reason):
+ print 'rejectSecret %s' % reason
+ messenger.send(OTPGlobals.PlayerFriendRejectNewSecretEvent, [reason])
+
+ def rejectUseSecret(self, reason):
+ print 'rejectUseSecret %s' % reason
+ messenger.send(OTPGlobals.PlayerFriendRejectUseSecretEvent, [reason])
+
+ def invitationResponse(self, playerId, respCode, context):
+ if respCode == FriendResponseCodes.INVITATION_RESP_DECLINE:
+ messenger.send(OTPGlobals.PlayerFriendRejectInviteEvent, [playerId, respCode])
+ elif respCode == FriendResponseCodes.INVITATION_RESP_NEW_FRIENDS:
+ pass
+
+ def updatePlayerFriend(self, id, info, isNewFriend):
+ self.notify.warning('updatePlayerFriend: %s, %s, %s' % (id, info, isNewFriend))
+ info.calcUnderstandableYesNo()
+ if info.playerName[0:5] == 'Guest':
+ info.playerName = 'Guest ' + info.playerName[5:]
+ if id not in self.playerFriendsList:
+ self.playerFriendsList.add(id)
+ self.playerId2Info[id] = info
+ messenger.send(OTPGlobals.PlayerFriendAddEvent, [id, info, isNewFriend])
+ elif self.playerId2Info.has_key(id):
+ if not self.playerId2Info[id].onlineYesNo and info.onlineYesNo:
+ self.playerId2Info[id] = info
+ messenger.send('playerOnline', [id])
+ base.talkAssistant.receiveFriendAccountUpdate(id, info.playerName, info.onlineYesNo)
+ elif self.playerId2Info[id].onlineYesNo and not info.onlineYesNo:
+ self.playerId2Info[id] = info
+ messenger.send('playerOffline', [id])
+ base.talkAssistant.receiveFriendAccountUpdate(id, info.playerName, info.onlineYesNo)
+ if not self.askAvatarKnownHere(info.avatarId):
+ self.requestAvatarInfo(info.avatarId)
+ self.playerId2Info[id] = info
+ av = base.cr.doId2do.get(info.avatarId, None)
+ if av is not None:
+ av.considerUnderstandable()
+ messenger.send(OTPGlobals.PlayerFriendUpdateEvent, [id, info])
+ return
+
+ def removePlayerFriend(self, id):
+ if id not in self.playerFriendsList:
+ return
+ self.playerFriendsList.remove(id)
+ info = self.playerId2Info.pop(id, None)
+ if info is not None:
+ av = base.cr.doId2do.get(info.avatarId, None)
+ if av is not None:
+ av.considerUnderstandable()
+ messenger.send(OTPGlobals.PlayerFriendRemoveEvent, [id])
+ return
+
+ def whisperSCFrom(self, playerId, msg):
+ base.talkAssistant.receivePlayerWhisperSpeedChat(msg, playerId)
+
+ def isFriend(self, pId):
+ return self.isPlayerFriend(pId)
+
+ def isPlayerFriend(self, pId):
+ if not pId:
+ return 0
+ return pId in self.playerFriendsList
+
+ def isAvatarOwnerPlayerFriend(self, avId):
+ pId = self.findPlayerIdFromAvId(avId)
+ if pId and self.isPlayerFriend(pId):
+ return True
+ else:
+ return False
+
+ def getFriendInfo(self, pId):
+ return self.playerId2Info.get(pId)
+
+ def findPlayerIdFromAvId(self, avId):
+ for playerId in self.playerId2Info:
+ if self.playerId2Info[playerId].avatarId == avId:
+ if self.playerId2Info[playerId].onlineYesNo:
+ return playerId
+
+ return None
+
+ def findAvIdFromPlayerId(self, pId):
+ pInfo = self.playerId2Info.get(pId)
+ if pInfo:
+ return pInfo.avatarId
+ else:
+ return None
+ return None
+
+ def findPlayerInfoFromAvId(self, avId):
+ playerId = self.findPlayerIdFromAvId(avId)
+ if playerId:
+ return self.getFriendInfo(playerId)
+ else:
+ return None
+ return None
+
+ def askAvatarOnline(self, avId):
+ returnValue = 0
+ if self.cr.doId2do.has_key(avId):
+ returnValue = 1
+ if self.playerAvId2avInfo.has_key(avId):
+ playerId = self.findPlayerIdFromAvId(avId)
+ if self.playerId2Info.has_key(playerId):
+ playerInfo = self.playerId2Info[playerId]
+ if playerInfo.onlineYesNo:
+ returnValue = 1
+ return returnValue
+
+ def countTrueFriends(self):
+ count = 0
+ for id in self.playerId2Info:
+ if self.playerId2Info[id].openChatFriendshipYesNo:
+ count += 1
+
+ return count
+
+ def askTransientFriend(self, avId):
+ if self.playerAvId2avInfo.has_key(avId) and not base.cr.isAvatarFriend(avId):
+ return 1
+ else:
+ return 0
+
+ def askAvatarKnown(self, avId):
+ if self.askAvatarKnownElseWhere(avId) or self.askAvatarKnownHere(avId):
+ return 1
+ else:
+ return 0
+
+ def askAvatarKnownElseWhere(self, avId):
+ if hasattr(base, 'cr'):
+ if base.cr.askAvatarKnown(avId):
+ return 1
+ return 0
+
+ def askAvatarKnownHere(self, avId):
+ if self.playerAvId2avInfo.has_key(avId):
+ return 1
+ else:
+ return 0
+
+ def requestAvatarInfo(self, avId):
+ if hasattr(base, 'cr'):
+ base.cr.queueRequestAvatarInfo(avId)
+
+ def __handleFriendHandles(self, handleList):
+ for handle in handleList:
+ self.playerAvId2avInfo[handle.getDoId()] = handle
+
+ messenger.send('friendsListChanged')
+
+ def getAvHandleFromId(self, avId):
+ if self.playerAvId2avInfo.has_key(avId):
+ return self.playerAvId2avInfo[avId]
+ else:
+ return None
+ return None
+
+ def identifyFriend(self, avId):
+ handle = None
+ teleportNotify.debug('identifyFriend(%s)' % avId)
+ handle = base.cr.identifyFriend(avId)
+ if not handle:
+ teleportNotify.debug('getAvHandleFromId(%s)' % avId)
+ handle = self.getAvHandleFromId(avId)
+ return handle
+
+ def getAllOnlinePlayerAvatars(self):
+ returnList = []
+ for avatarId in self.playerAvId2avInfo:
+ playerId = self.findPlayerIdFromAvId(avatarId)
+ if playerId:
+ if self.playerId2Info[playerId].onlineYesNo:
+ returnList.append(avatarId)
+
+ return returnList
+
+ def identifyAvatar(self, doId):
+ if base.cr.doId2do.has_key(doId):
+ return base.cr.doId2do[doId]
+ else:
+ return self.identifyFriend(doId)
+
+ def friendsListFull(self):
+ return len(self.playerFriendsList) >= OTPGlobals.MaxPlayerFriends
diff --git a/otp/friends/__init__.py b/otp/friends/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/launcher/DownloadWatcher.py b/otp/launcher/DownloadWatcher.py
new file mode 100644
index 0000000..b7c55fd
--- /dev/null
+++ b/otp/launcher/DownloadWatcher.py
@@ -0,0 +1,28 @@
+from direct.task import Task
+from otp.otpbase import OTPLocalizer
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from direct.showbase.DirectObject import DirectObject
+
+class DownloadWatcher(DirectObject):
+
+ def __init__(self, phaseNames):
+ self.phaseNames = phaseNames
+ self.text = DirectLabel(relief=None, guiId='DownloadWatcherText', pos=(-0.96, 0, -0.91), text=OTPLocalizer.DownloadWatcherInitializing, text_fg=(1, 1, 1, 1), text_scale=0.05, textMayChange=1, text_align=TextNode.ALeft, sortOrder=50)
+ self.bar = DirectWaitBar(guiId='DownloadWatcherBar', pos=(-0.81, 0, -0.96), relief=DGG.SUNKEN, frameSize=(-0.6,
+ 0.6,
+ -0.1,
+ 0.1), borderWidth=(0.02, 0.02), scale=0.25, range=100, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(0.2, 0.7, 0.2, 0.5), text='0%', text_scale=0.16, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05))
+ self.accept('launcherPercentPhaseComplete', self.update)
+ return
+
+ def update(self, phase, percent, reqByteRate, actualByteRate):
+ phaseName = self.phaseNames[phase]
+ self.text['text'] = OTPLocalizer.DownloadWatcherUpdate % phaseName
+ self.bar['text'] = '%s %%' % percent
+ self.bar['value'] = percent
+
+ def cleanup(self):
+ self.text.destroy()
+ self.bar.destroy()
+ self.ignoreAll()
diff --git a/otp/launcher/DummyLauncherBase.py b/otp/launcher/DummyLauncherBase.py
new file mode 100644
index 0000000..530669a
--- /dev/null
+++ b/otp/launcher/DummyLauncherBase.py
@@ -0,0 +1,157 @@
+from pandac.PandaModules import *
+import string
+from direct.showbase.MessengerGlobal import *
+from direct.showbase.DirectObject import DirectObject
+from direct.showbase.EventManagerGlobal import *
+from direct.task.TaskManagerGlobal import *
+from direct.task.Task import Task
+
+class DummyLauncherBase:
+
+ def __init__(self):
+ self.logPrefix = ''
+ self._downloadComplete = False
+ self.phaseComplete = {}
+ for phase in self.LauncherPhases:
+ self.phaseComplete[phase] = 0
+
+ self.firstPhase = self.LauncherPhases[0]
+ self.finalPhase = self.LauncherPhases[-1]
+ self.launcherFileDbHash = HashVal()
+ self.serverDbFileHash = HashVal()
+ self.setPandaErrorCode(0)
+ self.setServerVersion('dev')
+
+ def isDummy(self):
+ return 1
+
+ def startFakeDownload(self):
+ if ConfigVariableBool('fake-downloads', 0).getValue():
+ duration = ConfigVariableDouble('fake-download-duration', 60).getValue()
+ self.fakeDownload(duration)
+ else:
+ for phase in self.LauncherPhases:
+ self.phaseComplete[phase] = 100
+
+ self.downloadDoneTask(None)
+ return
+
+ def isTestServer(self):
+ return base.config.GetBool('is-test-server', 0)
+
+ def setPhaseCompleteArray(self, newPhaseComplete):
+ self.phaseComplete = newPhaseComplete
+
+ def setPhaseComplete(self, phase, percent):
+ self.phaseComplete[phase] = percent
+
+ def getPhaseComplete(self, phase):
+ return self.phaseComplete[phase] >= 100
+
+ def setPandaWindowOpen(self):
+ self.windowOpen = 1
+
+ def setPandaErrorCode(self, code):
+ self.pandaErrorCode = code
+
+ def getPandaErrorCode(self):
+ return self.pandaErrorCode
+
+ def setDisconnectDetailsNormal(self):
+ self.disconnectCode = 0
+ self.disconnectMsg = 'normal'
+
+ def setDisconnectDetails(self, newCode, newMsg):
+ self.disconnectCode = newCode
+ self.disconnectMsg = newMsg
+
+ def setServerVersion(self, version):
+ self.ServerVersion = version
+
+ def getServerVersion(self):
+ return self.ServerVersion
+
+ def getIsNewInstallation(self):
+ return base.config.GetBool('new-installation', 0)
+
+ def setIsNotNewInstallation(self):
+ pass
+
+ def getLastLogin(self):
+ if hasattr(self, 'lastLogin'):
+ return self.lastLogin
+ return ''
+
+ def setLastLogin(self, login):
+ self.lastLogin = login
+
+ def setUserLoggedIn(self):
+ self.userLoggedIn = 1
+
+ def setPaidUserLoggedIn(self):
+ self.paidUserLoggedIn = 1
+
+ def getGameServer(self):
+ return '206.16.11.19'
+
+ def getAccountServer(self):
+ return ''
+
+ def getDeployment(self):
+ return 'US'
+
+ def getBlue(self):
+ return None
+
+ def getPlayToken(self):
+ return None
+
+ def getDISLToken(self):
+ return None
+
+ def fakeDownloadPhaseTask(self, task):
+ percentComplete = min(100, int(round(task.time / float(task.timePerPhase) * 100)))
+ self.setPhaseComplete(task.phase, percentComplete)
+ messenger.send('launcherPercentPhaseComplete', [task.phase,
+ percentComplete,
+ 0,
+ 0])
+ if percentComplete >= 100.0:
+ messenger.send('phaseComplete-' + `(task.phase)`)
+ return Task.done
+ else:
+ return Task.cont
+
+ def downloadDoneTask(self, task):
+ self._downloadComplete = True
+ messenger.send('launcherAllPhasesComplete')
+ return Task.done
+
+ def fakeDownload(self, timePerPhase):
+ self.phaseComplete = {1: 100,
+ 2: 100,
+ 3: 0,
+ 3.5: 0,
+ 4: 0,
+ 5: 0,
+ 5.5: 0,
+ 6: 0,
+ 7: 0,
+ 8: 0,
+ 9: 0,
+ 10: 0,
+ 11: 0,
+ 12: 0,
+ 13: 0}
+ phaseTaskList = []
+ firstPhaseIndex = self.LauncherPhases.index(self.firstPhase)
+ for phase in self.LauncherPhases[firstPhaseIndex:]:
+ phaseTask = Task(self.fakeDownloadPhaseTask, 'phaseDownload' + str(phase))
+ phaseTask.timePerPhase = timePerPhase
+ phaseTask.phase = phase
+ phaseTaskList.append(phaseTask)
+
+ phaseTaskList.append(Task(self.downloadDoneTask))
+ downloadSequence = Task.sequence(*phaseTaskList)
+ taskMgr.remove('downloadSequence')
+ taskMgr.add(downloadSequence, 'downloadSequence')
diff --git a/otp/launcher/LauncherBase.py b/otp/launcher/LauncherBase.py
new file mode 100644
index 0000000..1d12bae
--- /dev/null
+++ b/otp/launcher/LauncherBase.py
@@ -0,0 +1,1877 @@
+import sys
+import os
+import time
+import string
+import __builtin__
+from pandac.libpandaexpressModules import *
+from direct.showbase.MessengerGlobal import *
+from direct.showbase.DirectObject import DirectObject
+from direct.showbase.EventManagerGlobal import *
+from direct.task.MiniTask import MiniTask, MiniTaskManager
+from direct.directnotify.DirectNotifyGlobal import *
+
+class LogAndOutput:
+
+ def __init__(self, orig, log):
+ self.orig = orig
+ self.log = log
+ self.console = False
+
+ def write(self, str):
+ self.log.write(str)
+ self.log.flush()
+ if self.console:
+ self.orig.write(str)
+ self.orig.flush()
+
+ def flush(self):
+ self.log.flush()
+ self.orig.flush()
+
+
+class LauncherBase(DirectObject):
+ GameName = 'game'
+ ArgCount = 6
+ LauncherPhases = [1,
+ 2,
+ 3,
+ 4]
+ TmpOverallMap = [0.25,
+ 0.25,
+ 0.25,
+ 0.25]
+ BANDWIDTH_ARRAY = [1800,
+ 3600,
+ 4200,
+ 6600,
+ 8000,
+ 12000,
+ 16000,
+ 24000,
+ 32000,
+ 48000,
+ 72000,
+ 96000,
+ 128000,
+ 192000,
+ 250000,
+ 500000,
+ 750000,
+ 1000000,
+ 1250000,
+ 1500000,
+ 1750000,
+ 2000000,
+ 3000000,
+ 4000000,
+ 6000000,
+ 8000000,
+ 10000000,
+ 12000000,
+ 14000000,
+ 16000000,
+ 24000000,
+ 32000000,
+ 48000000,
+ 64000000,
+ 96000000,
+ 128000000,
+ 256000000,
+ 512000000,
+ 1024000000]
+ win32con_FILE_PERSISTENT_ACLS = 8
+ InstallDirKey = 'INSTALL_DIR'
+ GameLogFilenameKey = 'GAMELOG_FILENAME'
+ PandaWindowOpenKey = 'PANDA_WINDOW_OPEN'
+ PandaErrorCodeKey = 'PANDA_ERROR_CODE'
+ NewInstallationKey = 'IS_NEW_INSTALLATION'
+ LastLoginKey = 'LAST_LOGIN'
+ UserLoggedInKey = 'USER_LOGGED_IN'
+ PaidUserLoggedInKey = 'PAID_USER_LOGGED_IN'
+ ReferrerKey = 'REFERRER_CODE'
+ PeriodTimeRemainingKey = 'PERIOD_TIME_REMAINING'
+ PeriodNameKey = 'PERIOD_NAME'
+ SwidKey = 'SWID'
+ PatchCDKey = 'FROM_CD'
+ DISLTokenKey = 'DISLTOKEN'
+ ProxyServerKey = 'PROXY_SERVER'
+ ProxyDirectHostsKey = 'PROXY_DIRECT_HOSTS'
+ launcherFileDbFilename = 'launcherFileDb'
+ webLauncherFlag = False
+
+ def __init__(self):
+ self.started = False
+ self.taskMgrStarted = False
+ self._downloadComplete = False
+ self.pandaErrorCode = 0
+ self.WIN32 = os.name == 'nt'
+ if self.WIN32:
+ self.VISTA = sys.getwindowsversion()[3] == 2 and sys.getwindowsversion()[0] == 6
+ else:
+ self.VISTA = 0
+ ltime = time.localtime()
+ logSuffix = '%02d%02d%02d_%02d%02d%02d' % (ltime[0] - 2000,
+ ltime[1],
+ ltime[2],
+ ltime[3],
+ ltime[4],
+ ltime[5])
+ logPrefix = ''
+ if not self.WIN32:
+ logPrefix = os.environ.get('LOGFILE_PREFIX', '')
+ logfile = logPrefix + self.getLogFileName() + '-' + logSuffix + '.log'
+ self.errorfile = 'errorCode'
+ log = open(logfile, 'a')
+ logOut = LogAndOutput(sys.__stdout__, log)
+ logErr = LogAndOutput(sys.__stderr__, log)
+ sys.stdout = logOut
+ sys.stderr = logErr
+ if sys.platform == 'darwin':
+ os.system('/usr/sbin/system_profiler >>' + logfile)
+ elif sys.platform == 'linux2':
+ os.system('cat /proc/cpuinfo >>' + logfile)
+ os.system('cat /proc/meminfo >>' + logfile)
+ os.system('/sbin/ifconfig -a >>' + logfile)
+ print '\n\nStarting %s...' % self.GameName
+ print 'Current time: ' + time.asctime(time.localtime(time.time())) + ' ' + time.tzname[0]
+ print 'sys.path = ', sys.path
+ print 'sys.argv = ', sys.argv
+ if len(sys.argv) >= self.ArgCount:
+ Configrc_args = sys.argv[self.ArgCount - 1]
+ print "generating configrc using: '" + Configrc_args + "'"
+ else:
+ Configrc_args = ''
+ print 'generating standard configrc'
+ if os.environ.has_key('PRC_EXECUTABLE_ARGS'):
+ print 'PRC_EXECUTABLE_ARGS is set to: ' + os.environ['PRC_EXECUTABLE_ARGS']
+ print 'Resetting PRC_EXECUTABLE_ARGS'
+ ExecutionEnvironment.setEnvironmentVariable('PRC_EXECUTABLE_ARGS', '-stdout ' + Configrc_args)
+ if os.environ.has_key('CONFIG_CONFIG'):
+ print 'CONFIG_CONFIG is set to: ' + os.environ['CONFIG_CONFIG']
+ print 'Resetting CONFIG_CONFIG'
+ os.environ['CONFIG_CONFIG'] = ':_:configdir_.:configpath_:configname_Configrc.exe:configexe_1:configargs_-stdout ' + Configrc_args
+ cpMgr = ConfigPageManager.getGlobalPtr()
+ cpMgr.reloadImplicitPages()
+ launcherConfig = getConfigExpress()
+ __builtin__.config = launcherConfig
+ if config.GetBool('log-private-info', 0):
+ print 'os.environ = ', os.environ
+ elif '__COMPAT_LAYER' in os.environ:
+ print '__COMPAT_LAYER = %s' % (os.environ['__COMPAT_LAYER'],)
+ self.miniTaskMgr = MiniTaskManager()
+ self.VerifyFiles = self.getVerifyFiles()
+ self.setServerVersion(launcherConfig.GetString('server-version', 'no_version_set'))
+ self.ServerVersionSuffix = launcherConfig.GetString('server-version-suffix', '')
+ self.UserUpdateDelay = launcherConfig.GetFloat('launcher-user-update-delay', 0.5)
+ self.TELEMETRY_BANDWIDTH = launcherConfig.GetInt('launcher-telemetry-bandwidth', 2000)
+ self.INCREASE_THRESHOLD = launcherConfig.GetFloat('launcher-increase-threshold', 0.75)
+ self.DECREASE_THRESHOLD = launcherConfig.GetFloat('launcher-decrease-threshold', 0.5)
+ self.BPS_WINDOW = launcherConfig.GetFloat('launcher-bps-window', 8.0)
+ self.DECREASE_BANDWIDTH = launcherConfig.GetBool('launcher-decrease-bandwidth', 1)
+ self.MAX_BANDWIDTH = launcherConfig.GetInt('launcher-max-bandwidth', 0)
+ self.nout = MultiplexStream()
+ Notify.ptr().setOstreamPtr(self.nout, 0)
+ self.nout.addFile(Filename(logfile))
+ if launcherConfig.GetBool('console-output', 0):
+ self.nout.addStandardOutput()
+ sys.stdout.console = True
+ sys.stderr.console = True
+ self.notify = directNotify.newCategory('Launcher')
+ self.clock = TrueClock.getGlobalPtr()
+ self.logPrefix = logPrefix
+ self.testServerFlag = self.getTestServerFlag()
+ self.notify.info('isTestServer: %s' % self.testServerFlag)
+ downloadServerString = launcherConfig.GetString('download-server', '')
+ if downloadServerString:
+ self.notify.info('Overriding downloadServer to %s.' % downloadServerString)
+ else:
+ downloadServerString = self.getValue('DOWNLOAD_SERVER', '')
+ self.notify.info('Download Server List %s' % downloadServerString)
+ self.downloadServerList = []
+ for name in string.split(downloadServerString, ';'):
+ url = URLSpec(name, 1)
+ self.downloadServerList.append(url)
+
+ self.nextDownloadServerIndex = 0
+ self.getNextDownloadServer()
+ self.gameServer = self.getGameServer()
+ self.notify.info('Game Server %s' % self.gameServer)
+ self.downloadServerRetries = 3
+ self.multifileRetries = 1
+ self.curMultifileRetry = 0
+ self.downloadServerRetryPause = 1
+ self.bandwidthIndex = len(self.BANDWIDTH_ARRAY) - 1
+ self.everIncreasedBandwidth = 0
+ self.goUserName = ''
+ self.downloadPercentage = 90
+ self.decompressPercentage = 5
+ self.extractPercentage = 4
+ self.lastLauncherMsg = None
+ self.topDir = Filename.fromOsSpecific(self.getValue(self.InstallDirKey, '.'))
+ self.setRegistry(self.GameLogFilenameKey, logfile)
+ tmpVal = self.getValue(self.PatchCDKey)
+ if tmpVal == None:
+ self.fromCD = 0
+ else:
+ self.fromCD = tmpVal
+ self.notify.info('patch directory is ' + `(self.fromCD)`)
+ self.dbDir = self.topDir
+ self.patchDir = self.topDir
+ self.mfDir = self.topDir
+ self.contentDir = 'content/'
+ self.clientDbFilename = 'client.ddb'
+ self.compClientDbFilename = self.clientDbFilename + '.pz'
+ self.serverDbFilename = 'server.ddb'
+ self.compServerDbFilename = self.serverDbFilename + '.pz'
+ self.serverDbFilePath = self.contentDir + self.compServerDbFilename
+ self.clientStarterDbFilePath = self.contentDir + self.compClientDbFilename
+ self.progressFilename = 'progress'
+ self.overallComplete = 0
+ self.progressSoFar = 0
+ self.patchExtension = 'pch'
+ self.scanForHacks()
+ self.firstPhase = self.LauncherPhases[0]
+ self.finalPhase = self.LauncherPhases[-1]
+ self.showPhase = 3.5
+ self.numPhases = len(self.LauncherPhases)
+ self.phaseComplete = {}
+ self.phaseNewDownload = {}
+ self.phaseOverallMap = {}
+ tmpOverallMap = self.TmpOverallMap
+ tmpPhase3Map = [0.001,
+ 0.996,
+ 0.0,
+ 0.0,
+ 0.003]
+ phaseIdx = 0
+ for phase in self.LauncherPhases:
+ percentPhaseCompleteKey = 'PERCENT_PHASE_COMPLETE_' + `phase`
+ self.setRegistry(percentPhaseCompleteKey, 0)
+ self.phaseComplete[phase] = 0
+ self.phaseNewDownload[phase] = 0
+ self.phaseOverallMap[phase] = tmpOverallMap[phaseIdx]
+ phaseIdx += 1
+
+ self.patchList = []
+ self.reextractList = []
+ self.byteRate = 0
+ self.byteRateRequested = 0
+ self.resetBytesPerSecond()
+ self.dldb = None
+ self.currentMfname = None
+ self.currentPhaseIndex = 0
+ self.currentPhase = self.LauncherPhases[self.currentPhaseIndex]
+ self.currentPhaseName = self.Localizer.LauncherPhaseNames[self.currentPhaseIndex]
+ if self.getServerVersion() == 'no_version_set':
+ self.setPandaErrorCode(10)
+ self.notify.info('Aborting, Configrc did not run!')
+ sys.exit()
+ self.launcherMessage(self.Localizer.LauncherStartingMessage)
+ self.http = HTTPClient()
+ if self.http.getProxySpec() == '':
+ self.http.setProxySpec(self.getValue(self.ProxyServerKey, ''))
+ self.http.setDirectHostSpec(self.getValue(self.ProxyDirectHostsKey, ''))
+ self.notify.info('Proxy spec is: %s' % self.http.getProxySpec())
+ if self.http.getDirectHostSpec() != '':
+ self.notify.info('Direct hosts list is: %s' % self.http.getDirectHostSpec())
+ self.httpChannel = self.http.makeChannel(0)
+ self.httpChannel.setDownloadThrottle(1)
+ connOk = 0
+ while not connOk:
+ proxies = self.http.getProxiesForUrl(self.downloadServer)
+ if proxies == 'DIRECT':
+ self.notify.info('No proxy for download.')
+ else:
+ self.notify.info('Download proxy: %s' % proxies)
+ testurl = self.addDownloadVersion(self.launcherFileDbFilename)
+ connOk = self.httpChannel.getHeader(DocumentSpec(testurl))
+ statusCode = self.httpChannel.getStatusCode()
+ statusString = self.httpChannel.getStatusString()
+ if not connOk:
+ self.notify.warning('Could not contact download server at %s' % testurl.cStr())
+ self.notify.warning('Status code = %s %s' % (statusCode, statusString))
+ if statusCode == 407 or statusCode == 1407 or statusCode == HTTPChannel.SCSocksNoAcceptableLoginMethod:
+ self.setPandaErrorCode(3)
+ elif statusCode == 404:
+ self.setPandaErrorCode(13)
+ elif statusCode < 100:
+ self.setPandaErrorCode(4)
+ elif statusCode > 1000:
+ self.setPandaErrorCode(9)
+ else:
+ self.setPandaErrorCode(6)
+ if not self.getNextDownloadServer():
+ sys.exit()
+
+ self.notify.info('Download server: %s' % self.downloadServer.cStr())
+ if self.notify.getDebug():
+ self.accept('page_up', self.increaseBandwidth)
+ self.accept('page_down', self.decreaseBandwidth)
+ self.httpChannel.setPersistentConnection(1)
+ self.foreground()
+ self.prepareClient()
+ self.setBandwidth()
+ self.downloadLauncherFileDb()
+ return
+
+ def getTime(self):
+ return self.clock.getShortTime()
+
+ def isDummy(self):
+ return 0
+
+ def getNextDownloadServer(self):
+ if self.nextDownloadServerIndex >= len(self.downloadServerList):
+ self.downloadServer = None
+ return 0
+ self.downloadServer = self.downloadServerList[self.nextDownloadServerIndex]
+ self.notify.info('Using download server %s.' % self.downloadServer.cStr())
+ self.nextDownloadServerIndex += 1
+ return 1
+
+ def getProductName(self):
+ config = getConfigExpress()
+ productName = config.GetString('product-name', '')
+ if productName and productName != 'DisneyOnline-US':
+ productName = '_%s' % productName
+ else:
+ productName = ''
+ return productName
+
+ def background(self):
+ self.notify.info('background: Launcher now operating in background')
+ self.backgrounded = 1
+
+ def foreground(self):
+ self.notify.info('foreground: Launcher now operating in foreground')
+ self.backgrounded = 0
+
+ def setRegistry(self, key, value):
+ self.notify.info('DEPRECATED setRegistry: %s = %s' % (key, value))
+
+ def getRegistry(self, key):
+ self.notify.info('DEPRECATED getRegistry: %s' % key)
+ return None
+
+ def handleInitiateFatalError(self, errorCode):
+ self.notify.warning('handleInitiateFatalError: ' + errorToText(errorCode))
+ sys.exit()
+
+ def handleDecompressFatalError(self, task, errorCode):
+ self.notify.warning('handleDecompressFatalError: ' + errorToText(errorCode))
+ self.miniTaskMgr.remove(task)
+ self.handleGenericMultifileError()
+
+ def handleDecompressWriteError(self, task, errorCode):
+ self.notify.warning('handleDecompressWriteError: ' + errorToText(errorCode))
+ self.miniTaskMgr.remove(task)
+ self.handleGenericMultifileError()
+
+ def handleDecompressZlibError(self, task, errorCode):
+ self.notify.warning('handleDecompressZlibError: ' + errorToText(errorCode))
+ self.miniTaskMgr.remove(task)
+ self.handleGenericMultifileError()
+
+ def handleExtractFatalError(self, task, errorCode):
+ self.notify.warning('handleExtractFatalError: ' + errorToText(errorCode))
+ self.miniTaskMgr.remove(task)
+ self.handleGenericMultifileError()
+
+ def handleExtractWriteError(self, task, errorCode):
+ self.notify.warning('handleExtractWriteError: ' + errorToText(errorCode))
+ self.miniTaskMgr.remove(task)
+ self.handleGenericMultifileError()
+
+ def handlePatchFatalError(self, task, errorCode):
+ self.notify.warning('handlePatchFatalError: ' + errorToText(errorCode))
+ self.miniTaskMgr.remove(task)
+ self.handleGenericMultifileError()
+
+ def handlePatchWriteError(self, task, errorCode):
+ self.notify.warning('handlePatchWriteError: ' + errorToText(errorCode))
+ self.miniTaskMgr.remove(task)
+ self.handleGenericMultifileError()
+
+ def handleDownloadFatalError(self, task):
+ self.notify.warning('handleDownloadFatalError: status code = %s %s' % (self.httpChannel.getStatusCode(), self.httpChannel.getStatusString()))
+ self.miniTaskMgr.remove(task)
+ statusCode = self.httpChannel.getStatusCode()
+ if statusCode == 404:
+ self.setPandaErrorCode(5)
+ elif statusCode < 100:
+ self.setPandaErrorCode(4)
+ else:
+ self.setPandaErrorCode(6)
+ if not self.getNextDownloadServer():
+ sys.exit()
+
+ def handleDownloadWriteError(self, task):
+ self.notify.warning('handleDownloadWriteError.')
+ self.miniTaskMgr.remove(task)
+ self.setPandaErrorCode(2)
+ sys.exit()
+
+ def handleGenericMultifileError(self):
+ if not self.currentMfname:
+ sys.exit()
+ if self.curMultifileRetry < self.multifileRetries:
+ self.notify.info('recover attempt: %s / %s' % (self.curMultifileRetry, self.multifileRetries))
+ self.curMultifileRetry += 1
+ self.notify.info('downloadPatchDone: Recovering from error.' + ' Deleting files in: ' + self.currentMfname)
+ self.dldb.setClientMultifileIncomplete(self.currentMfname)
+ self.dldb.setClientMultifileSize(self.currentMfname, 0)
+ self.notify.info('downloadPatchDone: Recovering from error.' + ' redownloading: ' + self.currentMfname)
+ self.httpChannel.reset()
+ self.getMultifile(self.currentMfname)
+ else:
+ self.setPandaErrorCode(6)
+ self.notify.info('handleGenericMultifileError: Failed to download multifile')
+ sys.exit()
+
+ def foregroundSleep(self):
+ if not self.backgrounded:
+ time.sleep(self.ForegroundSleepTime)
+
+ def forceSleep(self):
+ if not self.backgrounded:
+ time.sleep(3.0)
+
+ def addDownloadVersion(self, serverFilePath):
+ url = URLSpec(self.downloadServer)
+ origPath = url.getPath()
+ if origPath and origPath[-1] == '/':
+ origPath = origPath[:-1]
+ if self.fromCD:
+ url.setPath(self.getCDDownloadPath(origPath, serverFilePath))
+ else:
+ url.setPath(self.getDownloadPath(origPath, serverFilePath))
+ self.notify.info('***' + url.cStr())
+ return url
+
+ def download(self, serverFilePath, localFilename, callback, callbackProgress):
+ self.launcherMessage(self.Localizer.LauncherDownloadFile % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases})
+ task = MiniTask(self.downloadTask)
+ task.downloadRam = 0
+ task.serverFilePath = serverFilePath
+ task.serverFileURL = self.addDownloadVersion(serverFilePath)
+ self.notify.info('Download request: %s' % task.serverFileURL.cStr())
+ task.callback = callback
+ task.callbackProgress = callbackProgress
+ task.lastUpdate = 0
+ self.resetBytesPerSecond()
+ task.localFilename = localFilename
+ self.httpChannel.beginGetDocument(DocumentSpec(task.serverFileURL))
+ self.httpChannel.downloadToFile(task.localFilename)
+ self.miniTaskMgr.add(task, 'launcher-download')
+
+ def downloadRam(self, serverFilePath, callback):
+ self.ramfile = Ramfile()
+ task = MiniTask(self.downloadTask)
+ task.downloadRam = 1
+ task.serverFilePath = serverFilePath
+ task.serverFileURL = self.addDownloadVersion(serverFilePath)
+ self.notify.info('Download request: %s' % task.serverFileURL.cStr())
+ task.callback = callback
+ task.callbackProgress = None
+ task.lastUpdate = 0
+ self.resetBytesPerSecond()
+ self.httpChannel.beginGetDocument(DocumentSpec(task.serverFileURL))
+ self.httpChannel.downloadToRam(self.ramfile)
+ self.miniTaskMgr.add(task, 'launcher-download')
+ return
+
+ def downloadTask(self, task):
+ self.maybeStartGame()
+ if self.httpChannel.run():
+ now = self.getTime()
+ if now - task.lastUpdate >= self.UserUpdateDelay:
+ task.lastUpdate = now
+ self.testBandwidth()
+ if task.callbackProgress:
+ task.callbackProgress(task)
+ bytesWritten = self.httpChannel.getBytesDownloaded()
+ totalBytes = self.httpChannel.getFileSize()
+ if totalBytes:
+ pct = int(round(bytesWritten / float(totalBytes) * 100))
+ self.launcherMessage(self.Localizer.LauncherDownloadFilePercent % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'percent': pct})
+ else:
+ self.launcherMessage(self.Localizer.LauncherDownloadFileBytes % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'bytes': bytesWritten})
+ self.foregroundSleep()
+ return task.cont
+ statusCode = self.httpChannel.getStatusCode()
+ statusString = self.httpChannel.getStatusString()
+ self.notify.info('HTTP status %s: %s' % (statusCode, statusString))
+ if self.httpChannel.isValid() and self.httpChannel.isDownloadComplete():
+ bytesWritten = self.httpChannel.getBytesDownloaded()
+ totalBytes = self.httpChannel.getFileSize()
+ if totalBytes:
+ pct = int(round(bytesWritten / float(totalBytes) * 100))
+ self.launcherMessage(self.Localizer.LauncherDownloadFilePercent % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'percent': pct})
+ else:
+ self.launcherMessage(self.Localizer.LauncherDownloadFileBytes % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'bytes': bytesWritten})
+ self.notify.info('downloadTask: Download done: %s' % task.serverFileURL.cStr())
+ task.callback()
+ del task.callback
+ return task.done
+ else:
+ if statusCode == HTTPChannel.SCDownloadOpenError or statusCode == HTTPChannel.SCDownloadWriteError:
+ self.handleDownloadWriteError(task)
+ elif statusCode == HTTPChannel.SCLostConnection:
+ gotBytes = self.httpChannel.getBytesDownloaded()
+ self.notify.info('Connection lost while downloading; got %s bytes. Reconnecting.' % gotBytes)
+ if task.downloadRam:
+ self.downloadRam(task.serverFilePath, task.callback)
+ else:
+ self.download(task.serverFilePath, task.localFilename, task.callback, None)
+ else:
+ if self.httpChannel.isValid():
+ self.notify.info('Unexpected situation: no error status, but %s incompletely downloaded.' % task.serverFileURL.cStr())
+ self.handleDownloadFatalError(task)
+ if task.downloadRam:
+ self.downloadRam(task.serverFilePath, task.callback)
+ else:
+ self.download(task.serverFilePath, task.localFilename, task.callback, None)
+ return task.done
+ return
+
+ def downloadMultifile(self, serverFilename, localFilename, mfname, callback, totalSize, currentSize, callbackProgress):
+ if currentSize != 0 and currentSize == totalSize:
+ callback()
+ return
+ self.launcherMessage(self.Localizer.LauncherDownloadFile % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases})
+ task = MiniTask(self.downloadMultifileTask)
+ mfURL = self.addDownloadVersion(serverFilename)
+ task.mfURL = mfURL
+ self.notify.info('downloadMultifile: %s ' % task.mfURL.cStr())
+ task.callback = callback
+ task.callbackProgress = callbackProgress
+ task.lastUpdate = 0
+ self.httpChannel.getHeader(DocumentSpec(task.mfURL))
+ if self.httpChannel.isFileSizeKnown():
+ task.totalSize = self.httpChannel.getFileSize()
+ else:
+ task.totalSize = totalSize
+ self.resetBytesPerSecond()
+ task.serverFilename = serverFilename
+ task.localFilename = localFilename
+ task.mfname = mfname
+ if currentSize != 0:
+ if task.totalSize == currentSize:
+ self.notify.info('already have full file! Skipping download.')
+ callback()
+ return
+ self.httpChannel.beginGetSubdocument(DocumentSpec(task.mfURL), currentSize, task.totalSize)
+ self.httpChannel.downloadToFile(task.localFilename, True)
+ else:
+ self.httpChannel.beginGetDocument(DocumentSpec(task.mfURL))
+ self.httpChannel.downloadToFile(task.localFilename)
+ self._addMiniTask(task, 'launcher-download-multifile')
+
+ def downloadPatchSimpleProgress(self, task):
+ startingByte = self.httpChannel.getFirstByteDelivered()
+ bytesDownloaded = self.httpChannel.getBytesDownloaded()
+ bytesWritten = startingByte + bytesDownloaded
+ totalBytes = self.httpChannel.getFileSize()
+ percentPatchComplete = int(round(bytesWritten / float(totalBytes) * self.downloadPercentage))
+ self.setPercentPhaseComplete(self.currentPhase, percentPatchComplete)
+
+ def getPercentPatchComplete(self, bytesWritten):
+ return int(round((self.patchDownloadSoFar + bytesWritten) / float(self.totalPatchDownload) * self.downloadPercentage))
+
+ def downloadPatchOverallProgress(self, task):
+ startingByte = self.httpChannel.getFirstByteDelivered()
+ bytesDownloaded = self.httpChannel.getBytesDownloaded()
+ bytesWritten = startingByte + bytesDownloaded
+ percentPatchComplete = self.getPercentPatchComplete(bytesWritten)
+ self.setPercentPhaseComplete(self.currentPhase, percentPatchComplete)
+
+ def downloadMultifileWriteToDisk(self, task):
+ self.maybeStartGame()
+ startingByte = self.httpChannel.getFirstByteDelivered()
+ bytesDownloaded = self.httpChannel.getBytesDownloaded()
+ bytesWritten = startingByte + bytesDownloaded
+ if self.dldb:
+ self.dldb.setClientMultifileSize(task.mfname, bytesWritten)
+ percentComplete = 0
+ if task.totalSize != 0:
+ percentComplete = int(round(bytesWritten / float(task.totalSize) * self.downloadPercentage))
+ self.setPercentPhaseComplete(self.currentPhase, percentComplete)
+
+ def downloadMultifileTask(self, task):
+ task.totalSize = self.httpChannel.getFileSize()
+ if self.httpChannel.run():
+ now = self.getTime()
+ if now - task.lastUpdate >= self.UserUpdateDelay:
+ task.lastUpdate = now
+ self.testBandwidth()
+ if task.callbackProgress:
+ task.callbackProgress(task)
+ startingByte = self.httpChannel.getFirstByteDelivered()
+ bytesDownloaded = self.httpChannel.getBytesDownloaded()
+ bytesWritten = startingByte + bytesDownloaded
+ percentComplete = 0
+ if task.totalSize != 0:
+ percentComplete = int(round(100.0 * bytesWritten / float(task.totalSize)))
+ self.launcherMessage(self.Localizer.LauncherDownloadFilePercent % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'percent': percentComplete})
+ self.foregroundSleep()
+ return task.cont
+ statusCode = self.httpChannel.getStatusCode()
+ statusString = self.httpChannel.getStatusString()
+ self.notify.info('HTTP status %s: %s' % (statusCode, statusString))
+ if self.httpChannel.isValid() and self.httpChannel.isDownloadComplete():
+ if task.callbackProgress:
+ task.callbackProgress(task)
+ self.notify.info('done: %s' % task.mfname)
+ if self.dldb:
+ self.dldb.setClientMultifileComplete(task.mfname)
+ task.callback()
+ del task.callback
+ return task.done
+ else:
+ if statusCode == HTTPChannel.SCDownloadOpenError or statusCode == HTTPChannel.SCDownloadWriteError:
+ self.handleDownloadWriteError(task)
+ elif statusCode == HTTPChannel.SCLostConnection:
+ startingByte = self.httpChannel.getFirstByteDelivered()
+ bytesDownloaded = self.httpChannel.getBytesDownloaded()
+ bytesWritten = startingByte + bytesDownloaded
+ self.notify.info('Connection lost while downloading; got %s bytes. Reconnecting.' % bytesDownloaded)
+ self.downloadMultifile(task.serverFilename, task.localFilename, task.mfname, task.callback, task.totalSize, bytesWritten, task.callbackProgress)
+ elif (statusCode == 416 or statusCode == HTTPChannel.SCDownloadInvalidRange) and self.httpChannel.getFirstByteRequested() != 0:
+ self.notify.info('Invalid subrange; redownloading entire file.')
+ self.downloadMultifile(task.serverFilename, task.localFilename, task.mfname, task.callback, task.totalSize, 0, task.callbackProgress)
+ else:
+ if self.httpChannel.isValid():
+ self.notify.info('Unexpected situation: no error status, but %s incompletely downloaded.' % task.mfname)
+ self.handleDownloadFatalError(task)
+ self.downloadMultifile(task.serverFilename, task.localFilename, task.mfname, task.callback, task.totalSize, 0, task.callbackProgress)
+ return task.done
+
+ def decompressFile(self, localFilename, callback):
+ self.notify.info('decompress: request: ' + localFilename.cStr())
+ self.launcherMessage(self.Localizer.LauncherDecompressingFile % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases})
+ task = MiniTask(self.decompressFileTask)
+ task.localFilename = localFilename
+ task.callback = callback
+ task.lastUpdate = 0
+ task.decompressor = Decompressor()
+ errorCode = task.decompressor.initiate(task.localFilename)
+ if errorCode > 0:
+ self._addMiniTask(task, 'launcher-decompressFile')
+ else:
+ self.handleInitiateFatalError(errorCode)
+
+ def decompressFileTask(self, task):
+ errorCode = task.decompressor.run()
+ if errorCode == EUOk:
+ now = self.getTime()
+ if now - task.lastUpdate >= self.UserUpdateDelay:
+ task.lastUpdate = now
+ progress = task.decompressor.getProgress()
+ self.launcherMessage(self.Localizer.LauncherDecompressingPercent % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'percent': int(round(progress * 100))})
+ self.foregroundSleep()
+ return task.cont
+ elif errorCode == EUSuccess:
+ self.launcherMessage(self.Localizer.LauncherDecompressingPercent % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'percent': 100})
+ self.notify.info('decompressTask: Decompress done: ' + task.localFilename.cStr())
+ del task.decompressor
+ task.callback()
+ del task.callback
+ return task.done
+ elif errorCode == EUErrorAbort:
+ self.handleDecompressFatalError(task, errorCode)
+ return task.done
+ elif errorCode == EUErrorWriteOutOfFiles or errorCode == EUErrorWriteDiskFull or errorCode == EUErrorWriteDiskSectorNotFound or errorCode == EUErrorWriteOutOfMemory or errorCode == EUErrorWriteSharingViolation or errorCode == EUErrorWriteDiskFault or errorCode == EUErrorWriteDiskNotFound:
+ self.handleDecompressWriteError(task, errorCode)
+ return task.done
+ elif errorCode == EUErrorZlib:
+ self.handleDecompressZlibError(task, errorCode)
+ return task.done
+ elif errorCode > 0:
+ self.notify.warning('decompressMultifileTask: Unknown success return code: ' + errorToText(errorCode))
+ return task.cont
+ else:
+ self.notify.warning('decompressMultifileTask: Unknown return code: ' + errorToText(errorCode))
+ self.handleDecompressFatalError(task, errorCode)
+ return task.done
+
+ def decompressMultifile(self, mfname, localFilename, callback):
+ self.notify.info('decompressMultifile: request: ' + localFilename.cStr())
+ self.launcherMessage(self.Localizer.LauncherDecompressingFile % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases})
+ task = MiniTask(self.decompressMultifileTask)
+ task.mfname = mfname
+ task.localFilename = localFilename
+ task.callback = callback
+ task.lastUpdate = 0
+ task.decompressor = Decompressor()
+ errorCode = task.decompressor.initiate(task.localFilename)
+ if errorCode > 0:
+ self._addMiniTask(task, 'launcher-decompressMultifile')
+ else:
+ self.handleInitiateFatalError(errorCode)
+
+ def decompressMultifileTask(self, task):
+ errorCode = task.decompressor.run()
+ if errorCode == EUOk:
+ now = self.getTime()
+ if now - task.lastUpdate >= self.UserUpdateDelay:
+ task.lastUpdate = now
+ progress = task.decompressor.getProgress()
+ self.launcherMessage(self.Localizer.LauncherDecompressingPercent % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'percent': int(round(progress * 100))})
+ percentProgress = int(round(progress * self.decompressPercentage))
+ totalPercent = self.downloadPercentage + percentProgress
+ self.setPercentPhaseComplete(self.currentPhase, totalPercent)
+ self.foregroundSleep()
+ return task.cont
+ elif errorCode == EUSuccess:
+ self.launcherMessage(self.Localizer.LauncherDecompressingPercent % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'percent': 100})
+ totalPercent = self.downloadPercentage + self.decompressPercentage
+ self.setPercentPhaseComplete(self.currentPhase, totalPercent)
+ self.notify.info('decompressMultifileTask: Decompress multifile done: ' + task.localFilename.cStr())
+ self.dldb.setClientMultifileDecompressed(task.mfname)
+ del task.decompressor
+ task.callback()
+ del task.callback
+ return task.done
+ elif errorCode == EUErrorAbort:
+ self.handleDecompressFatalError(task, errorCode)
+ return task.done
+ elif errorCode == EUErrorWriteOutOfFiles or errorCode == EUErrorWriteDiskFull or errorCode == EUErrorWriteDiskSectorNotFound or errorCode == EUErrorWriteOutOfMemory or errorCode == EUErrorWriteSharingViolation or errorCode == EUErrorWriteDiskFault or errorCode == EUErrorWriteDiskNotFound:
+ self.handleDecompressWriteError(task, errorCode)
+ return task.done
+ elif errorCode == EUErrorZlib:
+ self.handleDecompressZlibError(task, errorCode)
+ return task.done
+ elif errorCode > 0:
+ self.notify.warning('decompressMultifileTask: Unknown success return code: ' + errorToText(errorCode))
+ return task.cont
+ else:
+ self.notify.warning('decompressMultifileTask: Unknown return code: ' + errorToText(errorCode))
+ self.handleDecompressFatalError(task, errorCode)
+ return task.done
+
+ def extract(self, mfname, localFilename, destDir, callback):
+ self.notify.info('extract: request: ' + localFilename.cStr() + ' destDir: ' + destDir.cStr())
+ self.launcherMessage(self.Localizer.LauncherExtractingFile % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases})
+ task = MiniTask(self.extractTask)
+ task.mfname = mfname
+ task.localFilename = localFilename
+ task.destDir = destDir
+ task.callback = callback
+ task.lastUpdate = 0
+ task.extractor = Extractor()
+ task.extractor.setExtractDir(task.destDir)
+ if not task.extractor.setMultifile(task.localFilename):
+ self.setPandaErrorCode(6)
+ self.notify.info('extract: Unable to open multifile %s' % task.localFilename.cStr())
+ sys.exit()
+ numFiles = self.dldb.getServerNumFiles(mfname)
+ for i in range(numFiles):
+ subfile = self.dldb.getServerFileName(mfname, i)
+ if not task.extractor.requestSubfile(Filename(subfile)):
+ self.setPandaErrorCode(6)
+ self.notify.info('extract: Unable to find subfile %s in multifile %s' % (subfile, mfname))
+ sys.exit()
+
+ self.notify.info('Extracting %d subfiles from multifile %s.' % (numFiles, mfname))
+ self._addMiniTask(task, 'launcher-extract')
+
+ def extractTask(self, task):
+ errorCode = task.extractor.step()
+ if errorCode == EUOk:
+ now = self.getTime()
+ if now - task.lastUpdate >= self.UserUpdateDelay:
+ task.lastUpdate = now
+ progress = task.extractor.getProgress()
+ self.launcherMessage(self.Localizer.LauncherExtractingPercent % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'percent': int(round(progress * 100.0))})
+ percentProgress = int(round(progress * self.extractPercentage))
+ totalPercent = self.downloadPercentage + self.decompressPercentage + percentProgress
+ self.setPercentPhaseComplete(self.currentPhase, totalPercent)
+ self.foregroundSleep()
+ return task.cont
+ elif errorCode == EUSuccess:
+ self.launcherMessage(self.Localizer.LauncherExtractingPercent % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'percent': 100})
+ totalPercent = self.downloadPercentage + self.decompressPercentage + self.extractPercentage
+ self.setPercentPhaseComplete(self.currentPhase, totalPercent)
+ self.notify.info('extractTask: Extract multifile done: ' + task.localFilename.cStr())
+ self.dldb.setClientMultifileExtracted(task.mfname)
+ del task.extractor
+ task.callback()
+ del task.callback
+ return task.done
+ elif errorCode == EUErrorAbort:
+ self.handleExtractFatalError(task, errorCode)
+ return task.done
+ elif errorCode == EUErrorFileEmpty:
+ self.handleExtractFatalError(task, errorCode)
+ return task.done
+ elif errorCode == EUErrorWriteOutOfFiles or errorCode == EUErrorWriteDiskFull or errorCode == EUErrorWriteDiskSectorNotFound or errorCode == EUErrorWriteOutOfMemory or errorCode == EUErrorWriteSharingViolation or errorCode == EUErrorWriteDiskFault or errorCode == EUErrorWriteDiskNotFound:
+ self.handleExtractWriteError(task, errorCode)
+ return task.done
+ elif errorCode > 0:
+ self.notify.warning('extractTask: Unknown success return code: ' + errorToText(errorCode))
+ return task.cont
+ else:
+ self.notify.warning('extractTask: Unknown error return code: ' + errorToText(errorCode))
+ self.handleExtractFatalError(task, errorCode)
+ return task.done
+
+ def patch(self, patchFile, patcheeFile, callback):
+ self.notify.info('patch: request: ' + patchFile.cStr() + ' patchee: ' + patcheeFile.cStr())
+ self.launcherMessage(self.Localizer.LauncherPatchingFile % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases})
+ task = MiniTask(self.patchTask)
+ task.patchFile = patchFile
+ task.patcheeFile = patcheeFile
+ task.callback = callback
+ task.lastUpdate = 0
+ task.patcher = Patcher()
+ errorCode = task.patcher.initiate(task.patchFile, task.patcheeFile)
+ if errorCode > 0:
+ self._addMiniTask(task, 'launcher-patch')
+ else:
+ self.handleInitiateFatalError(errorCode)
+
+ def patchTask(self, task):
+ errorCode = task.patcher.run()
+ if errorCode == EUOk:
+ now = self.getTime()
+ if now - task.lastUpdate >= self.UserUpdateDelay:
+ task.lastUpdate = now
+ progress = task.patcher.getProgress()
+ self.launcherMessage(self.Localizer.LauncherPatchingPercent % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'percent': int(round(progress * 100.0))})
+ self.foregroundSleep()
+ return task.cont
+ elif errorCode == EUSuccess:
+ self.launcherMessage(self.Localizer.LauncherPatchingPercent % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases,
+ 'percent': 100})
+ self.notify.info('patchTask: Patch done: ' + task.patcheeFile.cStr())
+ del task.patcher
+ task.callback()
+ del task.callback
+ return task.done
+ elif errorCode == EUErrorAbort:
+ self.handlePatchFatalError(task, errorCode)
+ return task.done
+ elif errorCode == EUErrorFileEmpty:
+ self.handlePatchFatalError(task, errorCode)
+ return task.done
+ elif errorCode == EUErrorWriteOutOfFiles or errorCode == EUErrorWriteDiskFull or errorCode == EUErrorWriteDiskSectorNotFound or errorCode == EUErrorWriteOutOfMemory or errorCode == EUErrorWriteSharingViolation or errorCode == EUErrorWriteDiskFault or errorCode == EUErrorWriteDiskNotFound:
+ self.handlePatchWriteError(task, errorCode)
+ return task.done
+ elif errorCode > 0:
+ self.notify.warning('patchTask: Unknown success return code: ' + errorToText(errorCode))
+ return task.cont
+ else:
+ self.notify.warning('patchTask: Unknown error return code: ' + errorToText(errorCode))
+ self.handlePatchFatalError(task, errorCode)
+ return task.done
+
+ def getProgressSum(self, phase):
+ sum = 0
+ for i in xrange(0, len(self.linesInProgress)):
+ if self.linesInProgress[i].find(phase) > -1:
+ nameSizeTuple = self.linesInProgress[i].split()
+ numSize = nameSizeTuple[1].split('L')
+ sum += string.atoi(numSize[0])
+
+ return sum
+
+ def readProgressFile(self):
+ localFilename = Filename(self.dbDir, Filename(self.progressFilename))
+ if not localFilename.exists():
+ self.notify.warning('File does not exist: %s' % localFilename.cStr())
+ self.linesInProgress = []
+ else:
+ f = open(localFilename.toOsSpecific())
+ self.linesInProgress = f.readlines()
+ f.close()
+ localFilename.unlink()
+ self.progressSum = 0
+ token = 'phase_'
+ self.progressSum = self.getProgressSum(token)
+ self.progressSum -= self.getProgressSum(token + '2')
+ self.notify.info('total phases to be downloaded = ' + `(self.progressSum)`)
+ self.checkClientDbExists()
+
+ def prepareClient(self):
+ self.notify.info('prepareClient: Preparing client for install')
+ if not self.topDir.exists():
+ self.notify.info('prepareClient: Creating top directory: ' + self.topDir.cStr())
+ os.makedirs(self.topDir.toOsSpecific())
+ if not self.dbDir.exists():
+ self.notify.info('prepareClient: Creating db directory: ' + self.dbDir.cStr())
+ os.makedirs(self.dbDir.toOsSpecific())
+ if not self.patchDir.exists():
+ self.notify.info('prepareClient: Creating patch directory: ' + self.patchDir.cStr())
+ os.makedirs(self.patchDir.toOsSpecific())
+ if not self.mfDir.exists():
+ self.notify.info('prepareClient: Creating mf directory: ' + self.mfDir.cStr())
+ os.makedirs(self.mfDir.toOsSpecific())
+
+ def downloadLauncherFileDb(self):
+ self.notify.info('Downloading launcherFileDb')
+ self.downloadRam(self.launcherFileDbFilename, self.downloadLauncherFileDbDone)
+
+ def downloadLauncherFileDbDone(self):
+ self.launcherFileDbHash = HashVal()
+ self.launcherFileDbHash.hashRamfile(self.ramfile)
+ if self.VerifyFiles:
+ self.notify.info('Validating Launcher files')
+ for fileDesc in self.ramfile.readlines():
+ try:
+ filename, hashStr = fileDesc.split(' ', 1)
+ except:
+ self.notify.info('Invalid line: "%s"' % fileDesc)
+ self.failLauncherFileDb('No hash in launcherFileDb')
+
+ serverHash = HashVal()
+ if not self.hashIsValid(serverHash, hashStr):
+ self.notify.info('Not a valid hash string: "%s"' % hashStr)
+ self.failLauncherFileDb('Invalid hash in launcherFileDb')
+ localHash = HashVal()
+ localFilename = Filename(self.topDir, Filename(filename))
+ localHash.hashFile(localFilename)
+ if localHash != serverHash:
+ self.failLauncherFileDb('%s does not match expected version.' % filename)
+
+ self.downloadServerDbFile()
+
+ def failLauncherFileDb(self, string):
+ self.notify.info(string)
+ self.setPandaErrorCode(15)
+ sys.exit()
+
+ def downloadServerDbFile(self):
+ self.notify.info('Downloading server db file')
+ self.launcherMessage(self.Localizer.LauncherDownloadServerFileList)
+ self.downloadRam(self.serverDbFilePath, self.downloadServerDbFileDone)
+
+ def downloadServerDbFileDone(self):
+ self.serverDbFileHash = HashVal()
+ self.serverDbFileHash.hashRamfile(self.ramfile)
+ self.readProgressFile()
+
+ def checkClientDbExists(self):
+ clientFilename = Filename(self.dbDir, Filename(self.clientDbFilename))
+ if clientFilename.exists():
+ self.notify.info('Client Db exists')
+ self.createDownloadDb()
+ else:
+ self.notify.info('Client Db does not exist')
+ self.downloadClientDbStarterFile()
+
+ def downloadClientDbStarterFile(self):
+ self.notify.info('Downloading Client Db starter file')
+ localFilename = Filename(self.dbDir, Filename(self.compClientDbFilename))
+ self.download(self.clientStarterDbFilePath, localFilename, self.downloadClientDbStarterFileDone, None)
+ return
+
+ def downloadClientDbStarterFileDone(self):
+ localFilename = Filename(self.dbDir, Filename(self.compClientDbFilename))
+ decompressor = Decompressor()
+ decompressor.decompress(localFilename)
+ self.createDownloadDb()
+
+ def createDownloadDb(self):
+ self.notify.info('Creating downloadDb')
+ self.launcherMessage(self.Localizer.LauncherCreatingDownloadDb)
+ clientFilename = Filename(self.dbDir, Filename(self.clientDbFilename))
+ self.notify.info('Client file name: ' + clientFilename.cStr())
+ self.launcherMessage(self.Localizer.LauncherDownloadClientFileList)
+ serverFile = self.ramfile
+ decompressor = Decompressor()
+ decompressor.decompress(serverFile)
+ self.notify.info('Finished decompress')
+ self.dldb = DownloadDb(serverFile, clientFilename)
+ self.notify.info('created download db')
+ self.launcherMessage(self.Localizer.LauncherFinishedDownloadDb)
+ self.currentPhase = self.LauncherPhases[0]
+ self.currentPhaseIndex = 1
+ self.currentPhaseName = self.Localizer.LauncherPhaseNames[self.currentPhase]
+ self.updatePhase(self.currentPhase)
+
+ def maybeStartGame(self):
+ if not self.started and self.currentPhase >= self.showPhase:
+ self.started = True
+ self.notify.info('maybeStartGame: starting game')
+ self.launcherMessage(self.Localizer.LauncherStartingGame)
+ self.background()
+ __builtin__.launcher = self
+ self.startGame()
+
+ def _runTaskManager(self):
+ if not self.taskMgrStarted:
+ self.miniTaskMgr.run()
+ self.notify.info('Switching task managers.')
+ taskMgr.run()
+
+ def _stepMiniTaskManager(self, task):
+ self.miniTaskMgr.step()
+ if self.miniTaskMgr.taskList:
+ return task.cont
+ self.notify.info('Stopping mini task manager.')
+ self.miniTaskMgr = None
+ return task.done
+
+ def _addMiniTask(self, task, name):
+ if not self.miniTaskMgr:
+ self.notify.info('Restarting mini task manager.')
+ self.miniTaskMgr = MiniTaskManager()
+ from direct.task.TaskManagerGlobal import taskMgr
+ taskMgr.remove('miniTaskManager')
+ taskMgr.add(self._stepMiniTaskManager, 'miniTaskManager')
+ self.miniTaskMgr.add(task, name)
+
+ def newTaskManager(self):
+ self.taskMgrStarted = True
+ if self.miniTaskMgr.running:
+ self.miniTaskMgr.stop()
+ from direct.task.TaskManagerGlobal import taskMgr
+ taskMgr.remove('miniTaskManager')
+ taskMgr.add(self._stepMiniTaskManager, 'miniTaskManager')
+
+ def mainLoop(self):
+ try:
+ self._runTaskManager()
+ except SystemExit:
+ if hasattr(__builtin__, 'base'):
+ base.destroy()
+ self.notify.info('Normal exit.')
+ raise
+ except:
+ self.setPandaErrorCode(12)
+ self.notify.warning('Handling Python exception.')
+ if hasattr(__builtin__, 'base') and getattr(base, 'cr', None):
+ if base.cr.timeManager:
+ from otp.otpbase import OTPGlobals
+ base.cr.timeManager.setDisconnectReason(OTPGlobals.DisconnectPythonError)
+ base.cr.timeManager.setExceptionInfo()
+ base.cr.sendDisconnect()
+ if hasattr(__builtin__, 'base'):
+ base.destroy()
+ self.notify.info('Exception exit.\n')
+ import traceback
+ traceback.print_exc()
+ sys.exit()
+
+ return
+
+ def updatePhase(self, phase):
+ self.notify.info('Updating multifiles in phase: ' + `phase`)
+ self.setPercentPhaseComplete(self.currentPhase, 0)
+ self.phaseMultifileNames = []
+ numfiles = self.dldb.getServerNumMultifiles()
+ for i in range(self.dldb.getServerNumMultifiles()):
+ mfname = self.dldb.getServerMultifileName(i)
+ if self.dldb.getServerMultifilePhase(mfname) == phase:
+ self.phaseMultifileNames.append(mfname)
+
+ self.updateNextMultifile()
+
+ def updateNextMultifile(self):
+ if len(self.phaseMultifileNames) > 0:
+ self.currentMfname = self.phaseMultifileNames.pop()
+ self.curMultifileRetry = 0
+ self.getMultifile(self.currentMfname)
+ else:
+ if self.currentMfname is None:
+ self.notify.warning('no multifile found! See below for debug info:')
+ for i in range(self.dldb.getServerNumMultifiles()):
+ mfname = self.dldb.getServerMultifileName(i)
+ phase = self.dldb.getServerMultifilePhase(mfname)
+ print i, mfname, phase
+
+ self.handleGenericMultifileError()
+ decompressedMfname = os.path.splitext(self.currentMfname)[0]
+ localFilename = Filename(self.mfDir, Filename(decompressedMfname))
+ nextIndex = self.LauncherPhases.index(self.currentPhase) + 1
+ if nextIndex < len(self.LauncherPhases):
+ self.MakeNTFSFilesGlobalWriteable(localFilename)
+ else:
+ self.MakeNTFSFilesGlobalWriteable()
+ vfs = VirtualFileSystem.getGlobalPtr()
+ vfs.mount(localFilename, '.', VirtualFileSystem.MFReadOnly)
+ self.setPercentPhaseComplete(self.currentPhase, 100)
+ self.notify.info('Done updating multifiles in phase: ' + `(self.currentPhase)`)
+ self.progressSoFar += int(round(self.phaseOverallMap[self.currentPhase] * 100))
+ self.notify.info('progress so far ' + `(self.progressSoFar)`)
+ messenger.send('phaseComplete-' + `(self.currentPhase)`)
+ if nextIndex < len(self.LauncherPhases):
+ self.currentPhase = self.LauncherPhases[nextIndex]
+ self.currentPhaseIndex = nextIndex + 1
+ self.currentPhaseName = self.Localizer.LauncherPhaseNames[self.currentPhase]
+ self.updatePhase(self.currentPhase)
+ else:
+ self.notify.info('ALL PHASES COMPLETE')
+ self.maybeStartGame()
+ messenger.send('launcherAllPhasesComplete')
+ self.cleanup()
+ return
+
+ def isDownloadComplete(self):
+ return self._downloadComplete
+
+ def updateMultifileDone(self):
+ self.updateNextMultifile()
+
+ def downloadMultifileDone(self):
+ self.getDecompressMultifile(self.currentMfname)
+
+ def getMultifile(self, mfname):
+ self.notify.info('Downloading multifile: ' + mfname)
+ if not self.dldb.clientMultifileExists(mfname):
+ self.maybeStartGame()
+ self.notify.info('Multifile does not exist in client db,' + 'creating new record: ' + mfname)
+ self.dldb.addClientMultifile(mfname)
+ curHash = self.dldb.getServerMultifileHash(mfname)
+ self.dldb.setClientMultifileHash(mfname, curHash)
+ localFilename = Filename(self.mfDir, Filename(mfname))
+ if localFilename.exists():
+ curSize = localFilename.getFileSize()
+ self.dldb.setClientMultifileSize(mfname, curSize)
+ if curSize == self.dldb.getServerMultifileSize(mfname):
+ self.dldb.setClientMultifileComplete(mfname)
+ decompressedMfname = os.path.splitext(mfname)[0]
+ decompressedFilename = Filename(self.mfDir, Filename(decompressedMfname))
+ if (not self.dldb.clientMultifileComplete(mfname) or not self.dldb.clientMultifileDecompressed(mfname)) and decompressedFilename.exists():
+ clientMd5 = HashVal()
+ clientMd5.hashFile(decompressedFilename)
+ clientVer = self.dldb.getVersion(Filename(decompressedMfname), clientMd5)
+ if clientVer != -1:
+ self.notify.info('Decompressed multifile is already on disk and correct: %s (version %s)' % (mfname, clientVer))
+ self.dldb.setClientMultifileComplete(mfname)
+ self.dldb.setClientMultifileDecompressed(mfname)
+ compressedFilename = Filename(self.mfDir, Filename(mfname))
+ compressedFilename.unlink()
+ extractedOk = True
+ numFiles = self.dldb.getServerNumFiles(mfname)
+ for i in range(numFiles):
+ subfile = self.dldb.getServerFileName(mfname, i)
+ fn = Filename(self.mfDir, Filename(subfile))
+ if fn.compareTimestamps(decompressedFilename) <= 0:
+ extractedOk = False
+ break
+
+ if extractedOk:
+ self.notify.info('Multifile appears to have been extracted already.')
+ self.dldb.setClientMultifileExtracted(mfname)
+ if not self.dldb.clientMultifileComplete(mfname) or not decompressedFilename.exists():
+ self.maybeStartGame()
+ currentSize = self.dldb.getClientMultifileSize(mfname)
+ totalSize = self.dldb.getServerMultifileSize(mfname)
+ localFilename = Filename(self.mfDir, Filename(mfname))
+ if not localFilename.exists():
+ currentSize = 0
+ else:
+ currentSize = min(currentSize, localFilename.getFileSize())
+ if currentSize == 0:
+ self.notify.info('Multifile has not been started, ' + 'downloading new file: ' + mfname)
+ curHash = self.dldb.getServerMultifileHash(mfname)
+ self.dldb.setClientMultifileHash(mfname, curHash)
+ self.phaseNewDownload[self.currentPhase] = 1
+ self.downloadMultifile(self.contentDir + mfname, localFilename, mfname, self.downloadMultifileDone, totalSize, 0, self.downloadMultifileWriteToDisk)
+ else:
+ clientHash = self.dldb.getClientMultifileHash(mfname)
+ serverHash = self.dldb.getServerMultifileHash(mfname)
+ if clientHash.eq(serverHash):
+ self.notify.info('Multifile is not complete, finishing download for %s, size = %s / %s' % (mfname, currentSize, totalSize))
+ self.downloadMultifile(self.contentDir + mfname, localFilename, mfname, self.downloadMultifileDone, totalSize, currentSize, self.downloadMultifileWriteToDisk)
+ elif self.curMultifileRetry < self.multifileRetries:
+ self.notify.info('recover attempt: %s / %s' % (self.curMultifileRetry, self.multifileRetries))
+ self.curMultifileRetry += 1
+ self.notify.info('Multifile is not complete, and is out of date. ' + 'Restarting download with newest multifile')
+ self.dldb.setClientMultifileIncomplete(self.currentMfname)
+ self.dldb.setClientMultifileSize(self.currentMfname, 0)
+ self.dldb.setClientMultifileHash(self.currentMfname, serverHash)
+ self.getMultifile(self.currentMfname)
+ else:
+ self.setPandaErrorCode(6)
+ self.notify.info('getMultifile: Failed to download multifile')
+ sys.exit()
+ else:
+ self.notify.info('Multifile already complete: ' + mfname)
+ self.downloadMultifileDone()
+
+ def updateMultifileDone(self):
+ self.updateNextMultifile()
+
+ def downloadMultifileDone(self):
+ self.getDecompressMultifile(self.currentMfname)
+
+ def getMultifile(self, mfname):
+ self.notify.info('Downloading multifile: ' + mfname)
+ if not self.dldb.clientMultifileExists(mfname):
+ self.maybeStartGame()
+ self.notify.info('Multifile does not exist in client db,' + 'creating new record: ' + mfname)
+ self.dldb.addClientMultifile(mfname)
+ if self.DecompressMultifiles:
+ curHash = self.dldb.getServerMultifileHash(mfname)
+ self.dldb.setClientMultifileHash(mfname, curHash)
+ localFilename = Filename(self.mfDir, Filename(mfname))
+ if localFilename.exists():
+ curSize = localFilename.getFileSize()
+ self.dldb.setClientMultifileSize(mfname, curSize)
+ if curSize == self.dldb.getServerMultifileSize(mfname):
+ self.dldb.setClientMultifileComplete(mfname)
+ decompressedMfname = os.path.splitext(mfname)[0]
+ decompressedFilename = Filename(self.mfDir, Filename(decompressedMfname))
+ if self.DecompressMultifiles:
+ if (not self.dldb.clientMultifileComplete(mfname) or not self.dldb.clientMultifileDecompressed(mfname)) and decompressedFilename.exists():
+ clientMd5 = HashVal()
+ clientMd5.hashFile(decompressedFilename)
+ clientVer = self.dldb.getVersion(Filename(decompressedMfname), clientMd5)
+ if clientVer != -1:
+ self.notify.info('Decompressed multifile is already on disk and correct: %s (version %s)' % (mfname, clientVer))
+ self.dldb.setClientMultifileComplete(mfname)
+ self.dldb.setClientMultifileDecompressed(mfname)
+ compressedFilename = Filename(self.mfDir, Filename(mfname))
+ compressedFilename.unlink()
+ extractedOk = True
+ numFiles = self.dldb.getServerNumFiles(mfname)
+ for i in range(numFiles):
+ subfile = self.dldb.getServerFileName(mfname, i)
+ fn = Filename(self.mfDir, Filename(subfile))
+ if fn.compareTimestamps(decompressedFilename) <= 0:
+ extractedOk = False
+ break
+
+ if extractedOk:
+ self.notify.info('Multifile appears to have been extracted already.')
+ self.dldb.setClientMultifileExtracted(mfname)
+ if not self.dldb.clientMultifileComplete(mfname) or not decompressedFilename.exists():
+ self.maybeStartGame()
+ currentSize = self.dldb.getClientMultifileSize(mfname)
+ totalSize = self.dldb.getServerMultifileSize(mfname)
+ localFilename = Filename(self.mfDir, Filename(mfname))
+ if not localFilename.exists():
+ currentSize = 0
+ if currentSize == 0:
+ self.notify.info('Multifile has not been started, ' + 'downloading new file: ' + mfname)
+ curHash = self.dldb.getServerMultifileHash(mfname)
+ self.dldb.setClientMultifileHash(mfname, curHash)
+ self.phaseNewDownload[self.currentPhase] = 1
+ self.downloadMultifile(self.contentDir + mfname, localFilename, mfname, self.downloadMultifileDone, totalSize, 0, self.downloadMultifileWriteToDisk)
+ else:
+ clientHash = self.dldb.getClientMultifileHash(mfname)
+ serverHash = self.dldb.getServerMultifileHash(mfname)
+ if clientHash.eq(serverHash):
+ self.notify.info('Multifile is not complete, finishing download for %s, size = %s / %s' % (mfname, currentSize, totalSize))
+ self.downloadMultifile(self.contentDir + mfname, localFilename, mfname, self.downloadMultifileDone, totalSize, currentSize, self.downloadMultifileWriteToDisk)
+ elif self.curMultifileRetry < self.multifileRetries:
+ self.notify.info('recover attempt: %s / %s' % (self.curMultifileRetry, self.multifileRetries))
+ self.curMultifileRetry += 1
+ self.notify.info('Multifile is not complete, and is out of date. ' + 'Restarting download with newest multifile')
+ self.dldb.setClientMultifileIncomplete(self.currentMfname)
+ self.dldb.setClientMultifileSize(self.currentMfname, 0)
+ if self.DecompressMultifiles:
+ self.dldb.setClientMultifileHash(self.currentMfname, serverHash)
+ self.getMultifile(self.currentMfname)
+ else:
+ self.setPandaErrorCode(6)
+ self.notify.info('getMultifile: Failed to download multifile')
+ sys.exit()
+ else:
+ self.notify.info('Multifile already complete: ' + mfname)
+ self.downloadMultifileDone()
+
+ def getDecompressMultifile(self, mfname):
+ if not self.DecompressMultifiles:
+ self.decompressMultifileDone()
+ elif not self.dldb.clientMultifileDecompressed(mfname):
+ self.maybeStartGame()
+ self.notify.info('decompressMultifile: Decompressing multifile: ' + mfname)
+ localFilename = Filename(self.mfDir, Filename(mfname))
+ self.decompressMultifile(mfname, localFilename, self.decompressMultifileDone)
+ else:
+ self.notify.info('decompressMultifile: Multifile already decompressed: ' + mfname)
+ self.decompressMultifileDone()
+
+ def decompressMultifileDone(self):
+ if self.phaseNewDownload[self.currentPhase]:
+ self.setPercentPhaseComplete(self.currentPhase, 95)
+ self.extractMultifile(self.currentMfname)
+
+ def extractMultifile(self, mfname):
+ if not self.dldb.clientMultifileExtracted(mfname):
+ self.maybeStartGame()
+ self.notify.info('extractMultifile: Extracting multifile: ' + mfname)
+ decompressedMfname = os.path.splitext(mfname)[0]
+ localFilename = Filename(self.mfDir, Filename(decompressedMfname))
+ destDir = Filename(self.topDir)
+ self.notify.info('extractMultifile: Extracting: ' + localFilename.cStr() + ' to: ' + destDir.cStr())
+ self.extract(mfname, localFilename, destDir, self.extractMultifileDone)
+ else:
+ self.notify.info('extractMultifile: Multifile already extracted: ' + mfname)
+ self.extractMultifileDone()
+
+ def extractMultifileDone(self):
+ if self.phaseNewDownload[self.currentPhase]:
+ self.setPercentPhaseComplete(self.currentPhase, 99)
+ self.notify.info('extractMultifileDone: Finished updating multifile: ' + self.currentMfname)
+ self.patchMultifile()
+
+ def getPatchFilename(self, fname, currentVersion):
+ return fname + '.v' + `currentVersion` + '.' + self.patchExtension
+
+ def downloadPatches(self):
+ if len(self.patchList) > 0:
+ self.currentPatch, self.currentPatchee, self.currentPatchVersion = self.patchList.pop()
+ self.notify.info(self.contentDir)
+ self.notify.info(self.currentPatch)
+ patchFile = self.currentPatch + '.pz'
+ serverPatchFilePath = self.contentDir + patchFile
+ self.notify.info(serverPatchFilePath)
+ localPatchFilename = Filename(self.patchDir, Filename(patchFile))
+ if self.currentPhase > 3:
+ self.download(serverPatchFilePath, localPatchFilename, self.downloadPatchDone, self.downloadPatchSimpleProgress)
+ else:
+ self.download(serverPatchFilePath, localPatchFilename, self.downloadPatchDone, self.downloadPatchOverallProgress)
+ else:
+ self.notify.info('applyNextPatch: Done patching multifile: ' + `(self.currentPhase)`)
+ self.patchDone()
+
+ def downloadPatchDone(self):
+ self.patchDownloadSoFar += self.httpChannel.getBytesDownloaded()
+ self.notify.info('downloadPatchDone: Decompressing patch file: ' + self.currentPatch + '.pz')
+ self.decompressFile(Filename(self.patchDir, Filename(self.currentPatch + '.pz')), self.decompressPatchDone)
+
+ def decompressPatchDone(self):
+ self.notify.info('decompressPatchDone: Patching file: ' + self.currentPatchee + ' from ver: ' + `(self.currentPatchVersion)`)
+ patchFile = Filename(self.patchDir, Filename(self.currentPatch))
+ patchFile.setBinary()
+ patchee = Filename(self.mfDir, Filename(self.currentPatchee))
+ patchee.setBinary()
+ self.patch(patchFile, patchee, self.downloadPatches)
+
+ def patchDone(self):
+ self.notify.info('patchDone: Patch successful')
+ del self.currentPatch
+ del self.currentPatchee
+ del self.currentPatchVersion
+ decompressedMfname = os.path.splitext(self.currentMfname)[0]
+ localFilename = Filename(self.mfDir, Filename(decompressedMfname))
+ destDir = Filename(self.topDir)
+ self.extract(self.currentMfname, localFilename, destDir, self.updateMultifileDone)
+
+ def startReextractingFiles(self):
+ self.notify.info('startReextractingFiles: Reextracting ' + `(len(self.reextractList))` + ' files for multifile: ' + self.currentMfname)
+ self.launcherMessage(self.Localizer.LauncherRecoverFiles)
+ self.currentMfile = Multifile()
+ decompressedMfname = os.path.splitext(self.currentMfname)[0]
+ self.currentMfile.openRead(Filename(self.mfDir, Filename(decompressedMfname)))
+ self.reextractNextFile()
+
+ def reextractNextFile(self):
+ failure = 0
+ while not failure and len(self.reextractList) > 0:
+ currentReextractFile = self.reextractList.pop()
+ subfileIndex = self.currentMfile.findSubfile(currentReextractFile)
+ if subfileIndex >= 0:
+ destFilename = Filename(self.topDir, Filename(currentReextractFile))
+ result = self.currentMfile.extractSubfile(subfileIndex, destFilename)
+ if not result:
+ self.notify.warning('reextractNextFile: Failure on reextract.')
+ failure = 1
+ else:
+ self.notify.warning('reextractNextFile: File not found in multifile: ' + `currentReextractFile`)
+ failure = 1
+
+ if failure:
+ sys.exit()
+ self.notify.info('reextractNextFile: Done reextracting files for multifile: ' + `(self.currentPhase)`)
+ del self.currentMfile
+ self.updateMultifileDone()
+
+ def patchMultifile(self):
+ self.launcherMessage(self.Localizer.LauncherCheckUpdates % {'name': self.currentPhaseName,
+ 'current': self.currentPhaseIndex,
+ 'total': self.numPhases})
+ self.notify.info('patchMultifile: Checking for patches on multifile: ' + self.currentMfname)
+ self.patchList = []
+ clientMd5 = HashVal()
+ decompressedMfname = os.path.splitext(self.currentMfname)[0]
+ localFilename = Filename(self.mfDir, Filename(decompressedMfname))
+ clientMd5.hashFile(localFilename)
+ clientVer = self.dldb.getVersion(Filename(decompressedMfname), clientMd5)
+ if clientVer == 1:
+ self.patchAndHash()
+ return
+ elif clientVer == -1:
+ self.notify.info('patchMultifile: Invalid hash for file: ' + self.currentMfname)
+ self.maybeStartGame()
+ if self.curMultifileRetry < self.multifileRetries:
+ self.notify.info('recover attempt: %s / %s' % (self.curMultifileRetry, self.multifileRetries))
+ self.curMultifileRetry += 1
+ self.notify.info('patchMultifile: Restarting download with newest multifile')
+ self.dldb.setClientMultifileIncomplete(self.currentMfname)
+ self.dldb.setClientMultifileSize(self.currentMfname, 0)
+ self.getMultifile(self.currentMfname)
+ else:
+ self.setPandaErrorCode(6)
+ self.notify.info('patchMultifile: Failed to download multifile')
+ sys.exit()
+ return
+ elif clientVer > 1:
+ self.notify.info('patchMultifile: Old version for multifile: ' + self.currentMfname + ' Client ver: ' + `clientVer`)
+ self.maybeStartGame()
+ self.totalPatchDownload = 0
+ self.patchDownloadSoFar = 0
+ for ver in range(1, clientVer):
+ patch = self.getPatchFilename(decompressedMfname, ver + 1)
+ patchee = decompressedMfname
+ patchVersion = ver + 1
+ self.patchList.append((patch, patchee, patchVersion))
+ if self.currentPhase == 3:
+ self.totalPatchDownload += self.getProgressSum(patch)
+
+ self.notify.info('total patch to be downloaded = ' + `(self.totalPatchDownload)`)
+ self.downloadPatches()
+ return
+
+ def patchAndHash(self):
+ self.reextractList = []
+ self.PAHClean = 1
+ self.PAHNumFiles = self.dldb.getServerNumFiles(self.currentMfname)
+ self.PAHFileCounter = 0
+ if self.PAHNumFiles > 0:
+ task = MiniTask(self.patchAndHashTask)
+ task.cleanCallback = self.updateMultifileDone
+ task.uncleanCallback = self.startReextractingFiles
+ self._addMiniTask(task, 'patchAndHash')
+ else:
+ self.updateMultifileDone()
+
+ def patchAndHashTask(self, task):
+ self.launcherMessage(self.Localizer.LauncherVerifyPhase)
+ if self.PAHFileCounter == self.PAHNumFiles:
+ if self.PAHClean:
+ task.cleanCallback()
+ else:
+ task.uncleanCallback()
+ return task.done
+ else:
+ i = self.PAHFileCounter
+ self.PAHFileCounter += 1
+ fname = self.dldb.getServerFileName(self.currentMfname, i)
+ fnameFilename = Filename(self.topDir, Filename(fname))
+ if not os.path.exists(fnameFilename.toOsSpecific()):
+ self.notify.info('patchAndHash: File not found: ' + fname)
+ self.reextractList.append(fname)
+ self.PAHClean = 0
+ return task.cont
+ if self.VerifyFiles and self.dldb.hasVersion(Filename(fname)):
+ clientMd5 = HashVal()
+ clientMd5.hashFile(fnameFilename)
+ clientVer = self.dldb.getVersion(Filename(fname), clientMd5)
+ if clientVer == 1:
+ return task.cont
+ else:
+ self.notify.info('patchAndHash: Invalid hash for file: ' + fname)
+ self.reextractList.append(fname)
+ self.PAHClean = 0
+ return task.cont
+
+ def launcherMessage(self, msg):
+ if msg != self.lastLauncherMsg:
+ self.lastLauncherMsg = msg
+ self.notify.info(msg)
+
+ def isTestServer(self):
+ return self.testServerFlag
+
+ def recordPeriodTimeRemaining(self, secondsRemaining):
+ self.setValue(self.PeriodTimeRemainingKey, int(secondsRemaining))
+
+ def recordPeriodName(self, periodName):
+ self.setValue(self.PeriodNameKey, periodName)
+
+ def recordSwid(self, swid):
+ self.setValue(self.SwidKey, swid)
+
+ def getGoUserName(self):
+ return self.goUserName
+
+ def setGoUserName(self, userName):
+ self.goUserName = userName
+
+ def getInstallDir(self):
+ return self.topDir.cStr()
+
+ def setPandaWindowOpen(self):
+ self.setValue(self.PandaWindowOpenKey, 1)
+
+ def setPandaErrorCode(self, code):
+ self.notify.info('setting panda error code to %s' % code)
+ self.pandaErrorCode = code
+ errorLog = open(self.errorfile, 'w')
+ errorLog.write(str(code) + '\n')
+ errorLog.flush()
+ errorLog.close()
+
+ def getPandaErrorCode(self):
+ return self.pandaErrorCode
+
+ def setDisconnectDetailsNormal(self):
+ self.notify.info('Setting Disconnect Details normal')
+ self.disconnectCode = 0
+ self.disconnectMsg = 'normal'
+
+ def setDisconnectDetails(self, newCode, newMsg):
+ self.notify.info('New Disconnect Details: %s - %s ' % (newCode, newMsg))
+ self.disconnectCode = newCode
+ self.disconnectMsg = newMsg
+
+ def setServerVersion(self, version):
+ self.ServerVersion = version
+
+ def getServerVersion(self):
+ return self.ServerVersion
+
+ def getIsNewInstallation(self):
+ result = self.getValue(self.NewInstallationKey, 1)
+ result = base.config.GetBool('new-installation', result)
+ return result
+
+ def setIsNotNewInstallation(self):
+ self.setValue(self.NewInstallationKey, 0)
+
+ def getLastLogin(self):
+ return self.getValue(self.LastLoginKey, '')
+
+ def setLastLogin(self, login):
+ self.setValue(self.LastLoginKey, login)
+
+ def setUserLoggedIn(self):
+ self.setValue(self.UserLoggedInKey, '1')
+
+ def setPaidUserLoggedIn(self):
+ self.setValue(self.PaidUserLoggedInKey, '1')
+
+ def getReferrerCode(self):
+ return self.getValue(self.ReferrerKey, None)
+
+ def getPhaseComplete(self, phase):
+ percentDone = self.phaseComplete[phase]
+ return percentDone == 100
+
+ def setPercentPhaseComplete(self, phase, percent):
+ self.notify.info('phase updating %s, %s' % (phase, percent))
+ oldPercent = self.phaseComplete[phase]
+ if oldPercent != percent:
+ self.phaseComplete[phase] = percent
+ messenger.send('launcherPercentPhaseComplete', [phase,
+ percent,
+ self.getBandwidth(),
+ self.byteRate])
+ percentPhaseCompleteKey = 'PERCENT_PHASE_COMPLETE_' + `phase`
+ self.setRegistry(percentPhaseCompleteKey, percent)
+ self.overallComplete = int(round(percent * self.phaseOverallMap[phase])) + self.progressSoFar
+ self.setRegistry('PERCENT_OVERALL_COMPLETE', self.overallComplete)
+
+ def getPercentPhaseComplete(self, phase):
+ return self.phaseComplete[phase]
+ dr = finalRequested - startRequested
+ if dt <= 0.0:
+ return -1
+ self.byteRate = db / dt
+ self.byteRateRequested = dr / dt
+ return self.byteRate
+
+ def addPhasePostProcess(self, phase, func, taskChain = 'default'):
+ if self.getPhaseComplete(phase):
+ func()
+ return
+ self.acceptOnce('phaseComplete-%s' % phase, func)
+
+ def testBandwidth(self):
+ self.recordBytesPerSecond()
+ byteRate = self.getBytesPerSecond()
+ if byteRate < 0:
+ return
+ if byteRate >= self.getBandwidth() * self.INCREASE_THRESHOLD:
+ self.increaseBandwidth(byteRate)
+ elif byteRate < self.byteRateRequested * self.DECREASE_THRESHOLD:
+ self.decreaseBandwidth(byteRate)
+
+ def getBandwidth(self):
+ if self.backgrounded:
+ bandwidth = self.BANDWIDTH_ARRAY[self.bandwidthIndex] - self.TELEMETRY_BANDWIDTH
+ else:
+ bandwidth = self.BANDWIDTH_ARRAY[self.bandwidthIndex]
+ if self.MAX_BANDWIDTH > 0:
+ bandwidth = min(bandwidth, self.MAX_BANDWIDTH)
+ return bandwidth
+
+ def increaseBandwidth(self, targetBandwidth = None):
+ maxBandwidthIndex = len(self.BANDWIDTH_ARRAY) - 1
+ if self.bandwidthIndex == maxBandwidthIndex:
+ self.notify.debug('increaseBandwidth: Already at maximum bandwidth')
+ return 0
+ self.bandwidthIndex += 1
+ self.everIncreasedBandwidth = 1
+ self.setBandwidth()
+ return 1
+
+ def decreaseBandwidth(self, targetBandwidth = None):
+ if not self.DECREASE_BANDWIDTH:
+ return 0
+ if self.backgrounded and self.everIncreasedBandwidth:
+ return 0
+ if self.bandwidthIndex == 0:
+ return 0
+ else:
+ self.bandwidthIndex -= 1
+ if targetBandwidth:
+ while self.bandwidthIndex > 0 and self.BANDWIDTH_ARRAY[self.bandwidthIndex] > targetBandwidth:
+ self.bandwidthIndex -= 1
+
+ self.setBandwidth()
+ return 1
+
+ def setBandwidth(self):
+ self.resetBytesPerSecond()
+ self.httpChannel.setMaxBytesPerSecond(self.getBandwidth())
+
+ def resetBytesPerSecond(self):
+ self.bpsList = []
+
+ def recordBytesPerSecond(self):
+ bytesDownloaded = self.httpChannel.getBytesDownloaded()
+ bytesRequested = self.httpChannel.getBytesRequested()
+ t = self.getTime()
+ self.bpsList.append((t, bytesDownloaded, bytesRequested))
+ while 1:
+ if len(self.bpsList) == 0:
+ break
+ ft, fb, fr = self.bpsList[0]
+ if ft < t-self.BPS_WINDOW:
+ self.bpsList.pop(0)
+ else:
+ break
+
+ def getBytesPerSecond(self):
+ if len(self.bpsList) < 2:
+ return -1
+ startTime, startBytes, startRequested = self.bpsList[0]
+ finalTime, finalBytes, finalRequested = self.bpsList[-1]
+ dt = finalTime - startTime
+ db = finalBytes - startBytes
+ dr = finalRequested - startRequested
+ if dt <= 0.0:
+ return -1
+ self.byteRate = db / dt
+ self.byteRateRequested = dr / dt
+ return self.byteRate
+
+ def testBandwidth(self):
+ self.recordBytesPerSecond()
+ byteRate = self.getBytesPerSecond()
+ if byteRate < 0:
+ return
+ if byteRate >= self.getBandwidth() * self.INCREASE_THRESHOLD:
+ self.increaseBandwidth(byteRate)
+ elif byteRate < self.byteRateRequested * self.DECREASE_THRESHOLD:
+ self.decreaseBandwidth(byteRate)
+
+ def getBandwidth(self):
+ if self.backgrounded:
+ bandwidth = self.BANDWIDTH_ARRAY[self.bandwidthIndex] - self.TELEMETRY_BANDWIDTH
+ else:
+ bandwidth = self.BANDWIDTH_ARRAY[self.bandwidthIndex]
+ if self.MAX_BANDWIDTH > 0:
+ bandwidth = min(bandwidth, self.MAX_BANDWIDTH)
+ return bandwidth
+
+ def increaseBandwidth(self, targetBandwidth = None):
+ maxBandwidthIndex = len(self.BANDWIDTH_ARRAY) - 1
+ if self.bandwidthIndex == maxBandwidthIndex:
+ return 0
+ self.bandwidthIndex += 1
+ self.everIncreasedBandwidth = 1
+ self.setBandwidth()
+ return 1
+
+ def decreaseBandwidth(self, targetBandwidth = None):
+ if not self.DECREASE_BANDWIDTH:
+ return 0
+ if self.backgrounded and self.everIncreasedBandwidth:
+ return 0
+ if self.bandwidthIndex == 0:
+ return 0
+ else:
+ self.bandwidthIndex -= 1
+ if targetBandwidth:
+ while self.bandwidthIndex > 0 and self.BANDWIDTH_ARRAY[self.bandwidthIndex] > targetBandwidth:
+ self.bandwidthIndex -= 1
+
+ self.setBandwidth()
+ return 1
+
+ def setBandwidth(self):
+ self.resetBytesPerSecond()
+ self.httpChannel.setMaxBytesPerSecond(self.getBandwidth())
+
+ def MakeNTFSFilesGlobalWriteable(self, pathToSet = None):
+ if not self.WIN32:
+ return
+ import win32api
+ if pathToSet == None:
+ pathToSet = self.getInstallDir()
+ else:
+ pathToSet = pathToSet.cStr() + '*'
+ DrivePath = pathToSet[0:3]
+ try:
+ volname, volsernum, maxfilenamlen, sysflags, filesystemtype = win32api.GetVolumeInformation(DrivePath)
+ except:
+ return
+
+ if self.win32con_FILE_PERSISTENT_ACLS & sysflags:
+ self.notify.info('NTFS detected, making files global writeable\n')
+ win32dir = win32api.GetWindowsDirectory()
+ cmdLine = win32dir + '\\system32\\cacls.exe "' + pathToSet + '" /T /E /C /G Everyone:F > nul'
+ os.system(cmdLine)
+ return
+
+ def cleanup(self):
+ self.notify.info('cleanup: cleaning up Launcher')
+ self.ignoreAll()
+ del self.clock
+ del self.dldb
+ del self.httpChannel
+ del self.http
+
+ def scanForHacks(self):
+ if not self.WIN32:
+ return
+ import _winreg
+ hacksInstalled = {}
+ hacksRunning = {}
+ hackName = ['!xSpeed.net', 'A Speeder', 'Speed Gear']
+ knownHacksRegistryKeys = {
+ hackName[0] : [
+ [_winreg.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows\\CurrentVersion\\Run\\!xSpeed'],
+ [_winreg.HKEY_CURRENT_USER, 'Software\\!xSpeednethy'],
+ [_winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MenuOrder\\Start Menu\\Programs\\!xSpeednet'],
+ [_winreg.HKEY_LOCAL_MACHINE, 'Software\\Gentee\\Paths\\!xSpeednet'],
+ [_winreg.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\!xSpeed.net 2.0']],
+ hackName[1] : [
+ [_winreg.HKEY_CURRENT_USER, 'Software\\aspeeder'],
+ [_winreg.HKEY_LOCAL_MACHINE, 'Software\\aspeeder'],
+ [_winreg.HKEY_LOCAL_MACHINE, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\aspeeder']]
+ }
+ try:
+ for prog in knownHacksRegistryKeys.keys():
+ for key in knownHacksRegistryKeys[prog]:
+ try:
+ h = _winreg.OpenKey(key[0], key[1])
+ hacksInstalled[prog] = 1
+ _winreg.CloseKey(h)
+ break
+ except:
+ pass
+ except:
+ pass
+ knownHacksMUI = {'!xspeednet': hackName[0], 'aspeeder': hackName[1], 'speed gear': hackName[2]}
+ i = 0
+ try:
+ rh = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache')
+ while 1:
+ name, value, type = _winreg.EnumValue(rh, i)
+ i += 1
+ if type == 1:
+ val = value.lower()
+ for hackprog in knownHacksMUI:
+ if val.find(hackprog) != -1:
+ hacksInstalled[knownHacksMUI[hackprog]] = 1
+ break
+ _winreg.CloseKey(rh)
+ except:
+ pass
+
+ try:
+ import otp.launcher.procapi
+ except:
+ pass
+ else:
+ knownHacksExe = {'!xspeednet.exe': hackName[0], 'aspeeder.exe': hackName[1], 'speedgear.exe': hackName[2]}
+ try:
+ for p in procapi.getProcessList():
+ pname = p.name
+ if knownHacksExe.has_key(pname):
+ hacksRunning[knownHacksExe[pname]] = 1
+ except:
+ pass
+
+ if len(hacksInstalled) > 0:
+ self.notify.info("Third party programs installed:")
+ for hack in hacksInstalled.keys():
+ self.notify.info(hack)
+
+ if len(hacksRunning) > 0:
+ self.notify.info("Third party programs running:")
+ for hack in hacksRunning.keys():
+ self.notify.info(hack)
+ self.setPandaErrorCode(8)
+ sys.exit()
+
+ def getBlue(self):
+ return None
+
+ def getPlayToken(self):
+ return None
+
+ def getDISLToken(self):
+ DISLToken = self.getValue(self.DISLTokenKey)
+ self.setValue(self.DISLTokenKey, '')
+ if DISLToken == 'NO DISLTOKEN':
+ DISLToken = None
+ return DISLToken
diff --git a/otp/launcher/__init__.py b/otp/launcher/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/launcher/procapi.py b/otp/launcher/procapi.py
new file mode 100644
index 0000000..5c3eaf6
--- /dev/null
+++ b/otp/launcher/procapi.py
@@ -0,0 +1,43 @@
+import ctypes
+from ctypes.wintypes import *
+TH32CS_SNAPPROCESS = 2
+INVALID_HANDLE_VALUE = -1
+cwk = ctypes.windll.kernel32
+
+class PROCESSENTRY32(ctypes.Structure):
+ _fields_ = [('dwSize', DWORD),
+ ('cntUsage', DWORD),
+ ('th32ProcessID', DWORD),
+ ('th32DefaultHeapId', HANDLE),
+ ('th32ModuleID', DWORD),
+ ('cntThreads', DWORD),
+ ('th32ParentProcessID', DWORD),
+ ('pcPriClassBase', LONG),
+ ('dwFlags', DWORD),
+ ('szExeFile', c_char * MAX_PATH)]
+
+
+class ProcessEntryPY:
+
+ def __init__(self, name, pid):
+ self.name = name
+ self.pid = pid
+
+
+def getProcessList():
+ hProcessSnap = cwk.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
+
+ processList = []
+ if hProcessSnap != INVALID_HANDLE_VALUE:
+ pe32 = PROCESSENTRY32()
+ pe32.dwSize = sizeof(pe32)
+
+ if cwk.Process32First(hProcessSnap, ctypes.byref(pe32)):
+ while 1:
+ processList.append(ProcessEntryPY(pe32.szExeFile.lower(), int(pe32.th32ProcessID)))
+ if not cwk.Process32Next(hProcessSnap, ctypes.byref(pe32)):
+ break
+
+ cwk.CloseHandle(hProcessSnap)
+
+ return processList
diff --git a/otp/level/AmbientSound.py b/otp/level/AmbientSound.py
new file mode 100644
index 0000000..dd717c0
--- /dev/null
+++ b/otp/level/AmbientSound.py
@@ -0,0 +1,39 @@
+from direct.interval.IntervalGlobal import *
+import BasicEntities
+import random
+
+class AmbientSound(BasicEntities.NodePathEntity):
+
+ def __init__(self, level, entId):
+ BasicEntities.NodePathEntity.__init__(self, level, entId)
+ self.initSound()
+
+ def destroy(self):
+ self.destroySound()
+ BasicEntities.NodePathEntity.destroy(self)
+
+ def initSound(self):
+ if not self.enabled:
+ return
+ if self.soundPath == '':
+ return
+ self.sound = base.loadSfx(self.soundPath)
+ if self.sound is None:
+ return
+ self.soundIval = SoundInterval(self.sound, node=self, volume=self.volume)
+ self.soundIval.loop()
+ self.soundIval.setT(random.random() * self.sound.length())
+ return
+
+ def destroySound(self):
+ if hasattr(self, 'soundIval'):
+ self.soundIval.pause()
+ del self.soundIval
+ if hasattr(self, 'sound'):
+ del self.sound
+
+ if __dev__:
+
+ def attribChanged(self, *args):
+ self.destroySound()
+ self.initSound()
diff --git a/otp/level/AttribDesc.py b/otp/level/AttribDesc.py
new file mode 100644
index 0000000..c962a35
--- /dev/null
+++ b/otp/level/AttribDesc.py
@@ -0,0 +1,30 @@
+
+
+class AttribDesc:
+
+ def __init__(self, name, default, datatype = 'string', params = {}):
+ self.name = name
+ self.default = default
+ self.datatype = datatype
+ self.params = params
+
+ def getName(self):
+ return self.name
+
+ def getDefaultValue(self):
+ return self.default
+
+ def getDatatype(self):
+ return self.datatype
+
+ def getParams(self):
+ return self.params
+
+ def __str__(self):
+ return self.name
+
+ def __repr__(self):
+ return 'AttribDesc(%s, %s, %s, %s)' % (repr(self.name),
+ repr(self.default),
+ repr(self.datatype),
+ repr(self.params))
diff --git a/otp/level/BasicEntities.py b/otp/level/BasicEntities.py
new file mode 100644
index 0000000..617a039
--- /dev/null
+++ b/otp/level/BasicEntities.py
@@ -0,0 +1,131 @@
+import Entity
+import DistributedEntity
+from pandac.PandaModules import NodePath
+
+class NodePathEntityBase:
+
+ def initNodePathAttribs(self, doReparent = 1):
+ self.callSetters('pos', 'x', 'y', 'z', 'hpr', 'h', 'p', 'r', 'scale', 'sx', 'sy', 'sz')
+ if doReparent:
+ self.callSetters('parentEntId')
+ self.getNodePath().setName('%s-%s' % (self.__class__.__name__, self.entId))
+ if __dev__:
+ self.getNodePath().setTag('entity', '1')
+
+ def setParentEntId(self, parentEntId):
+ self.parentEntId = parentEntId
+ self.level.requestReparent(self, self.parentEntId)
+
+ def destroy(self):
+ if __dev__:
+ self.getNodePath().clearTag('entity')
+
+
+class NodePathAttribs(NodePathEntityBase):
+
+ def initNodePathAttribs(self, doReparent = 1):
+ NodePathEntityBase.initNodePathAttribs(self, doReparent)
+
+ def destroy(self):
+ NodePathEntityBase.destroy(self)
+
+ def getNodePath(self):
+ return self
+
+
+class NodePathAndAttribs(NodePathEntityBase, NodePath):
+
+ def __init__(self):
+ node = hidden.attachNewNode('EntityNodePath')
+ NodePath.__init__(self, node)
+
+ def initNodePathAttribs(self, doReparent = 1):
+ NodePathEntityBase.initNodePathAttribs(self, doReparent)
+
+ def destroy(self):
+ NodePathEntityBase.destroy(self)
+ self.removeNode()
+
+ def getNodePath(self):
+ return self
+
+
+class NodePathAttribsProxy(NodePathEntityBase):
+
+ def initNodePathAttribs(self, doReparent = 1):
+ NodePathEntityBase.initNodePathAttribs(self, doReparent)
+
+ def destroy(self):
+ NodePathEntityBase.destroy(self)
+
+ def setPos(self, *args):
+ self.getNodePath().setPos(*args)
+
+ def setX(self, *args):
+ self.getNodePath().setX(*args)
+
+ def setY(self, *args):
+ self.getNodePath().setY(*args)
+
+ def setZ(self, *args):
+ self.getNodePath().setZ(*args)
+
+ def setHpr(self, *args):
+ self.getNodePath().setHpr(*args)
+
+ def setH(self, *args):
+ self.getNodePath().setH(*args)
+
+ def setP(self, *args):
+ self.getNodePath().setP(*args)
+
+ def setR(self, *args):
+ self.getNodePath().setR(*args)
+
+ def setScale(self, *args):
+ self.getNodePath().setScale(*args)
+
+ def setSx(self, *args):
+ self.getNodePath().setSx(*args)
+
+ def setSy(self, *args):
+ self.getNodePath().setSy(*args)
+
+ def setSz(self, *args):
+ self.getNodePath().setSz(*args)
+
+ def reparentTo(self, *args):
+ self.getNodePath().reparentTo(*args)
+
+
+class NodePathEntity(Entity.Entity, NodePath, NodePathAttribs):
+
+ def __init__(self, level, entId):
+ node = hidden.attachNewNode('NodePathEntity')
+ NodePath.__init__(self, node)
+ Entity.Entity.__init__(self, level, entId)
+ self.initNodePathAttribs(self)
+
+ def destroy(self):
+ NodePathAttribs.destroy(self)
+ Entity.Entity.destroy(self)
+ self.removeNode()
+
+
+class DistributedNodePathEntity(DistributedEntity.DistributedEntity, NodePath, NodePathAttribs):
+
+ def __init__(self, cr):
+ DistributedEntity.DistributedEntity.__init__(self, cr)
+
+ def generateInit(self):
+ DistributedEntity.DistributedEntity.generateInit(self)
+ node = hidden.attachNewNode('DistributedNodePathEntity')
+ NodePath.__init__(self, node)
+
+ def announceGenerate(self):
+ DistributedEntity.DistributedEntity.announceGenerate(self)
+ self.initNodePathAttribs(self)
+
+ def delete(self):
+ self.removeNode()
+ DistributedEntity.DistributedEntity.delete(self)
diff --git a/otp/level/CollisionSolidEntity.py b/otp/level/CollisionSolidEntity.py
new file mode 100644
index 0000000..48feec0
--- /dev/null
+++ b/otp/level/CollisionSolidEntity.py
@@ -0,0 +1,45 @@
+from pandac.PandaModules import *
+from otp.otpbase import OTPGlobals
+from direct.directnotify import DirectNotifyGlobal
+import BasicEntities
+
+class CollisionSolidEntity(BasicEntities.NodePathEntity):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CollisionSolidEntity')
+
+ def __init__(self, level, entId):
+ self.collNodePath = None
+ BasicEntities.NodePathEntity.__init__(self, level, entId)
+ self.initSolid()
+ return
+
+ def destroy(self):
+ self.destroySolid()
+ BasicEntities.NodePathEntity.destroy(self)
+
+ def initSolid(self):
+ self.destroySolid()
+ if self.solidType == 'sphere':
+ solid = CollisionSphere(0, 0, 0, self.radius)
+ else:
+ solid = CollisionTube(0, 0, 0, 0, 0, self.length, self.radius)
+ node = CollisionNode(self.getUniqueName(self.__class__.__name__))
+ node.addSolid(solid)
+ node.setCollideMask(OTPGlobals.WallBitmask)
+ self.collNodePath = self.attachNewNode(node)
+ if __dev__:
+ if self.showSolid:
+ self.showCS()
+ else:
+ self.hideCS()
+
+ def destroySolid(self):
+ if self.collNodePath is not None:
+ self.collNodePath.removeNode()
+ self.collNodePath = None
+ return
+
+ if __dev__:
+
+ def attribChanged(self, attrib, value):
+ print 'attribChanged'
+ self.initSolid()
diff --git a/otp/level/CutScene.py b/otp/level/CutScene.py
new file mode 100644
index 0000000..a686d1e
--- /dev/null
+++ b/otp/level/CutScene.py
@@ -0,0 +1,99 @@
+from direct.showbase import DirectObject
+from direct.directnotify import DirectNotifyGlobal
+import BasicEntities
+from pandac.PandaModules import *
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from direct.distributed.ClockDelta import *
+from toontown.toonbase import ToontownGlobals
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+
+def nothing(self, track, subjectNodePath, duration):
+ return track
+
+
+def irisInOut(self, track, subjectNodePath, duration):
+ track.append(Sequence(Func(base.transitions.irisOut, 0.5), Func(base.transitions.irisIn, 1.5), Wait(duration), Func(base.transitions.irisOut, 1.0), Func(base.transitions.irisIn, 0.5)))
+ return track
+
+
+def letterBox(self, track, subjectNodePath, duration):
+ track.append(Sequence(Wait(duration)))
+ return track
+
+
+def foo1(self, track, subjectNodePath, duration):
+ track.append(Sequence(Func(base.localAvatar.stopUpdateSmartCamera), PosHprInterval(camera, other=subjectNodePath, pos=Point3(-2, -35, 7.5), hpr=VBase3(-7, 0, 0)), LerpPosHprInterval(nodePath=camera, other=subjectNodePath, duration=duration, pos=Point3(2, -22, 7.5), hpr=VBase3(4, 0, 0), blendType='easeInOut'), PosHprInterval(camera, other=subjectNodePath, pos=Point3(0, -28, 7.5), hpr=VBase3(0, 0, 0)), Func(base.localAvatar.startUpdateSmartCamera)))
+ return track
+
+
+def doorUnlock(self, track, subjectNodePath, duration):
+ track.append(Sequence(Func(base.localAvatar.stopUpdateSmartCamera), PosHprInterval(camera, other=self, pos=Point3(-2, -35, 7.5), hpr=VBase3(-7, 0, 0)), LerpPosHprInterval(nodePath=camera, other=self, duration=duration, pos=Point3(2, -22, 7.5), hpr=VBase3(4, 0, 0), blendType='easeInOut'), PosHprInterval(camera, other=self, pos=Point3(0, -28, 7.5), hpr=VBase3(0, 0, 0)), Func(base.localAvatar.startUpdateSmartCamera)))
+ return track
+
+
+class CutScene(BasicEntities.NodePathEntity, DirectObject.DirectObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CutScene')
+ effects = {'nothing': nothing,
+ 'irisInOut': irisInOut,
+ 'letterBox': letterBox}
+ motions = {'foo1': foo1,
+ 'doorUnlock': doorUnlock}
+
+ def __init__(self, level, entId):
+ DirectObject.DirectObject.__init__(self)
+ BasicEntities.NodePathEntity.__init__(self, level, entId)
+ self.track = None
+ self.setEffect(self.effect)
+ self.setMotion(self.motion)
+ self.subjectNodePath = render.attachNewNode('CutScene')
+ self.subjectNodePath.setPos(self.pos)
+ self.subjectNodePath.setHpr(self.hpr)
+ self.setStartStop(self.startStopEvent)
+ return
+
+ def destroy(self):
+ self.ignore(self.startStopEvent)
+ self.startStopEvent = None
+ BasicEntities.NodePathEntity.destroy(self)
+ return
+
+ def setEffect(self, effect):
+ self.effect = effect
+ self.getEffect = self.effects[effect]
+
+ def setMotion(self, motion):
+ self.motionType = motion
+ self.getMotion = self.motions[motion]
+
+ def setSubjectNodePath(self, subjectNodePath):
+ self.subjectNodePath = subjectNodePath
+
+ def startOrStop(self, start):
+ trackName = 'cutSceneTrack-%d' % (id(self),)
+ if start:
+ if self.track:
+ self.track.finish()
+ self.track = None
+ track = Parallel(name=trackName)
+ track = self.getEffect(self, track, self.subjectNodePath, self.duration)
+ track = self.getMotion(self, track, self.subjectNodePath, self.duration)
+ track = Sequence(Wait(0.4), track)
+ track.start(0.0)
+ self.track = track
+ elif self.track:
+ self.track.pause()
+ self.track = None
+ base.localAvatar.startUpdateSmartCamera()
+ return
+
+ def setStartStop(self, event):
+ if self.startStopEvent:
+ self.ignore(self.startStopEvent)
+ self.startStopEvent = self.getOutputEventName(event)
+ if self.startStopEvent:
+ self.accept(self.startStopEvent, self.startOrStop)
+
+ def getName(self):
+ return 'switch-%s' % (self.entId,)
diff --git a/otp/level/DistributedEntity.py b/otp/level/DistributedEntity.py
new file mode 100644
index 0000000..9d07859
--- /dev/null
+++ b/otp/level/DistributedEntity.py
@@ -0,0 +1,50 @@
+from direct.distributed import DistributedObject
+import Entity
+from direct.directnotify import DirectNotifyGlobal
+
+class DistributedEntity(DistributedObject.DistributedObject, Entity.Entity):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedEntity')
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ Entity.Entity.__init__(self)
+ self.levelDoId = 0
+ self.entId = 0
+ self.level = None
+ return
+
+ def generateInit(self):
+ DistributedEntity.notify.debug('generateInit')
+ DistributedObject.DistributedObject.generateInit(self)
+
+ def generate(self):
+ DistributedEntity.notify.debug('generate')
+ DistributedObject.DistributedObject.generate(self)
+
+ def setLevelDoId(self, levelDoId):
+ DistributedEntity.notify.debug('setLevelDoId: %s' % levelDoId)
+ self.levelDoId = levelDoId
+
+ def setEntId(self, entId):
+ DistributedEntity.notify.debug('setEntId: %s' % entId)
+ self.entId = entId
+
+ def announceGenerate(self):
+ DistributedEntity.notify.debug('announceGenerate (%s)' % self.entId)
+ if self.levelDoId != 0:
+ level = base.cr.doId2do[self.levelDoId]
+ self.initializeEntity(level, self.entId)
+ self.level.onEntityCreate(self.entId)
+ else:
+ self.level = None
+ DistributedObject.DistributedObject.announceGenerate(self)
+ return
+
+ def disable(self):
+ DistributedEntity.notify.debug('disable (%s)' % self.entId)
+ self.destroy()
+ DistributedObject.DistributedObject.disable(self)
+
+ def delete(self):
+ DistributedEntity.notify.debug('delete')
+ DistributedObject.DistributedObject.delete(self)
diff --git a/otp/level/DistributedEntityAI.py b/otp/level/DistributedEntityAI.py
new file mode 100644
index 0000000..06e30af
--- /dev/null
+++ b/otp/level/DistributedEntityAI.py
@@ -0,0 +1,45 @@
+from direct.distributed import DistributedObjectAI
+import Entity
+from direct.directnotify import DirectNotifyGlobal
+
+class DistributedEntityAI(DistributedObjectAI.DistributedObjectAI, Entity.Entity):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedEntityAI')
+
+ def __init__(self, level, entId):
+ if hasattr(level, 'air'):
+ air = level.air
+ self.levelDoId = level.doId
+ else:
+ air = level
+ level = None
+ self.levelDoId = 0
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ Entity.Entity.__init__(self, level, entId)
+ return
+
+ def generate(self):
+ self.notify.debug('generate')
+ DistributedObjectAI.DistributedObjectAI.generate(self)
+
+ def destroy(self):
+ self.notify.debug('destroy')
+ Entity.Entity.destroy(self)
+ self.requestDelete()
+
+ def delete(self):
+ self.notify.debug('delete')
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def getLevelDoId(self):
+ return self.levelDoId
+
+ def getEntId(self):
+ return self.entId
+
+ if __dev__:
+
+ def setParentEntId(self, parentEntId):
+ self.parentEntId = parentEntId
+ newZoneId = self.getZoneEntity().getZoneId()
+ if newZoneId != self.zoneId:
+ self.sendSetZone(newZoneId)
diff --git a/otp/level/DistributedInteractiveEntity.py b/otp/level/DistributedInteractiveEntity.py
new file mode 100644
index 0000000..b8eaa5a
--- /dev/null
+++ b/otp/level/DistributedInteractiveEntity.py
@@ -0,0 +1,69 @@
+from pandac.PandaModules import *
+from direct.distributed.ClockDelta import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+import DistributedEntity
+
+class DistributedInteractiveEntity(DistributedEntity.DistributedEntity):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedInteractiveEntity')
+
+ def __init__(self, cr):
+ DistributedEntity.DistributedEntity.__init__(self, cr)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedInteractiveEntity', [State.State('off', self.enterOff, self.exitOff, ['playing', 'attract']), State.State('attract', self.enterAttract, self.exitAttract, ['playing']), State.State('playing', self.enterPlaying, self.exitPlaying, ['attract'])], 'off', 'off')
+ self.fsm.enterInitialState()
+
+ def generate(self):
+ DistributedEntity.DistributedEntity.generate(self)
+
+ def disable(self):
+ self.fsm.request('off')
+ DistributedEntity.DistributedEntity.disable(self)
+
+ def delete(self):
+ del self.fsm
+ DistributedEntity.DistributedEntity.delete(self)
+
+ def setAvatarInteract(self, avatarId):
+ self.avatarId = avatarId
+
+ def setOwnerDoId(self, ownerDoId):
+ self.ownerDoId = ownerDoId
+
+ def setState(self, state, timestamp):
+ if self.isGenerated():
+ self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])
+ else:
+ self.initialState = state
+ self.initialStateTimestamp = timestamp
+
+ def enterTrigger(self, args = None):
+ messenger.send('DistributedInteractiveEntity_enterTrigger')
+ self.sendUpdate('requestInteract')
+
+ def exitTrigger(self, args = None):
+ messenger.send('DistributedInteractiveEntity_exitTrigger')
+ self.sendUpdate('requestExit')
+
+ def rejectInteract(self):
+ self.cr.playGame.getPlace().setState('walk')
+
+ def avatarExit(self, avatarId):
+ pass
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterAttract(self, ts):
+ pass
+
+ def exitAttract(self):
+ pass
+
+ def enterPlaying(self, ts):
+ pass
+
+ def exitPlaying(self):
+ pass
diff --git a/otp/level/DistributedLevel.py b/otp/level/DistributedLevel.py
new file mode 100644
index 0000000..96eef3d
--- /dev/null
+++ b/otp/level/DistributedLevel.py
@@ -0,0 +1,586 @@
+from direct.distributed.ClockDelta import *
+from pandac.PandaModules import *
+from direct.showbase.PythonUtil import Functor, sameElements, list2dict, uniqueElements
+from direct.interval.IntervalGlobal import *
+from toontown.distributed.ToontownMsgTypes import *
+from toontown.toonbase import ToontownGlobals
+from otp.otpbase import OTPGlobals
+from direct.distributed import DistributedObject
+import Level
+import LevelConstants
+from direct.directnotify import DirectNotifyGlobal
+import EntityCreator
+from direct.gui import OnscreenText
+from direct.task import Task
+import LevelUtil
+import random
+
+class DistributedLevel(DistributedObject.DistributedObject, Level.Level):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevel')
+ WantVisibility = config.GetBool('level-visibility', 1)
+ ColorZonesAllDOs = 0
+ FloorCollPrefix = 'zoneFloor'
+ OuchTaskName = 'ouchTask'
+ VisChangeTaskName = 'visChange'
+ EmulateEntrancePoint = True
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ Level.Level.__init__(self)
+ self.lastToonZone = None
+ self.lastCamZone = 0
+ self.titleColor = (1, 1, 1, 1)
+ self.titleText = OnscreenText.OnscreenText('', fg=self.titleColor, shadow=(0, 0, 0, 1), font=ToontownGlobals.getSuitFont(), pos=(0, -0.5), scale=0.16, drawOrder=0, mayChange=1)
+ self.smallTitleText = OnscreenText.OnscreenText('', fg=self.titleColor, font=ToontownGlobals.getSuitFont(), pos=(0.65, 0.9), scale=0.08, drawOrder=0, mayChange=1, bg=(0.5, 0.5, 0.5, 0.5), align=TextNode.ARight)
+ self.zonesEnteredList = []
+ self.fColorZones = 0
+ self.scenarioIndex = 0
+ return
+
+ def generate(self):
+ DistributedLevel.notify.debug('generate')
+ DistributedObject.DistributedObject.generate(self)
+ self.parent2pendingChildren = {}
+ self.curSpec = None
+ if base.cr.timeManager is not None:
+ base.cr.timeManager.synchronize('DistributedLevel.generate')
+ else:
+ self.notify.warning('generate(): no TimeManager!')
+ return
+
+ def setLevelZoneId(self, zoneId):
+ self.levelZone = zoneId
+
+ def setPlayerIds(self, avIdList):
+ self.avIdList = avIdList
+
+ def setEntranceId(self, entranceId):
+ self.entranceId = entranceId
+
+ def getEntranceId(self):
+ return self.entranceId
+
+ def setZoneIds(self, zoneIds):
+ DistributedLevel.notify.debug('setZoneIds: %s' % zoneIds)
+ self.zoneIds = zoneIds
+
+ def setStartTimestamp(self, timestamp):
+ DistributedLevel.notify.debug('setStartTimestamp: %s' % timestamp)
+ self.startTime = globalClockDelta.networkToLocalTime(timestamp, bits=32)
+ self.privGotAllRequired()
+
+ def privGotAllRequired(self):
+ self.levelAnnounceGenerate()
+
+ def levelAnnounceGenerate(self):
+ pass
+
+ def initializeLevel(self, levelSpec):
+ if __dev__:
+ self.candidateSpec = levelSpec
+ self.sendUpdate('requestCurrentLevelSpec', [levelSpec.stringHash(), levelSpec.entTypeReg.getHashStr()])
+ else:
+ self.privGotSpec(levelSpec)
+
+ if __dev__:
+
+ def reportModelSpecSyncError(self, msg):
+ DistributedLevel.notify.error('%s\n\nyour spec does not match the level model\nuse SpecUtil.updateSpec, then restart your AI and client' % msg)
+
+ def setSpecDeny(self, reason):
+ DistributedLevel.notify.error(reason)
+
+ def setSpecSenderDoId(self, doId):
+ DistributedLevel.notify.debug('setSpecSenderDoId: %s' % doId)
+ blobSender = base.cr.doId2do[doId]
+
+ def setSpecBlob(specBlob, blobSender = blobSender, self = self):
+ blobSender.sendAck()
+ from LevelSpec import LevelSpec
+ spec = eval(specBlob)
+ if spec is None:
+ spec = self.candidateSpec
+ del self.candidateSpec
+ self.privGotSpec(spec)
+ return
+
+ if blobSender.isComplete():
+ setSpecBlob(blobSender.getBlob())
+ else:
+ evtName = self.uniqueName('specDone')
+ blobSender.setDoneEvent(evtName)
+ self.acceptOnce(evtName, setSpecBlob)
+
+ def privGotSpec(self, levelSpec):
+ Level.Level.initializeLevel(self, self.doId, levelSpec, self.scenarioIndex)
+ modelZoneNums = self.zoneNums
+ specZoneNums = self.zoneNum2zoneId.keys()
+ if not sameElements(modelZoneNums, specZoneNums):
+ self.reportModelSpecSyncError('model zone nums (%s) do not match spec zone nums (%s)' % (modelZoneNums, specZoneNums))
+ self.initVisibility()
+ self.placeLocalToon()
+
+ def announceLeaving(self):
+ DistributedLevel.notify.debug('announceLeaving')
+ self.doneBarrier()
+
+ def placeLocalToon(self, moveLocalAvatar = True):
+ initialZoneEnt = None
+ if self.entranceId in self.entranceId2entity:
+ epEnt = self.entranceId2entity[self.entranceId]
+ if moveLocalAvatar:
+ epEnt.placeToon(base.localAvatar, self.avIdList.index(base.localAvatar.doId), len(self.avIdList))
+ initialZoneEnt = self.getEntity(epEnt.getZoneEntId())
+ elif self.EmulateEntrancePoint:
+ self.notify.debug('unknown entranceId %s' % self.entranceId)
+ if moveLocalAvatar:
+ base.localAvatar.reparentTo(render)
+ base.localAvatar.setPosHpr(0, 0, 0, 0, 0, 0)
+ self.notify.debug('showing all zones')
+ self.setColorZones(1)
+ zoneEntIds = list(self.entType2ids['zone'])
+ zoneEntIds.remove(LevelConstants.UberZoneEntId)
+ if len(zoneEntIds):
+ zoneEntId = random.choice(zoneEntIds)
+ initialZoneEnt = self.getEntity(zoneEntId)
+ if moveLocalAvatar:
+ base.localAvatar.setPos(render, initialZoneEnt.getZoneNode().getPos(render))
+ else:
+ initialZoneEnt = self.getEntity(LevelConstants.UberZoneEntId)
+ if moveLocalAvatar:
+ base.localAvatar.setPos(render, 0, 0, 0)
+ if initialZoneEnt is not None:
+ self.enterZone(initialZoneEnt.entId)
+ return
+
+ def createEntityCreator(self):
+ return EntityCreator.EntityCreator(level=self)
+
+ def onEntityTypePostCreate(self, entType):
+ Level.Level.onEntityTypePostCreate(self, entType)
+ if entType == 'levelMgr':
+ self.__handleLevelMgrCreated()
+
+ def __handleLevelMgrCreated(self):
+ levelMgr = self.getEntity(LevelConstants.LevelMgrEntId)
+ self.geom = levelMgr.geom
+ self.zoneNum2node = LevelUtil.getZoneNum2Node(self.geom)
+ self.zoneNums = self.zoneNum2node.keys()
+ self.zoneNums.sort()
+ self.zoneNumDict = list2dict(self.zoneNums)
+ DistributedLevel.notify.debug('zones from model: %s' % self.zoneNums)
+ self.fixupLevelModel()
+
+ def fixupLevelModel(self):
+ for zoneNum, zoneNode in self.zoneNum2node.items():
+ if zoneNum == LevelConstants.UberZoneEntId:
+ continue
+ allColls = zoneNode.findAllMatches('**/+CollisionNode')
+ floorColls = []
+ for coll in allColls:
+ bitmask = coll.node().getIntoCollideMask()
+ if not (bitmask & ToontownGlobals.FloorBitmask).isZero():
+ floorColls.append(coll)
+
+ if len(floorColls) > 0:
+ floorCollName = '%s%s' % (DistributedLevel.FloorCollPrefix, zoneNum)
+ others = zoneNode.findAllMatches('**/%s' % floorCollName)
+ for other in others:
+ other.setName('%s_renamed' % floorCollName)
+
+ for floorColl in floorColls:
+ floorColl.setName(floorCollName)
+
+ def handleZoneEnter(collisionEntry, self = self, zoneNum = zoneNum):
+ self.toonEnterZone(zoneNum)
+ floorNode = collisionEntry.getIntoNode()
+ if floorNode.hasTag('ouch'):
+ ouchLevel = int(self.getFloorOuchLevel())
+ self.startOuch(ouchLevel)
+
+ self.accept('enter%s' % floorCollName, handleZoneEnter)
+
+ def handleZoneExit(collisionEntry, self = self, zoneNum = zoneNum):
+ floorNode = collisionEntry.getIntoNode()
+ if floorNode.hasTag('ouch'):
+ self.stopOuch()
+
+ self.accept('exit%s' % floorCollName, handleZoneExit)
+
+ def getFloorOuchLevel(self):
+ return 1
+
+ def announceGenerate(self):
+ DistributedLevel.notify.debug('announceGenerate')
+ DistributedObject.DistributedObject.announceGenerate(self)
+
+ def disable(self):
+ DistributedLevel.notify.debug('disable')
+ if hasattr(self, 'geom'):
+ del self.geom
+ self.shutdownVisibility()
+ self.destroyLevel()
+ self.ignoreAll()
+ taskMgr.remove(self.uniqueName('titleText'))
+ if self.smallTitleText:
+ self.smallTitleText.cleanup()
+ self.smallTitleText = None
+ if self.titleText:
+ self.titleText.cleanup()
+ self.titleText = None
+ self.zonesEnteredList = []
+ DistributedObject.DistributedObject.disable(self)
+ return
+
+ def delete(self):
+ DistributedLevel.notify.debug('delete')
+ DistributedObject.DistributedObject.delete(self)
+ self.stopOuch()
+
+ def requestReparent(self, entity, parentId, wrt = False):
+ parent = self.getEntity(parentId)
+ if parent is not None:
+ if wrt:
+ entity.wrtReparentTo(parent.getNodePath())
+ else:
+ entity.reparentTo(parent.getNodePath())
+ else:
+ DistributedLevel.notify.debug('entity %s requesting reparent to %s, not yet created' % (entity, parentId))
+ entity.reparentTo(hidden)
+ if not self.parent2pendingChildren.has_key(parentId):
+ self.parent2pendingChildren[parentId] = []
+
+ def doReparent(parentId = parentId, self = self, wrt = wrt):
+ parent = self.getEntity(parentId)
+ for child in self.parent2pendingChildren[parentId]:
+ DistributedLevel.notify.debug('performing pending reparent of %s to %s' % (child, parent))
+ if wrt:
+ child.wrtReparentTo(parent.getNodePath())
+ else:
+ child.reparentTo(parent.getNodePath())
+
+ del self.parent2pendingChildren[parentId]
+ self.ignore(self.getEntityCreateEvent(parentId))
+
+ self.accept(self.getEntityCreateEvent(parentId), doReparent)
+ self.parent2pendingChildren[parentId].append(entity)
+ return
+
+ def getZoneNode(self, zoneEntId):
+ return self.zoneNum2node.get(zoneEntId)
+
+ def warpToZone(self, zoneNum):
+ zoneNode = self.getZoneNode(zoneNum)
+ if zoneNode is None:
+ return
+ base.localAvatar.setPos(zoneNode, 0, 0, 0)
+ base.localAvatar.setHpr(zoneNode, 0, 0, 0)
+ self.enterZone(zoneNum)
+ return
+
+ def showZone(self, zoneNum):
+ zone = self.getZoneNode(zoneNum)
+ zone.unstash()
+ zone.clearColor()
+
+ def setColorZones(self, fColorZones):
+ self.fColorZones = fColorZones
+ self.resetVisibility()
+
+ def getColorZones(self):
+ return self.fColorZones
+
+ def hideZone(self, zoneNum):
+ zone = self.getZoneNode(zoneNum)
+ if self.fColorZones:
+ zone.unstash()
+ zone.setColor(1, 0, 0)
+ else:
+ zone.stash()
+
+ def setTransparency(self, alpha, zone = None):
+ self.geom.setTransparency(1)
+ if zone is None:
+ node = self.geom
+ else:
+ node = self.getZoneNode(zoneNum)
+ node.setAlphaScale(alpha)
+ return
+
+ def initVisibility(self):
+ self.curVisibleZoneNums = list2dict(self.zoneNums)
+ del self.curVisibleZoneNums[LevelConstants.UberZoneEntId]
+ self.curZoneNum = None
+ self.visChangedThisFrame = 0
+ self.fForceSetZoneThisFrame = 0
+
+ def handleCameraRayFloorCollision(collEntry, self = self):
+ name = collEntry.getIntoNode().getName()
+ self.notify.debug('camera floor ray collided with: %s' % name)
+ prefixLen = len(DistributedLevel.FloorCollPrefix)
+ if name[:prefixLen] == DistributedLevel.FloorCollPrefix:
+ try:
+ zoneNum = int(name[prefixLen:])
+ except:
+ DistributedLevel.notify.warning('Invalid zone floor collision node: %s' % name)
+ else:
+ self.camEnterZone(zoneNum)
+
+ self.accept('on-floor', handleCameraRayFloorCollision)
+ if not DistributedLevel.WantVisibility:
+ zoneNums = list(self.zoneNums)
+ zoneNums.remove(LevelConstants.UberZoneEntId)
+ self.forceSetZoneThisFrame()
+ self.setVisibility(zoneNums)
+ taskMgr.add(self.visChangeTask, self.uniqueName(DistributedLevel.VisChangeTaskName), priority=49)
+ return
+
+ def shutdownVisibility(self):
+ taskMgr.remove(self.uniqueName(DistributedLevel.VisChangeTaskName))
+
+ def toonEnterZone(self, zoneNum, ouchLevel = None):
+ DistributedLevel.notify.debug('toonEnterZone%s' % zoneNum)
+ if zoneNum != self.lastToonZone:
+ self.lastToonZone = zoneNum
+ self.notify.debug('toon is standing in zone %s' % zoneNum)
+ messenger.send('factoryZoneChanged', [zoneNum])
+
+ def camEnterZone(self, zoneNum):
+ DistributedLevel.notify.debug('camEnterZone%s' % zoneNum)
+ self.enterZone(zoneNum)
+ if zoneNum != self.lastCamZone:
+ self.lastCamZone = zoneNum
+ self.smallTitleText.hide()
+ self.spawnTitleText()
+
+ def lockVisibility(self, zoneNum = None, zoneId = None):
+ if zoneId is not None:
+ zoneNum = self.getZoneNumFromId(zoneId)
+ self.notify.debug('lockVisibility to zoneNum %s' % zoneNum)
+ self.lockVizZone = zoneNum
+ self.enterZone(self.lockVizZone)
+ return
+
+ def unlockVisibility(self):
+ self.notify.debug('unlockVisibility')
+ if not hasattr(self, 'lockVizZone'):
+ self.notify.warning('visibility already unlocked')
+ else:
+ del self.lockVizZone
+ self.updateVisibility()
+
+ def enterZone(self, zoneNum):
+ DistributedLevel.notify.debug('entering zone %s' % zoneNum)
+ if not DistributedLevel.WantVisibility:
+ return
+ if zoneNum == self.curZoneNum:
+ return
+ if zoneNum not in self.zoneNumDict:
+ DistributedLevel.notify.error('no ZoneEntity for this zone (%s)!!' % zoneNum)
+ self.updateVisibility(zoneNum)
+
+ def updateVisibility(self, zoneNum = None):
+ if zoneNum is None:
+ zoneNum = self.curZoneNum
+ if zoneNum is None:
+ return
+ if hasattr(self, 'lockVizZone'):
+ zoneNum = self.lockVizZone
+ zoneEnt = self.getEntity(zoneNum)
+ visibleZoneNums = list2dict([zoneNum])
+ visibleZoneNums.update(list2dict(zoneEnt.getVisibleZoneNums()))
+ if not __debug__:
+ if self.lastToonZone not in visibleZoneNums:
+ if self.lastToonZone is not None:
+ self.notify.warning('adding zoneNum %s to visibility list because toon is standing in that zone!' % self.lastToonZone)
+ visibleZoneNums.update(list2dict([self.lastToonZone]))
+ zoneEntIds = list(self.entType2ids['zone'])
+ zoneEntIds.remove(LevelConstants.UberZoneEntId)
+ if len(zoneEntIds):
+ pass
+ vizZonesChanged = 1
+ addedZoneNums = []
+ removedZoneNums = []
+ allVZ = dict(visibleZoneNums)
+ allVZ.update(self.curVisibleZoneNums)
+ for vz, dummy in allVZ.items():
+ new = vz in visibleZoneNums
+ old = vz in self.curVisibleZoneNums
+ if new and old:
+ continue
+ if new:
+ addedZoneNums.append(vz)
+ else:
+ removedZoneNums.append(vz)
+
+ if not addedZoneNums and not removedZoneNums:
+ DistributedLevel.notify.debug('visible zone list has not changed')
+ vizZonesChanged = 0
+ else:
+ DistributedLevel.notify.debug('showing zones %s' % addedZoneNums)
+ for az in addedZoneNums:
+ self.showZone(az)
+
+ DistributedLevel.notify.debug('hiding zones %s' % removedZoneNums)
+ for rz in removedZoneNums:
+ self.hideZone(rz)
+
+ if vizZonesChanged or self.fForceSetZoneThisFrame:
+ self.setVisibility(visibleZoneNums.keys())
+ self.fForceSetZoneThisFrame = 0
+ self.curZoneNum = zoneNum
+ self.curVisibleZoneNums = visibleZoneNums
+ return
+
+ def setVisibility(self, vizList):
+ if self.fColorZones and DistributedLevel.ColorZonesAllDOs:
+ vizList = list(self.zoneNums)
+ vizList.remove(LevelConstants.UberZoneEntId)
+ uberZone = self.getZoneId(LevelConstants.UberZoneEntId)
+ visibleZoneIds = [OTPGlobals.UberZone, self.levelZone, uberZone]
+ for vz in vizList:
+ if vz is not LevelConstants.UberZoneEntId:
+ visibleZoneIds.append(self.getZoneId(vz))
+
+ DistributedLevel.notify.debug('new viz list: %s' % visibleZoneIds)
+ base.cr.sendSetZoneMsg(self.levelZone, visibleZoneIds)
+
+ def resetVisibility(self):
+ self.curVisibleZoneNums = list2dict(self.zoneNums)
+ del self.curVisibleZoneNums[LevelConstants.UberZoneEntId]
+ for vz, dummy in self.curVisibleZoneNums.items():
+ self.showZone(vz)
+
+ self.updateVisibility()
+
+ def handleVisChange(self):
+ Level.Level.handleVisChange(self)
+ self.visChangedThisFrame = 1
+
+ def forceSetZoneThisFrame(self):
+ self.fForceSetZoneThisFrame = 1
+
+ def visChangeTask(self, task):
+ if self.visChangedThisFrame or self.fForceSetZoneThisFrame:
+ self.updateVisibility()
+ self.visChangedThisFrame = 0
+ return Task.cont
+
+ if __dev__:
+
+ def setAttribChange(self, entId, attribName, valueStr, username):
+ value = eval(valueStr)
+ self.levelSpec.setAttribChange(entId, attribName, value, username)
+
+ def spawnTitleText(self):
+
+ def getDescription(zoneNum, self = self):
+ ent = self.entities.get(zoneNum)
+ if ent and hasattr(ent, 'description'):
+ return ent.description
+ return None
+
+ description = getDescription(self.lastCamZone)
+ if description and description != '':
+ taskMgr.remove(self.uniqueName('titleText'))
+ self.smallTitleText.setText(description)
+ self.titleText.setText(description)
+ self.titleText.setColor(Vec4(*self.titleColor))
+ self.titleText.setFg(self.titleColor)
+ titleSeq = None
+ if self.lastCamZone not in self.zonesEnteredList:
+ self.zonesEnteredList.append(self.lastCamZone)
+ titleSeq = Task.sequence(Task.Task(self.hideSmallTitleTextTask), Task.Task(self.showTitleTextTask), Task.pause(0.1), Task.pause(6.0), self.titleText.lerpColor(Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], self.titleColor[3]), Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], 0.0), 0.5))
+ smallTitleSeq = Task.sequence(Task.Task(self.hideTitleTextTask), Task.Task(self.showSmallTitleTask))
+ if titleSeq:
+ seq = Task.sequence(titleSeq, smallTitleSeq)
+ else:
+ seq = smallTitleSeq
+ taskMgr.add(seq, self.uniqueName('titleText'))
+ return
+
+ def showInfoText(self, text = 'hello world'):
+ description = text
+ if description and description != '':
+ taskMgr.remove(self.uniqueName('titleText'))
+ self.smallTitleText.setText(description)
+ self.titleText.setText(description)
+ self.titleText.setColor(Vec4(*self.titleColor))
+ self.titleText.setFg(self.titleColor)
+ titleSeq = None
+ titleSeq = Task.sequence(Task.Task(self.hideSmallTitleTextTask), Task.Task(self.showTitleTextTask), Task.pause(0.1), Task.pause(3.0), self.titleText.lerpColor(Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], self.titleColor[3]), Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], 0.0), 0.5))
+ if titleSeq:
+ seq = Task.sequence(titleSeq)
+ taskMgr.add(seq, self.uniqueName('titleText'))
+ return
+
+ def showTitleTextTask(self, task):
+ self.titleText.show()
+ return Task.done
+
+ def hideTitleTextTask(self, task):
+ if self.titleText:
+ self.titleText.hide()
+ return Task.done
+
+ def showSmallTitleTask(self, task):
+ if self.titleText:
+ self.titleText.hide()
+ self.smallTitleText.show()
+ return Task.done
+
+ def hideSmallTitleTextTask(self, task):
+ if self.smallTitleText:
+ self.smallTitleText.hide()
+ return Task.done
+
+ def startOuch(self, ouchLevel, period = 2):
+ self.notify.debug('startOuch %s' % ouchLevel)
+ if not hasattr(self, 'doingOuch'):
+
+ def doOuch(task, self = self, ouchLevel = ouchLevel, period = period):
+ self.b_setOuch(ouchLevel)
+ self.lastOuchTime = globalClock.getFrameTime()
+ taskMgr.doMethodLater(period, doOuch, DistributedLevel.OuchTaskName)
+
+ delay = 0
+ if hasattr(self, 'lastOuchTime'):
+ curFrameTime = globalClock.getFrameTime()
+ timeSinceLastOuch = curFrameTime - self.lastOuchTime
+ if timeSinceLastOuch < period:
+ delay = period - timeSinceLastOuch
+ if delay > 0:
+ taskMgr.doMethodLater(period, doOuch, DistributedLevel.OuchTaskName)
+ else:
+ doOuch(None)
+ self.doingOuch = 1
+ return
+
+ def stopOuch(self):
+ if hasattr(self, 'doingOuch'):
+ taskMgr.remove(DistributedLevel.OuchTaskName)
+ del self.doingOuch
+
+ def b_setOuch(self, penalty, anim = None):
+ self.notify.debug('b_setOuch %s' % penalty)
+ av = base.localAvatar
+ if not av.isStunned:
+ self.d_setOuch(penalty)
+ self.setOuch(penalty, anim)
+
+ def d_setOuch(self, penalty):
+ self.sendUpdate('setOuch', [penalty])
+
+ def setOuch(self, penalty, anim = None):
+ if anim == 'Squish':
+ if base.cr.playGame.getPlace():
+ base.cr.playGame.getPlace().fsm.request('squished')
+ elif anim == 'Fall':
+ if base.cr.playGame.getPlace():
+ base.cr.playGame.getPlace().fsm.request('fallDown')
+ av = base.localAvatar
+ av.stunToon()
+ av.playDialogueForString('!')
+
+ def complexVis(self):
+ return 1
diff --git a/otp/level/DistributedLevelAI.py b/otp/level/DistributedLevelAI.py
new file mode 100644
index 0000000..53a7924
--- /dev/null
+++ b/otp/level/DistributedLevelAI.py
@@ -0,0 +1,180 @@
+from pandac import PandaModules as PM
+from otp.ai.AIBaseGlobal import *
+from direct.distributed.ClockDelta import *
+from direct.distributed import DistributedObjectAI
+import Level
+from direct.directnotify import DirectNotifyGlobal
+import EntityCreatorAI
+from direct.showbase.PythonUtil import Functor, weightedChoice
+
+class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI, Level.Level):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevelAI')
+
+ def __init__(self, air, zoneId, entranceId, avIds):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ Level.Level.__init__(self)
+ self.zoneId = zoneId
+ self.entranceId = entranceId
+ if len(avIds) <= 0 or len(avIds) > 4:
+ self.notify.warning('How do we have this many avIds? avIds: %s' % avIds)
+ self.avIdList = avIds
+ self.numPlayers = len(self.avIdList)
+ self.presentAvIds = list(self.avIdList)
+ self.notify.debug('expecting avatars: %s' % str(self.avIdList))
+ if __dev__:
+ self.modified = 0
+
+ def setLevelSpec(self, levelSpec):
+ self.levelSpec = levelSpec
+
+ def generate(self, levelSpec=None):
+ self.notify.debug('generate')
+ DistributedObjectAI.DistributedObjectAI.generate(self)
+ if levelSpec == None:
+ levelSpec = self.levelSpec
+ self.initializeLevel(levelSpec)
+ self.sendUpdate('setZoneIds', [self.zoneIds])
+ self.sendUpdate('setStartTimestamp', [self.startTimestamp])
+ if __dev__:
+ pass
+ return
+
+ def getLevelZoneId(self):
+ return self.zoneId
+
+ def getPlayerIds(self):
+ return self.avIdList
+
+ def getEntranceId(self):
+ return self.entranceId
+
+ def getBattleCreditMultiplier(self):
+ return 1.0
+
+ def delete(self, deAllocZone=True):
+ self.notify.debug('delete')
+ if __dev__:
+ self.removeAutosaveTask()
+ self.destroyLevel()
+ self.ignoreAll()
+ if deAllocZone:
+ self.air.deallocateZone(self.zoneId)
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def initializeLevel(self, levelSpec):
+ self.startTime = globalClock.getRealTime()
+ self.startTimestamp = globalClockDelta.localToNetworkTime(self.startTime, bits=32)
+ lol = zip([1] * levelSpec.getNumScenarios(), range(levelSpec.getNumScenarios()))
+ scenarioIndex = weightedChoice(lol)
+ Level.Level.initializeLevel(self, self.doId, levelSpec, scenarioIndex)
+ if __dev__:
+ self.accept(self.editMgrEntity.getSpecSaveEvent(), self.saveSpec)
+ for avId in self.avIdList:
+ self.acceptOnce(self.air.getAvatarExitEvent(avId), Functor(self.handleAvatarDisconnect, avId))
+
+ self.allToonsGoneBarrier = self.beginBarrier('allToonsGone', self.avIdList, 3 * 24 * 60 * 60, self.allToonsGone)
+
+ def handleAvatarDisconnect(self, avId):
+ try:
+ self.presentAvIds.remove(avId)
+ DistributedLevelAI.notify.warning('av %s has disconnected' % avId)
+ except:
+ DistributedLevelAI.notify.warning('got disconnect for av %s, not in list' % avId)
+
+ if not self.presentAvIds:
+ self.allToonsGone([])
+
+ def _levelControlsRequestDelete(self):
+ return True
+
+ def allToonsGone(self, toonsThatCleared):
+ DistributedLevelAI.notify.info('allToonsGone')
+ if hasattr(self, 'allToonsGoneBarrier'):
+ self.ignoreBarrier(self.allToonsGoneBarrier)
+ del self.allToonsGoneBarrier
+ for avId in self.avIdList:
+ self.ignore(self.air.getAvatarExitEvent(avId))
+
+ if self._levelControlsRequestDelete():
+ self.requestDelete()
+
+ def createEntityCreator(self):
+ return EntityCreatorAI.EntityCreatorAI(level=self)
+
+ def setOuch(self, penalty):
+ avId = self.air.getAvatarIdFromSender()
+ av = self.air.doId2do.get(avId)
+ self.notify.debug('setOuch %s' % penalty)
+ if av and penalty > 0:
+ av.takeDamage(penalty)
+ if av.getHp() <= 0:
+ av.inventory.zeroInv()
+ av.d_setInventory(av.inventory.makeNetString())
+
+ def requestCurrentLevelSpec(self, specHash, entTypeRegHash):
+ senderId = self.air.getAvatarIdFromSender()
+ self.notify.info('av %s: specHash %s, entTypeRegHash %s' % (senderId, specHash, entTypeRegHash))
+ if not __dev__:
+ self.notify.info('client is in dev mode and we are not')
+ self.sendUpdateToAvatarId(senderId, 'setSpecDeny', [
+ 'AI server is not running in dev mode. Set want-dev to false on your client or true on the AI.'])
+ return
+ srvHash = self.levelSpec.entTypeReg.getHashStr()
+ self.notify.info('srv entTypeRegHash %s' % srvHash)
+ if srvHash != entTypeRegHash:
+ self.sendUpdateToAvatarId(senderId, 'setSpecDeny', [
+ 'EntityTypeRegistry hashes do not match! (server:%s, client:%s' % (srvHash, entTypeRegHash)])
+ return
+ if self.levelSpec.stringHash() != specHash:
+ self.notify.info('spec hashes do not match, sending our spec')
+ spec = self.levelSpec
+ useDisk = simbase.air._specByDisk
+ else:
+ self.notify.info('spec hashes match, sending null spec')
+ spec = None
+ useDisk = 0
+ specStr = repr(spec)
+ from direct.directutil import DistributedLargeBlobSenderAI
+ largeBlob = DistributedLargeBlobSenderAI.DistributedLargeBlobSenderAI(self.air, self.zoneId, senderId, specStr, useDisk=useDisk)
+ self.sendUpdateToAvatarId(senderId, 'setSpecSenderDoId', [largeBlob.doId])
+ return
+
+ if __dev__:
+
+ def setAttribChange(self, entId, attribName, value, username='SYSTEM'):
+ DistributedLevelAI.notify.info('setAttribChange(%s): %s, %s = %s' % (username, entId, attribName, repr(value)))
+ self.sendUpdate('setAttribChange', [
+ entId, attribName, repr(value), username])
+ self.levelSpec.setAttribChange(entId, attribName, value, username)
+ self.modified = 1
+ self.scheduleAutosave()
+
+ AutosavePeriod = simbase.config.GetFloat('level-autosave-period-minutes', 5)
+
+ def scheduleAutosave(self):
+ if hasattr(self, 'autosaveTask'):
+ return
+ self.autosaveTaskName = self.uniqueName('autosaveSpec')
+ self.autosaveTask = taskMgr.doMethodLater(DistributedLevelAI.AutosavePeriod * 60, self.autosaveSpec, self.autosaveTaskName)
+
+ def removeAutosaveTask(self):
+ if hasattr(self, 'autosaveTask'):
+ taskMgr.remove(self.autosaveTaskName)
+ del self.autosaveTask
+
+ def autosaveSpec(self, task=None):
+ self.removeAutosaveTask()
+ if self.modified:
+ DistributedLevelAI.notify.info('autosaving spec')
+ filename = self.levelSpec.getFilename()
+ filename = '%s.autosave' % filename
+ self.levelSpec.saveToDisk(filename, makeBackup=0)
+
+ def saveSpec(self, task=None):
+ DistributedLevelAI.notify.info('saving spec')
+ self.removeAutosaveTask()
+ if not self.modified:
+ DistributedLevelAI.notify.info('no changes to save')
+ return
+ self.levelSpec.saveToDisk()
+ self.modified = 0
diff --git a/otp/level/EditMgr.py b/otp/level/EditMgr.py
new file mode 100644
index 0000000..3a1927b
--- /dev/null
+++ b/otp/level/EditMgr.py
@@ -0,0 +1,4 @@
+import EditMgrBase
+
+class EditMgr(EditMgrBase.EditMgrBase):
+ pass
diff --git a/otp/level/EditMgrAI.py b/otp/level/EditMgrAI.py
new file mode 100644
index 0000000..dcb8b5e
--- /dev/null
+++ b/otp/level/EditMgrAI.py
@@ -0,0 +1,41 @@
+import EditMgrBase
+if __dev__:
+ from direct.showbase.PythonUtil import list2dict
+ import EditorGlobals
+
+class EditMgrAI(EditMgrBase.EditMgrBase):
+ if __dev__:
+
+ def setRequestNewEntity(self, data):
+ spec = self.level.levelSpec
+ entIds = spec.getAllEntIds()
+ entIdDict = list2dict(entIds)
+ allocRange = EditorGlobals.getEntIdAllocRange()
+ if not hasattr(self, 'lastAllocatedEntId'):
+ self.lastAllocatedEntId = allocRange[0]
+ idChosen = 0
+ while not idChosen:
+ for id in xrange(self.lastAllocatedEntId, allocRange[1]):
+ print id
+ if id not in entIdDict:
+ idChosen = 1
+ break
+ else:
+ if self.lastAllocatedEntId != allocRange[0]:
+ self.lastAllocatedEntId = allocRange[0]
+ else:
+ self.notify.error('out of entIds')
+
+ data.update({'entId': id})
+ self.lastAllocatedEntId = id
+ self.level.setAttribChange(self.entId, 'insertEntity', data)
+ self.level.levelSpec.doSetAttrib(self.entId, 'requestNewEntity', None)
+ return
+
+ def getSpecSaveEvent(self):
+ return 'requestSave-%s' % self.level.levelId
+
+ def setRequestSave(self, data):
+ messenger.send(self.getSpecSaveEvent())
+ self.level.levelSpec.doSetAttrib(self.entId, 'requestSave', None)
+ return
diff --git a/otp/level/EditMgrBase.py b/otp/level/EditMgrBase.py
new file mode 100644
index 0000000..fe4c7b1
--- /dev/null
+++ b/otp/level/EditMgrBase.py
@@ -0,0 +1,25 @@
+import Entity
+from direct.directnotify import DirectNotifyGlobal
+
+class EditMgrBase(Entity.Entity):
+ notify = DirectNotifyGlobal.directNotify.newCategory('EditMgr')
+
+ def __init__(self, level, entId):
+ Entity.Entity.__init__(self, level, entId)
+
+ def destroy(self):
+ Entity.Entity.destroy(self)
+ self.ignoreAll()
+
+ if __dev__:
+
+ def setInsertEntity(self, data):
+ self.level.setEntityCreatorUsername(data['entId'], data['username'])
+ self.level.levelSpec.insertEntity(data['entId'], data['entType'], data['parentEntId'])
+ self.level.levelSpec.doSetAttrib(self.entId, 'insertEntity', None)
+ return
+
+ def setRemoveEntity(self, data):
+ self.level.levelSpec.removeEntity(data['entId'])
+ self.level.levelSpec.doSetAttrib(self.entId, 'removeEntity', None)
+ return
diff --git a/otp/level/EditorGlobals.py b/otp/level/EditorGlobals.py
new file mode 100644
index 0000000..7edb648
--- /dev/null
+++ b/otp/level/EditorGlobals.py
@@ -0,0 +1,40 @@
+from direct.showbase.PythonUtil import uniqueElements
+EditTargetPostName = 'inGameEditTarget'
+EntIdRange = 10000
+username2entIdBase = {'darren': 1 * EntIdRange,
+ 'samir': 2 * EntIdRange,
+ 'skyler': 3 * EntIdRange,
+ 'joe': 4 * EntIdRange,
+ 'DrEvil': 5 * EntIdRange,
+ 'asad': 6 * EntIdRange,
+ 'drose': 7 * EntIdRange,
+ 'pappy': 8 * EntIdRange,
+ 'patricia': 9 * EntIdRange,
+ 'jloehrle': 10 * EntIdRange,
+ 'rurbino': 11 * EntIdRange}
+usernameConfigVar = 'level-edit-username'
+undefinedUsername = 'UNDEFINED_USERNAME'
+editUsername = config.GetString(usernameConfigVar, undefinedUsername)
+
+def checkNotReadyToEdit():
+ if editUsername == undefinedUsername:
+ return "you must config '%s'; see %s.py" % (usernameConfigVar, __name__)
+ if editUsername not in username2entIdBase:
+ return "unknown editor username '%s'; see %s.py" % (editUsername, __name__)
+ return None
+
+
+def assertReadyToEdit():
+ msg = checkNotReadyToEdit()
+ if msg is not None:
+ pass
+ return
+
+
+def getEditUsername():
+ return editUsername
+
+
+def getEntIdAllocRange():
+ baseId = username2entIdBase[editUsername]
+ return [baseId, baseId + EntIdRange]
diff --git a/otp/level/Entity.py b/otp/level/Entity.py
new file mode 100644
index 0000000..cb87f1c
--- /dev/null
+++ b/otp/level/Entity.py
@@ -0,0 +1,101 @@
+from direct.showbase.DirectObject import DirectObject
+from direct.showbase.PythonUtil import lineInfo
+import string
+from direct.directnotify import DirectNotifyGlobal
+
+class Entity(DirectObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('Entity')
+
+ def __init__(self, level = None, entId = None):
+ self.initializeEntity(level, entId)
+
+ def initializeEntity(self, level, entId):
+ self.level = level
+ self.entId = entId
+ if self.level is not None and self.entId is not None:
+ self.level.initializeEntity(self)
+ return
+
+ def __str__(self):
+ if hasattr(self, 'level') and self.level:
+ return 'ent%s(%s)' % (self.entId, self.level.getEntityType(self.entId))
+ elif hasattr(self, 'name'):
+ return self.name
+ elif hasattr(self, 'entId'):
+ return '%s-%s' % (self.__class__.__name__, self.entId)
+ else:
+ return self.__class__.__name__
+
+ def destroy(self):
+ Entity.notify.debug('Entity.destroy() %s' % self.entId)
+ if self.level:
+ if self.level.isInitialized():
+ self.level.onEntityDestroy(self.entId)
+ else:
+ Entity.notify.warning('Entity %s destroyed after level??' % self.entId)
+ self.ignoreAll()
+ del self.level
+ del self.entId
+
+ def getUniqueName(self, name, entId = None):
+ if entId is None:
+ entId = self.entId
+ return '%s-%s-%s' % (name, self.level.levelId, entId)
+
+ def getParentToken(self):
+ return self.level.getParentTokenForEntity(self.entId)
+
+ def getOutputEventName(self, entId = None):
+ if entId is None:
+ entId = self.entId
+ return self.getUniqueName('entityOutput', entId)
+
+ def getZoneEntId(self):
+ return self.level.getEntityZoneEntId(self.entId)
+
+ def getZoneEntity(self):
+ return self.level.getEntity(self.getZoneEntId())
+
+ def getZoneNode(self):
+ return self.getZoneEntity().getNodePath()
+
+ def privGetSetter(self, attrib):
+ setFuncName = 'set%s%s' % (string.upper(attrib[0]), attrib[1:])
+ if hasattr(self, setFuncName):
+ return getattr(self, setFuncName)
+ return None
+
+ def callSetters(self, *attribs):
+ self.privCallSetters(0, *attribs)
+
+ def callSettersAndDelete(self, *attribs):
+ self.privCallSetters(1, *attribs)
+
+ def privCallSetters(self, doDelete, *attribs):
+ for attrib in attribs:
+ if hasattr(self, attrib):
+ setter = self.privGetSetter(attrib)
+ if setter is not None:
+ value = getattr(self, attrib)
+ if doDelete:
+ delattr(self, attrib)
+ setter(value)
+
+ return
+
+ def setAttribInit(self, attrib, value):
+ self.__dict__[attrib] = value
+
+ if __dev__:
+
+ def handleAttribChange(self, attrib, value):
+ setter = self.privGetSetter(attrib)
+ if setter is not None:
+ setter(value)
+ else:
+ self.__dict__[attrib] = value
+ self.attribChanged(attrib, value)
+ return
+
+ def attribChanged(self, attrib, value):
+ pass
diff --git a/otp/level/EntityCreator.py b/otp/level/EntityCreator.py
new file mode 100644
index 0000000..8d73961
--- /dev/null
+++ b/otp/level/EntityCreator.py
@@ -0,0 +1,49 @@
+import CutScene
+import EntityCreatorBase
+import BasicEntities
+from direct.directnotify import DirectNotifyGlobal
+import EditMgr
+import EntrancePoint
+import LevelMgr
+import LogicGate
+import ZoneEntity
+import ModelEntity
+import PathEntity
+import VisibilityExtender
+import PropSpinner
+import AmbientSound
+import LocatorEntity
+import CollisionSolidEntity
+
+def nothing(*args):
+ return 'nothing'
+
+
+def nonlocal(*args):
+ return 'nonlocal'
+
+
+class EntityCreator(EntityCreatorBase.EntityCreatorBase):
+
+ def __init__(self, level):
+ EntityCreatorBase.EntityCreatorBase.__init__(self, level)
+ self.level = level
+ self.privRegisterTypes({'attribModifier': nothing,
+ 'ambientSound': AmbientSound.AmbientSound,
+ 'collisionSolid': CollisionSolidEntity.CollisionSolidEntity,
+ 'cutScene': CutScene.CutScene,
+ 'editMgr': EditMgr.EditMgr,
+ 'entityGroup': nothing,
+ 'entrancePoint': EntrancePoint.EntrancePoint,
+ 'levelMgr': LevelMgr.LevelMgr,
+ 'locator': LocatorEntity.LocatorEntity,
+ 'logicGate': LogicGate.LogicGate,
+ 'model': ModelEntity.ModelEntity,
+ 'nodepath': BasicEntities.NodePathEntity,
+ 'path': PathEntity.PathEntity,
+ 'propSpinner': PropSpinner.PropSpinner,
+ 'visibilityExtender': VisibilityExtender.VisibilityExtender,
+ 'zone': ZoneEntity.ZoneEntity})
+
+ def doCreateEntity(self, ctor, entId):
+ return ctor(self.level, entId)
diff --git a/otp/level/EntityCreatorAI.py b/otp/level/EntityCreatorAI.py
new file mode 100644
index 0000000..d14a2d3
--- /dev/null
+++ b/otp/level/EntityCreatorAI.py
@@ -0,0 +1,48 @@
+import EntityCreatorBase
+import LogicGate
+import EditMgrAI
+import LevelMgrAI
+import ZoneEntityAI
+from direct.showbase.PythonUtil import Functor
+
+def createDistributedEntity(AIclass, level, entId, zoneId):
+ ent = AIclass(level, entId)
+ ent.generateWithRequired(zoneId)
+ return ent
+
+
+def createLocalEntity(AIclass, level, entId, zoneId):
+ ent = AIclass(level, entId)
+ return ent
+
+
+def nothing(*args):
+ return 'nothing'
+
+
+class EntityCreatorAI(EntityCreatorBase.EntityCreatorBase):
+
+ def __init__(self, level):
+ EntityCreatorBase.EntityCreatorBase.__init__(self, level)
+ cLE = createLocalEntity
+ self.privRegisterTypes({'attribModifier': nothing,
+ 'ambientSound': nothing,
+ 'collisionSolid': nothing,
+ 'cutScene': nothing,
+ 'editMgr': Functor(cLE, EditMgrAI.EditMgrAI),
+ 'entityGroup': nothing,
+ 'entrancePoint': nothing,
+ 'levelMgr': Functor(cLE, LevelMgrAI.LevelMgrAI),
+ 'locator': nothing,
+ 'logicGate': Functor(cLE, LogicGate.LogicGate),
+ 'model': nothing,
+ 'nodepath': nothing,
+ 'path': nothing,
+ 'propSpinner': nothing,
+ 'visibilityExtender': nothing,
+ 'zone': Functor(cLE, ZoneEntityAI.ZoneEntityAI)})
+
+ def doCreateEntity(self, ctor, entId):
+ zoneId = self.level.getEntityZoneId(entId)
+ self.notify.debug('creating entity %s in zone %s' % (entId, zoneId))
+ return ctor(self.level, entId, zoneId)
diff --git a/otp/level/EntityCreatorBase.py b/otp/level/EntityCreatorBase.py
new file mode 100644
index 0000000..7c84f45
--- /dev/null
+++ b/otp/level/EntityCreatorBase.py
@@ -0,0 +1,27 @@
+from direct.directnotify import DirectNotifyGlobal
+
+class EntityCreatorBase:
+ notify = DirectNotifyGlobal.directNotify.newCategory('EntityCreator')
+
+ def __init__(self, level):
+ self.level = level
+ self.entType2Ctor = {}
+
+ def createEntity(self, entId):
+ entType = self.level.getEntityType(entId)
+ if not self.entType2Ctor.has_key(entType):
+ self.notify.error('unknown entity type: %s (ent%s)' % (entType, entId))
+ ent = self.doCreateEntity(self.entType2Ctor[entType], entId)
+ return ent
+
+ def getEntityTypes(self):
+ return self.entType2Ctor.keys()
+
+ def privRegisterType(self, entType, ctor):
+ if self.entType2Ctor.has_key(entType):
+ self.notify.debug('replacing %s ctor %s with %s' % (entType, self.entType2Ctor[entType], ctor))
+ self.entType2Ctor[entType] = ctor
+
+ def privRegisterTypes(self, type2ctor):
+ for entType, ctor in type2ctor.items():
+ self.privRegisterType(entType, ctor)
diff --git a/otp/level/EntityStateVarSet.py b/otp/level/EntityStateVarSet.py
new file mode 100644
index 0000000..8c471ae
--- /dev/null
+++ b/otp/level/EntityStateVarSet.py
@@ -0,0 +1,35 @@
+from direct.fsm.StatePush import StateVar
+from direct.showbase.PythonUtil import getSetterName
+from otp.level.Entity import Entity
+
+class EntityStateVarSet(Entity):
+
+ def __init__(self, entType):
+ self._entType = entType
+ self._attribNames = []
+ for attrib in self._entType.attribs:
+ name, defaultVal, type = attrib
+ self._addAttrib(name, defaultVal, type)
+
+ def initializeEntity(self, level, entId):
+ stateVars = {}
+ for attribName in self._attribNames:
+ stateVars[attribName] = getattr(self, attribName)
+
+ Entity.initializeEntity(self, level, entId)
+ for attribName in self._attribNames:
+ stateVars[attribName].set(getattr(self, attribName))
+
+ for attribName in self._attribNames:
+ setattr(self, attribName, stateVars[attribName])
+
+ def _getAttributeNames(self):
+ return self._attribNames[:]
+
+ def _setter(self, name, value):
+ getattr(self, name).set(value)
+
+ def _addAttrib(self, name, defaultVal, type):
+ setattr(self, name, StateVar(defaultVal))
+ setattr(self, getSetterName(name), Functor(self._setter, name))
+ self._attribNames.append(name)
diff --git a/otp/level/EntityTypeDesc.py b/otp/level/EntityTypeDesc.py
new file mode 100644
index 0000000..7642dc0
--- /dev/null
+++ b/otp/level/EntityTypeDesc.py
@@ -0,0 +1,85 @@
+from direct.directnotify import DirectNotifyGlobal
+import AttribDesc
+from direct.showbase.PythonUtil import mostDerivedLast
+
+class EntityTypeDesc:
+ notify = DirectNotifyGlobal.directNotify.newCategory('EntityTypeDesc')
+ output = None
+
+ def __init__(self):
+ self.__class__.privCompileAttribDescs(self.__class__)
+ self.attribNames = []
+ self.attribDescDict = {}
+ attribDescs = self.__class__._attribDescs
+ for desc in attribDescs:
+ attribName = desc.getName()
+ self.attribNames.append(attribName)
+ self.attribDescDict[attribName] = desc
+
+ def isConcrete(self):
+ return not self.__class__.__dict__.has_key('abstract')
+
+ def isPermanent(self):
+ return self.__class__.__dict__.has_key('permanent')
+
+ def getOutputType(self):
+ return self.output
+
+ def getAttribNames(self):
+ return self.attribNames
+
+ def getAttribDescDict(self):
+ return self.attribDescDict
+
+ def getAttribsOfType(self, type):
+ names = []
+ for attribName, desc in self.attribDescDict.items():
+ if desc.getDatatype() == type:
+ names.append(attribName)
+
+ return names
+
+ @staticmethod
+ def privCompileAttribDescs(entTypeClass):
+ if entTypeClass.__dict__.has_key('_attribDescs'):
+ return
+ c = entTypeClass
+ EntityTypeDesc.notify.debug('compiling attrib descriptors for %s' % c.__name__)
+ for base in c.__bases__:
+ EntityTypeDesc.privCompileAttribDescs(base)
+
+ blockAttribs = c.__dict__.get('blockAttribs', [])
+ baseADs = []
+ bases = list(c.__bases__)
+ mostDerivedLast(bases)
+ for base in bases:
+ for desc in base._attribDescs:
+ if desc.getName() in blockAttribs:
+ continue
+ for d in baseADs:
+ if desc.getName() == d.getName():
+ EntityTypeDesc.notify.warning('%s inherits attrib %s from multiple bases' % (c.__name__, desc.getName()))
+ break
+ else:
+ baseADs.append(desc)
+
+ attribDescs = []
+ if c.__dict__.has_key('attribs'):
+ for attrib in c.attribs:
+ desc = AttribDesc.AttribDesc(*attrib)
+ if desc.getName() == 'type' and entTypeClass.__name__ != 'Entity':
+ EntityTypeDesc.notify.error("(%s): '%s' is a reserved attribute name" % (entTypeClass.__name__, desc.getName()))
+ for ad in baseADs:
+ if ad.getName() == desc.getName():
+ baseADs.remove(ad)
+ break
+
+ attribDescs.append(desc)
+
+ c._attribDescs = baseADs + attribDescs
+
+ def __str__(self):
+ return str(self.__class__)
+
+ def __repr__(self):
+ return str(self.__class__.__dict__.get('type', None)) + str(self.output) + str(self.attribDescDict)
diff --git a/otp/level/EntityTypeRegistry.py b/otp/level/EntityTypeRegistry.py
new file mode 100644
index 0000000..fd873fa
--- /dev/null
+++ b/otp/level/EntityTypeRegistry.py
@@ -0,0 +1,99 @@
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+import types
+import AttribDesc
+import EntityTypeDesc
+from direct.showbase.PythonUtil import mostDerivedLast
+import os
+import string
+
+class EntityTypeRegistry:
+ notify = DirectNotifyGlobal.directNotify.newCategory('EntityTypeRegistry')
+
+ def __init__(self, entityTypeModule):
+ self.entTypeModule = entityTypeModule
+ hv = HashVal()
+ import EntityTypes
+ reload(EntityTypes)
+ reload(self.entTypeModule)
+
+ def getPyExtVersion(filename):
+ base, ext = os.path.splitext(filename)
+ if ext == '.pyc' or ext == '.pyo':
+ filename = base + '.py'
+ return filename
+
+ fileLines = file(getPyExtVersion(EntityTypes.__file__)).readlines()
+ hv.hashString(string.join(fileLines, ''))
+ s = str(hv.asHex())
+ s += '.'
+ fileLines = file(getPyExtVersion(self.entTypeModule.__file__)).readlines()
+ hv.hashString(string.join(fileLines, ''))
+ s += str(hv.asHex())
+ self.hashStr = s
+ getPyExtVersion = None
+ classes = []
+ for key, value in entityTypeModule.__dict__.items():
+ if type(value) is types.ClassType:
+ if issubclass(value, EntityTypeDesc.EntityTypeDesc):
+ classes.append(value)
+
+ self.entTypeName2typeDesc = {}
+ mostDerivedLast(classes)
+ for c in classes:
+ if c.__dict__.has_key('type'):
+ if self.entTypeName2typeDesc.has_key(c.type):
+ EntityTypeRegistry.notify.debug("replacing %s with %s for entity type '%s'" % (self.entTypeName2typeDesc[c.type].__class__, c, c.type))
+ self.entTypeName2typeDesc[c.type] = c()
+
+ self.output2typeNames = {}
+ for typename, typeDesc in self.entTypeName2typeDesc.items():
+ if typeDesc.isConcrete():
+ if hasattr(typeDesc, 'output'):
+ outputType = typeDesc.output
+ self.output2typeNames.setdefault(outputType, [])
+ self.output2typeNames[outputType].append(typename)
+
+ self.permanentTypeNames = []
+ for typename, typeDesc in self.entTypeName2typeDesc.items():
+ if typeDesc.isPermanent():
+ self.permanentTypeNames.append(typename)
+
+ self.typeName2derivedTypeNames = {}
+ for typename, typeDesc in self.entTypeName2typeDesc.items():
+ typenames = []
+ for tn, td in self.entTypeName2typeDesc.items():
+ if td.isConcrete():
+ if issubclass(td.__class__, typeDesc.__class__):
+ typenames.append(tn)
+
+ self.typeName2derivedTypeNames[typename] = typenames
+
+ return
+
+ def getAllTypeNames(self):
+ return self.entTypeName2typeDesc.keys()
+
+ def getTypeDesc(self, entTypeName):
+ return self.entTypeName2typeDesc[entTypeName]
+
+ def getTypeNamesFromOutputType(self, outputType):
+ return self.output2typeNames.get(outputType, [])
+
+ def getDerivedTypeNames(self, entTypeName):
+ return self.typeName2derivedTypeNames[entTypeName]
+
+ def isDerivedAndBase(self, entType, baseEntType):
+ return entType in self.getDerivedTypeNames(baseEntType)
+
+ def getPermanentTypeNames(self):
+ return self.permanentTypeNames
+
+ def getHashStr(self):
+ return self.hashStr
+
+ def __hash__(self):
+ return hash(repr(self))
+
+ def __repr__(self):
+ return str(self.entTypeName2typeDesc)
diff --git a/otp/level/EntityTypes.py b/otp/level/EntityTypes.py
new file mode 100644
index 0000000..fb7b22f
--- /dev/null
+++ b/otp/level/EntityTypes.py
@@ -0,0 +1,176 @@
+from EntityTypeDesc import EntityTypeDesc
+from toontown.coghq.SpecImports import *
+
+class Entity(EntityTypeDesc):
+ abstract = 1
+ type = 'entity'
+ attribs = (('type', None, 'const'),
+ ('name', '', 'string'),
+ ('comment', '', 'string'),
+ ('parentEntId', 0, 'entId'))
+
+
+class LevelMgr(Entity):
+ type = 'levelMgr'
+ permanent = 1
+ attribs = (('name', 'LevelMgr', 'const'), ('parentEntId', 0, 'const'), ('modelFilename', '', 'const'))
+
+
+class EditMgr(Entity):
+ type = 'editMgr'
+ permanent = 1
+ blockAttribs = ('comment',)
+ attribs = (('name', 'LevelMgr', 'const'),
+ ('parentEntId', 0, 'const'),
+ ('requestSave', None, 'const'),
+ ('requestNewEntity', None, 'const'),
+ ('insertEntity', None, 'const'),
+ ('removeEntity', None, 'const'))
+
+
+class AttribModifier(Entity):
+ type = 'attribModifier'
+ attribs = (('recursive', 0, 'bool'),
+ ('typeName', '', 'string'),
+ ('attribName', '', 'string'),
+ ('value', '', 'string'))
+
+
+class Locator(Entity):
+ type = 'locator'
+ attribs = (('searchPath', '', 'string'),)
+
+
+class Nodepath(Entity):
+ type = 'nodepath'
+ attribs = (('parentEntId',
+ 0,
+ 'entId',
+ {'type': 'nodepath'}),
+ ('pos', Point3(0, 0, 0), 'pos'),
+ ('hpr', Vec3(0, 0, 0), 'hpr'),
+ ('scale', 1, 'scale'))
+
+
+class Zone(Nodepath):
+ type = 'zone'
+ permanent = 1
+ blockAttribs = ('pos', 'hpr')
+ attribs = (('parentEntId', 0, 'const'), ('description', '', 'string'), ('visibility', [], 'visZoneList'))
+
+
+class EntrancePoint(Nodepath):
+ type = 'entrancePoint'
+ attribs = (('entranceId', -1, 'int'), ('radius',
+ 15,
+ 'float',
+ {'min': 0}), ('theta',
+ 20,
+ 'float',
+ {'min': 0}))
+
+
+class LogicGate(Entity):
+ type = 'logicGate'
+ output = 'bool'
+ attribs = (('input1Event',
+ 0,
+ 'entId',
+ {'output': 'bool'}),
+ ('input2Event',
+ 0,
+ 'entId',
+ {'output': 'bool'}),
+ ('isInput1', 0, 'bool'),
+ ('isInput2', 0, 'bool'),
+ ('logicType',
+ 'or',
+ 'choice',
+ {'choiceSet': ['or',
+ 'and',
+ 'xor',
+ 'nand',
+ 'nor',
+ 'xnor']}))
+
+
+class CutScene(Entity):
+ type = 'cutScene'
+ output = 'bool'
+ attribs = (('pos', Point3(0, 0, 0), 'pos'),
+ ('hpr', Vec3(0, 0, 0), 'hpr'),
+ ('startStopEvent',
+ 0,
+ 'entId',
+ {'output': 'bool'}),
+ ('effect',
+ 'irisInOut',
+ 'choice',
+ {'choiceSet': ['nothing', 'irisInOut', 'letterBox']}),
+ ('motion',
+ 'foo1',
+ 'choice',
+ {'choiceSet': ['foo1']}),
+ ('duration', 5.0, 'float'))
+
+
+class CollisionSolid(Nodepath):
+ type = 'collisionSolid'
+ attribs = (('solidType',
+ 'sphere',
+ 'choice',
+ {'choiceSet': ['sphere', 'tube']}),
+ ('radius', 1.0, 'float'),
+ ('length', 0.0, 'float'),
+ ('showSolid', 0, 'bool'))
+
+
+class Model(Nodepath):
+ type = 'model'
+ attribs = (('loadType',
+ 'loadModelCopy',
+ 'choice',
+ {'choiceSet': ['loadModelCopy', 'loadModel', 'loadModelOnce']}),
+ ('modelPath', None, 'bamfilename'),
+ ('flattenType',
+ 'light',
+ 'choice',
+ {'choiceSet': ['none',
+ 'light',
+ 'medium',
+ 'strong']}),
+ ('collisionsOnly', 0, 'bool'),
+ ('goonHatType',
+ 'none',
+ 'choice',
+ {'choiceSet': ['none', 'hardhat', 'security']}))
+
+
+class Path(Nodepath):
+ type = 'path'
+ attribs = (('pathIndex', 0, 'int'), ('pathScale', 1.0, 'float'))
+
+
+class VisibilityExtender(Entity):
+ type = 'visibilityExtender'
+ attribs = (('event',
+ None,
+ 'entId',
+ {'output': 'bool'}), ('newZones', [], 'visZoneList'))
+
+
+class AmbientSound(Nodepath):
+ type = 'ambientSound'
+ attribs = (('soundPath', '', 'bamfilename'), ('volume',
+ 1,
+ 'float',
+ {'min': 0,
+ 'max': 1}), ('enabled', 1, 'bool'))
+
+
+class PropSpinner(Entity):
+ type = 'propSpinner'
+
+
+class EntityGroup(Entity):
+ type = 'entityGroup'
diff --git a/otp/level/EntrancePoint.py b/otp/level/EntrancePoint.py
new file mode 100644
index 0000000..6a4726b
--- /dev/null
+++ b/otp/level/EntrancePoint.py
@@ -0,0 +1,40 @@
+from toontown.toonbase.ToontownGlobals import *
+from direct.directnotify import DirectNotifyGlobal
+import BasicEntities
+
+class EntrancePoint(BasicEntities.NodePathEntity):
+
+ def __init__(self, level, entId):
+ BasicEntities.NodePathEntity.__init__(self, level, entId)
+ self.rotator = self.attachNewNode('rotator')
+ self.placer = self.rotator.attachNewNode('placer')
+ self.initEntrancePoint()
+
+ def destroy(self):
+ self.destroyEntrancePoint()
+ self.placer.removeNode()
+ self.rotator.removeNode()
+ del self.placer
+ del self.rotator
+ BasicEntities.NodePathEntity.destroy(self)
+
+ def placeToon(self, toon, toonIndex, numToons):
+ self.placer.setY(-self.radius)
+ self.rotator.setH(-self.theta * (numToons - 1) * 0.5 + toonIndex * self.theta)
+ toon.setPosHpr(self.placer, 0, 0, 0, 0, 0, 0)
+
+ def initEntrancePoint(self):
+ if self.entranceId >= 0:
+ self.level.entranceId2entity[self.entranceId] = self
+
+ def destroyEntrancePoint(self):
+ if self.entranceId >= 0:
+ if self.level.entranceId2entity.has_key(self.entranceId):
+ del self.level.entranceId2entity[self.entranceId]
+
+ if __dev__:
+
+ def attribChanged(self, *args):
+ BasicEntities.NodePathEntity.attribChanged(self, *args)
+ self.destroyEntrancePoint()
+ self.initEntrancePoint()
diff --git a/otp/level/Level.py b/otp/level/Level.py
new file mode 100644
index 0000000..62220d5
--- /dev/null
+++ b/otp/level/Level.py
@@ -0,0 +1,261 @@
+from direct.directnotify import DirectNotifyGlobal
+import string
+import LevelConstants
+from direct.showbase.PythonUtil import lineInfo, uniqueElements
+import types
+
+class Level:
+ notify = DirectNotifyGlobal.directNotify.newCategory('Level')
+
+ def __init__(self):
+ self.levelSpec = None
+ self.initialized = 0
+ return
+
+ def initializeLevel(self, levelId, levelSpec, scenarioIndex):
+ self.levelId = levelId
+ self.levelSpec = levelSpec
+ self.scenarioIndex = scenarioIndex
+ self.levelSpec.setScenario(self.scenarioIndex)
+ if __dev__:
+ self.levelSpec.setLevel(self)
+ self.entranceId2entity = {}
+ self.entId2createCallbacks = {}
+ self.createdEntIds = []
+ self.nonlocalEntIds = {}
+ self.nothingEntIds = {}
+ self.entityCreator = self.createEntityCreator()
+ self.entType2ids = self.levelSpec.getEntType2ids(self.levelSpec.getAllEntIds())
+ for entType in self.entityCreator.getEntityTypes():
+ self.entType2ids.setdefault(entType, [])
+
+ self.createAllEntities(priorityTypes=['levelMgr', 'zone', 'propSpinner'])
+ self.levelMgrEntity = self.getEntity(LevelConstants.LevelMgrEntId)
+ self.uberZoneEntity = self.getEntity(LevelConstants.UberZoneEntId)
+ self.initialized = 1
+
+ def isInitialized(self):
+ return self.initialized
+
+ def getLevelId(self):
+ return self.levelId
+
+ def destroyLevel(self):
+ self.destroyAllEntities()
+ if self.initialized:
+ del self.levelMgrEntity
+ del self.uberZoneEntity
+ del self.entityCreator
+ del self.entId2createCallbacks
+ del self.entranceId2entity
+ self.levelSpec.destroy()
+ del self.levelSpec
+ self.initialized = 0
+ del self.createdEntIds
+ del self.nonlocalEntIds
+ del self.nothingEntIds
+ if hasattr(self, 'entities'):
+ del self.entities
+ if hasattr(self, 'levelSpec'):
+ self.levelSpec.destroy()
+ del self.levelSpec
+
+ def createEntityCreator(self):
+ Level.notify.error('concrete Level class must override %s' % lineInfo()[2])
+
+ def createAllEntities(self, priorityTypes = []):
+ self.entities = {}
+ entTypes = self.entityCreator.getEntityTypes()
+ self.onLevelPreCreate()
+ for type in priorityTypes:
+ self.createAllEntitiesOfType(type)
+ entTypes.remove(type)
+
+ for type in entTypes:
+ self.createAllEntitiesOfType(type)
+
+ self.onLevelPostCreate()
+
+ def destroyAllEntities(self):
+ self.nonlocalEntIds = {}
+ self.nothingEntIds = {}
+ if not uniqueElements(self.createdEntIds):
+ Level.notify.warning('%s: self.createdEntIds is not unique: %s' % (getattr(self, 'doId', None), self.createdEntIds))
+ while len(self.createdEntIds) > 0:
+ entId = self.createdEntIds.pop()
+ entity = self.getEntity(entId)
+ if entity is not None:
+ Level.notify.debug('destroying %s %s' % (self.getEntityType(entId), entId))
+ entity.destroy()
+ else:
+ Level.notify.error('trying to destroy entity %s, but it is already gone' % entId)
+
+ return
+
+ def createAllEntitiesOfType(self, entType):
+ self.onEntityTypePreCreate(entType)
+ for entId in self.entType2ids[entType]:
+ self.createEntity(entId)
+
+ self.onEntityTypePostCreate(entType)
+
+ def createEntity(self, entId):
+ spec = self.levelSpec.getEntitySpec(entId)
+ Level.notify.debug('creating %s %s' % (spec['type'], entId))
+ entity = self.entityCreator.createEntity(entId)
+ announce = False
+ if entity is 'nonlocal':
+ self.nonlocalEntIds[entId] = None
+ elif entity is 'nothing':
+ self.nothingEntIds[entId] = None
+ announce = True
+ else:
+ self.createdEntIds.append(entId)
+ announce = True
+ if announce:
+ self.onEntityCreate(entId)
+ return entity
+
+ def initializeEntity(self, entity):
+ entId = entity.entId
+ spec = self.levelSpec.getEntitySpec(entId)
+ for key, value in spec.items():
+ if key in ('type', 'name', 'comment'):
+ continue
+ entity.setAttribInit(key, value)
+
+ self.entities[entId] = entity
+
+ def getEntity(self, entId):
+ if hasattr(self, 'entities'):
+ return self.entities.get(entId)
+ else:
+ return None
+ return None
+
+ def getEntityType(self, entId):
+ return self.levelSpec.getEntityType(entId)
+
+ def getEntityZoneEntId(self, entId):
+ return self.levelSpec.getEntityZoneEntId(entId)
+
+ def getEntityZoneId(self, entId):
+ zoneEntId = self.getEntityZoneEntId(entId)
+ if not hasattr(self, 'zoneNum2zoneId'):
+ return None
+ return self.zoneNum2zoneId.get(zoneEntId)
+
+ def getZoneId(self, zoneEntId):
+ return self.zoneNum2zoneId[zoneEntId]
+
+ def getZoneNumFromId(self, zoneId):
+ return self.zoneId2zoneNum[zoneId]
+
+ def getParentTokenForEntity(self, entId):
+ return entId
+
+ def getLevelPreCreateEvent(self):
+ return 'levelPreCreate-%s' % self.levelId
+
+ def getLevelPostCreateEvent(self):
+ return 'levelPostCreate-%s' % self.levelId
+
+ def getEntityTypePreCreateEvent(self, entType):
+ return 'entityTypePreCreate-%s-%s' % (self.levelId, entType)
+
+ def getEntityTypePostCreateEvent(self, entType):
+ return 'entityTypePostCreate-%s-%s' % (self.levelId, entType)
+
+ def getEntityCreateEvent(self, entId):
+ return 'entityCreate-%s-%s' % (self.levelId, entId)
+
+ def getEntityOfTypeCreateEvent(self, entType):
+ return 'entityOfTypeCreate-%s-%s' % (self.levelId, entType)
+
+ def onLevelPreCreate(self):
+ messenger.send(self.getLevelPreCreateEvent())
+
+ def onLevelPostCreate(self):
+ messenger.send(self.getLevelPostCreateEvent())
+
+ def onEntityTypePreCreate(self, entType):
+ messenger.send(self.getEntityTypePreCreateEvent(entType))
+
+ def onEntityTypePostCreate(self, entType):
+ messenger.send(self.getEntityTypePostCreateEvent(entType))
+
+ def onEntityCreate(self, entId):
+ messenger.send(self.getEntityCreateEvent(entId))
+ messenger.send(self.getEntityOfTypeCreateEvent(self.getEntityType(entId)), [entId])
+ if entId in self.entId2createCallbacks:
+ for callback in self.entId2createCallbacks[entId]:
+ callback()
+
+ del self.entId2createCallbacks[entId]
+
+ def setEntityCreateCallback(self, entId, callback):
+ ent = self.getEntity(entId)
+ if ent is not None:
+ callNow = True
+ elif entId in self.nothingEntIds:
+ callNow = True
+ else:
+ callNow = False
+ if callNow:
+ callback()
+ else:
+ self.entId2createCallbacks.setdefault(entId, [])
+ self.entId2createCallbacks[entId].append(callback)
+ return
+
+ def getEntityDestroyEvent(self, entId):
+ return 'entityDestroy-%s-%s' % (self.levelId, entId)
+
+ def onEntityDestroy(self, entId):
+ messenger.send(self.getEntityDestroyEvent(entId))
+ del self.entities[entId]
+ if entId in self.createdEntIds:
+ self.createdEntIds.remove(entId)
+
+ def handleVisChange(self):
+ pass
+
+ if __dev__:
+
+ def getAttribChangeEventName(self):
+ return 'attribChange-%s' % self.levelId
+
+ def getInsertEntityEventName(self):
+ return 'insertEntity-%s' % self.levelId
+
+ def getRemoveEntityEventName(self):
+ return 'removeEntity-%s' % self.levelId
+
+ def handleAttribChange(self, entId, attrib, value, username = None):
+ entity = self.getEntity(entId)
+ if entity is not None:
+ entity.handleAttribChange(attrib, value)
+ messenger.send(self.getAttribChangeEventName(), [entId,
+ attrib,
+ value,
+ username])
+ return
+
+ def setEntityCreatorUsername(self, entId, editUsername):
+ pass
+
+ def handleEntityInsert(self, entId):
+ self.entType2ids[self.getEntityType(entId)].append(entId)
+ self.createEntity(entId)
+ messenger.send(self.getInsertEntityEventName(), [entId])
+
+ def handleEntityRemove(self, entId):
+ messenger.send(self.getRemoveEntityEventName(), [entId])
+ if entId in self.createdEntIds:
+ entity = self.getEntity(entId)
+ entity.destroy()
+ elif entId in self.nothingEntIds:
+ del self.nothingEntIds[entId]
+ elif entId in self.nonlocalEntIds:
+ del self.nonlocalEntIds[entId]
+ self.entType2ids[self.getEntityType(entId)].remove(entId)
diff --git a/otp/level/LevelConstants.py b/otp/level/LevelConstants.py
new file mode 100644
index 0000000..d7e2283
--- /dev/null
+++ b/otp/level/LevelConstants.py
@@ -0,0 +1,5 @@
+MinZoneNum = 0
+MaxZoneNum = 999
+UberZoneEntId = 0
+LevelMgrEntId = 1000
+EditMgrEntId = 1001
diff --git a/otp/level/LevelMgr.py b/otp/level/LevelMgr.py
new file mode 100644
index 0000000..6c082d3
--- /dev/null
+++ b/otp/level/LevelMgr.py
@@ -0,0 +1,46 @@
+from direct.showbase.PythonUtil import Functor
+import LevelMgrBase
+
+class LevelMgr(LevelMgrBase.LevelMgrBase):
+
+ def __init__(self, level, entId):
+ LevelMgrBase.LevelMgrBase.__init__(self, level, entId)
+ self.geom = loader.loadModel(self.modelFilename)
+ if not self.geom:
+ import pdb
+ pdb.set_trace()
+ self.zoneNums = []
+ self.level.zoneNum2zoneId = {}
+ self.level.zoneId2zoneNum = {}
+ self.accept(self.level.getEntityOfTypeCreateEvent('zone'), self.handleZoneCreated)
+
+ def destroy(self):
+ del self.level.zoneIds
+ del self.level.zoneId2zoneNum
+ del self.level.zoneNum2zoneId
+ self.geom.removeNode()
+ del self.geom
+ LevelMgrBase.LevelMgrBase.destroy(self)
+
+ def handleZoneCreated(self, entId):
+ zoneEnt = self.level.getEntity(entId)
+ self.zoneNums.append(zoneEnt.entId)
+ self.privAssignZoneIds()
+ self.accept(self.level.getEntityDestroyEvent(entId), Functor(self.handleZoneDestroy, entId))
+
+ def handleZoneDestroy(self, entId):
+ zoneEnt = self.level.getEntity(entId)
+ del self.level.zoneId2zoneNum[self.level.zoneNum2zoneId[zoneEnt.entId]]
+ del self.level.zoneNum2zoneId[zoneEnt.entId]
+ self.zoneNums.remove(zoneEnt.entId)
+ self.privAssignZoneIds()
+
+ def privAssignZoneIds(self):
+ self.zoneNums.sort()
+ for i in range(len(self.zoneNums)):
+ zoneNum = self.zoneNums[i]
+ zoneEnt = self.level.getEntity(zoneNum)
+ zoneId = self.level.zoneIds[i]
+ zoneEnt.setZoneId(zoneId)
+ self.level.zoneNum2zoneId[zoneNum] = zoneId
+ self.level.zoneId2zoneNum[zoneId] = zoneNum
diff --git a/otp/level/LevelMgrAI.py b/otp/level/LevelMgrAI.py
new file mode 100644
index 0000000..1370019
--- /dev/null
+++ b/otp/level/LevelMgrAI.py
@@ -0,0 +1,33 @@
+from direct.showbase.PythonUtil import Functor
+import LevelMgrBase
+
+class LevelMgrAI(LevelMgrBase.LevelMgrBase):
+
+ def __init__(self, level, entId):
+ LevelMgrBase.LevelMgrBase.__init__(self, level, entId)
+ self.level.zoneNum2zoneId = {}
+ self.level.zoneIds = []
+ self.accept(self.level.getEntityOfTypeCreateEvent('zone'), self.handleZoneCreated)
+
+ def destroy(self):
+ del self.level.zoneIds
+ del self.level.zoneNum2zoneId
+ LevelMgrBase.LevelMgrBase.destroy(self)
+
+ def handleZoneCreated(self, entId):
+ zoneEnt = self.level.getEntity(entId)
+ self.level.zoneNum2zoneId[zoneEnt.entId] = zoneEnt.getZoneId()
+ self.privCreateSortedZoneIdList()
+ self.accept(self.level.getEntityDestroyEvent(entId), Functor(self.handleZoneDestroy, entId))
+
+ def handleZoneDestroy(self, entId):
+ zoneEnt = self.level.getEntity(entId)
+ del self.level.zoneNum2zoneId[zoneEnt.entId]
+ self.privCreateSortedZoneIdList()
+
+ def privCreateSortedZoneIdList(self):
+ zoneNums = self.level.zoneNum2zoneId.keys()
+ zoneNums.sort()
+ self.level.zoneIds = []
+ for zoneNum in zoneNums:
+ self.level.zoneIds.append(self.level.zoneNum2zoneId[zoneNum])
diff --git a/otp/level/LevelMgrBase.py b/otp/level/LevelMgrBase.py
new file mode 100644
index 0000000..8a20694
--- /dev/null
+++ b/otp/level/LevelMgrBase.py
@@ -0,0 +1,10 @@
+import Entity
+
+class LevelMgrBase(Entity.Entity):
+
+ def __init__(self, level, entId):
+ Entity.Entity.__init__(self, level, entId)
+
+ def destroy(self):
+ Entity.Entity.destroy(self)
+ self.ignoreAll()
diff --git a/otp/level/LevelSpec.py b/otp/level/LevelSpec.py
new file mode 100644
index 0000000..d5bd867
--- /dev/null
+++ b/otp/level/LevelSpec.py
@@ -0,0 +1,422 @@
+from pandac import PandaModules as PM
+from direct.directnotify import DirectNotifyGlobal
+from direct.showbase.PythonUtil import list2dict, uniqueElements
+import string
+import LevelConstants
+import types
+if __dev__:
+ import os
+
+class LevelSpec:
+ notify = DirectNotifyGlobal.directNotify.newCategory('LevelSpec')
+ SystemEntIds = (LevelConstants.UberZoneEntId, LevelConstants.LevelMgrEntId, LevelConstants.EditMgrEntId)
+
+ def __init__(self, spec = None, scenario = 0):
+ newSpec = 0
+ if type(spec) is types.ModuleType:
+ if __dev__:
+ reload(spec)
+ self.specDict = spec.levelSpec
+ if __dev__:
+ self.setFilename(spec.__file__)
+ elif type(spec) is types.DictType:
+ self.specDict = spec
+ elif spec is None:
+ if __dev__:
+ newSpec = 1
+ self.specDict = {'globalEntities': {},
+ 'scenarios': [{}]}
+ self.entId2specDict = {}
+ self.entId2specDict.update(list2dict(self.getGlobalEntIds(), value=self.privGetGlobalEntityDict()))
+ for i in range(self.getNumScenarios()):
+ self.entId2specDict.update(list2dict(self.getScenarioEntIds(i), value=self.privGetScenarioEntityDict(i)))
+
+ self.setScenario(scenario)
+ if __dev__:
+ if newSpec:
+ import EntityTypes
+ import EntityTypeRegistry
+ etr = EntityTypeRegistry.EntityTypeRegistry(EntityTypes)
+ self.setEntityTypeReg(etr)
+ entId = LevelConstants.UberZoneEntId
+ self.insertEntity(entId, 'zone')
+ self.doSetAttrib(entId, 'name', 'UberZone')
+ entId = LevelConstants.LevelMgrEntId
+ self.insertEntity(entId, 'levelMgr')
+ self.doSetAttrib(entId, 'name', 'LevelMgr')
+ entId = LevelConstants.EditMgrEntId
+ self.insertEntity(entId, 'editMgr')
+ self.doSetAttrib(entId, 'name', 'EditMgr')
+ return
+
+ def destroy(self):
+ del self.specDict
+ del self.entId2specDict
+ del self.scenario
+ if hasattr(self, 'level'):
+ del self.level
+ if hasattr(self, 'entTypeReg'):
+ del self.entTypeReg
+
+ def getNumScenarios(self):
+ return len(self.specDict['scenarios'])
+
+ def setScenario(self, scenario):
+ self.scenario = scenario
+
+ def getScenario(self):
+ return self.scenario
+
+ def getGlobalEntIds(self):
+ return self.privGetGlobalEntityDict().keys()
+
+ def getScenarioEntIds(self, scenario = None):
+ if scenario is None:
+ scenario = self.scenario
+ return self.privGetScenarioEntityDict(scenario).keys()
+
+ def getAllEntIds(self):
+ return self.getGlobalEntIds() + self.getScenarioEntIds()
+
+ def getAllEntIdsFromAllScenarios(self):
+ entIds = self.getGlobalEntIds()
+ for scenario in xrange(self.getNumScenarios()):
+ entIds.extend(self.getScenarioEntIds(scenario))
+
+ return entIds
+
+ def getEntitySpec(self, entId):
+ specDict = self.entId2specDict[entId]
+ return specDict[entId]
+
+ def getCopyOfSpec(self, spec):
+ specCopy = {}
+ if not isClient():
+ print 'EXECWARNING LevelSpec exec: %s' % self.getSpecImportsModuleName()
+ printStack()
+ exec 'from %s import *' % self.getSpecImportsModuleName()
+ for key in spec.keys():
+ specCopy[key] = eval(repr(spec[key]))
+
+ return specCopy
+
+ def getEntitySpecCopy(self, entId):
+ specDict = self.entId2specDict[entId]
+ return self.getCopyOfSpec(specDict[entId])
+
+ def getEntityType(self, entId):
+ return self.getEntitySpec(entId)['type']
+
+ def getEntityZoneEntId(self, entId):
+ spec = self.getEntitySpec(entId)
+ type = spec['type']
+ if type == 'zone':
+ return entId
+ return self.getEntityZoneEntId(spec['parentEntId'])
+
+ def getEntType2ids(self, entIds):
+ entType2ids = {}
+ for entId in entIds:
+ type = self.getEntityType(entId)
+ entType2ids.setdefault(type, [])
+ entType2ids[type].append(entId)
+
+ return entType2ids
+
+ def privGetGlobalEntityDict(self):
+ return self.specDict['globalEntities']
+
+ def privGetScenarioEntityDict(self, scenario):
+ return self.specDict['scenarios'][scenario]
+
+ def printZones(self):
+ allIds = self.getAllEntIds()
+ type2id = self.getEntType2ids(allIds)
+ zoneIds = type2id['zone']
+ if 0 in zoneIds:
+ zoneIds.remove(0)
+ zoneIds.sort()
+ for zoneNum in zoneIds:
+ spec = self.getEntitySpec(zoneNum)
+ print 'zone %s: %s' % (zoneNum, spec['name'])
+
+ if __dev__:
+
+ def setLevel(self, level):
+ self.level = level
+
+ def hasLevel(self):
+ return hasattr(self, 'level')
+
+ def setEntityTypeReg(self, entTypeReg):
+ self.entTypeReg = entTypeReg
+ for entId in self.getAllEntIds():
+ spec = self.getEntitySpec(entId)
+ type = self.getEntityType(entId)
+ typeDesc = self.entTypeReg.getTypeDesc(type)
+ attribDescDict = typeDesc.getAttribDescDict()
+ for attribName, desc in attribDescDict.iteritems():
+ if attribName not in spec:
+ spec[attribName] = desc.getDefaultValue()
+
+ self.checkSpecIntegrity()
+
+ def hasEntityTypeReg(self):
+ return hasattr(self, 'entTypeReg')
+
+ def setFilename(self, filename):
+ self.filename = filename
+
+ def doSetAttrib(self, entId, attrib, value):
+ specDict = self.entId2specDict[entId]
+ specDict[entId][attrib] = value
+
+ def setAttribChange(self, entId, attrib, value, username):
+ LevelSpec.notify.info('setAttribChange(%s): %s, %s = %s' % (username,
+ entId,
+ attrib,
+ repr(value)))
+ self.doSetAttrib(entId, attrib, value)
+ if self.hasLevel():
+ self.level.handleAttribChange(entId, attrib, value, username)
+
+ def insertEntity(self, entId, entType, parentEntId = 'unspecified'):
+ LevelSpec.notify.info('inserting entity %s (%s)' % (entId, entType))
+ globalEnts = self.privGetGlobalEntityDict()
+ self.entId2specDict[entId] = globalEnts
+ globalEnts[entId] = {}
+ spec = globalEnts[entId]
+ attribDescs = self.entTypeReg.getTypeDesc(entType).getAttribDescDict()
+ for name, desc in attribDescs.items():
+ spec[name] = desc.getDefaultValue()
+
+ spec['type'] = entType
+ if parentEntId != 'unspecified':
+ spec['parentEntId'] = parentEntId
+ if self.hasLevel():
+ self.level.handleEntityInsert(entId)
+ else:
+ LevelSpec.notify.warning('no level to be notified of insertion')
+
+ def removeEntity(self, entId):
+ LevelSpec.notify.info('removing entity %s' % entId)
+ if self.hasLevel():
+ self.level.handleEntityRemove(entId)
+ else:
+ LevelSpec.notify.warning('no level to be notified of removal')
+ dict = self.entId2specDict[entId]
+ del dict[entId]
+ del self.entId2specDict[entId]
+
+ def removeZoneReferences(self, removedZoneNums):
+ type2ids = self.getEntType2ids(self.getAllEntIdsFromAllScenarios())
+ for type in type2ids:
+ typeDesc = self.entTypeReg.getTypeDesc(type)
+ visZoneListAttribs = typeDesc.getAttribsOfType('visZoneList')
+ if len(visZoneListAttribs) > 0:
+ for entId in type2ids[type]:
+ spec = self.getEntitySpec(entId)
+ for attribName in visZoneListAttribs:
+ for zoneNum in removedZoneNums:
+ while zoneNum in spec[attribName]:
+ spec[attribName].remove(zoneNum)
+
+ def getSpecImportsModuleName(self):
+ return 'toontown.coghq.SpecImports'
+
+ def getFilename(self):
+ return self.filename
+
+ def privGetBackupFilename(self, filename):
+ return '%s.bak' % filename
+
+ def saveToDisk(self, filename = None, makeBackup = 1):
+ if filename is None:
+ filename = self.filename
+ if filename.endswith('.pyc'):
+ filename = filename.replace('.pyc', '.py')
+ if makeBackup and self.privFileExists(filename):
+ try:
+ backupFilename = self.privGetBackupFilename(filename)
+ self.privRemoveFile(backupFilename)
+ os.rename(filename, backupFilename)
+ except OSError, e:
+ LevelSpec.notify.warning('error during backup: %s' % str(e))
+
+ LevelSpec.notify.info("writing to '%s'" % filename)
+ self.privRemoveFile(filename)
+ self.privSaveToDisk(filename)
+ return
+
+ def privSaveToDisk(self, filename):
+ retval = 1
+ f = file(filename, 'wb')
+ try:
+ f.write(self.getPrettyString())
+ except IOError:
+ retval = 0
+
+ f.close()
+ return retval
+
+ def privFileExists(self, filename):
+ try:
+ os.stat(filename)
+ return 1
+ except OSError:
+ return 0
+
+ def privRemoveFile(self, filename):
+ try:
+ os.remove(filename)
+ return 1
+ except OSError:
+ return 0
+
+ def getPrettyString(self):
+ import pprint
+ tabWidth = 4
+ tab = ' ' * tabWidth
+ globalEntitiesName = 'GlobalEntities'
+ scenarioEntitiesName = 'Scenario%s'
+ topLevelName = 'levelSpec'
+
+ def getPrettyEntityDictStr(name, dict, tabs = 0):
+
+ def t(n):
+ return (tabs + n) * tab
+
+ def sortList(lst, firstElements = []):
+ elements = list(lst)
+ result = []
+ for el in firstElements:
+ if el in elements:
+ result.append(el)
+ elements.remove(el)
+
+ elements.sort()
+ result.extend(elements)
+ return result
+
+ firstTypes = ('levelMgr', 'editMgr', 'zone')
+ firstAttribs = ('type', 'name', 'comment', 'parentEntId', 'pos', 'x', 'y', 'z', 'hpr', 'h', 'p', 'r', 'scale', 'sx', 'sy', 'sz', 'color', 'model')
+ str = t(0) + '%s = {\n' % name
+ entIds = dict.keys()
+ entType2ids = self.getEntType2ids(entIds)
+ types = sortList(entType2ids.keys(), firstTypes)
+ for type in types:
+ str += t(1) + '# %s\n' % string.upper(type)
+ entIds = entType2ids[type]
+ entIds.sort()
+ for entId in entIds:
+ str += t(1) + '%s: {\n' % entId
+ spec = dict[entId]
+ attribs = sortList(spec.keys(), firstAttribs)
+ for attrib in attribs:
+ str += t(2) + "'%s': %s,\n" % (attrib, repr(spec[attrib]))
+
+ str += t(2) + '}, # end entity %s\n' % entId
+
+ str += t(1) + '}\n'
+ return str
+
+ def getPrettyTopLevelDictStr(tabs = 0):
+
+ def t(n):
+ return (tabs + n) * tab
+
+ str = t(0) + '%s = {\n' % topLevelName
+ str += t(1) + "'globalEntities': %s,\n" % globalEntitiesName
+ str += t(1) + "'scenarios': [\n"
+ for i in range(self.getNumScenarios()):
+ str += t(2) + '%s,\n' % (scenarioEntitiesName % i)
+
+ str += t(2) + '],\n'
+ str += t(1) + '}\n'
+ return str
+
+ str = 'from %s import *\n' % self.getSpecImportsModuleName()
+ str += '\n'
+ str += getPrettyEntityDictStr('GlobalEntities', self.privGetGlobalEntityDict())
+ str += '\n'
+ numScenarios = self.getNumScenarios()
+ for i in range(numScenarios):
+ str += getPrettyEntityDictStr('Scenario%s' % i, self.privGetScenarioEntityDict(i))
+ str += '\n'
+
+ str += getPrettyTopLevelDictStr()
+ self.testPrettyString(prettyString=str)
+ return str
+
+ def _recurKeyTest(self, dict1, dict2):
+ s = ''
+ errorCount = 0
+ if set(dict1.keys()) != set(dict2.keys()):
+ return 0
+ for key in dict1:
+ if type(dict1[key]) == type({}) and type(dict2[key]) == type({}):
+ if not self._recurKeyTest(dict1[key], dict2[key]):
+ return 0
+ else:
+ strd1 = repr(dict1[key])
+ strd2 = repr(dict2[key])
+ if strd1 != strd2:
+ s += '\nBAD VALUE(%s): %s != %s\n' % (key, strd1, strd2)
+ errorCount += 1
+
+ print s
+ if errorCount == 0:
+ return 1
+ else:
+ return 0
+
+ def testPrettyString(self, prettyString = None):
+ if prettyString is None:
+ prettyString = self.getPrettyString()
+ if not isClient():
+ print 'EXECWARNING LevelSpec exec 2: %s' % prettyString
+ printStack()
+ exec prettyString
+ if self._recurKeyTest(levelSpec, self.specDict):
+ return 1
+ return
+
+ def checkSpecIntegrity(self):
+ entIds = self.getGlobalEntIds()
+ entIds = list2dict(entIds)
+ for i in range(self.getNumScenarios()):
+ for id in self.getScenarioEntIds(i):
+ entIds[id] = None
+
+ if self.entTypeReg is not None:
+ allEntIds = entIds
+ for entId in allEntIds:
+ spec = self.getEntitySpec(entId)
+ entType = spec['type']
+ typeDesc = self.entTypeReg.getTypeDesc(entType)
+ attribNames = typeDesc.getAttribNames()
+ attribDescs = typeDesc.getAttribDescDict()
+ for attrib in spec.keys():
+ if attrib not in attribNames:
+ LevelSpec.notify.warning("entId %s (%s): unknown attrib '%s', omitting" % (entId, spec['type'], attrib))
+ del spec[attrib]
+
+ for attribName in attribNames:
+ if not spec.has_key(attribName):
+ LevelSpec.notify.warning("entId %s (%s): missing attrib '%s'" % (entId, spec['type'], attribName))
+
+ return
+
+ def stringHash(self):
+ h = PM.HashVal()
+ h.hashString(repr(self))
+ return h.asHex()
+
+ def __hash__(self):
+ return hash(repr(self))
+
+ def __str__(self):
+ return 'LevelSpec'
+
+ def __repr__(self):
+ return 'LevelSpec(%s, scenario=%s)' % (repeatableRepr(self.specDict), repeatableRepr(self.scenario))
diff --git a/otp/level/LevelUtil.py b/otp/level/LevelUtil.py
new file mode 100644
index 0000000..93d692b
--- /dev/null
+++ b/otp/level/LevelUtil.py
@@ -0,0 +1,40 @@
+import string
+import LevelConstants
+
+def getZoneNum2Node(levelModel, logFunc = lambda str: str):
+
+ def findNumberedNodes(baseString, model, caseInsens = 1):
+ srch = '**/%s*' % baseString
+ if caseInsens:
+ srch += ';+i'
+ potentialNodes = model.findAllMatches(srch)
+ num2node = {}
+ for potentialNode in potentialNodes:
+ name = potentialNode.getName()
+ logFunc('potential match for %s: %s' % (baseString, name))
+ name = name[len(baseString):]
+ numDigits = 0
+ while numDigits < len(name):
+ if name[numDigits] not in string.digits:
+ break
+ numDigits += 1
+
+ if numDigits == 0:
+ continue
+ num = int(name[:numDigits])
+ if num == LevelConstants.UberZoneEntId:
+ logFunc('warning: cannot use UberZone zoneNum (%s). ignoring %s' % (LevelConstants.UberZoneEntId, potentialNode))
+ continue
+ if num < LevelConstants.MinZoneNum or num > LevelConstants.MaxZoneNum:
+ logFunc('warning: zone %s is out of range. ignoring %s' % (num, potentialNode))
+ continue
+ if num in num2node:
+ logFunc('warning: zone %s already assigned to %s. ignoring %s' % (num, num2node[num], potentialNode))
+ continue
+ num2node[num] = potentialNode
+
+ return num2node
+
+ zoneNum2node = findNumberedNodes('zone', levelModel)
+ zoneNum2node[LevelConstants.UberZoneEntId] = levelModel
+ return zoneNum2node
diff --git a/otp/level/LocatorEntity.py b/otp/level/LocatorEntity.py
new file mode 100644
index 0000000..c0b593e
--- /dev/null
+++ b/otp/level/LocatorEntity.py
@@ -0,0 +1,33 @@
+import Entity, BasicEntities
+from pandac.PandaModules import NodePath
+from direct.directnotify import DirectNotifyGlobal
+
+class LocatorEntity(Entity.Entity, NodePath):
+ notify = DirectNotifyGlobal.directNotify.newCategory('LocatorEntity')
+
+ def __init__(self, level, entId):
+ node = hidden.attachNewNode('LocatorEntity-%s' % entId)
+ NodePath.__init__(self, node)
+ Entity.Entity.__init__(self, level, entId)
+ self.doReparent()
+
+ def destroy(self):
+ Entity.Entity.destroy(self)
+ self.removeNode()
+
+ def getNodePath(self):
+ return self
+
+ def doReparent(self):
+ if self.searchPath != '':
+ parent = self.level.geom.find(self.searchPath)
+ if parent.isEmpty():
+ LocatorEntity.notify.warning("could not find '%s'" % self.searchPath)
+ self.reparentTo(hidden)
+ else:
+ self.reparentTo(parent)
+
+ if __dev__:
+
+ def attribChanged(self, attrib, value):
+ self.doReparent()
diff --git a/otp/level/LogicGate.py b/otp/level/LogicGate.py
new file mode 100644
index 0000000..6775631
--- /dev/null
+++ b/otp/level/LogicGate.py
@@ -0,0 +1,91 @@
+from direct.showbase import DirectObject
+from direct.directnotify import DirectNotifyGlobal
+import Entity
+
+def andTest(self, a, b):
+ if b:
+ messenger.send(self.getOutputEventName(), [a])
+
+
+def orTest(self, a, b):
+ if not b:
+ messenger.send(self.getOutputEventName(), [a])
+
+
+def xorTest(self, a, b):
+ messenger.send(self.getOutputEventName(), [not (a and b) and (a or b)])
+
+
+def nandTest(self, a, b):
+ if b:
+ messenger.send(self.getOutputEventName(), [not (a and b)])
+
+
+def norTest(self, a, b):
+ if not b:
+ messenger.send(self.getOutputEventName(), [not (a or b)])
+
+
+def xnorTest(self, a, b):
+ messenger.send(self.getOutputEventName(), [(a and b) or not (a or b)])
+
+
+class LogicGate(Entity.Entity, DirectObject.DirectObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('LogicGate')
+ logicTests = {'and': andTest,
+ 'or': orTest,
+ 'xor': xorTest,
+ 'nand': nandTest,
+ 'nor': norTest,
+ 'xnor': xnorTest}
+
+ def __init__(self, level, entId):
+ self.input1Event = None
+ self.input2Event = None
+ DirectObject.DirectObject.__init__(self)
+ Entity.Entity.__init__(self, level, entId)
+ self.setLogicType(self.logicType)
+ self.setIsInput1(self.isInput1)
+ self.setIsInput2(self.isInput2)
+ self.setInput1Event(self.input1Event)
+ self.setInput2Event(self.input2Event)
+ return
+
+ def destroy(self):
+ self.ignore(self.input1Event)
+ self.input1Event = None
+ self.ignore(self.input2Event)
+ self.input2Event = None
+ Entity.Entity.destroy(self)
+ return
+
+ def setLogicType(self, logicType):
+ self.logicType = logicType
+ self.logicTest = self.logicTests[logicType]
+
+ def setIsInput1(self, isTrue):
+ if 1 or (not isTrue) != (not self.input1Event):
+ self.isInput1 = isTrue
+ self.logicTest(self, isTrue, self.isInput2)
+
+ def setIsInput2(self, isTrue):
+ if 1 or (not isTrue) != (not self.input2Event):
+ self.isInput2 = isTrue
+ self.logicTest(self, isTrue, self.isInput1)
+
+ def setInput1Event(self, event):
+ if self.input1Event:
+ self.ignore(self.input1Event)
+ self.input1Event = self.getOutputEventName(event)
+ if self.input1Event:
+ self.accept(self.input1Event, self.setIsInput1)
+
+ def setInput2Event(self, event):
+ if self.input2Event:
+ self.ignore(self.input2Event)
+ self.input2Event = self.getOutputEventName(event)
+ if self.input2Event:
+ self.accept(self.input2Event, self.setIsInput2)
+
+ def getName(self):
+ return 'switch-%s' % (self.entId,)
diff --git a/otp/level/ModelEntity.py b/otp/level/ModelEntity.py
new file mode 100644
index 0000000..34757ae
--- /dev/null
+++ b/otp/level/ModelEntity.py
@@ -0,0 +1,80 @@
+from toontown.toonbase.ToontownGlobals import *
+from direct.directnotify import DirectNotifyGlobal
+import BasicEntities
+
+class ModelEntity(BasicEntities.NodePathEntity):
+ LoadFuncs = {'loadModelCopy': loader.loadModelCopy,
+ 'loadModel': loader.loadModel,
+ 'loadModelOnce': loader.loadModelOnce}
+
+ def __init__(self, level, entId):
+ self.collisionsOnly = False
+ self.loadType = 'loadModelCopy'
+ self.flattenType = 'light'
+ self.goonHatType = 'none'
+ self.entInitialized = False
+ BasicEntities.NodePathEntity.__init__(self, level, entId)
+ self.entInitialized = True
+ self.model = None
+ self.loadModel()
+ return
+
+ def destroy(self):
+ if self.model:
+ self.model.removeNode()
+ del self.model
+ BasicEntities.NodePathEntity.destroy(self)
+
+ def loadModel(self):
+ if self.model:
+ self.model.removeNode()
+ self.model = None
+ if self.modelPath is None:
+ return
+ self.model = ModelEntity.LoadFuncs[self.loadType](self.modelPath)
+ if self.model:
+ self.model.reparentTo(self)
+ if self.collisionsOnly:
+ if __dev__:
+ self.model.setTransparency(1)
+ self.model.setColorScale(1, 1, 1, 0.1)
+ else:
+ self.model.hide()
+ else:
+ self.model.show()
+ if self.modelPath in ('phase_9/models/cogHQ/woodCrateB.bam', 'phase_9/models/cogHQ/metal_crateB.bam', 'phase_10/models/cashbotHQ/CBMetalCrate.bam', 'phase_10/models/cogHQ/CBMetalCrate2.bam', 'phase_10/models/cashbotHQ/CBWoodCrate.bam', 'phase_11/models/lawbotHQ/LB_metal_crate.bam', 'phase_11/models/lawbotHQ/LB_metal_crate2.bam'):
+ cNode = self.find('**/wall')
+ cNode.setZ(cNode, -.75)
+ colNode = self.find('**/collision')
+ floor = colNode.find('**/floor')
+ floor2 = floor.copyTo(colNode)
+ floor2.setZ(floor2, -.75)
+ if self.goonHatType is not 'none':
+ self.goonType = {'hardhat': 'pg',
+ 'security': 'sg'}[self.goonHatType]
+ self.hat = self.model
+ if self.goonType == 'pg':
+ self.hat.find('**/security_hat').hide()
+ elif self.goonType == 'sg':
+ self.hat.find('**/hard_hat').hide()
+ del self.hat
+ del self.goonType
+ if self.flattenType == 'light':
+ self.model.flattenLight()
+ elif self.flattenType == 'medium':
+ self.model.flattenMedium()
+ elif self.flattenType == 'strong':
+ self.model.flattenStrong()
+ return
+
+ def setModelPath(self, path):
+ self.modelPath = path
+ self.loadModel()
+
+ def setCollisionsOnly(self, collisionsOnly):
+ self.collisionsOnly = collisionsOnly
+ self.loadModel()
+
+ def setGoonHatType(self, goonHatType):
+ self.goonHatType = goonHatType
+ self.loadModel()
diff --git a/otp/level/PathEntity.py b/otp/level/PathEntity.py
new file mode 100644
index 0000000..5e27e9d
--- /dev/null
+++ b/otp/level/PathEntity.py
@@ -0,0 +1,57 @@
+from toontown.toonbase.ToontownGlobals import *
+from direct.interval.IntervalGlobal import *
+from direct.directnotify import DirectNotifyGlobal
+import BasicEntities
+from toontown.suit import GoonPathData
+
+class PathEntity(BasicEntities.NodePathEntity):
+ notify = DirectNotifyGlobal.directNotify.newCategory('PathEntity')
+
+ def __init__(self, level, entId):
+ self.pathScale = 1.0
+ BasicEntities.NodePathEntity.__init__(self, level, entId)
+ self.setPathIndex(self.pathIndex)
+
+ def destroy(self):
+ BasicEntities.NodePathEntity.destroy(self)
+
+ def setPathIndex(self, pathIndex):
+ self.pathIndex = pathIndex
+ pathTableId = GoonPathData.taskZoneId2pathId[self.level.getTaskZoneId()]
+ if self.pathIndex in GoonPathData.Paths[pathTableId]:
+ self.path = GoonPathData.Paths[pathTableId][self.pathIndex]
+ if __dev__:
+ messenger.send(self.getChangeEvent())
+ else:
+ PathEntity.notify.warning('invalid pathIndex: %s' % pathIndex)
+ self.path = None
+ return
+
+ def makePathTrack(self, node, velocity, name, turnTime = 1, lookAroundNode = None):
+ track = Sequence(name=name)
+ if self.path is None:
+ track.append(WaitInterval(1.0))
+ return track
+ path = self.path + [self.path[0]]
+ for pointIndex in range(len(path) - 1):
+ startPoint = Point3(path[pointIndex]) * self.pathScale
+ endPoint = Point3(path[pointIndex + 1]) * self.pathScale
+ v = startPoint - endPoint
+ node.setPos(startPoint[0], startPoint[1], startPoint[2])
+ node.headsUp(endPoint[0], endPoint[1], endPoint[2])
+ theta = node.getH() % 360
+ track.append(LerpHprInterval(node, turnTime, Vec3(theta, 0, 0)))
+ distance = Vec3(v).length()
+ duration = distance / velocity
+ track.append(LerpPosInterval(node, duration=duration, pos=endPoint, startPos=startPoint))
+
+ return track
+
+ if __dev__:
+
+ def getChangeEvent(self):
+ return self.getUniqueName('pathChanged')
+
+ def setPathScale(self, pathScale):
+ self.pathScale = pathScale
+ self.setPathIndex(self.pathIndex)
diff --git a/otp/level/PropSpinner.py b/otp/level/PropSpinner.py
new file mode 100644
index 0000000..0a4f341
--- /dev/null
+++ b/otp/level/PropSpinner.py
@@ -0,0 +1,58 @@
+import string
+from direct.interval.IntervalGlobal import *
+from Entity import Entity
+from pandac.PandaModules import Vec3
+
+class PropSpinner(Entity):
+
+ def __init__(self, level, entId):
+ Entity.__init__(self, level, entId)
+ self.initProps()
+
+ def destroy(self):
+ self.destroyProps()
+ Entity.destroy(self)
+
+ def initProps(self):
+ topNode = self.getZoneNode()
+ props = topNode.findAllMatches('**/Prop_*')
+ spinTracks = Parallel()
+ for prop in props:
+ name = prop.getName()
+ nameParts = name.split('_')
+ axis = nameParts[2]
+ rate = 0
+ neg = string.upper(nameParts[3][0]) == 'N'
+ if neg:
+ nameParts[3] = nameParts[3][1:]
+ try:
+ rate = int(nameParts[3])
+ except:
+ print 'invalid prop rotate string: %s' % name
+
+ if neg:
+ rate = -rate
+ prop.setHpr(0, 0, 0)
+ if axis == 'X':
+ hpr = Vec3(0, rate * 360, 0)
+ elif axis == 'Y':
+ hpr = Vec3(rate * 360, 0, 0)
+ elif axis == 'Z':
+ hpr = Vec3(0, 0, rate * 360)
+ else:
+ print 'error', axis
+ spinTracks.append(LerpHprInterval(prop, 60, hpr))
+
+ spinTracks.loop()
+ self.spinTracks = spinTracks
+
+ def destroyProps(self):
+ if hasattr(self, 'spinTracks'):
+ self.spinTracks.pause()
+ del self.spinTracks
+
+ if __dev__:
+
+ def attribChanged(self, *args):
+ self.destroyProps()
+ self.initProps()
diff --git a/otp/level/VisibilityBlocker.py b/otp/level/VisibilityBlocker.py
new file mode 100644
index 0000000..3f604ce
--- /dev/null
+++ b/otp/level/VisibilityBlocker.py
@@ -0,0 +1,29 @@
+import Entity
+
+class VisibilityBlocker:
+
+ def __init__(self):
+ self.__nextSetZoneDoneEvent = None
+ return
+
+ def destroy(self):
+ self.cancelUnblockVis()
+
+ def requestUnblockVis(self):
+ if self.__nextSetZoneDoneEvent is None:
+ self.__nextSetZoneDoneEvent = self.level.cr.getNextSetZoneDoneEvent()
+ self.acceptOnce(self.__nextSetZoneDoneEvent, self.okToUnblockVis)
+ self.level.forceSetZoneThisFrame()
+ return
+
+ def cancelUnblockVis(self):
+ if self.__nextSetZoneDoneEvent is not None:
+ self.ignore(self.__nextSetZoneDoneEvent)
+ self.__nextSetZoneDoneEvent = None
+ return
+
+ def isWaitingForUnblockVis(self):
+ return self.__nextSetZoneDoneEvent is not None
+
+ def okToUnblockVis(self):
+ self.cancelUnblockVis()
diff --git a/otp/level/VisibilityExtender.py b/otp/level/VisibilityExtender.py
new file mode 100644
index 0000000..d5e72ea
--- /dev/null
+++ b/otp/level/VisibilityExtender.py
@@ -0,0 +1,63 @@
+import Entity
+
+class VisibilityExtender(Entity.Entity):
+
+ def __init__(self, level, entId):
+ Entity.Entity.__init__(self, level, entId)
+ self.initVisExt()
+
+ def initVisExt(self):
+ self.extended = 0
+ self.zoneEntId = self.getZoneEntId()
+ self.eventName = None
+ if self.event is not None:
+ self.eventName = self.getOutputEventName(self.event)
+ self.accept(self.eventName, self.handleEvent)
+ return
+
+ def destroyVisExt(self):
+ if self.eventName is not None:
+ self.ignore(self.eventName)
+ if self.extended:
+ self.retract()
+ return
+
+ def handleEvent(self, doExtend):
+ if doExtend:
+ if not self.extended:
+ self.extend()
+ elif self.extended:
+ self.retract()
+
+ def extend(self):
+ zoneEnt = self.level.getEntity(self.getZoneEntId())
+ zoneEnt.incrementRefCounts(self.newZones)
+ self.extended = 1
+ self.level.handleVisChange()
+
+ def retract(self):
+ zoneEnt = self.level.getEntity(self.getZoneEntId())
+ zoneEnt.decrementRefCounts(self.newZones)
+ self.extended = 0
+ self.level.handleVisChange()
+
+ def destroy(self):
+ self.destroyVisExt()
+ Entity.Entity.destroy(self)
+
+ if __dev__:
+
+ def setNewZones(self, newZones):
+ extended = self.extended
+ self.destroyVisExt()
+ self.newZones = newZones
+ self.initVisExt()
+ if extended:
+ self.extend()
+
+ def attribChanged(self, *args):
+ extended = self.extended
+ self.destroyVisExt()
+ self.initVisExt()
+ if extended:
+ self.extend()
diff --git a/otp/level/ZoneEntity.py b/otp/level/ZoneEntity.py
new file mode 100644
index 0000000..6b1f8cd
--- /dev/null
+++ b/otp/level/ZoneEntity.py
@@ -0,0 +1,46 @@
+import ZoneEntityBase
+import BasicEntities
+
+class ZoneEntity(ZoneEntityBase.ZoneEntityBase, BasicEntities.NodePathAttribs):
+
+ def __init__(self, level, entId):
+ ZoneEntityBase.ZoneEntityBase.__init__(self, level, entId)
+ self.nodePath = self.level.getZoneNode(self.entId)
+ if self.nodePath is None:
+ if __dev__:
+ self.level.reportModelSpecSyncError('unknown zoneNum %s; zone was removed from model?' % self.entId)
+ else:
+ self.notify.error('zone %s not found in level model' % self.entId)
+ BasicEntities.NodePathAttribs.initNodePathAttribs(self, doReparent=0)
+ self.visibleZoneNums = {}
+ self.incrementRefCounts(self.visibility)
+ return
+
+ def destroy(self):
+ BasicEntities.NodePathAttribs.destroy(self)
+ ZoneEntityBase.ZoneEntityBase.destroy(self)
+
+ def getNodePath(self):
+ return self.nodePath
+
+ def getVisibleZoneNums(self):
+ return self.visibleZoneNums.keys()
+
+ def incrementRefCounts(self, zoneNumList):
+ for zoneNum in zoneNumList:
+ self.visibleZoneNums.setdefault(zoneNum, 0)
+ self.visibleZoneNums[zoneNum] += 1
+
+ def decrementRefCounts(self, zoneNumList):
+ for zoneNum in zoneNumList:
+ self.visibleZoneNums[zoneNum] -= 1
+ if self.visibleZoneNums[zoneNum] == 0:
+ del self.visibleZoneNums[zoneNum]
+
+ if __dev__:
+
+ def setVisibility(self, visibility):
+ self.decrementRefCounts(self.visibility)
+ self.visibility = visibility
+ self.incrementRefCounts(self.visibility)
+ self.level.handleVisChange()
diff --git a/otp/level/ZoneEntityAI.py b/otp/level/ZoneEntityAI.py
new file mode 100644
index 0000000..bdb5c3a
--- /dev/null
+++ b/otp/level/ZoneEntityAI.py
@@ -0,0 +1,12 @@
+import ZoneEntityBase
+
+class ZoneEntityAI(ZoneEntityBase.ZoneEntityBase):
+
+ def __init__(self, level, entId):
+ ZoneEntityBase.ZoneEntityBase.__init__(self, level, entId)
+ self.setZoneId(self.level.air.allocateZone())
+
+ def destroy(self):
+ if not self.isUberZone():
+ self.level.air.deallocateZone(self.getZoneId())
+ ZoneEntityBase.ZoneEntityBase.destroy(self)
diff --git a/otp/level/ZoneEntityBase.py b/otp/level/ZoneEntityBase.py
new file mode 100644
index 0000000..cf17b39
--- /dev/null
+++ b/otp/level/ZoneEntityBase.py
@@ -0,0 +1,25 @@
+import Entity
+import LevelConstants
+
+class ZoneEntityBase(Entity.Entity):
+
+ def __init__(self, level, entId):
+ Entity.Entity.__init__(self, level, entId)
+ self.zoneId = None
+ return
+
+ def destroy(self):
+ del self.zoneId
+ Entity.Entity.destroy(self)
+
+ def isUberZone(self):
+ return self.entId == LevelConstants.UberZoneEntId
+
+ def setZoneId(self, zoneId):
+ self.zoneId = zoneId
+
+ def getZoneId(self):
+ return self.zoneId
+
+ def getZoneNum(self):
+ return self.entId
diff --git a/otp/level/__init__.py b/otp/level/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/login/AccountServerConstants.py b/otp/login/AccountServerConstants.py
new file mode 100644
index 0000000..51fe790
--- /dev/null
+++ b/otp/login/AccountServerConstants.py
@@ -0,0 +1,67 @@
+from pandac.PandaModules import *
+from RemoteValueSet import *
+from direct.directnotify import DirectNotifyGlobal
+import TTAccount
+import HTTPUtil
+
+class AccountServerConstants(RemoteValueSet):
+ notify = DirectNotifyGlobal.directNotify.newCategory('AccountServerConstants')
+
+ def __init__(self, cr):
+ self.expectedConstants = ['minNameLength',
+ 'minPwLength',
+ 'allowNewAccounts',
+ 'freeTrialPeriodInDays',
+ 'priceFirstMonth',
+ 'pricePerMonth',
+ 'customerServicePhoneNumber',
+ 'creditCardUpFront']
+ self.defaults = {'minNameLength': '1',
+ 'minPwLength': '1',
+ 'allowNewAccounts': '1',
+ 'creditCardUpFront': '0',
+ 'priceFirstMonth': '9.95',
+ 'pricePerMonth': '9.95'}
+ noquery = 1
+ if cr.productName == 'DisneyOnline-US':
+ if base.config.GetBool('tt-specific-login', 0):
+ pass
+ else:
+ noquery = 0
+ if cr.accountOldAuth or base.config.GetBool('default-server-constants', noquery):
+ self.notify.debug('setting defaults, not using account server constants')
+ self.dict = {}
+ for constantName in self.expectedConstants:
+ self.dict[constantName] = 'DEFAULT'
+
+ self.dict.update(self.defaults)
+ return
+ url = URLSpec(AccountServerConstants.getServer())
+ url.setPath('/constants.php')
+ self.notify.debug('grabbing account server constants from %s' % url.cStr())
+ RemoteValueSet.__init__(self, url, cr.http, expectedHeader='ACCOUNT SERVER CONSTANTS', expectedFields=self.expectedConstants)
+
+ def getBool(self, name):
+ return self.__getConstant(name, RemoteValueSet.getBool)
+
+ def getInt(self, name):
+ return self.__getConstant(name, RemoteValueSet.getInt)
+
+ def getFloat(self, name):
+ return self.__getConstant(name, RemoteValueSet.getFloat)
+
+ def getString(self, name):
+ return self.__getConstant(name, RemoteValueSet.getString)
+
+ def __getConstant(self, constantName, accessor):
+ if constantName not in self.expectedConstants:
+ self.notify.warning("requested constant '%s' not in expected constant list; if it's a new constant, add it to the list" % constantName)
+ return accessor(self, constantName)
+
+ @staticmethod
+ def getServer():
+ return TTAccount.getAccountServer().cStr()
+
+ @staticmethod
+ def getServerURL():
+ return TTAccount.getAccountServer()
diff --git a/otp/login/CreateAccountScreen.py b/otp/login/CreateAccountScreen.py
new file mode 100644
index 0000000..66a6f39
--- /dev/null
+++ b/otp/login/CreateAccountScreen.py
@@ -0,0 +1,256 @@
+from pandac.PandaModules import *
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from direct.fsm import StateData
+from otp.otpgui import OTPDialog
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from direct.directnotify import DirectNotifyGlobal
+from otp.otpbase import OTPLocalizer
+import TTAccount
+import GuiScreen
+from otp.otpbase import OTPGlobals
+from direct.distributed.MsgTypes import *
+
+class CreateAccountScreen(StateData.StateData, GuiScreen.GuiScreen):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CreateAccountScreen')
+ ActiveEntryColor = Vec4(1, 1, 1, 1)
+ InactiveEntryColor = Vec4(0.8, 0.8, 0.8, 1)
+ labelFg = (1, 1, 1, 1)
+ labelFgActive = (1, 1, 0, 1)
+
+ def __init__(self, cr, doneEvent):
+ StateData.StateData.__init__(self, doneEvent)
+ GuiScreen.GuiScreen.__init__(self)
+ self.cr = cr
+ self.loginInterface = self.cr.loginInterface
+ self.fsm = ClassicFSM.ClassicFSM('CreateAccountScreen', [State.State('off', self.enterOff, self.exitOff, ['create']), State.State('create', self.enterCreate, self.exitCreate, ['waitForLoginResponse', 'create']), State.State('waitForLoginResponse', self.enterWaitForLoginResponse, self.exitWaitForLoginResponse, ['create'])], 'off', 'off')
+ self.fsm.enterInitialState()
+
+ def load(self):
+ self.notify.debug('load')
+ masterScale = 0.8
+ textScale = 0.1 * masterScale
+ entryScale = 0.08 * masterScale
+ lineHeight = 0.21 * masterScale
+ buttonScale = 1.3 * masterScale
+ buttonLineHeight = 0.16 * masterScale
+ self.frame = DirectFrame(parent=aspect2d, relief=None)
+ self.frame.hide()
+ linePos = 0.5
+ linePos -= lineHeight
+ self.nameLabel = DirectLabel(parent=self.frame, relief=None, pos=(-0.21, 0, linePos), text=OTPLocalizer.CreateAccountScreenUserName, text_scale=textScale, text_align=TextNode.ARight, text_fg=self.labelFg, text_shadow=(0, 0, 0, 1), text_shadowOffset=(0.08, 0.08))
+ self.nameEntry = DirectEntry(parent=self.frame, relief=DGG.SUNKEN, borderWidth=(0.1, 0.1), scale=entryScale, pos=(-0.125, 0.0, linePos), width=OTPGlobals.maxLoginWidth, numLines=1, focus=0, cursorKeys=1)
+ self.nameEntry.label = self.nameLabel
+ linePos -= lineHeight
+ self.passwordLabel = DirectLabel(parent=self.frame, relief=None, pos=(-0.21, 0, linePos), text=OTPLocalizer.CreateAccountScreenPassword, text_scale=textScale, text_align=TextNode.ARight, text_fg=self.labelFg, text_shadow=(0, 0, 0, 1), text_shadowOffset=(0.08, 0.08))
+ self.passwordEntry = DirectEntry(parent=self.frame, relief=DGG.SUNKEN, borderWidth=(0.1, 0.1), scale=entryScale, pos=(-0.125, 0.0, linePos), width=OTPGlobals.maxLoginWidth, numLines=1, focus=0, cursorKeys=1, obscured=1)
+ self.passwordEntry.label = self.passwordLabel
+ linePos -= lineHeight
+ self.passwordConfirmLabel = DirectLabel(parent=self.frame, relief=None, pos=(-0.21, 0, linePos), text=OTPLocalizer.CreateAccountScreenConfirmPassword, text_scale=textScale, text_align=TextNode.ARight, text_fg=self.labelFg, text_shadow=(0, 0, 0, 1), text_shadowOffset=(0.08, 0.08))
+ self.passwordConfirmEntry = DirectEntry(parent=self.frame, relief=DGG.SUNKEN, borderWidth=(0.1, 0.1), scale=entryScale, pos=(-0.125, 0.0, linePos), width=OTPGlobals.maxLoginWidth, numLines=1, focus=0, cursorKeys=1, obscured=1)
+ self.passwordConfirmEntry.label = self.passwordConfirmLabel
+ linePos -= lineHeight
+ linePos -= lineHeight
+ self.submitButton = DirectButton(parent=self.frame, relief=DGG.RAISED, borderWidth=(0.01, 0.01), pos=(0, 0, linePos), scale=buttonScale, text=OTPLocalizer.CreateAccountScreenSubmit, text_scale=0.06, text_pos=(0, -0.02), command=self.__handleSubmit)
+ linePos -= buttonLineHeight
+ self.cancelButton = DirectButton(parent=self.frame, relief=DGG.RAISED, borderWidth=(0.01, 0.01), pos=(0, 0, linePos), scale=buttonScale, text=OTPLocalizer.CreateAccountScreenCancel, text_scale=0.06, text_pos=(0, -0.02), command=self.__handleCancel)
+ linePos -= buttonLineHeight
+ self.dialogDoneEvent = 'createAccountDialogAck'
+ dialogClass = OTPGlobals.getGlobalDialogClass()
+ self.dialog = dialogClass(dialogName='createAccountDialog', doneEvent=self.dialogDoneEvent, message='', style=OTPDialog.Acknowledge, sortOrder=NO_FADE_SORT_INDEX + 100)
+ self.dialog.hide()
+ return
+
+ def unload(self):
+ self.notify.debug('unload')
+ self.dialog.cleanup()
+ del self.dialog
+ self.frame.destroy()
+ del self.fsm
+ del self.loginInterface
+ del self.cr
+
+ def enter(self):
+ self.__firstTime = 1
+ self.frame.show()
+ self.fsm.request('create')
+
+ def exit(self):
+ self.ignore(self.dialogDoneEvent)
+ self.fsm.requestFinalState()
+ self.frame.hide()
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterCreate(self):
+ self.password = ''
+ self.passwordEntry.set('')
+ self.passwordConfirmEntry.set('')
+ if self.__firstTime:
+ self.userName = ''
+ self.nameEntry.set(self.userName)
+ self.__firstTime = 0
+ self.focusList = [self.nameEntry, self.passwordEntry, self.passwordConfirmEntry]
+ self.startFocusMgmt(overrides={}, globalFocusHandler=self.__handleFocusChange)
+
+ def exitCreate(self):
+ self.stopFocusMgmt()
+
+ def __handleFocusChange(self, focusItem):
+ for item in self.focusList:
+ item.label.component('text0').setFg(self.labelFg)
+
+ if focusItem is not None:
+ focusItem.label.component('text0').setFg(self.labelFgActive)
+ return
+
+ def __handleSubmit(self):
+ self.removeFocus()
+ self.userName = self.nameEntry.get()
+ self.password = self.passwordEntry.get()
+ passwordConfirm = self.passwordConfirmEntry.get()
+ minNameLength = self.cr.accountServerConstants.getInt('minNameLength')
+ minPwdLength = self.cr.accountServerConstants.getInt('minPwLength')
+ if self.userName == '':
+ self.dialog.setMessage(OTPLocalizer.CreateAccountScreenNoAccountName)
+ self.dialog.show()
+ self.acceptOnce(self.dialogDoneEvent, self.__handleUsernameAck)
+ elif len(self.userName) < minNameLength:
+ self.dialog.setMessage(OTPLocalizer.CreateAccountScreenAccountNameTooShort % minNameLength)
+ self.dialog.show()
+ self.acceptOnce(self.dialogDoneEvent, self.__handleUsernameAck)
+ elif len(self.password) < minPwdLength:
+ self.dialog.setMessage(OTPLocalizer.CreateAccountScreenPasswordTooShort % minPwdLength)
+ self.dialog.show()
+ self.acceptOnce(self.dialogDoneEvent, self.__handlePasswordAck)
+ elif self.password != passwordConfirm:
+ self.dialog.setMessage(OTPLocalizer.CreateAccountScreenPasswordMismatch)
+ self.dialog.show()
+ self.acceptOnce(self.dialogDoneEvent, self.__handlePasswordAck)
+ else:
+ self.fsm.request('waitForLoginResponse')
+
+ def __handleCancel(self):
+ messenger.send(self.doneEvent, [{'mode': 'cancel'}])
+
+ def __handleUsernameAck(self):
+ self.dialog.hide()
+ self.fsm.request('create')
+ self.setFocus(self.nameEntry)
+
+ def __handlePasswordAck(self):
+ self.dialog.hide()
+ self.fsm.request('create')
+ self.setFocus(self.passwordEntry)
+
+ def enterWaitForLoginResponse(self):
+ self.cr.handler = self.handleWaitForLoginResponse
+ self.cr.userName = self.userName
+ self.cr.password = self.password
+ try:
+ data = {}
+ referrer = launcher.getReferrerCode()
+ if referrer is not None:
+ data['referrer'] = referrer
+ error = self.loginInterface.createAccount(self.userName, self.password, data)
+ except TTAccount.TTAccountException, e:
+ error = str(e)
+ self.notify.info(error)
+ self.dialog.setMessage(error + OTPLocalizer.CreateAccountScreenConnectionErrorSuffix)
+ self.dialog.show()
+ self.acceptOnce(self.dialogDoneEvent, self.__handleConnectionErrorAck)
+ return
+
+ if error:
+ self.notify.info(error)
+ self.dialog.setMessage(error)
+ self.dialog.show()
+ self.acceptOnce(self.dialogDoneEvent, self.__handleBadAccountAck)
+ else:
+ self.cr.logAccountInfo()
+ self.loginInterface.sendLoginMsg()
+ self.waitForDatabaseTimeout(requestName='CreateAccountWaitForLoginResponse')
+ return
+
+ def exitWaitForLoginResponse(self):
+ self.cleanupWaitingForDatabase()
+ self.cr.handler = None
+ return
+
+ def handleWaitForLoginResponse(self, msgType, di):
+ if msgType == CLIENT_LOGIN_2_RESP:
+ self.handleLoginResponseMsg2(di)
+ elif msgType == CLIENT_LOGIN_RESP:
+ self.handleLoginResponseMsg(di)
+ else:
+ self.cr.handleMessageType(msgType, di)
+
+ def handleLoginResponseMsg2(self, di):
+ returnCode = di.getUint8()
+ self.notify.info('Login response return code: ' + str(returnCode))
+ if returnCode == 0:
+ self.__handleLoginSuccess()
+ else:
+ errorString = di.getString()
+ self.notify.warning(errorString)
+ messenger.send(self.doneEvent, [{'mode': 'reject'}])
+
+ def __handleLoginSuccess(self):
+ self.notify.info('Logged in with username: %s' % self.userName)
+ launcher.setGoUserName(self.userName)
+ launcher.setLastLogin(self.userName)
+ launcher.setUserLoggedIn()
+ messenger.send(self.doneEvent, [{'mode': 'success'}])
+
+ def handleLoginResponseMsg(self, di):
+ returnCode = di.getUint8()
+ self.notify.info('Login response return code: ' + str(returnCode))
+ if returnCode == 0:
+ accountCode = di.getUint32()
+ commentString = di.getString()
+ sec = di.getUint32()
+ usec = di.getUint32()
+ self.__handleLoginSuccess()
+ elif returnCode == 12:
+ self.notify.info('Bad password')
+ self.dialog.setMessage(OTPLocalizer.CreateAccountScreenUserNameTaken)
+ self.dialog.show()
+ self.acceptOnce(self.dialogDoneEvent, self.__handleBadPasswordAck)
+ elif returnCode == 14:
+ self.notify.info('Bad word in user name')
+ self.dialog.setMessage(OTPLocalizer.CreateAccountScreenInvalidUserName)
+ self.dialog.show()
+ self.acceptOnce(self.dialogDoneEvent, self.__handleBadWordInUserName)
+ elif returnCode == 129:
+ self.notify.info('Username not found')
+ self.dialog.setMessage(OTPLocalizer.CreateAccountScreenUserNameNotFound)
+ self.dialog.show()
+ self.acceptOnce(self.dialogDoneEvent, self.__handleBadAccountAck)
+ else:
+ accountCode = di.getUint32()
+ errorString = di.getString()
+ self.notify.warning(errorString)
+ messenger.send(self.doneEvent, [{'mode': 'reject'}])
+
+ def __handleConnectionErrorAck(self):
+ self.dialog.hide()
+ messenger.send(self.doneEvent, [{'mode': 'failure'}])
+
+ def __handleBadPasswordAck(self):
+ self.dialog.hide()
+ self.fsm.request('create')
+
+ def __handleBadAccountAck(self):
+ self.dialog.hide()
+ self.fsm.request('create')
+
+ def __handleBadWordInUserName(self):
+ self.userName = ''
+ self.nameEntry.set('')
+ self.dialog.hide()
+ self.fsm.request('create')
diff --git a/otp/login/GuiScreen.py b/otp/login/GuiScreen.py
new file mode 100644
index 0000000..6c50c0a
--- /dev/null
+++ b/otp/login/GuiScreen.py
@@ -0,0 +1,276 @@
+from pandac.PandaModules import *
+from otp.otpbase import OTPGlobals
+from direct.gui.DirectGui import *
+from otp.otpgui import OTPDialog
+from direct.directnotify import DirectNotifyGlobal
+from otp.otpbase import OTPLocalizer
+from direct.task.Task import Task
+
+class GuiScreen:
+ notify = DirectNotifyGlobal.directNotify.newCategory('GuiScreen')
+ DGG.ENTERPRESS_ADVANCE = 0
+ DGG.ENTERPRESS_ADVANCE_IFNOTEMPTY = 1
+ DGG.ENTERPRESS_DONT_ADVANCE = 2
+ DGG.ENTERPRESS_REMOVE_FOCUS = 3
+ ENTRY_WIDTH = 20
+
+ def __init__(self):
+ self.waitingForDatabase = None
+ self.focusIndex = None
+ self.suppressClickSound = 0
+ return
+
+ def startFocusMgmt(self, startFocus = 0, enterPressBehavior = DGG.ENTERPRESS_ADVANCE_IFNOTEMPTY, overrides = {}, globalFocusHandler = None):
+ GuiScreen.notify.debug('startFocusMgmt:\nstartFocus=%s,\nenterPressBehavior=%s\noverrides=%s' % (startFocus, enterPressBehavior, overrides))
+ self.accept('tab', self.__handleTab)
+ self.accept('shift-tab', self.__handleShiftTab)
+ self.accept('enter', self.__handleEnter)
+ self.__startFrameStartTask()
+ self.userGlobalFocusHandler = globalFocusHandler
+ self.focusHandlerAbsorbCounts = {}
+ for i in xrange(len(self.focusList)):
+ item = self.focusList[i]
+ if isinstance(item, DirectEntry):
+ self.focusHandlerAbsorbCounts[item] = 0
+
+ self.userFocusHandlers = {}
+ self.userCommandHandlers = {}
+ for i in xrange(len(self.focusList)):
+ item = self.focusList[i]
+ if isinstance(item, DirectEntry):
+ self.userFocusHandlers[item] = (item['focusInCommand'], item['focusInExtraArgs'])
+ item['focusInCommand'] = self.__handleFocusChangeAbsorb
+ item['focusInExtraArgs'] = [i]
+ self.userCommandHandlers[item] = (item['command'], item['extraArgs'])
+ item['command'] = None
+ item['extraArgs'] = []
+ elif isinstance(item, DirectScrolledList):
+ self.userCommandHandlers[item] = (item['command'], item['extraArgs'])
+ item['command'] = self.__handleDirectScrolledListCommand
+ item['extraArgs'] = [i]
+
+ self.enterPressHandlers = {}
+ for i in xrange(len(self.focusList)):
+ item = self.focusList[i]
+ behavior = enterPressBehavior
+ if overrides.has_key(item):
+ behavior = overrides[item]
+ if callable(behavior):
+ self.enterPressHandlers[item] = behavior
+ else:
+ if not isinstance(item, DirectEntry) and behavior == GuiScreen_ENTERPRESS_ADVANCE_IFNOTEMPTY:
+ behavior = GuiScreen_ENTERPRESS_ADVANCE
+ commandHandlers = (self.__alwaysAdvanceFocus,
+ self.__advanceFocusIfNotEmpty,
+ self.__neverAdvanceFocus,
+ self.__ignoreEnterPress)
+ self.enterPressHandlers[item] = commandHandlers[behavior]
+
+ self.setFocus(startFocus)
+ return
+
+ def focusMgmtActive(self):
+ return self.focusIndex != None
+
+ def stopFocusMgmt(self):
+ GuiScreen.notify.debug('stopFocusMgmt')
+ if not self.focusMgmtActive():
+ return
+ self.ignore('tab')
+ self.ignore('shift-tab')
+ self.ignore('enter')
+ self.__stopFrameStartTask()
+ self.userGlobalFocusHandler = None
+ self.focusIndex = None
+ self.focusHandlerAbsorbCounts = {}
+ for item in self.focusList:
+ if isinstance(item, DirectEntry):
+ userHandler, userHandlerArgs = self.userFocusHandlers[item]
+ item['focusInCommand'] = userHandler
+ item['focusInExtraArgs'] = userHandlerArgs
+ userHandler, userHandlerArgs = self.userCommandHandlers[item]
+ item['command'] = userHandler
+ item['extraArgs'] = userHandlerArgs
+ elif isinstance(item, DirectScrolledList):
+ userHandler, userHandlerArgs = self.userCommandHandlers[item]
+ item['command'] = userHandler
+ item['extraArgs'] = userHandlerArgs
+
+ self.userFocusHandlers = {}
+ self.userCommandHandlers = {}
+ self.enterPressHandlers = {}
+ return
+
+ def setFocus(self, arg, suppressSound = 1):
+ if type(arg) == type(0):
+ index = arg
+ else:
+ index = self.focusList.index(arg)
+ if suppressSound:
+ self.suppressClickSound += 1
+ self.__setFocusIndex(index)
+
+ def advanceFocus(self, condition = 1):
+ index = self.getFocusIndex()
+ if condition:
+ index += 1
+ self.setFocus(index, suppressSound=0)
+
+ def getFocusIndex(self):
+ if not self.focusMgmtActive():
+ return None
+ return self.focusIndex
+
+ def getFocusItem(self):
+ if not self.focusMgmtActive():
+ return None
+ return self.focusList[self.focusIndex]
+
+ def removeFocus(self):
+ focusItem = self.getFocusItem()
+ if isinstance(focusItem, DirectEntry):
+ focusItem['focus'] = 0
+ if self.userGlobalFocusHandler:
+ self.userGlobalFocusHandler(None)
+ return
+
+ def restoreFocus(self):
+ self.setFocus(self.getFocusItem())
+
+ def __setFocusIndex(self, index):
+ focusIndex = index % len(self.focusList)
+ focusItem = self.focusList[focusIndex]
+ if isinstance(focusItem, DirectEntry):
+ focusItem['focus'] = 1
+ self.focusHandlerAbsorbCounts[focusItem] += 1
+ self.__handleFocusChange(focusIndex)
+
+ def __chainToUserCommandHandler(self, item):
+ userHandler, userHandlerArgs = self.userCommandHandlers[item]
+ if userHandler:
+ if isinstance(item, DirectEntry):
+ enteredText = item.get()
+ apply(userHandler, [enteredText] + userHandlerArgs)
+ elif isinstance(item, DirectScrolledList):
+ apply(userHandler, userHandlerArgs)
+
+ def __chainToUserFocusHandler(self, item):
+ if isinstance(item, DirectEntry):
+ userHandler, userHandlerArgs = self.userFocusHandlers[item]
+ if userHandler:
+ apply(userHandler, userHandlerArgs)
+
+ def __handleTab(self):
+ self.tabPressed = 1
+ self.focusDirection = 1
+ self.__setFocusIndex(self.getFocusIndex() + self.focusDirection)
+
+ def __handleShiftTab(self):
+ self.tabPressed = 1
+ self.focusDirection = -1
+ self.__setFocusIndex(self.getFocusIndex() + self.focusDirection)
+
+ def __handleFocusChangeAbsorb(self, index):
+ item = self.focusList[index]
+ if self.focusHandlerAbsorbCounts[item] > 0:
+ self.focusHandlerAbsorbCounts[item] -= 1
+ else:
+ self.__handleFocusChange(index)
+
+ def playFocusChangeSound(self):
+ base.playSfx(DGG.getDefaultClickSound())
+
+ def __handleFocusChange(self, index):
+ if index != self.focusIndex:
+ self.removeFocus()
+ self.__focusChangedThisFrame = 1
+ if hasattr(self, 'tabPressed'):
+ del self.tabPressed
+ else:
+ self.focusDirection = 1
+ self.focusIndex = index
+ if self.suppressClickSound > 0:
+ self.suppressClickSound -= 1
+ else:
+ self.playFocusChangeSound()
+ focusItem = self.getFocusItem()
+ if self.userGlobalFocusHandler:
+ self.userGlobalFocusHandler(focusItem)
+ if self.getFocusItem() != focusItem:
+ GuiScreen.notify.debug('focus changed by global focus handler')
+ if self.focusMgmtActive():
+ self.__chainToUserFocusHandler(focusItem)
+
+ def __startFrameStartTask(self):
+ self.__focusChangedThisFrame = 0
+ self.frameStartTaskName = 'GuiScreenFrameStart'
+ taskMgr.add(self.__handleFrameStart, self.frameStartTaskName, -100)
+
+ def __stopFrameStartTask(self):
+ taskMgr.remove(self.frameStartTaskName)
+ del self.frameStartTaskName
+ del self.__focusChangedThisFrame
+
+ def __handleFrameStart(self, task):
+ self.__focusChangedThisFrame = 0
+ return Task.cont
+
+ def __handleDirectScrolledListCommand(self, index):
+ self.__chainToUserCommandHandler(self.focusList[index])
+ self.setFocus(index, suppressSound=self.getFocusIndex() == index)
+
+ def __handleEnter(self):
+ if self.__focusChangedThisFrame:
+ return
+ focusItem = self.getFocusItem()
+ if isinstance(focusItem, DirectEntry):
+ self.__chainToUserCommandHandler(focusItem)
+ if self.focusMgmtActive() and focusItem == self.getFocusItem():
+ self.enterPressHandlers[focusItem]()
+
+ def __alwaysAdvanceFocus(self):
+ self.advanceFocus()
+
+ def __advanceFocusIfNotEmpty(self):
+ focusItem = self.getFocusItem()
+ enteredText = focusItem.get()
+ if enteredText != '':
+ self.advanceFocus()
+ else:
+ self.setFocus(self.getFocusIndex())
+
+ def __neverAdvanceFocus(self):
+ self.setFocus(self.getFocusIndex())
+
+ def __ignoreEnterPress(self):
+ pass
+
+ def waitForDatabaseTimeout(self, requestName = 'unknown'):
+ GuiScreen.notify.debug('waiting for database timeout %s at %s' % (requestName, globalClock.getFrameTime()))
+ globalClock.tick()
+ taskMgr.doMethodLater(OTPGlobals.DatabaseDialogTimeout, self.__showWaitingForDatabase, 'waitingForDatabase', extraArgs=[requestName])
+
+ def __showWaitingForDatabase(self, requestName):
+ GuiScreen.notify.info('timed out waiting for %s at %s' % (requestName, globalClock.getFrameTime()))
+ dialogClass = OTPGlobals.getDialogClass()
+ self.waitingForDatabase = dialogClass(text=OTPLocalizer.GuiScreenToontownUnavailable, dialogName='WaitingForDatabase', buttonTextList=[OTPLocalizer.GuiScreenCancel], style=OTPDialog.Acknowledge, command=self.__handleCancelWaiting)
+ self.waitingForDatabase.show()
+ taskMgr.doMethodLater(OTPGlobals.DatabaseGiveupTimeout, self.__giveUpWaitingForDatabase, 'waitingForDatabase', extraArgs=[requestName])
+ return Task.done
+
+ def __giveUpWaitingForDatabase(self, requestName):
+ GuiScreen.notify.info('giving up waiting for %s at %s' % (requestName, globalClock.getFrameTime()))
+ self.cleanupWaitingForDatabase()
+ messenger.send(self.doneEvent, [{'mode': 'failure'}])
+ return Task.done
+
+ def cleanupWaitingForDatabase(self):
+ if self.waitingForDatabase != None:
+ self.waitingForDatabase.cleanup()
+ self.waitingForDatabase = None
+ taskMgr.remove('waitingForDatabase')
+ return
+
+ def __handleCancelWaiting(self, value):
+ self.cleanupWaitingForDatabase()
+ messenger.send(self.doneEvent, [{'mode': 'quit'}])
diff --git a/otp/login/HTTPUtil.py b/otp/login/HTTPUtil.py
new file mode 100644
index 0000000..6d8600b
--- /dev/null
+++ b/otp/login/HTTPUtil.py
@@ -0,0 +1,36 @@
+from pandac.PandaModules import *
+
+class HTTPUtilException(Exception):
+
+ def __init__(self, what):
+ Exception.__init__(self, what)
+
+
+class ConnectionError(HTTPUtilException):
+
+ def __init__(self, what, statusCode):
+ HTTPUtilException.__init__(self, what)
+ self.statusCode = statusCode
+
+
+class UnexpectedResponse(HTTPUtilException):
+
+ def __init__(self, what):
+ HTTPUtilException.__init__(self, what)
+
+
+def getHTTPResponse(url, http, body = ''):
+ if body:
+ hd = http.postForm(url, body)
+ else:
+ hd = http.getDocument(url)
+ if not hd.isValid():
+ raise ConnectionError('Unable to reach %s' % url.cStr(), hd.getStatusCode())
+ stream = hd.openReadBody()
+ sr = StreamReader(stream, 1)
+ response = sr.readlines()
+ for i in xrange(len(response)):
+ if response[i][-1] == '\n':
+ response[i] = response[i][:-1]
+
+ return response
diff --git a/otp/login/LeaveToPayDialog.py b/otp/login/LeaveToPayDialog.py
new file mode 100644
index 0000000..f76f2c8
--- /dev/null
+++ b/otp/login/LeaveToPayDialog.py
@@ -0,0 +1,95 @@
+from otp.otpbase import OTPGlobals
+from otp.otpbase import OTPLauncherGlobals
+from otp.otpbase import OTPLocalizer
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+import os
+
+class LeaveToPayDialog:
+
+ def __init__(self, paidUser, destructorHook = None, doneFunc = None):
+ self.destructorHook = destructorHook
+ self.dialog = None
+ self.okHandler = self.__handleLeaveToPayOK
+ self.cancelHandler = self.__handleLeaveToPayCancel
+ self.paidUser = paidUser
+ self.doneFunc = doneFunc
+ return
+
+ def setOK(self, handler):
+ self.okHandler = handler
+
+ def setCancel(self, handler):
+ self.cancelHandler = handler
+
+ def show(self):
+ if self.paidUser:
+ if base.cr.productName in ['DisneyOnline-AP',
+ 'DisneyOnline-UK',
+ 'JP',
+ 'FR']:
+ directFrameText = OTPLocalizer.LeaveToEnableChatUK
+ directButtonYesText = OTPLocalizer.LeaveToEnableChatUKYes
+ directButtonNoText = OTPLocalizer.LeaveToEnableChatUKNo
+ else:
+ directFrameText = OTPLocalizer.LeaveToSetParentPassword
+ directButtonYesText = OTPLocalizer.LeaveToSetParentPasswordYes
+ directButtonNoText = OTPLocalizer.LeaveToSetParentPasswordNo
+ else:
+ directFrameText = OTPLocalizer.LeaveToPay
+ directButtonYesText = OTPLocalizer.LeaveToPayYes
+ directButtonNoText = OTPLocalizer.LeaveToPayNo
+ if self.dialog == None:
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ cancelButtonImage = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr'))
+ self.dialog = DirectFrame(parent=aspect2dp, pos=(0.0, 0.0, 0.0), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(0.9, 1.0, 0.5), text=directFrameText, text_align=TextNode.ALeft, text_wordwrap=14, text_scale=OTPLocalizer.LTPDdirectFrameText, text_pos=(-0.4, 0.15), textMayChange=0)
+ DirectButton(self.dialog, image=okButtonImage, relief=None, text=directButtonYesText, text_scale=OTPLocalizer.LTPDdirectButtonYesText, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.23, 0.0, -0.1), command=self.okHandler)
+ DirectButton(self.dialog, image=cancelButtonImage, relief=None, text=directButtonNoText, text_scale=OTPLocalizer.LTPDdirectButtonNoText, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.23, 0.0, -0.1), command=self.cancelHandler)
+ buttons.removeNode()
+ self.dialog.show()
+ return
+
+ def hide(self):
+ self.dialog.hide()
+
+ def destroy(self):
+ if self.destructorHook:
+ self.destructorHook()
+ if self.dialog:
+ self.dialog.hide()
+ self.dialog.destroy()
+ self.destructorHook
+ self.dialog = None
+ self.okHandler = None
+ self.cancelHandler = None
+ return
+
+ def removed(self):
+ if hasattr(self, 'dialog') and self.dialog:
+ return self.dialog.removed()
+ else:
+ return 1
+
+ def __handleLeaveToPayOK(self):
+ self.destroy()
+ errorCode = None
+ if self.paidUser:
+ if base.cr.productName in ['DisneyOnline-AP',
+ 'DisneyOnline-UK',
+ 'JP',
+ 'DE',
+ 'FR']:
+ errorCode = OTPLauncherGlobals.ExitEnableChat
+ else:
+ errorCode = OTPLauncherGlobals.ExitSetParentPassword
+ else:
+ errorCode = OTPLauncherGlobals.ExitPurchase
+ base.setExitErrorCode(errorCode)
+ base.cr.loginFSM.request('shutdown', [errorCode])
+ return
+
+ def __handleLeaveToPayCancel(self):
+ if self.doneFunc:
+ self.doneFunc()
+ self.destroy()
diff --git a/otp/login/LoginBase.py b/otp/login/LoginBase.py
new file mode 100644
index 0000000..48b8509
--- /dev/null
+++ b/otp/login/LoginBase.py
@@ -0,0 +1,16 @@
+
+
+class LoginBase:
+ freeTimeExpires = -1
+
+ def __init__(self, cr):
+ self.cr = cr
+
+ def sendLoginMsg(self, loginName, password, createFlag):
+ pass
+
+ def getErrorCode(self):
+ return 0
+
+ def needToSetParentPassword(self):
+ return 0
diff --git a/otp/login/LoginDISLTokenAccount.py b/otp/login/LoginDISLTokenAccount.py
new file mode 100644
index 0000000..87a8619
--- /dev/null
+++ b/otp/login/LoginDISLTokenAccount.py
@@ -0,0 +1,35 @@
+from direct.showbase.ShowBaseGlobal import *
+from direct.distributed.MsgTypes import *
+from direct.directnotify import DirectNotifyGlobal
+import LoginBase
+from direct.distributed.PyDatagram import PyDatagram
+
+class LoginDISLTokenAccount(LoginBase.LoginBase):
+
+ def __init__(self, cr):
+ LoginBase.LoginBase.__init__(self, cr)
+
+ def supportsRelogin(self):
+ return 0
+
+ def authorize(self, loginName, password):
+ self.loginName = loginName
+ self.DISLToken = password
+
+ def sendLoginMsg(self):
+ cr = self.cr
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_LOGIN_3)
+ datagram.addString(self.DISLToken)
+ datagram.addString(cr.serverVersion)
+ datagram.addUint32(cr.hashVal)
+ datagram.addInt32(CLIENT_LOGIN_3_DISL_TOKEN)
+ datagram.addString(cr.validateDownload)
+ datagram.addString(cr.wantMagicWords)
+ cr.send(datagram)
+
+ def supportsParentPassword(self):
+ return 0
+
+ def supportsAuthenticateDelete(self):
+ return 0
diff --git a/otp/login/LoginGSAccount.py b/otp/login/LoginGSAccount.py
new file mode 100644
index 0000000..2eddaba
--- /dev/null
+++ b/otp/login/LoginGSAccount.py
@@ -0,0 +1,80 @@
+from pandac.PandaModules import *
+from direct.distributed.MsgTypes import *
+from direct.directnotify import DirectNotifyGlobal
+import LoginBase
+from direct.distributed.PyDatagram import PyDatagram
+
+class LoginGSAccount(LoginBase.LoginBase):
+
+ def __init__(self, cr):
+ LoginBase.LoginBase.__init__(self, cr)
+
+ def createAccount(self, loginName, password, data):
+ self.loginName = loginName
+ self.password = password
+ self.createFlag = 1
+ self.cr.freeTimeExpiresAt = -1
+ self.cr.setIsPaid(1)
+ return None
+
+ def authorize(self, loginName, password):
+ self.loginName = loginName
+ self.password = password
+ self.createFlag = 0
+ self.cr.freeTimeExpiresAt = -1
+ self.cr.setIsPaid(1)
+ return None
+
+ def supportsRelogin(self):
+ return 1
+
+ def sendLoginMsg(self):
+ DISLID = config.GetInt('fake-DISL-PlayerAccountId', 0)
+ if not DISLID:
+ NameStringId = 'DISLID_%s' % self.loginName
+ DISLID = config.GetInt(NameStringId, 0)
+ cr = self.cr
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_LOGIN)
+ datagram.addString(self.loginName)
+ if cr.connectMethod != cr.CM_HTTP:
+ datagram.addUint32(cr.tcpConn.getAddress().getIp())
+ else:
+ datagram.addUint32(0)
+ datagram.addUint16(5150)
+ datagram.addString(cr.serverVersion)
+ datagram.addUint32(cr.hashVal)
+ datagram.addString(self.password)
+ datagram.addBool(self.createFlag)
+ datagram.addString(cr.validateDownload)
+ datagram.addString(cr.wantMagicWords)
+ datagram.addUint32(DISLID)
+ datagram.addString(config.GetString('otp-whitelist', 'YES'))
+ cr.send(datagram)
+
+ def resendPlayToken(self):
+ pass
+
+ def requestPwdReminder(self, email = None, acctName = None):
+ return 0
+
+ def getAccountData(self, loginName, password):
+ return 'Unsupported'
+
+ def supportsParentPassword(self):
+ return 1
+
+ def authenticateParentPassword(self, loginName, password, parentPassword):
+ return (password == parentPassword, None)
+
+ def authenticateParentUsernameAndPassword(self, loginName, password, parentUsername, parentPassword):
+ return (password == parentPassword, None)
+
+ def supportsAuthenticateDelete(self):
+ return 1
+
+ def authenticateDelete(self, loginName, password):
+ return (password == self.cr.password, None)
+
+ def enableSecretFriends(self, loginName, password, parentPassword, enable = 1):
+ return (password == parentPassword, None)
diff --git a/otp/login/LoginGoAccount.py b/otp/login/LoginGoAccount.py
new file mode 100644
index 0000000..959a375
--- /dev/null
+++ b/otp/login/LoginGoAccount.py
@@ -0,0 +1,57 @@
+from pandac.PandaModules import *
+from direct.distributed.MsgTypes import *
+from direct.directnotify import DirectNotifyGlobal
+import LoginBase
+from direct.distributed.PyDatagram import PyDatagram
+
+class LoginGoAccount(LoginBase.LoginBase):
+
+ def __init__(self, cr):
+ LoginBase.LoginBase.__init__(self, cr)
+
+ def createAccount(self, loginName, password, data):
+ return 'Unsupported'
+
+ def authorize(self, loginName, password):
+ self.loginName = loginName
+ self.password = password
+ return None
+
+ def supportsRelogin(self):
+ return 0
+
+ def sendLoginMsg(self):
+ cr = self.cr
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_LOGIN_2)
+ datagram.addString(self.password)
+ datagram.addString(cr.serverVersion)
+ datagram.addUint32(cr.hashVal)
+ self.__addTokenType(datagram)
+ datagram.addString(cr.validateDownload)
+ datagram.addString(cr.wantMagicWords)
+ cr.send(datagram)
+
+ def resendPlayToken(self):
+ pass
+
+ def requestPwdReminder(self, email = None, acctName = None):
+ return 0
+
+ def getAccountData(self, loginName, password):
+ return 'Unsupported'
+
+ def supportsParentPassword(self):
+ return 0
+
+ def authenticateParentPassword(self, loginName, password, parentPassword):
+ return (0, None)
+
+ def supportsAuthenticateDelete(self):
+ return 0
+
+ def enableSecretFriends(self, loginName, password, parentPassword, enable = 1):
+ return (0, None)
+
+ def __addTokenType(self, datagram):
+ datagram.addInt32(CLIENT_LOGIN_2_BLUE)
diff --git a/otp/login/LoginScreen.py b/otp/login/LoginScreen.py
new file mode 100644
index 0000000..9a1f92b
--- /dev/null
+++ b/otp/login/LoginScreen.py
@@ -0,0 +1,564 @@
+import os
+import time
+from datetime import datetime
+from pandac.PandaModules import *
+from direct.distributed.MsgTypes import *
+from direct.gui.DirectGui import *
+from direct.fsm import StateData
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from direct.directnotify import DirectNotifyGlobal
+from direct.task import Task
+from otp.otpgui import OTPDialog
+from otp.otpbase import OTPLocalizer
+from otp.otpbase import OTPGlobals
+from otp.uberdog.AccountDetailRecord import AccountDetailRecord, SubDetailRecord
+import TTAccount
+import GuiScreen
+
+class LoginScreen(StateData.StateData, GuiScreen.GuiScreen):
+ AutoLoginName = base.config.GetString('%s-auto-login%s' % (game.name, os.getenv('otp_client', '')), '')
+ AutoLoginPassword = base.config.GetString('%s-auto-password%s' % (game.name, os.getenv('otp_client', '')), '')
+ notify = DirectNotifyGlobal.directNotify.newCategory('LoginScreen')
+ ActiveEntryColor = Vec4(1, 1, 1, 1)
+ InactiveEntryColor = Vec4(0.8, 0.8, 0.8, 1)
+
+ def __init__(self, cr, doneEvent):
+ self.notify.debug('__init__')
+ StateData.StateData.__init__(self, doneEvent)
+ GuiScreen.GuiScreen.__init__(self)
+ self.cr = cr
+ self.loginInterface = self.cr.loginInterface
+ self.userName = ''
+ self.password = ''
+ self.fsm = ClassicFSM.ClassicFSM('LoginScreen', [State.State('off', self.enterOff, self.exitOff, ['login', 'waitForLoginResponse']),
+ State.State('login', self.enterLogin, self.exitLogin, ['waitForLoginResponse', 'login', 'showLoginFailDialog']),
+ State.State('showLoginFailDialog', self.enterShowLoginFailDialog, self.exitShowLoginFailDialog, ['login', 'showLoginFailDialog']),
+ State.State('waitForLoginResponse', self.enterWaitForLoginResponse, self.exitWaitForLoginResponse, ['login', 'showLoginFailDialog', 'showConnectionProblemDialog']),
+ State.State('showConnectionProblemDialog', self.enterShowConnectionProblemDialog, self.exitShowConnectionProblemDialog, ['login'])], 'off', 'off')
+ self.fsm.enterInitialState()
+
+ def load(self):
+ self.notify.debug('load')
+ masterScale = 0.8
+ textScale = 0.1 * masterScale
+ entryScale = 0.08 * masterScale
+ lineHeight = 0.21 * masterScale
+ buttonScale = 1.15 * masterScale
+ buttonLineHeight = 0.14 * masterScale
+ self.frame = DirectFrame(parent=aspect2d, relief=None, sortOrder=20)
+ self.frame.hide()
+ linePos = -0.26
+ self.nameLabel = DirectLabel(parent=self.frame, relief=None, pos=(-0.21, 0, linePos), text=OTPLocalizer.LoginScreenUserName, text_scale=textScale, text_align=TextNode.ARight)
+ self.nameEntry = DirectEntry(parent=self.frame, relief=DGG.SUNKEN, borderWidth=(0.1, 0.1), scale=entryScale, pos=(-0.125, 0.0, linePos), width=OTPGlobals.maxLoginWidth, numLines=1, focus=0, cursorKeys=1)
+ linePos -= lineHeight
+ self.passwordLabel = DirectLabel(parent=self.frame, relief=None, pos=(-0.21, 0, linePos), text=OTPLocalizer.LoginScreenPassword, text_scale=textScale, text_align=TextNode.ARight)
+ self.passwordEntry = DirectEntry(parent=self.frame, relief=DGG.SUNKEN, borderWidth=(0.1, 0.1), scale=entryScale, pos=(-0.125, 0.0, linePos), width=OTPGlobals.maxLoginWidth, numLines=1, focus=0, cursorKeys=1, obscured=1, command=self.__handleLoginPassword)
+ linePos -= lineHeight
+ buttonImageScale = (1.7, 1.1, 1.1)
+ self.loginButton = DirectButton(parent=self.frame, relief=DGG.RAISED, borderWidth=(0.01, 0.01), pos=(0, 0, linePos), scale=buttonScale, text=OTPLocalizer.LoginScreenLogin, text_scale=0.06, text_pos=(0, -0.02), command=self.__handleLoginButton)
+ linePos -= buttonLineHeight
+ self.createAccountButton = DirectButton(parent=self.frame, relief=DGG.RAISED, borderWidth=(0.01, 0.01), pos=(0, 0, linePos), scale=buttonScale, text=OTPLocalizer.LoginScreenCreateAccount, text_scale=0.06, text_pos=(0, -0.02), command=self.__handleCreateAccount)
+ linePos -= buttonLineHeight
+ self.quitButton = DirectButton(parent=self.frame, relief=DGG.RAISED, borderWidth=(0.01, 0.01), pos=(0, 0, linePos), scale=buttonScale, text=OTPLocalizer.LoginScreenQuit, text_scale=0.06, text_pos=(0, -0.02), command=self.__handleQuit)
+ linePos -= buttonLineHeight
+ self.dialogDoneEvent = 'loginDialogAck'
+ dialogClass = OTPGlobals.getGlobalDialogClass()
+ self.dialog = dialogClass(dialogName='loginDialog', doneEvent=self.dialogDoneEvent, message='', style=OTPDialog.Acknowledge, sortOrder=NO_FADE_SORT_INDEX + 100)
+ self.dialog.hide()
+ self.failDialog = DirectFrame(parent=aspect2dp, relief=DGG.RAISED, borderWidth=(0.01, 0.01), pos=(0, 0.1, 0), text='', text_scale=0.08, text_pos=(0.0, 0.3), text_wordwrap=15, sortOrder=NO_FADE_SORT_INDEX)
+ linePos = -.05
+ self.failTryAgainButton = DirectButton(parent=self.failDialog, relief=DGG.RAISED, borderWidth=(0.01, 0.01), pos=(0, 0, linePos), scale=0.9, text=OTPLocalizer.LoginScreenTryAgain, text_scale=0.06, text_pos=(0, -.02), command=self.__handleFailTryAgain)
+ linePos -= buttonLineHeight
+ self.failCreateAccountButton = DirectButton(parent=self.failDialog, relief=DGG.RAISED, borderWidth=(0.01, 0.01), pos=(0, 0, linePos), scale=0.9, text=OTPLocalizer.LoginScreenCreateAccount, text_scale=0.06, text_pos=(0, -.02), command=self.__handleFailCreateAccount)
+ linePos -= buttonLineHeight
+ self.failDialog.hide()
+ self.connectionProblemDialogDoneEvent = 'loginConnectionProblemDlgAck'
+ dialogClass = OTPGlobals.getGlobalDialogClass()
+ self.connectionProblemDialog = dialogClass(dialogName='connectionProblemDialog', doneEvent=self.connectionProblemDialogDoneEvent, message='', style=OTPDialog.Acknowledge, sortOrder=NO_FADE_SORT_INDEX + 100)
+ self.connectionProblemDialog.hide()
+ return
+
+ def unload(self):
+ self.notify.debug('unload')
+ self.nameEntry.destroy()
+ self.passwordEntry.destroy()
+ self.failTryAgainButton.destroy()
+ self.failCreateAccountButton.destroy()
+ self.createAccountButton.destroy()
+ self.loginButton.destroy()
+ self.quitButton.destroy()
+ self.dialog.cleanup()
+ del self.dialog
+ self.failDialog.destroy()
+ del self.failDialog
+ self.connectionProblemDialog.cleanup()
+ del self.connectionProblemDialog
+ self.frame.destroy()
+ del self.fsm
+ del self.loginInterface
+ del self.cr
+
+ def enter(self):
+ if self.cr.blue:
+ self.userName = 'blue'
+ self.password = self.cr.blue
+ self.fsm.request('waitForLoginResponse')
+ elif self.cr.playToken:
+ self.userName = '*'
+ self.password = self.cr.playToken
+ self.fsm.request('waitForLoginResponse')
+ elif hasattr(self.cr, 'DISLToken') and self.cr.DISLToken:
+ self.userName = '*'
+ self.password = self.cr.DISLToken
+ self.fsm.request('waitForLoginResponse')
+ elif self.AutoLoginName:
+ self.userName = self.AutoLoginName
+ self.password = self.AutoLoginPassword
+ self.fsm.request('waitForLoginResponse')
+ else:
+ self.fsm.request('login')
+
+ def exit(self):
+ self.frame.hide()
+ self.ignore(self.dialogDoneEvent)
+ self.fsm.requestFinalState()
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterLogin(self):
+ self.cr.resetPeriodTimer(None)
+ self.userName = ''
+ self.password = ''
+ self.userName = launcher.getLastLogin()
+ if self.userName and self.nameEntry.get():
+ if self.userName != self.nameEntry.get():
+ self.userName = ''
+ self.frame.show()
+ self.nameEntry.enterText(self.userName)
+ self.passwordEntry.enterText(self.password)
+ self.focusList = [self.nameEntry, self.passwordEntry]
+ focusIndex = 0
+ if self.userName:
+ focusIndex = 1
+ self.startFocusMgmt(startFocus=focusIndex)
+ return
+
+ def exitLogin(self):
+ self.stopFocusMgmt()
+
+ def enterShowLoginFailDialog(self, msg):
+ base.transitions.fadeScreen(0.5)
+ self.failDialog['text'] = msg
+ self.failDialog.show()
+
+ def __handleFailTryAgain(self):
+ self.fsm.request('login')
+
+ def __handleFailCreateAccount(self):
+ messenger.send(self.doneEvent, [{'mode': 'createAccount'}])
+
+ def __handleFailNoNewAccountsAck(self):
+ self.dialog.hide()
+ self.fsm.request('showLoginFailDialog', [self.failDialog['text']])
+
+ def exitShowLoginFailDialog(self):
+ base.transitions.noTransitions()
+ self.failDialog.hide()
+
+ def __handleLoginPassword(self, password):
+ if password != '':
+ if self.nameEntry.get() != '':
+ self.__handleLoginButton()
+
+ def __handleLoginButton(self):
+ self.removeFocus()
+ self.userName = self.nameEntry.get()
+ self.password = self.passwordEntry.get()
+ if self.userName == '':
+ self.dialog.setMessage(OTPLocalizer.LoginScreenLoginPrompt)
+ self.dialog.show()
+ self.acceptOnce(self.dialogDoneEvent, self.__handleEnterLoginAck)
+ else:
+ self.fsm.request('waitForLoginResponse')
+
+ def __handleQuit(self):
+ self.removeFocus()
+ messenger.send(self.doneEvent, [{'mode': 'quit'}])
+
+ def __handleCreateAccount(self):
+ self.removeFocus()
+ messenger.send(self.doneEvent, [{'mode': 'createAccount'}])
+
+ def enterWaitForLoginResponse(self):
+ self.cr.handler = self.handleWaitForLoginResponse
+ self.cr.userName = self.userName
+ self.cr.password = self.password
+ try:
+ error = self.loginInterface.authorize(self.userName, self.password)
+ except TTAccount.TTAccountException, e:
+ self.fsm.request('showConnectionProblemDialog', [str(e)])
+ return
+
+ if error:
+ self.notify.info(error)
+ freeTimeExpired = self.loginInterface.getErrorCode() == 10
+ if freeTimeExpired:
+ self.cr.logAccountInfo()
+ messenger.send(self.doneEvent, [{'mode': 'freeTimeExpired'}])
+ else:
+ self.fsm.request('showLoginFailDialog', [error])
+ else:
+ self.loginInterface.sendLoginMsg()
+ self.waitForDatabaseTimeout(requestName='WaitForLoginResponse')
+
+ def exitWaitForLoginResponse(self):
+ self.cleanupWaitingForDatabase()
+ self.cr.handler = None
+ return
+
+ def enterShowConnectionProblemDialog(self, msg):
+ self.connectionProblemDialog.setMessage(msg)
+ self.connectionProblemDialog.show()
+ self.acceptOnce(self.connectionProblemDialogDoneEvent, self.__handleConnectionProblemAck)
+
+ def __handleConnectionProblemAck(self):
+ self.connectionProblemDialog.hide()
+ self.fsm.request('login')
+
+ def exitShowConnectionProblemDialog(self):
+ pass
+
+ def handleWaitForLoginResponse(self, msgType, di):
+ if msgType == CLIENT_LOGIN_2_RESP:
+ self.handleLoginResponseMsg2(di)
+ elif msgType == CLIENT_LOGIN_RESP:
+ self.handleLoginResponseMsg(di)
+ elif msgType == CLIENT_LOGIN_3_RESP:
+ self.handleLoginResponseMsg3(di)
+ elif msgType == CLIENT_LOGIN_TOONTOWN_RESP:
+ self.handleLoginToontownResponse(di)
+ else:
+ self.cr.handleMessageType(msgType, di)
+
+ def getExtendedErrorMsg(self, errorString):
+ prefix = 'Bad DC Version Compare'
+ if len(errorString) < len(prefix):
+ return errorString
+ if errorString[:len(prefix)] == prefix:
+ return '%s%s' % (errorString, ', address=%s' % base.cr.getServerAddress())
+ return errorString
+
+ def handleLoginResponseMsg3(self, di):
+ now = time.time()
+ returnCode = di.getInt8()
+ errorString = self.getExtendedErrorMsg(di.getString())
+ self.notify.info('Login response return code %s' % returnCode)
+ if returnCode != 0:
+ self.notify.info('Login failed: %s' % errorString)
+ messenger.send(self.doneEvent, [{'mode': 'reject'}])
+ return
+ accountDetailRecord = AccountDetailRecord()
+ accountDetailRecord.openChatEnabled = di.getString() == 'YES'
+ accountDetailRecord.createFriendsWithChat = di.getString() == 'YES'
+ chatCodeCreation = di.getString()
+ accountDetailRecord.chatCodeCreation = chatCodeCreation == 'YES'
+ parentControlledChat = chatCodeCreation == 'PARENT'
+ access = di.getString()
+ if access == 'VELVET':
+ access = OTPGlobals.AccessVelvetRope
+ elif access == 'FULL':
+ access = OTPGlobals.AccessFull
+ else:
+ self.notify.warning('Unknown access: %s' % access)
+ access = OTPGlobals.AccessUnknown
+ accountDetailRecord.piratesAccess = access
+ accountDetailRecord.familyAccountId = di.getInt32()
+ accountDetailRecord.playerAccountId = di.getInt32()
+ accountDetailRecord.playerName = di.getString()
+ accountDetailRecord.playerNameApproved = di.getInt8()
+ accountDetailRecord.maxAvatars = di.getInt32()
+ self.cr.openChatAllowed = accountDetailRecord.openChatEnabled
+ self.cr.secretChatAllowed = accountDetailRecord.chatCodeCreation or parentControlledChat
+ self.cr.setIsPaid(accountDetailRecord.piratesAccess)
+ self.userName = accountDetailRecord.playerName
+ self.cr.userName = accountDetailRecord.playerName
+ accountDetailRecord.numSubs = di.getUint16()
+ for i in range(accountDetailRecord.numSubs):
+ subDetailRecord = SubDetailRecord()
+ subDetailRecord.subId = di.getUint32()
+ subDetailRecord.subOwnerId = di.getUint32()
+ subDetailRecord.subName = di.getString()
+ subDetailRecord.subActive = di.getString()
+ access = di.getString()
+ if access == 'VELVET':
+ access = OTPGlobals.AccessVelvetRope
+ elif access == 'FULL':
+ access = OTPGlobals.AccessFull
+ else:
+ access = OTPGlobals.AccessUnknown
+ subDetailRecord.subAccess = access
+ subDetailRecord.subLevel = di.getUint8()
+ subDetailRecord.subNumAvatars = di.getUint8()
+ subDetailRecord.subNumConcur = di.getUint8()
+ subDetailRecord.subFounder = di.getString() == 'YES'
+ accountDetailRecord.subDetails[subDetailRecord.subId] = subDetailRecord
+
+ accountDetailRecord.WLChatEnabled = di.getString() == 'YES'
+ if accountDetailRecord.WLChatEnabled:
+ self.cr.whiteListChatEnabled = 1
+ else:
+ self.cr.whiteListChatEnabled = 0
+ self.notify.info('End of DISL token parse')
+ if base.logPrivateInfo:
+ self.notify.info('accountDetailRecord: %s' % accountDetailRecord)
+ self.cr.accountDetailRecord = accountDetailRecord
+ self.__handleLoginSuccess()
+
+ def handleLoginResponseMsg2(self, di):
+ self.notify.debug('handleLoginResponseMsg2')
+ if base.logPrivateInfo:
+ if self.notify.getDebug():
+ dgram = di.getDatagram()
+ dgram.dumpHex(ostream)
+ now = time.time()
+ returnCode = di.getUint8()
+ errorString = self.getExtendedErrorMsg(di.getString())
+ self.userName = di.getString()
+ self.cr.userName = self.userName
+ accountDetailRecord = AccountDetailRecord()
+ self.cr.accountDetailRecord = accountDetailRecord
+ canChat = di.getUint8()
+ self.cr.secretChatAllowed = canChat
+ if base.logPrivateInfo:
+ self.notify.info('Chat from game server login: %s' % canChat)
+ sec = di.getUint32()
+ usec = di.getUint32()
+ serverTime = sec + usec / 1000000.0
+ self.cr.serverTimeUponLogin = serverTime
+ self.cr.clientTimeUponLogin = now
+ self.cr.globalClockRealTimeUponLogin = globalClock.getRealTime()
+ if hasattr(self.cr, 'toontownTimeManager'):
+ self.cr.toontownTimeManager.updateLoginTimes(serverTime, now, self.cr.globalClockRealTimeUponLogin)
+ serverDelta = serverTime - now
+ self.cr.setServerDelta(serverDelta)
+ self.notify.setServerDelta(serverDelta, 28800)
+ self.isPaid = di.getUint8()
+ self.cr.setIsPaid(self.isPaid)
+ if self.isPaid:
+ launcher.setPaidUserLoggedIn()
+ if base.logPrivateInfo:
+ self.notify.info('Paid from game server login: %s' % self.isPaid)
+ self.cr.resetPeriodTimer(None)
+ if di.getRemainingSize() >= 4:
+ minutesRemaining = di.getInt32()
+ if base.logPrivateInfo:
+ self.notify.info('Minutes remaining from server %s' % minutesRemaining)
+ if base.logPrivateInfo:
+ if minutesRemaining >= 0:
+ self.notify.info('Spawning period timer')
+ self.cr.resetPeriodTimer(minutesRemaining * 60)
+ elif self.isPaid:
+ self.notify.warning('Negative minutes remaining for paid user (?)')
+ else:
+ self.notify.warning('Not paid, but also negative minutes remaining (?)')
+ elif base.logPrivateInfo:
+ self.notify.info('Minutes remaining not returned from server; not spawning period timer')
+ familyStr = di.getString()
+ WhiteListResponse = di.getString()
+ if WhiteListResponse == 'YES':
+ self.cr.whiteListChatEnabled = 1
+ else:
+ self.cr.whiteListChatEnabled = 0
+ if di.getRemainingSize() > 0:
+ self.cr.accountDays = self.parseAccountDays(di.getInt32())
+ else:
+ self.cr.accountDays = 100000
+ if di.getRemainingSize() > 0:
+ self.lastLoggedInStr = di.getString()
+ self.notify.info('last logged in = %s' % self.lastLoggedInStr)
+ else:
+ self.lastLoggedInStr = ''
+ self.cr.lastLoggedIn = datetime.now()
+ if hasattr(self.cr, 'toontownTimeManager'):
+ self.cr.lastLoggedIn = self.cr.toontownTimeManager.convertStrToToontownTime(self.lastLoggedInStr)
+ self.cr.withParentAccount = False
+ self.notify.info('Login response return code %s' % returnCode)
+ if returnCode == 0:
+ self.__handleLoginSuccess()
+ elif returnCode == -13:
+ self.notify.info('Period Time Expired')
+ self.fsm.request('showLoginFailDialog', [OTPLocalizer.LoginScreenPeriodTimeExpired])
+ else:
+ self.notify.info('Login failed: %s' % errorString)
+ messenger.send(self.doneEvent, [{'mode': 'reject'}])
+ return
+
+ def handleLoginResponseMsg(self, di):
+ self.notify.debug('handleLoginResponseMsg1')
+ if base.logPrivateInfo:
+ if self.notify.getDebug():
+ dgram = di.getDatagram()
+ dgram.dumpHex(ostream)
+ now = time.time()
+ accountDetailRecord = AccountDetailRecord()
+ self.cr.accountDetailRecord = accountDetailRecord
+ returnCode = di.getUint8()
+ accountCode = di.getUint32()
+ errorString = self.getExtendedErrorMsg(di.getString())
+ sec = di.getUint32()
+ usec = di.getUint32()
+ serverTime = sec + usec / 1000000.0
+ serverDelta = serverTime - now
+ self.cr.serverTimeUponLogin = serverTime
+ self.cr.clientTimeUponLogin = now
+ self.cr.globalClockRealTimeUponLogin = globalClock.getRealTime()
+ if hasattr(self.cr, 'toontownTimeManager'):
+ self.cr.toontownTimeManager.updateLoginTimes(serverTime, now, self.cr.globalClockRealTimeUponLogin)
+ self.cr.setServerDelta(serverDelta)
+ self.notify.setServerDelta(serverDelta, 28800)
+ if di.getRemainingSize() > 0:
+ self.cr.accountDays = self.parseAccountDays(di.getInt32())
+ else:
+ self.cr.accountDays = 100000
+ if di.getRemainingSize() > 0:
+ WhiteListResponse = di.getString()
+ else:
+ WhiteListResponse = 'NO'
+ if WhiteListResponse == 'YES':
+ self.cr.whiteListChatEnabled = 1
+ else:
+ self.cr.whiteListChatEnabled = 0
+ self.lastLoggedInStr = base.config.GetString('last-logged-in', '')
+ self.cr.lastLoggedIn = datetime.now()
+ if hasattr(self.cr, 'toontownTimeManager'):
+ self.cr.lastLoggedIn = self.cr.toontownTimeManager.convertStrToToontownTime(self.lastLoggedInStr)
+ self.cr.withParentAccount = base.config.GetBool('dev-with-parent-account', 0)
+ self.notify.info('Login response return code %s' % returnCode)
+ if returnCode == 0:
+ self.__handleLoginSuccess()
+ elif returnCode == 12:
+ self.notify.info('Bad password')
+ self.fsm.request('showLoginFailDialog', [OTPLocalizer.LoginScreenBadPassword])
+ elif returnCode == 14:
+ self.notify.info('Bad word in user name')
+ self.fsm.request('showLoginFailDialog', [OTPLocalizer.LoginScreenInvalidUserName])
+ elif returnCode == 129:
+ self.notify.info('Username not found')
+ self.fsm.request('showLoginFailDialog', [OTPLocalizer.LoginScreenUserNameNotFound])
+ else:
+ self.notify.info('Login failed: %s' % errorString)
+ messenger.send(self.doneEvent, [{'mode': 'reject'}])
+
+ def __handleLoginSuccess(self):
+ self.cr.logAccountInfo()
+ launcher.setGoUserName(self.userName)
+ launcher.setLastLogin(self.userName)
+ launcher.setUserLoggedIn()
+ if self.loginInterface.freeTimeExpires == -1:
+ launcher.setPaidUserLoggedIn()
+ if self.loginInterface.needToSetParentPassword():
+ messenger.send(self.doneEvent, [{'mode': 'getChatPassword'}])
+ else:
+ messenger.send(self.doneEvent, [{'mode': 'success'}])
+
+ def __handleEnterLoginAck(self):
+ self.dialog.hide()
+ self.fsm.request('login')
+
+ def __handleNoNewAccountsAck(self):
+ self.dialog.hide()
+ self.fsm.request('login')
+
+ def parseAccountDays(self, accountDays):
+ result = 100000
+ if accountDays >= 0:
+ result = accountDays
+ else:
+ self.notify.warning('account days is negative %s' % accountDays)
+ self.notify.debug('result=%s' % result)
+ return result
+
+ def handleLoginToontownResponse(self, di):
+ self.notify.debug("handleLoginToontownResponse")
+ if 1:
+ if base.logPrivateInfo:
+ dgram = di.getDatagram()
+ dgram.dumpHex(ostream)
+ now = time.time()
+ returnCode = di.getUint8()
+ respString = di.getString()
+ errorString = self.getExtendedErrorMsg(respString)
+ self.accountNumber = di.getUint32()
+ self.cr.DISLIdFromLogin = self.accountNumber
+ self.accountName = di.getString()
+ self.accountNameApproved = di.getUint8()
+ accountDetailRecord = AccountDetailRecord()
+ self.cr.accountDetailRecord = accountDetailRecord
+ self.openChatEnabled = di.getString() == "YES"
+ createFriendsWithChat = di.getString()
+ canChat = createFriendsWithChat == "YES" or createFriendsWithChat == "CODE"
+ self.cr.secretChatAllowed = canChat
+ if base.logPrivateInfo:
+ self.notify.info("CREATE_FRIENDS_WITH_CHAT from game server login: %s %s" % (createFriendsWithChat, canChat))
+ self.chatCodeCreationRule = di.getString()
+ self.cr.chatChatCodeCreationRule = self.chatCodeCreationRule
+ if base.logPrivateInfo:
+ self.notify.info("Chat code creation rule = %s" % self.chatCodeCreationRule)
+ self.cr.secretChatNeedsParentPassword = self.chatCodeCreationRule == "PARENT"
+ sec = di.getUint32()
+ usec = di.getUint32()
+ serverTime = sec + (usec/1000000.0)
+ self.cr.serverTimeUponLogin = serverTime
+ self.cr.clientTimeUponLogin = now
+ self.cr.globalClockRealTimeUponLogin = globalClock.getRealTime()
+ if hasattr(self.cr, "toontownTimeManager"):
+ self.cr.toontownTimeManager.updateLoginTimes(serverTime, now, self.cr.globalClockRealTimeUponLogin)
+ serverDelta = serverTime - now
+ self.cr.setServerDelta(serverDelta)
+ self.notify.setServerDelta(serverDelta, 28800)
+ access = di.getString()
+ self.isPaid = access == "FULL"
+ self.cr.parentPasswordSet = self.isPaid
+ self.cr.setIsPaid(self.isPaid)
+ if self.isPaid:
+ launcher.setPaidUserLoggedIn()
+ if base.logPrivateInfo:
+ self.notify.info("Paid from game server login: %s" % self.isPaid)
+ WhiteListResponse = di.getString()
+ if WhiteListResponse == "YES":
+ self.cr.whiteListChatEnabled = 1
+ else:
+ self.cr.whiteListChatEnabled = 0
+ self.lastLoggedInStr = di.getString()
+ self.cr.lastLoggedIn = datetime.now()
+ if hasattr(self.cr, "toontownTimeManager"):
+ self.cr.lastLoggedIn = self.cr.toontownTimeManager.convertStrToToontownTime(self.lastLoggedInStr)
+ if di.getRemainingSize() > 0:
+ self.cr.accountDays = self.parseAccountDays(di.getInt32())
+ else:
+ self.cr.accountDays = 100000
+ self.toonAccountType = di.getString()
+ if self.toonAccountType == "WITH_PARENT_ACCOUNT":
+ self.cr.withParentAccount = True
+ elif self.toonAccountType == "NO_PARENT_ACCOUNT":
+ self.cr.withParentAccount = False
+ else:
+ self.notify.error("unknown toon account type %s" % self.toonAccountType)
+ if base.logPrivateInfo:
+ self.notify.info("toonAccountType=%s" % self.toonAccountType)
+ self.userName = di.getString()
+ self.cr.userName = self.userName
+ self.notify.info("Login response return code %s" % returnCode)
+ if returnCode == 0:
+ self.__handleLoginSuccess()
+ elif returnCode == -13:
+ self.notify.info("Period Time Expired")
+ self.fsm.request("showLoginFailDialog", [OTPLocalizer.LoginScreenPeriodTimeExpired])
+ else:
+ self.notify.info("Login failed: %s" % errorString)
+ messenger.send(self.doneEvent, [{'mode': 'reject'}])
diff --git a/otp/login/LoginTTAccount.py b/otp/login/LoginTTAccount.py
new file mode 100644
index 0000000..0cc6515
--- /dev/null
+++ b/otp/login/LoginTTAccount.py
@@ -0,0 +1,111 @@
+from pandac.PandaModules import *
+from direct.distributed.MsgTypes import *
+from direct.directnotify import DirectNotifyGlobal
+import LoginBase
+import TTAccount
+from TTAccount import TTAccountException
+from direct.distributed.PyDatagram import PyDatagram
+
+class LoginTTAccount(LoginBase.LoginBase, TTAccount.TTAccount):
+ notify = DirectNotifyGlobal.directNotify.newCategory('LoginTTAcct')
+
+ def __init__(self, cr):
+ LoginBase.LoginBase.__init__(self, cr)
+ TTAccount.TTAccount.__init__(self, cr)
+ self.useTTSpecificLogin = base.config.GetBool('tt-specific-login', 0)
+ self.notify.info('self.useTTSpecificLogin =%s' % self.useTTSpecificLogin)
+
+ def supportsRelogin(self):
+ return 1
+
+ def sendLoginMsg(self):
+ cr = self.cr
+ datagram = PyDatagram()
+ if self.useTTSpecificLogin:
+ datagram.addUint16(CLIENT_LOGIN_TOONTOWN)
+ self.__addPlayToken(datagram)
+ datagram.addString(cr.serverVersion)
+ datagram.addUint32(cr.hashVal)
+ self.__addTokenType(datagram)
+ datagram.addString(cr.wantMagicWords)
+ else:
+ datagram.addUint16(CLIENT_LOGIN_2)
+ self.__addPlayToken(datagram)
+ datagram.addString(cr.serverVersion)
+ datagram.addUint32(cr.hashVal)
+ self.__addTokenType(datagram)
+ datagram.addString(cr.validateDownload)
+ datagram.addString(cr.wantMagicWords)
+ cr.send(datagram)
+
+ def resendPlayToken(self):
+ cr = self.cr
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_SET_SECURITY)
+ self.__addPlayToken(datagram)
+ self.__addTokenType(datagram)
+ cr.send(datagram)
+
+ def __addPlayToken(self, datagram):
+ self.playToken = self.playToken.strip()
+ datagram.addString(self.playToken)
+
+ def __addTokenType(self, datagram):
+ if self.useTTSpecificLogin:
+ datagram.addInt32(CLIENT_LOGIN_3_DISL_TOKEN)
+ elif self.playTokenIsEncrypted:
+ datagram.addInt32(CLIENT_LOGIN_2_PLAY_TOKEN)
+ else:
+ datagram.addInt32(CLIENT_LOGIN_2_PLAY_TOKEN)
+
+ def getErrorCode(self):
+ return self.response.getInt('errorCode', 0)
+
+ def needToSetParentPassword(self):
+ return self.response.getBool('secretsPasswordNotSet', 0)
+
+ def authenticateParentPassword(self, loginName, password, parentPassword):
+ if base.cr.withParentAccount:
+ self.notify.error('authenticateParentPassword called, but with parentAccount')
+ try:
+ errorMsg = self.talk('authenticateParentUsernameAndPassword', data=self.makeLoginDict(loginName, parentPassword))
+ if not errorMsg:
+ return (1, None)
+ if self.response.getInt('errorCode') in (5, 72):
+ return (0, None)
+ return (0, errorMsg)
+ except TTAccountException, e:
+ return (0, str(e))
+
+ elif self.useTTSpecificLogin:
+ try:
+ errorMsg = self.talk('authenticateParentPasswordNewStyle', data=self.makeLoginDict(loginName, parentPassword))
+ if not errorMsg:
+ return (1, None)
+ if self.response.getInt('errorCode') in (5, 72):
+ return (0, None)
+ return (0, errorMsg)
+ except TTAccountException, e:
+ return (0, str(e))
+
+ else:
+ return TTAccount.TTAccount.authenticateParentPassword(self, loginName, password, parentPassword)
+ return None
+
+ def authenticateDelete(self, loginName, password):
+ if self.useTTSpecificLogin:
+ try:
+ errorMsg = self.talk('authenticateDeleteNewStyle', data=self.makeLoginDict(loginName, password))
+ if not errorMsg:
+ return (1, None)
+ if self.response.getInt('errorCode') in (5, 72):
+ return (0, None)
+ return (0, errorMsg)
+ except TTAccountException, e:
+ return (0, str(e))
+
+ else:
+ self.notify.info('using old style authenticate delete')
+ result = TTAccount.TTAccount.authenticateDelete(self, loginName, password)
+ return result
+ return None
diff --git a/otp/login/LoginTTSpecificDevAccount.py b/otp/login/LoginTTSpecificDevAccount.py
new file mode 100644
index 0000000..c0beb96
--- /dev/null
+++ b/otp/login/LoginTTSpecificDevAccount.py
@@ -0,0 +1,115 @@
+from pandac.PandaModules import *
+from direct.distributed.MsgTypes import *
+from direct.directnotify import DirectNotifyGlobal
+import LoginTTAccount
+from direct.distributed.PyDatagram import PyDatagram
+from TTAccount import TTAccountException
+
+class LoginTTSpecificDevAccount(LoginTTAccount.LoginTTAccount):
+ notify = DirectNotifyGlobal.directNotify.newCategory('LoginTTSpecificDevAccount')
+
+ def __init__(self, cr):
+ LoginTTAccount.LoginTTAccount.__init__(self, cr)
+
+ def createAccount(self, loginName, password, data):
+ self.loginName = loginName
+ self.password = password
+ self.createFlag = 1
+ self.cr.freeTimeExpiresAt = -1
+ self.cr.setIsPaid(1)
+ return None
+
+ def authorize(self, loginName, password):
+ self.loginName = loginName
+ self.password = password
+ self.createFlag = 0
+ self.cr.freeTimeExpiresAt = -1
+ self.cr.setIsPaid(1)
+ return None
+
+ def supportsRelogin(self):
+ return 1
+
+ def sendLoginMsg(self):
+ cr = self.cr
+ tokenString = ''
+ access = base.config.GetString('force-paid-status', '')
+ if access == '':
+ access = 'FULL'
+ elif access == 'paid':
+ access = 'FULL'
+ elif access == 'unpaid':
+ access = 'VELVET_ROPE'
+ elif access == 'VELVET':
+ access = 'VELVET_ROPE'
+ else:
+ self.notify.error("don't know what to do with force-paid-status %s" % access)
+ tokenString += 'TOONTOWN_ACCESS=%s&' % access
+ tokenString += 'TOONTOWN_GAME_KEY=%s&' % self.loginName
+ wlChatEnabled = 'YES'
+ if base.config.GetString('otp-whitelist', 'YES') == 'NO':
+ wlChatEnabled = 'NO'
+ tokenString += 'WL_CHAT_ENABLED=%s &' % wlChatEnabled
+ openChatEnabled = 'NO'
+ if cr.openChatAllowed:
+ openChatEnabled = 'YES'
+ tokenString += 'OPEN_CHAT_ENABLED=%s&' % openChatEnabled
+ createFriendsWithChat = 'NO'
+ if cr.allowSecretChat:
+ createFriendsWithChat = 'CODE'
+ tokenString += 'CREATE_FRIENDS_WITH_CHAT=%s&' % createFriendsWithChat
+ chatCodeCreationRule = 'No'
+ if cr.allowSecretChat:
+ if base.config.GetBool('secret-chat-needs-parent-password', 0):
+ chatCodeCreationRule = 'PARENT'
+ else:
+ chatCodeCreationRule = 'YES'
+ tokenString += 'CHAT_CODE_CREATION_RULE=%s&' % chatCodeCreationRule
+ DISLID = config.GetInt('fake-DISL-PlayerAccountId', 0)
+ if not DISLID:
+ NameStringId = 'DISLID_%s' % self.loginName
+ DISLID = config.GetInt(NameStringId, 0)
+ tokenString += 'ACCOUNT_NUMBER=%d&' % DISLID
+ tokenString += 'ACCOUNT_NAME=%s&' % self.loginName
+ tokenString += 'GAME_USERNAME=%s&' % self.loginName
+ tokenString += 'ACCOUNT_NAME_APPROVED=TRUE&'
+ tokenString += 'FAMILY_NUMBER=&'
+ tokenString += 'Deployment=US&'
+ withParentAccount = base.config.GetBool('dev-with-parent-account', 0)
+ if withParentAccount:
+ tokenString += 'TOON_ACCOUNT_TYPE=WITH_PARENT_ACCOUNT&'
+ else:
+ tokenString += 'TOON_ACCOUNT_TYPE=NO_PARENT_ACCOUNT&'
+ tokenString += 'valid=true'
+ self.notify.info('tokenString=\n%s' % tokenString)
+ datagram = PyDatagram()
+ datagram.addUint16(CLIENT_LOGIN_TOONTOWN)
+ playToken = tokenString
+ datagram.addString(playToken)
+ datagram.addString('dev')
+ datagram.addUint32(cr.hashVal)
+ datagram.addUint32(4)
+ magicWords = base.config.GetString('want-magic-words', '')
+ datagram.addString(magicWords)
+ cr.send(datagram)
+
+ def resendPlayToken(self):
+ pass
+
+ def requestPwdReminder(self, email = None, acctName = None):
+ return 0
+
+ def getAccountData(self, loginName, password):
+ return 'Unsupported'
+
+ def supportsParentPassword(self):
+ return 1
+
+ def supportsAuthenticateDelete(self):
+ return 1
+
+ def enableSecretFriends(self, loginName, password, parentPassword, enable = 1):
+ return (password == parentPassword, None)
+
+ def needToSetParentPassword(self):
+ return False
diff --git a/otp/login/LoginWebPlayTokenAccount.py b/otp/login/LoginWebPlayTokenAccount.py
new file mode 100644
index 0000000..fab0a02
--- /dev/null
+++ b/otp/login/LoginWebPlayTokenAccount.py
@@ -0,0 +1,47 @@
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+import LoginTTAccount
+
+class LoginWebPlayTokenAccount(LoginTTAccount.LoginTTAccount):
+ notify = DirectNotifyGlobal.directNotify.newCategory('LoginWebPlayTokenAccount')
+
+ def supportsRelogin(self):
+ return 0
+
+ def createAccount(self, loginName, password, data):
+ pass
+
+ def authorize(self, loginName, password):
+ self.playToken = password
+ self.playTokenIsEncrypted = 1
+ self.freeTimeExpires = -1
+ self.cr.freeTimeExpiresAt = self.freeTimeExpires
+
+ def createBilling(self, loginName, password, data):
+ pass
+
+ def setParentPassword(self, loginName, password, parentPassword):
+ pass
+
+ def supportsParentPassword(self):
+ return 1
+
+ def changePassword(self, loginName, password, newPassword):
+ pass
+
+ def requestPwdReminder(self, email = None, acctName = None):
+ pass
+
+ def cancelAccount(self, loginName, password):
+ pass
+
+ def getAccountData(self, loginName, password):
+ pass
+
+ def getErrorCode(self):
+ if not self.has_key('response'):
+ return 0
+ return self.response.getInt('errorCode', 0)
+
+ def needToSetParentPassword(self):
+ return 0
diff --git a/otp/login/MultiPageTextFrame.py b/otp/login/MultiPageTextFrame.py
new file mode 100644
index 0000000..b7d82c2
--- /dev/null
+++ b/otp/login/MultiPageTextFrame.py
@@ -0,0 +1,80 @@
+from pandac.PandaModules import *
+from direct.gui.DirectGui import *
+from otp.otpbase import OTPLocalizer
+
+class MultiPageTextFrame(DirectFrame):
+ defWidth = 1.8
+ defHeight = 0.9
+
+ def __init__(self, textList, width = defWidth, height = defHeight, wordWrap = None, hidePageNum = 0, pageChangeCallback = None, parent = aspect2d, **kw):
+ self.textList = textList
+ self.numPages = len(self.textList)
+ self.pageChangeCallback = pageChangeCallback
+ if not wordWrap:
+ wordWrap = round(18.8 * width)
+ hWidth = width / 2.0
+ hHeight = height / 2.0
+ optiondefs = (('relief', DGG.SUNKEN, None),
+ ('frameSize', (-hWidth,
+ hWidth,
+ -hHeight,
+ hHeight), None),
+ ('frameColor', (0.85, 0.85, 0.6, 1), None),
+ ('borderWidth', (0.01, 0.01), None),
+ ('text', '', None),
+ ('text_pos', (-hWidth * 0.95, hHeight * 0.93), None),
+ ('text_scale', 0.05, None),
+ ('text_align', TextNode.ALeft, None),
+ ('text_wordwrap', wordWrap, None))
+ self.defineoptions(kw, optiondefs)
+ DirectFrame.__init__(self, parent)
+ self.initialiseoptions(MultiPageTextFrame)
+ guiButton = loader.loadModel('phase_3/models/gui/quit_button')
+ buttonScale = 0.7 * (float(height) / self.defHeight)
+ buttonZ = -hHeight * 0.83
+ self.nextButton = DirectButton(parent=self, relief=None, scale=buttonScale, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.75, 1, 1), pos=(hWidth * 0.35, 0, buttonZ), text=OTPLocalizer.MultiPageTextFrameNext, text_scale=0.05, text_pos=(0, -0.02), command=self.turnPage, extraArgs=[1])
+ self.prevButton = DirectButton(parent=self, relief=None, scale=buttonScale, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.75, 1, 1), pos=(-hWidth * 0.35, 0, buttonZ), text=OTPLocalizer.MultiPageTextFramePrev, text_scale=0.05, text_pos=(0, -0.02), command=self.turnPage, extraArgs=[-1])
+ self.pageNum = DirectLabel(relief=None, parent=self, pos=(0, 0, -hHeight * 0.86), text='', text_scale=0.05, text_pos=(0, 0))
+ if hidePageNum:
+ self.pageNum.hide()
+ self.setPage(0)
+ guiButton.removeNode()
+ return
+
+ def setPageChangeCallback(self, callback):
+ self.pageChangeCallback = callback
+ self.setPage(self.curPage)
+
+ def setPage(self, pageNum):
+ self.curPage = max(0, min(self.numPages - 1, pageNum))
+ if self.numPages == 1:
+ self.nextButton.hide()
+ self.prevButton.hide()
+ self.curPage = 0
+ elif self.curPage == self.numPages - 1:
+ self.nextButton.hide()
+ self.prevButton.show()
+ elif self.curPage == 0:
+ self.nextButton.show()
+ self.prevButton.hide()
+ else:
+ self.nextButton.show()
+ self.prevButton.show()
+ self.pageNum['text'] = OTPLocalizer.MultiPageTextFramePage % (self.curPage + 1, self.numPages)
+ self['text'] = self.textList[self.curPage]
+ if self.pageChangeCallback:
+ self.pageChangeCallback(self.getCurPage())
+
+ def getCurPage(self):
+ return self.curPage
+
+ def turnPage(self, delta):
+ self.setPage(self.curPage + delta)
+
+ def acceptAgreementKeypresses(self):
+ self.accept('page_down-up', self.turnPage, extraArgs=[1])
+ self.accept('page_up-up', self.turnPage, extraArgs=[-1])
+
+ def ignoreAgreementKeypresses(self):
+ self.ignore('page_down-up')
+ self.ignore('page_up-up')
diff --git a/otp/login/PrivacyPolicyPanel.py b/otp/login/PrivacyPolicyPanel.py
new file mode 100644
index 0000000..667acd7
--- /dev/null
+++ b/otp/login/PrivacyPolicyPanel.py
@@ -0,0 +1,97 @@
+from pandac.PandaModules import *
+from otp.otpbase.OTPGlobals import *
+from direct.gui.DirectGui import *
+from MultiPageTextFrame import *
+from direct.directnotify import DirectNotifyGlobal
+from otp.otpbase import OTPLocalizer
+from otp.otpgui import OTPDialog
+
+class PrivacyPolicyTextPanel(getGlobalDialogClass()):
+ notify = DirectNotifyGlobal.directNotify.newCategory('PrivacyPolicyTextPanel')
+
+ def __init__(self, doneEvent, hidePageNum = 0, pageChangeCallback = None, textList = []):
+ dialogClass = getGlobalDialogClass()
+ dialogClass.__init__(self, parent=aspect2d, dialogName='privacyPolicyTextDialog', doneEvent=doneEvent, okButtonText=OTPLocalizer.PrivacyPolicyClose, style=OTPDialog.Acknowledge, text='', topPad=1.5, sidePad=1.2, pos=(0, 0, -.55), scale=0.9)
+ self.privacyPolicyText = MultiPageTextFrame(parent=self, textList=textList, hidePageNum=hidePageNum, pageChangeCallback=pageChangeCallback, pos=(0, 0, 0.7), width=2.4, height=1.5)
+ self['image'] = self['image']
+ self['image_pos'] = (0, 0, 0.65)
+ self['image_scale'] = (2.7, 1, 1.9)
+ closeButton = self.getChild(0)
+ closeButton.setZ(-.13)
+
+
+class PrivacyPolicyPanel(getGlobalDialogClass()):
+ notify = DirectNotifyGlobal.directNotify.newCategory('PrivacyPolicyPanel')
+
+ def __init__(self, doneEvent, hidePageNum = 0, pageChangeCallback = None, textList = 1):
+ dialogClass = getGlobalDialogClass()
+ dialogClass.__init__(self, parent=aspect2d, dialogName='privacyPolicyDialog', doneEvent=doneEvent, okButtonText=OTPLocalizer.PrivacyPolicyClose, style=OTPDialog.Acknowledge, text='', topPad=1.5, sidePad=1.2, pos=(0, 0, -.15), scale=0.6)
+ self.chatPrivacyPolicy = None
+ self.fsm = ClassicFSM.ClassicFSM('privacyPolicyPanel', [State.State('off', self.enterOff, self.exitOff),
+ State.State('version1Adult', self.enterVersion1Adult, self.exitPrivacyPolicy),
+ State.State('version1Kids', self.enterVersion1Kids, self.exitPrivacyPolicy),
+ State.State('version2Adult', self.enterVersion2Adult, self.exitPrivacyPolicy),
+ State.State('version2Kids', self.enterVersion2Kids, self.exitPrivacyPolicy)], 'off', 'off')
+ self.fsm.enterInitialState()
+ guiButton = loader.loadModel('phase_3/models/gui/quit_button')
+ moreButtonImage = (guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR'))
+ DirectFrame(self, pos=(-0.4, 0.1, 0.4), relief=None, text=OTPLocalizer.PrivacyPolicyText_Intro, text_align=TextNode.ALeft, text_wordwrap=28, text_scale=0.09, text_pos=(-0.82, 1.0), textMayChange=0)
+ textScale = 0.05
+ buttonFrame = DirectFrame(self, pos=(0.0, 0.1, 0.0), scale=1.4, relief=None)
+ DirectButton(buttonFrame, image=moreButtonImage, image_scale=(1.75, 1.0, 1.0), relief=None, text=OTPLocalizer.ActivateChatPrivacyPolicy_Button1A, text_scale=textScale, text_pos=(0, -0.01), textMayChange=0, pos=(-0.45, 0.0, 0.4), command=self.__handlePrivacyPolicy, extraArgs=['version1Adult'])
+ DirectButton(buttonFrame, image=moreButtonImage, image_scale=(1.75, 1.0, 1.0), relief=None, text=OTPLocalizer.ActivateChatPrivacyPolicy_Button1K, text_scale=textScale, text_pos=(0, -0.01), textMayChange=0, pos=(-0.45, 0.0, 0.2), command=self.__handlePrivacyPolicy, extraArgs=['version1Kids'])
+ DirectButton(buttonFrame, image=moreButtonImage, image_scale=(1.75, 1.0, 1.0), relief=None, text=OTPLocalizer.ActivateChatPrivacyPolicy_Button2A, text_scale=textScale, text_pos=(0, -0.01), textMayChange=0, pos=(0.45, 0.0, 0.4), command=self.__handlePrivacyPolicy, extraArgs=['version2Adult'])
+ DirectButton(buttonFrame, image=moreButtonImage, image_scale=(1.75, 1.0, 1.0), relief=None, text=OTPLocalizer.ActivateChatPrivacyPolicy_Button2K, text_scale=textScale, text_pos=(0, -0.01), textMayChange=0, pos=(0.45, 0.0, 0.2), command=self.__handlePrivacyPolicy, extraArgs=['version2Kids'])
+ self['image'] = self['image']
+ self['image_pos'] = (0, 0, 0.65)
+ self['image_scale'] = (2.7, 1, 1.9)
+ closeButton = self.getChild(0)
+ closeButton.setZ(-.13)
+ return
+
+ def delete(self):
+ self.ignoreAll()
+ del self.fsm
+ if self.chatPrivacyPolicy:
+ self.chatPrivacyPolicy.destroy()
+ self.chatPrivacyPolicy = None
+ return
+
+ def __handlePrivacyPolicy(self, state, *oooo):
+ self.fsm.request(state)
+
+ def __privacyPolicyTextDone(self):
+ self.exitPrivacyPolicy()
+
+ def enterPrivacyPolicy(self, textList):
+ if self.chatPrivacyPolicy == None:
+ self.chatPrivacyPolicy = PrivacyPolicyTextPanel('privacyPolicyTextDone', textList=textList)
+ self.chatPrivacyPolicy.show()
+ self.acceptOnce('privacyPolicyTextDone', self.__privacyPolicyTextDone)
+ return
+
+ def exitPrivacyPolicy(self):
+ self.ignore('privacyPolicyTextDone')
+ if self.chatPrivacyPolicy:
+ cleanupDialog('privacyPolicyTextDialog')
+ self.chatPrivacyPolicy = None
+ return
+
+ def enterVersion1Adult(self):
+ self.enterPrivacyPolicy(OTPLocalizer.PrivacyPolicyText_1A)
+
+ def enterVersion1Kids(self):
+ self.enterPrivacyPolicy(OTPLocalizer.PrivacyPolicyText_1K)
+
+ def enterVersion2Adult(self):
+ self.enterPrivacyPolicy(OTPLocalizer.PrivacyPolicyText_2A)
+
+ def enterVersion2Kids(self):
+ self.enterPrivacyPolicy(OTPLocalizer.PrivacyPolicyText_2K)
+
+ def enterOff(self):
+ self.ignoreAll()
+ self.exitPrivacyPolicy()
+
+ def exitOff(self):
+ pass
diff --git a/otp/login/RemoteValueSet.py b/otp/login/RemoteValueSet.py
new file mode 100644
index 0000000..dd0c762
--- /dev/null
+++ b/otp/login/RemoteValueSet.py
@@ -0,0 +1,71 @@
+from direct.directnotify import DirectNotifyGlobal
+import TTAccount
+import HTTPUtil
+
+class RemoteValueSet:
+ notify = DirectNotifyGlobal.directNotify.newCategory('RemoteValueSet')
+
+ def __init__(self, url, http, body = '', expectedHeader = None, expectedFields = [], onUnexpectedResponse = None):
+ if onUnexpectedResponse is None:
+ onUnexpectedResponse = self.__onUnexpectedResponse
+ response = HTTPUtil.getHTTPResponse(url, http, body)
+ if expectedHeader is not None:
+ if response[0] != expectedHeader:
+ errMsg = 'unexpected response: %s' % response
+ self.notify.warning(errMsg)
+ onUnexpectedResponse(errMsg)
+ return
+ response = response[1:]
+ self.dict = {}
+ for line in response:
+ if not len(line):
+ continue
+ try:
+ name, value = line.split('=', 1)
+ except ValueError, e:
+ errMsg = 'unexpected response: %s' % response
+ self.notify.warning(errMsg)
+ onUnexpectedResponse(errMsg)
+ return
+
+ if len(expectedFields):
+ if name not in expectedFields:
+ self.notify.warning("received field '%s' that is not in expected field list" % name)
+ self.dict[name] = value
+
+ for name in expectedFields:
+ if not self.dict.has_key(name):
+ errMsg = "missing expected field '%s'" % name
+ self.notify.warning(errMsg)
+ onUnexpectedResponse(errMsg)
+ return
+
+ return
+
+ def __repr__(self):
+ return 'RemoteValueSet:%s' % str(self.dict)
+
+ def hasKey(self, key):
+ return self.dict.has_key(key)
+
+ def getBool(self, name, default = None):
+ return self.__getValue(name, lambda x: int(x) != 0, default)
+
+ def getInt(self, name, default = None):
+ return self.__getValue(name, int, default)
+
+ def getFloat(self, name, default = None):
+ return self.__getValue(name, float, default)
+
+ def getString(self, name, default = None):
+ return self.__getValue(name, str, default)
+
+ def __getValue(self, name, convOp, default):
+ if default is None:
+ return convOp(self.dict[name])
+ else:
+ return convOp(self.dict.get(name, default))
+ return
+
+ def __onUnexpectedResponse(self, errStr):
+ raise HTTPUtil.UnexpectedResponse(errStr)
diff --git a/otp/login/SecretFriendsInfoPanel.py b/otp/login/SecretFriendsInfoPanel.py
new file mode 100644
index 0000000..1c0b8ea
--- /dev/null
+++ b/otp/login/SecretFriendsInfoPanel.py
@@ -0,0 +1,18 @@
+from pandac.PandaModules import *
+from otp.otpbase.OTPGlobals import *
+from direct.gui.DirectGui import *
+from MultiPageTextFrame import *
+from otp.otpbase import OTPLocalizer
+from otp.otpgui import OTPDialog
+
+class SecretFriendsInfoPanel(getGlobalDialogClass()):
+
+ def __init__(self, doneEvent, hidePageNum = 0, pageChangeCallback = None):
+ dialogClass = getGlobalDialogClass()
+ dialogClass.__init__(self, parent=aspect2d, dialogName='secretFriendsInfoDialog', doneEvent=doneEvent, okButtonText=OTPLocalizer.SecretFriendsInfoPanelClose, style=OTPDialog.Acknowledge, text='', topPad=1.5, sidePad=1.2, pos=(0, 0, 0.1), scale=0.9)
+ self.textPanel = MultiPageTextFrame(parent=self, textList=OTPLocalizer.SecretFriendsInfoPanelText, hidePageNum=hidePageNum, pageChangeCallback=pageChangeCallback)
+ self['image'] = self['image']
+ self['image_pos'] = (0, 0, -.1)
+ self['image_scale'] = (2, 1, 1.3)
+ closeButton = self.getChild(0)
+ closeButton.setZ(-.56)
diff --git a/otp/login/TTAccount.py b/otp/login/TTAccount.py
new file mode 100644
index 0000000..eaac446
--- /dev/null
+++ b/otp/login/TTAccount.py
@@ -0,0 +1,280 @@
+from pandac.PandaModules import *
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.showbase import PythonUtil
+from otp.otpbase import OTPLocalizer
+import HTTPUtil
+import RemoteValueSet
+import copy
+accountServer = ''
+accountServer = launcher.getAccountServer()
+print 'TTAccount: accountServer from launcher: ', accountServer
+configAccountServer = base.config.GetString('account-server', '')
+if configAccountServer:
+ accountServer = configAccountServer
+ print 'TTAccount: overriding accountServer from config: ', accountServer
+if not accountServer:
+ accountServer = 'https://toontown.go.com'
+ print 'TTAccount: default accountServer: ', accountServer
+accountServer = URLSpec(accountServer, 1)
+
+def getAccountServer():
+ return accountServer
+
+
+TTAccountException = HTTPUtil.HTTPUtilException
+
+class TTAccount:
+ notify = DirectNotifyGlobal.directNotify.newCategory('TTAccount')
+
+ def __init__(self, cr):
+ self.cr = cr
+ self.response = None
+ return
+
+ def createAccount(self, loginName, password, data):
+ return self.talk('create', data=self.__makeLoginDict(loginName, password, data))
+
+ def authorize(self, loginName, password):
+ return self.talk('play', data=self.__makeLoginDict(loginName, password))
+
+ def createBilling(self, loginName, password, data):
+ return self.talk('purchase', data=self.__makeLoginDict(loginName, password, data))
+
+ def setParentPassword(self, loginName, password, parentPassword):
+ return self.talk('setParentPassword', data=self.__makeLoginDict(loginName, password, {'parentPassword': parentPassword}))
+
+ def supportsParentPassword(self):
+ return 1
+
+ def authenticateParentPassword(self, loginName, password, parentPassword):
+ try:
+ errorMsg = self.talk('authenticateParentPassword', data=self.__makeLoginDict(loginName, parentPassword))
+ if not errorMsg:
+ return (1, None)
+ if self.response.getInt('errorCode') in (5, 72):
+ return (0, None)
+ return (0, errorMsg)
+ except TTAccountException, e:
+ return (0, str(e))
+
+ return None
+
+ def supportsAuthenticateDelete(self):
+ return 1
+
+ def authenticateDelete(self, loginName, password):
+ try:
+ errorMsg = self.talk('authenticateDelete', data=self.__makeLoginDict(loginName, password))
+ if not errorMsg:
+ return (1, None)
+ if self.response.getInt('errorCode') in (5, 72):
+ return (0, None)
+ return (0, errorMsg)
+ except TTAccountException, e:
+ return (0, str(e))
+
+ return None
+
+ def enableSecretFriends(self, loginName, password, parentPassword, enable = 1):
+ try:
+ errorMsg = self.talk('setSecretChat', data=self.__makeLoginDict(loginName, parentPassword, {'chat': base.cr.secretChatAllowed,
+ 'secretsNeedParentPassword': base.cr.secretChatNeedsParentPassword}))
+ if not errorMsg:
+ return (1, None)
+ if self.response.getInt('errorCode') in (5, 72):
+ return (0, None)
+ return (0, errorMsg)
+ except TTAccountException, e:
+ return (0, str(e))
+
+ return None
+
+ def changePassword(self, loginName, password, newPassword):
+ return self.talk('purchase', data=self.__makeLoginDict(loginName, password, {'newPassword': newPassword}))
+
+ def requestPwdReminder(self, email = None, acctName = None):
+ data = {}
+ if email is not None:
+ data['email'] = email
+ else:
+ data['accountName'] = acctName
+ return self.talk('forgotPassword', data)
+
+ def cancelAccount(self, loginName, password):
+ return self.talk('cancel', data=self.__makeLoginDict(loginName, password))
+
+ def getAccountData(self, loginName, password):
+ errorMsg = self.talk('get', data=self.__makeLoginDict(loginName, password))
+ if errorMsg:
+ self.notify.warning('getAccountData error: %s' % errorMsg)
+ return errorMsg
+ if self.response.hasKey('errorMsg'):
+ self.notify.warning("error field is: '%s'" % self.response.getString('errorMsg'))
+ self.accountData = copy.deepcopy(self.response)
+ fieldNameMap = {'em': 'email',
+ 'l1': 'addr1',
+ 'l2': 'addr2',
+ 'l3': 'addr3'}
+ dict = self.accountData.dict
+ for fieldName in dict.keys():
+ if fieldNameMap.has_key(fieldName):
+ dict[fieldNameMap[fieldName]] = dict[fieldName]
+ del dict[fieldName]
+
+ return None
+
+ def getLastErrorMsg(self, forceCustServNum = 0):
+ errCode = self.response.getInt('errorCode')
+ if errCode < 100:
+ msg = self.response.getString('errorMsg')
+ if forceCustServNum:
+ msg += ' ' + OTPLocalizer.TTAccountCustomerServiceHelp % self.cr.accountServerConstants.getString('customerServicePhoneNumber')
+ elif errCode < 200:
+ msg = self.response.getString('errorMsg')
+ msg += ' ' + OTPLocalizer.TTAccountCustomerServiceHelp % self.cr.accountServerConstants.getString('customerServicePhoneNumber')
+ elif errCode >= 500:
+ msg = OTPLocalizer.TTAccountIntractibleError
+ msg += ' ' + OTPLocalizer.TTAccountCallCustomerService % self.cr.accountServerConstants.getString('customerServicePhoneNumber')
+ else:
+ self.notify.warning('unknown error code class: %s: %s' % (self.response.getInt('errorCode'), self.response.getString('errorMsg')))
+ msg = self.response.getString('errorMsg')
+ msg += ' ' + OTPLocalizer.TTAccountCallCustomerService % self.cr.accountServerConstants.getString('customerServicePhoneNumber')
+ return msg
+
+ def __makeLoginDict(self, loginName, password, data = None):
+ dict = {'accountName': loginName,
+ 'password': password}
+ if data:
+ dict.update(data)
+ return dict
+
+ def makeLoginDict(self, loginName, password, data = None):
+ return self.__makeLoginDict(loginName, password, data)
+
+ def talk(self, operation, data = {}):
+ self.notify.debug('TTAccount.talk()')
+ for key in data.keys():
+ data[key] = str(data[key])
+
+ if operation in ('play', 'get', 'cancel', 'authenticateParentPassword', 'authenticateDelete', 'authenticateParentPasswordNewStyle', 'authenticateDeleteNewStyle'):
+ pass
+ elif operation == 'authenticateParentUsernameAndPassword':
+ pass
+ elif operation == 'forgotPassword':
+ pass
+ elif operation == 'setParentPassword':
+ pass
+ elif operation == 'setSecretChat':
+ pass
+ elif operation == 'create':
+ pass
+ elif operation == 'purchase':
+ if data.has_key('newPassword'):
+ pass
+ else:
+ self.notify.error("Internal TTAccount error: need to add 'required data' checking for %s operation" % operation)
+ op2Php = {'play': 'play',
+ 'get': 'get',
+ 'cancel': 'cancel',
+ 'create': 'create',
+ 'purchase': 'purchase',
+ 'setParentPassword': 'setSecrets',
+ 'authenticateParentPassword': 'authenticateChat',
+ 'authenticateDelete': 'authDelete',
+ 'setSecretChat': 'setChat',
+ 'forgotPassword': 'forgotPw',
+ 'authenticateParentPasswordNewStyle': 'api/authChat',
+ 'authenticateParentUsernameAndPassword': 'api/authParentChat',
+ 'authenticateDeleteNewStyle': 'api/authDelete'}
+ newWebOperations = ('authenticateParentPasswordNewStyle', 'authenticateParentUsernameAndPassword', 'authenticateDeleteNewStyle')
+ url = URLSpec(getAccountServer())
+ if operation in newWebOperations:
+ url.setPath('/%s' % op2Php[operation])
+ else:
+ url.setPath('/%s.php' % op2Php[operation])
+ body = ''
+ if data.has_key('accountName'):
+ if operation not in newWebOperations:
+ url.setQuery('n=%s' % URLSpec.quote(data['accountName']))
+ serverFields = {'accountName': 'n',
+ 'password': 'p',
+ 'parentPassword': 'sp',
+ 'newPassword': 'np',
+ 'chat': 'chat',
+ 'email': 'em',
+ 'dobYear': 'doby',
+ 'dobMonth': 'dobm',
+ 'dobDay': 'dobd',
+ 'ccNumber': 'ccn',
+ 'ccMonth': 'ccm',
+ 'ccYear': 'ccy',
+ 'nameOnCard': 'noc',
+ 'addr1': 'l1',
+ 'addr2': 'l2',
+ 'addr3': 'l3',
+ 'city': 'city',
+ 'state': 'state',
+ 'country': 'country',
+ 'zip': 'zip',
+ 'referrer': 'ref',
+ 'secretsNeedParentPassword': 'secretsNeedsParentPassword',
+ 'parentPasswordNewStyle': 'pp',
+ 'parentUsername': 'pu',
+ 'userid': 'userid'}
+ ignoredFields = ('ccType',)
+ outBoundFields = {}
+ for fieldName in data.keys():
+ if not serverFields.has_key(fieldName):
+ if fieldName not in ignoredFields:
+ self.notify.error('unknown data field: %s' % fieldName)
+ else:
+ outBoundFields[serverFields[fieldName]] = data[fieldName]
+
+ orderedFields = outBoundFields.keys()
+ orderedFields.sort()
+ for fieldName in orderedFields:
+ if len(body):
+ body += '&'
+ body += '%s=%s' % (fieldName, URLSpec.quotePlus(outBoundFields[fieldName]))
+
+ self.notify.debug('url=' + url.cStr())
+ self.notify.debug('body=' + body)
+ if operation in ('get',):
+ expectedHeader = 'ACCOUNT INFO'
+ elif operation in ('play', 'cancel', 'create', 'purchase', 'setParentPassword', 'setSecretChat', 'authenticateParentPassword', 'authenticateDelete', 'forgotPassword', 'authenticateParentPasswordNewStyle', 'authenticateParentUsernameAndPassword', 'authenticateDeleteNewStyle'):
+ expectedHeader = 'ACCOUNT SERVER RESPONSE'
+ else:
+ self.notify.error("Internal TTAccount error: need to set expected response header for '%s' operation" % operation)
+ self.response = RemoteValueSet.RemoteValueSet(url, self.cr.http, body=body, expectedHeader=expectedHeader)
+ self.notify.debug(' self.response=' + str(self.response))
+ if self.response.hasKey('errorCode'):
+ errorCode = self.response.getInt('errorCode')
+ self.notify.info('account server error code: %s' % errorCode)
+ if errorCode == 10:
+ self.cr.freeTimeExpiresAt = 0
+ if self.response.hasKey('errorMsg'):
+ return self.getLastErrorMsg()
+ if operation in ('get', 'forgotPassword', 'authenticateDelete', 'play', 'cancel', 'create', 'purchase', 'setParentPassword', 'authenticateParentPassword', 'authenticateParentPasswordNewStyle', 'authenticateParentUsernameAndPassword', 'authenticateDeleteNewStyle'):
+ pass
+ elif operation == 'setSecretChat':
+ self.playToken = self.response.getString('playToken')
+ self.playTokenIsEncrypted = 1
+ else:
+ self.notify.error('Internal TTAccount error: need to extract useful data for %s operation' % operation)
+ return None
+
+ def authenticateParentUsernameAndPassword(self, loginName, password, parentUsername, parentPassword):
+ try:
+ errorMsg = self.talk('authenticateParentUsernameAndPassword', data=self.__makeLoginDict(loginName, password, {'parentUsername': parentUsername,
+ 'parentPasswordNewStyle': parentPassword,
+ 'userid': loginName}))
+ if not errorMsg:
+ return (1, None)
+ if self.response.getInt('errorCode') in (5, 72):
+ return (0, None)
+ return (0, errorMsg)
+ except TTAccountException, e:
+ return (0, str(e))
+
+ return None
diff --git a/otp/login/__init__.py b/otp/login/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/movement/Impulse.py b/otp/movement/Impulse.py
new file mode 100644
index 0000000..d03c320
--- /dev/null
+++ b/otp/movement/Impulse.py
@@ -0,0 +1,29 @@
+from pandac.PandaModules import *
+from direct.showbase import DirectObject
+
+class Impulse(DirectObject.DirectObject):
+
+ def __init__(self):
+ self.mover = None
+ self.nodePath = None
+ return
+
+ def destroy(self):
+ pass
+
+ def _process(self, dt):
+ pass
+
+ def _setMover(self, mover):
+ self.mover = mover
+ self.nodePath = self.mover.getNodePath()
+ self.VecType = self.mover.VecType
+
+ def _clearMover(self, mover):
+ if self.mover == mover:
+ self.mover = None
+ self.nodePath = None
+ return
+
+ def isCpp(self):
+ return 0
diff --git a/otp/movement/Mover.py b/otp/movement/Mover.py
new file mode 100644
index 0000000..4db6d54
--- /dev/null
+++ b/otp/movement/Mover.py
@@ -0,0 +1,76 @@
+from pandac.PandaModules import *
+from libotp import CMover
+from direct.directnotify import DirectNotifyGlobal
+from otp.movement.PyVec3 import PyVec3
+from direct.showbase import PythonUtil
+import __builtin__
+
+class Mover(CMover):
+ notify = DirectNotifyGlobal.directNotify.newCategory('Mover')
+ SerialNum = 0
+ Profile = 0
+ Pstats = 1
+ PSCCpp = 'App:Show code:moveObjects:MoverC++'
+ PSCPy = 'App:Show code:moveObjects:MoverPy'
+ PSCInt = 'App:Show code:moveObjects:MoverIntegrate'
+
+ def __init__(self, objNodePath, fwdSpeed = 1, rotSpeed = 1):
+ CMover.__init__(self, objNodePath, fwdSpeed, rotSpeed)
+ self.serialNum = Mover.SerialNum
+ Mover.SerialNum += 1
+ self.VecType = Vec3
+ self.impulses = {}
+ if Mover.Pstats:
+ self.pscCpp = PStatCollector(Mover.PSCCpp)
+ self.pscPy = PStatCollector(Mover.PSCPy)
+ self.pscInt = PStatCollector(Mover.PSCInt)
+
+ def destroy(self):
+ for name, impulse in self.impulses.items():
+ Mover.notify.debug('removing impulse: %s' % name)
+ self.removeImpulse(name)
+
+ def addImpulse(self, name, impulse):
+ if impulse.isCpp():
+ CMover.addCImpulse(self, name, impulse)
+ else:
+ self.impulses[name] = impulse
+ impulse._setMover(self)
+
+ def removeImpulse(self, name):
+ if name not in self.impulses:
+ if not CMover.removeCImpulse(self, name):
+ Mover.notify.warning("Mover.removeImpulse: unknown impulse '%s'" % name)
+ return
+ self.impulses[name]._clearMover(self)
+ del self.impulses[name]
+
+ def getCollisionEventName(self):
+ return 'moverCollision-%s' % self.serialNum
+
+ def move(self, dt = -1, profile = 0):
+ if Mover.Profile and not profile:
+
+ def func(doMove = self.move):
+ for i in xrange(10000):
+ doMove(dt, profile=1)
+
+ __builtin__.func = func
+ PythonUtil.startProfile(cmd='func()', filename='profile', sorts=['cumulative'], callInfo=0)
+ del __builtin__.func
+ return
+ if Mover.Pstats:
+ self.pscCpp.start()
+ CMover.processCImpulses(self, dt)
+ if Mover.Pstats:
+ self.pscCpp.stop()
+ self.pscPy.start()
+ for impulse in self.impulses.values():
+ impulse._process(self.getDt())
+
+ if Mover.Pstats:
+ self.pscPy.stop()
+ self.pscInt.start()
+ CMover.integrate(self)
+ if Mover.Pstats:
+ self.pscInt.stop()
diff --git a/otp/movement/PyVec3.py b/otp/movement/PyVec3.py
new file mode 100644
index 0000000..8de9419
--- /dev/null
+++ b/otp/movement/PyVec3.py
@@ -0,0 +1,175 @@
+from pandac.PandaModules import *
+import types
+import math
+
+class PyVec3:
+ Epsilon = 0.0001
+ ScalarTypes = (types.FloatType, types.IntType, types.LongType)
+
+ def __init__(self, *args):
+ self.assign(*args)
+
+ def assign(self, *args):
+ if len(args) == 1:
+ arg = args[0]
+ if type(arg) in PyVec3.ScalarTypes:
+ x = y = z = arg
+ elif isinstance(arg, self.__class__):
+ x = arg.x
+ y = arg.y
+ z = arg.z
+ elif isinstance(arg, VBase3):
+ x = arg.getX()
+ y = arg.getY()
+ z = arg.getZ()
+ else:
+ raise TypeError
+ elif len(args) == 3:
+ x = args[0]
+ y = args[1]
+ z = args[2]
+ self.x = x
+ self.y = y
+ self.z = z
+
+ def getX(self):
+ return self.x
+
+ def getY(self):
+ return self.y
+
+ def getZ(self):
+ return self.z
+
+ def setX(self, x):
+ self.x = x
+
+ def setY(self, y):
+ self.y = y
+
+ def setZ(self, z):
+ self.z = z
+
+ def set(self, x, y, z):
+ self.x = x
+ self.y = y
+ self.z = z
+
+ def fill(self, s):
+ self.x = self.y = self.z = s
+
+ def length(self):
+ return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
+
+ def lengthSquared(self):
+ return self.x * self.x + self.y * self.y + self.z * self.z
+
+ def normalize(self):
+ self /= self.length()
+
+ def dot(self, other):
+ return self.x * other.getX() + self.y * other.getY() + self.z * other.getZ()
+
+ def _crossResults(self, other):
+ return (self.y * other.getZ() - self.z * other.getY(), self.z * other.getX() - self.x * other.getZ(), self.x * other.getY() - self.y * other.getX())
+
+ def cross(self, other):
+ return PyVec3(*self._crossResults(other))
+
+ def crossInto(self, other):
+ self.x, self.y, self.z = self._crossResults(other)
+
+ def __lt__(a, b):
+ return a.length() < b.length()
+
+ def __le__(a, b):
+ return a < b or a == b
+
+ def __eq__(a, b):
+ return abs(a.length() - b.length()) < PyVec3.Epsilon
+
+ def __ne__(a, b):
+ return not a == b
+
+ def __ge__(a, b):
+ return a > b or a == b
+
+ def __gt__(a, b):
+ return a.length() > b.length()
+
+ def __add__(a, b):
+ return PyVec3(a.getX() + b.getX(), a.getY() + b.getY(), a.getZ() + b.getZ())
+
+ def __sub__(a, b):
+ return PyVec3(a.getX() - b.getX(), a.getY() - b.getY(), a.getZ() - b.getZ())
+
+ def __mul__(a, s):
+ return PyVec3(a.getX() * s, a.getY() * s, a.getZ() * s)
+
+ def __div__(a, s):
+ return PyVec3(a.getX() / s, a.getY() / s, a.getZ() / s)
+
+ def __iadd__(self, other):
+ self.x += other.getX()
+ self.y += other.getY()
+ self.z += other.getZ()
+ return self
+
+ def __isub__(self, other):
+ self.x -= other.getX()
+ self.y -= other.getY()
+ self.z -= other.getZ()
+ return self
+
+ def __imul__(self, s):
+ self.x *= s
+ self.y *= s
+ self.z *= s
+ return self
+
+ def __idiv__(self, s):
+ self.x /= s
+ self.y /= s
+ self.z /= s
+ return self
+
+ def addX(self, s):
+ self.x += s
+
+ def addY(self, s):
+ self.y += s
+
+ def addZ(self, s):
+ self.z += s
+
+ def eq(self, other):
+ return self == other
+
+ def lessThan(self, other):
+ return self < other
+
+ def ne(self, other):
+ return self != other
+
+ def __getitem__(self, i):
+ if i == 0:
+ return self.x
+ elif i == 1:
+ return self.y
+ elif i == 2:
+ return self.z
+ else:
+ raise IndexError
+
+ def __setitem__(self, i, s):
+ if i == 0:
+ self.x = s
+ elif i == 1:
+ self.y = s
+ elif i == 2:
+ self.z = s
+ else:
+ raise IndexError
+
+ def __repr__(self):
+ return 'PyVec3(%s,%s,%s)' % (self.x, self.y, self.z)
diff --git a/otp/movement/__init__.py b/otp/movement/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/namepanel/NameCheck.py b/otp/namepanel/NameCheck.py
new file mode 100644
index 0000000..f360d58
--- /dev/null
+++ b/otp/namepanel/NameCheck.py
@@ -0,0 +1,335 @@
+import string
+from otp.otpbase import OTPLocalizer
+from direct.directnotify import DirectNotifyGlobal
+from pandac.PandaModules import NSError
+from pandac.PandaModules import TextEncoder, TextNode
+notify = DirectNotifyGlobal.directNotify.newCategory('NameCheck')
+
+def filterString(str, filter):
+ result = ''
+ for char in str:
+ if char in filter:
+ result = result + char
+
+ return result
+
+
+def justLetters(str):
+ letters = ''
+ for c in str:
+ if c.isalpha():
+ letters = letters + c
+
+ return letters
+
+
+def justUpper(str):
+ upperCaseLetters = ''
+ for c in str:
+ if c.upper() != c.lower():
+ if c == c.upper():
+ upperCaseLetters = upperCaseLetters + c
+
+ return upperCaseLetters
+
+
+def wordList(str):
+ words = str.split()
+ result = []
+ for word in words:
+ subWords = word.split('-')
+ for sw in subWords:
+ if sw:
+ result.append(sw)
+
+ return result
+
+
+def checkName(name, otherCheckFuncs = [], font = None):
+
+ def longEnough(name):
+ if len(name) < 2:
+ notify.info('name is too short')
+ return OTPLocalizer.NCTooShort
+
+ def emptyName(name):
+ if name.strip() == '':
+ notify.info('name is empty')
+ return OTPLocalizer.NCTooShort
+
+ def printableChars(name):
+ for char in name:
+ if ord(char) < 128 and char not in string.printable:
+ notify.info('name contains non-printable char #%s' % ord(char))
+ return OTPLocalizer.NCGeneric
+
+ validAsciiChars = set(".,'-" + string.letters + string.whitespace)
+
+ def _validCharacter(c, validAsciiChars = validAsciiChars, font = font):
+ if c in validAsciiChars:
+ return True
+ if c.isalpha() or c.isspace():
+ return True
+ return False
+
+ def badCharacters(name, _validCharacter = _validCharacter):
+ for char in name:
+ if not _validCharacter(char):
+ if char in string.digits:
+ notify.info('name contains digits')
+ return OTPLocalizer.NCNoDigits
+ else:
+ notify.info('name contains bad char: %s' % TextEncoder().encodeWtext(char))
+ return OTPLocalizer.NCBadCharacter % TextEncoder().encodeWtext(char)
+
+ def fontHasCharacters(name, font = font):
+ if font:
+ tn = TextNode('NameCheck')
+ tn.setFont(font)
+ for c in name:
+ if not tn.hasCharacter(ord(c)):
+ notify.info('name contains bad char: %s' % TextEncoder().encodeWtext(c))
+ return OTPLocalizer.NCBadCharacter % TextEncoder().encodeWtext(c)
+
+ def hasLetters(name):
+ words = wordList(name)
+ for word in words:
+ letters = justLetters(word)
+ if len(letters) == 0:
+ notify.info('word "%s" has no letters' % TextEncoder().encodeWtext(word))
+ return OTPLocalizer.NCNeedLetters
+
+ def hasVowels(name):
+
+ def perWord(word):
+ if '.' in word:
+ return None
+ for char in word:
+ if ord(char) >= 128:
+ return None
+
+ letters = filterString(word, string.letters)
+ if len(letters) > 2:
+ vowels = filterString(letters, 'aeiouyAEIOUY')
+ if len(vowels) == 0:
+ notify.info('word "%s" has no vowels' % TextEncoder().encodeWtext(word))
+ return OTPLocalizer.NCNeedVowels
+ return None
+
+ for word in wordList(name):
+ problem = perWord(word)
+ if problem:
+ return problem
+
+ def monoLetter(name):
+
+ def perWord(word):
+ word = word
+ letters = justLetters(word)
+ if len(letters) > 2:
+ letters = TextEncoder().decodeText(TextEncoder.lower(TextEncoder().encodeWtext(letters)))
+ filtered = filterString(letters, letters[0])
+ if filtered == letters:
+ notify.info('word "%s" uses only one letter' % TextEncoder().encodeWtext(word))
+ return OTPLocalizer.NCGeneric
+
+ for word in wordList(name):
+ problem = perWord(word)
+ if problem:
+ return problem
+
+ def checkDashes(name):
+ def validDash(index, name=name):
+ if index == 0 or i == len(name)-1:
+ return 0
+ if not name[i-1].isalpha():
+ return 0
+ if not name[i+1].isalpha():
+ return 0
+ return 1
+
+ i=0
+ while 1:
+ i = name.find('-', i, len(name))
+ if i < 0:
+ return None
+ if not validDash(i):
+ notify.info('name makes invalid use of dashes')
+ return OTPLocalizer.NCDashUsage
+ i += 1
+
+ def checkCommas(name):
+ def validComma(index, name=name):
+ if index == 0 or i == len(name)-1:
+ return OTPLocalizer.NCCommaEdge
+ if name[i-1].isspace():
+ return OTPLocalizer.NCCommaAfterWord
+ if not name[i+1].isspace():
+ return OTPLocalizer.NCCommaUsage
+ return None
+
+ i=0
+ while 1:
+ i = name.find(',', i, len(name))
+ if i < 0:
+ return None
+ problem = validComma(i)
+ if problem:
+ notify.info('name makes invalid use of commas')
+ return problem
+ i += 1
+
+ def checkPeriods(name):
+ words = wordList(name)
+ for word in words:
+ if word[-1] == ',':
+ word = word[:-1]
+ numPeriods = word.count('.')
+ if not numPeriods:
+ continue
+ letters = justLetters(word)
+ numLetters = len(letters)
+ if word[-1] != '.':
+ notify.info('word "%s" does not end in a period' % TextEncoder().encodeWtext(word))
+ return OTPLocalizer.NCPeriodUsage
+ if numPeriods > 2:
+ notify.info('word "%s" has too many periods' % TextEncoder().encodeWtext(word))
+ return OTPLocalizer.NCPeriodUsage
+ if numPeriods == 2:
+ if not (word[1] == '.' and word[3] == '.'):
+ notify.info('word "%s" does not fit the J.T. pattern' % TextEncoder().encodeWtext(word))
+ return OTPLocalizer.NCPeriodUsage
+
+ return None
+
+ def checkApostrophes(name):
+ words = wordList(name)
+ for word in words:
+ numApos = word.count("'")
+ if numApos > 2:
+ notify.info('word "%s" has too many apostrophes.' % TextEncoder().encodeWtext(word))
+ return OTPLocalizer.NCApostrophes
+
+ numApos = name.count("'")
+ if numApos > 3:
+ notify.info('name has too many apostrophes.')
+ return OTPLocalizer.NCApostrophes
+
+ def tooManyWords(name):
+ if len(wordList(name)) > 4:
+ notify.info('name has too many words')
+ return OTPLocalizer.NCTooManyWords
+
+ def allCaps(name):
+ letters = justLetters(name)
+ if len(letters) > 2:
+ upperLetters = TextEncoder().decodeText(TextEncoder.upper(TextEncoder().encodeWtext(letters)))
+ for i in xrange(len(upperLetters)):
+ if not upperLetters[0].isupper():
+ return
+
+ if upperLetters == letters:
+ notify.info('name is all caps')
+ return OTPLocalizer.NCAllCaps
+
+ def mixedCase(name):
+ words = wordList(name)
+ for word in words:
+ if len(word) > 2:
+ capitals = justUpper(word)
+ if len(capitals) > 2:
+ notify.info('name has mixed case')
+ return OTPLocalizer.NCMixedCase
+
+ def checkJapanese(name):
+ asciiSpace = range(32, 33)
+ asciiDigits = range(48, 64)
+ hiragana = range(12353, 12448)
+ katakana = range(12449, 12544)
+ halfwidthKatakana = range(65381, 65440)
+ halfwidthCharacter = set(asciiSpace + halfwidthKatakana)
+ allowedUtf8 = set(asciiSpace + hiragana + katakana + halfwidthKatakana)
+
+ te = TextEncoder()
+ dc = 0.0
+
+ for char in (ord(char) for char in te.decodeText(name)):
+ if char not in allowedUtf8:
+ if char in asciiDigits:
+ notify.info('name contains not allowed ascii digits')
+ return OTPLocalizer.NCNoDigits
+ else:
+ notify.info('name contains not allowed utf8 char: 0x%04x' % char)
+ return OTPLocalizer.NCBadCharacter % te.encodeWtext(unichr(char))
+ elif char in halfwidthCharacter:
+ dc += 0.5
+ else:
+ dc += 1
+
+ if dc < 2:
+ notify.info('name is too short: %0.1f' % dc)
+ return OTPLocalizer.NCTooShort
+ elif dc > 8:
+ notify.info('name has been occupied more than eight display cells: %0.1f' % dc)
+ return OTPLocalizer.NCGeneric
+
+ def repeatedChars(name):
+ count = 1
+ lastChar = None
+ i = 0
+ while i < len(name):
+ char = name[i]
+ i += 1
+ if char == lastChar:
+ count += 1
+ else:
+ count = 1
+ lastChar = char
+ if count > 2:
+ notify.info('character %s is repeated too many times' % TextEncoder().encodeWtext(char))
+ return OTPLocalizer.NCRepeatedChar % TextEncoder().encodeWtext(char)
+
+ return
+
+ checks = [printableChars,
+ badCharacters,
+ fontHasCharacters,
+ longEnough,
+ emptyName,
+ hasLetters,
+ hasVowels,
+ monoLetter,
+ checkDashes,
+ checkCommas,
+ checkPeriods,
+ checkApostrophes,
+ tooManyWords,
+ allCaps,
+ mixedCase,
+ repeatedChars] + otherCheckFuncs
+ symmetricChecks = []
+ name = TextEncoder().decodeText(name)
+ notify.info('checking name "%s"...' % TextEncoder().encodeWtext(name))
+ for check in checks:
+ problem = check(name[:])
+ if not problem and check in symmetricChecks:
+ nName = name[:]
+ bName.reverse()
+ problem = check(bName)
+ print 'problem = %s' % problem
+ if problem:
+ return problem
+
+ return None
+
+
+severity = notify.getSeverity()
+notify.setSeverity(NSError)
+for i in xrange(32):
+ pass
+
+for c in '!"#$%&()*+/:;<=>?@[\\]^_`{|}~':
+ pass
+
+notify.setSeverity(severity)
+del severity
diff --git a/otp/namepanel/NameTumbler.py b/otp/namepanel/NameTumbler.py
new file mode 100644
index 0000000..fb3e4f3
--- /dev/null
+++ b/otp/namepanel/NameTumbler.py
@@ -0,0 +1,143 @@
+from pandac.PandaModules import *
+from direct.showbase import DirectObject
+import random
+from direct.task import Task
+from direct.gui.DirectGui import *
+import string
+from direct.gui import OnscreenText
+
+class NameTumbler(DirectFrame):
+
+ def __init__(self, nameList, category):
+ DirectFrame.__init__(self, parent=aspect2d, relief='flat', scale=(1, 1, 1), state='disabled', frameColor=(1, 1, 1, 0))
+ self.initialiseoptions(NameTumbler)
+ self.nameList = nameList
+ self.nameList.sort()
+ self.category = category
+ self.tumblerColor = Vec4(1, 1, 1, 1)
+ self.displayList = [' '] + [' '] + self.nameList + [' '] + [' ']
+ self.nameIndex = -1
+ self.isActive = 1
+ self.loadTumblerGUI()
+
+ def loadTumblerGUI(self):
+ self.circle = 'src/maps/NameTumblerCheck.tif'
+ self.background = 'src/maps/NameTumbler.tif'
+ self.upArrow = 'src/maps/NameTumblerUpArrow.tif'
+ self.downArrow = 'src/maps/NameTumblerDownArrow.tif'
+ self.tumblerscrollList = self.makeScrollList(self.displayList, self.makeLabel, [TextNode.ACenter, 'title'])
+ self.tumblerscrollList['command'] = self.listsChanged
+ self.tumblerscrollList.reparentTo(self)
+ self.hilight = self.makeHighlight((0, 0, -0.15))
+ self.hilight.reparentTo(self.tumblerscrollList)
+ if self.category != '':
+ self.check = self.makeCheckBox((-0.617, 0, 0.374), self.category, (0, 0.25, 0.5, 1), self.toggleTumbler)
+ self.check.reparentTo(self)
+ self.getRandomResult()
+
+ def unloadTumblerGUI(self):
+ if self.category != '':
+ self.check.destroy()
+ del self.check
+ self.tumblerscrollList.destroy()
+ del self.tumblerscrollList
+ self.hilight.destroy()
+ del self.hilight
+
+ def toggleTumbler(self, value):
+ if self.isActive:
+ if self.priority == 1:
+ messenger.send('CheckTumblerPriority', [self.category])
+ else:
+ self.deactivateTumbler()
+ else:
+ self.activateTumbler()
+ if self.linkage > 0:
+ messenger.send('CheckTumblerLinkage', [self.category])
+ self.listsChanged()
+ if self.isActive:
+ self.tumblerscrollList.refresh()
+ self.updateCheckBoxes()
+
+ def listsChanged(self):
+ newname = ''
+ self.nameIndex = self.tumblerscrollList.index + 2
+ messenger.send('updateNameResult')
+
+ def updateLists(self):
+ self.tumblerscrollList.scrollTo(self.nameIndex - 2)
+ messenger.send('updateNameResult')
+
+ def updateCheckBoxes(self):
+ if self.category != '':
+ if self.isActive:
+ self.check['indicatorValue'] = self.isActive
+ else:
+ self.check['indicatorValue'] = -1
+ self.check.setIndicatorValue()
+
+ def nameClickedOn(self, index):
+ self.nameIndex = index
+ self.updateLists()
+ self.listsChanged()
+
+ def activateTumbler(self):
+ self.hilight.show()
+ self.isActive = 1
+ self.tumblerscrollList.itemFrame['frameColor'] = self.tumblerColor
+
+ def deactivateTumbler(self):
+ self.hilight.hide()
+ self.isActive = 0
+ self.tumblerscrollList.itemFrame['frameColor'] = (0.7, 0.7, 0.7, 1)
+
+ def getName(self):
+ if self.isActive:
+ name = self.nameList[self.nameIndex - 2]
+ else:
+ name = ''
+ return name
+
+ def makeLabel(self, te, index, others):
+ alig = others[0]
+ if alig == TextNode.ARight:
+ newpos = (0.44, 0, 0)
+ elif alig == TextNode.ALeft:
+ newpos = (0, 0, 0)
+ else:
+ newpos = (0.2, 0, 0)
+ df = DirectFrame(state='normal', relief=None, text=te, text_scale=0.1, text_pos=newpos, text_align=alig, textMayChange=0)
+ df.bind(DGG.B1PRESS, lambda x, df = df: self.nameClickedOn(index))
+ return df
+
+ def makeScrollList(self, nitems, nitemMakeFunction, nitemMakeExtraArgs):
+ it = nitems[:]
+ ds = DirectScrolledList(items=it, itemMakeFunction=nitemMakeFunction, itemMakeExtraArgs=nitemMakeExtraArgs, parent=aspect2d, relief=None, command=None, scale=0.6, pad=(0.1, 0.1), incButton_image=(self.downArrow,
+ self.upArrow,
+ self.circle,
+ self.downArrow), incButton_relief=None, incButton_scale=(0.2, 0.05, 0.05), incButton_pos=(0, 0, -0.58), decButton_image=(self.upArrow,
+ self.downArrow,
+ self.circle,
+ self.upArrow), decButton_relief=None, decButton_scale=(0.2, 0.05, 0.05), decButton_pos=(0, 0, 0.23), itemFrame_pos=(-.2, 0, 0.028), itemFrame_scale=1.0, itemFrame_relief=None, itemFrame_image=self.background, itemFrame_image_scale=(0.38, 0, 0.33), itemFrame_image_pos=(0.2, 0, -0.2), itemFrame_frameSize=(-0.05,
+ 0.48,
+ -0.5,
+ 0.1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=5)
+ ds.setTransparency(1)
+ return ds
+
+ def makeCheckBox(self, npos, ntex, ntexcolor, comm):
+ dcf = DirectCheckButton(parent=aspect2d, relief=None, scale=0.1, boxBorder=0.08, boxImage=self.circle, boxImageScale=(0.4, 0.4, 0.4), boxRelief=None, pos=npos, text=ntex, text_fg=ntexcolor, text_scale=0.8, text_pos=(0.2, 0), indicator_pos=(-0.566667, 0, -0.045), indicator_image_pos=(-0.26, 0, 0.075), command=comm, text_align=TextNode.ALeft)
+ dcf.setTransparency(1)
+ return dcf
+
+ def makeHighlight(self, npos):
+ return DirectFrame(parent=aspect2d, relief='flat', state='disabled', frameSize=(-0.25,
+ 0.26,
+ -0.05,
+ 0.05), borderWidth=(0.01, 0.01), pos=npos, frameColor=(1, 0, 1, 0.4))
+
+ def getRandomResult(self):
+ randomName = random.choice(self.nameList)
+ self.nameIndex = self.displayList.index(randomName)
+ self.updateCheckBoxes()
+ self.updateLists()
diff --git a/otp/namepanel/PickANamePattern.py b/otp/namepanel/PickANamePattern.py
new file mode 100644
index 0000000..2d033e4
--- /dev/null
+++ b/otp/namepanel/PickANamePattern.py
@@ -0,0 +1,135 @@
+
+
+class PickANamePattern:
+
+ def __init__(self, nameStr, gender):
+ self._nameStr = nameStr
+ self._namePattern = self._compute(self._nameStr, gender)
+
+ def hasNamePattern(self):
+ return self._namePattern is not None
+
+ def getNamePattern(self):
+ return self._namePattern
+
+ def getNameString(self, pattern, gender):
+ nameParts = self._getNameParts(gender)
+ invNameParts = []
+ for i in xrange(len(nameParts)):
+ invNameParts.append(invertDict(nameParts[i]))
+
+ name = ''
+ for i in xrange(len(pattern)):
+ if pattern[i] != -1:
+ if len(name):
+ name += ' '
+ name += invNameParts[i][pattern[i]]
+
+ return name
+
+ def getNamePartString(self, gender, patternIndex, partIndex):
+ nameParts = self._getNameParts(gender)
+ invNamePart = invertDict(nameParts[patternIndex])
+ return invNamePart[partIndex]
+
+ def _genWordListSplitPermutations(self, words):
+ if not len(words):
+
+ return
+ if len(words) == 1:
+
+ yield words
+ return
+
+ for permutation in self._genWordListSplitPermutations(words[1:]):
+ yield [words[0]]+permutation
+ yield [(words[0] + ' ')+permutation[0]]+permutation[1:]
+
+ def _genNameSplitPermutations(self, name):
+ for splitName in self._genWordListSplitPermutations(name.split()):
+ yield splitName
+
+ def _compute(self, nameStr, gender):
+ return self._computeWithNameParts(nameStr, self._getNameParts(gender))
+
+ def _computeWithNameParts(self, nameStr, nameParts):
+ for splitPermutation in self._genNameSplitPermutations(nameStr):
+ pattern = self._recursiveCompute(splitPermutation, nameParts)
+ if pattern is not None:
+ return pattern
+
+ return
+
+ def _getNameParts(self, gender):
+ pass
+
+ def _recursiveCompute(self, words, nameParts, wi = 0, nwli = 0, pattern = None):
+ if wi >= len(words):
+ return pattern
+ if nwli >= len(nameParts):
+ return
+ if words[wi] in nameParts[nwli]:
+ if pattern is None:
+ pattern = [-1] * len(nameParts)
+ word2index = nameParts[nwli]
+ newPattern = pattern[:]
+ newPattern[nwli] = word2index[words[wi]]
+ result = self._recursiveCompute(words, nameParts, wi + 1, nwli + 1, newPattern)
+ if result:
+ return result
+ return self._recursiveCompute(words, nameParts, wi, nwli + 1, pattern)
+
+
+class PickANamePatternTwoPartLastName(PickANamePattern):
+
+ def getNameString(self, pattern, gender):
+ name = PickANamePattern.getNameString(self, pattern, gender)
+ if pattern[-2] != -1:
+ words = name.split()
+ name = ''
+ for word in words[:-2]:
+ if len(name):
+ name += ' '
+ name += word
+
+ if len(name):
+ name += ' '
+ name += words[-2]
+ if words[-2] in set(self._getLastNameCapPrefixes()):
+ name += words[-1].capitalize()
+ else:
+ name += words[-1]
+ return name
+
+ def _getLastNameCapPrefixes(self):
+ return []
+
+ def _compute(self, nameStr, gender):
+ nameParts = self._getNameParts(gender)
+ combinedNameParts = nameParts[:-2]
+ combinedNameParts.append({})
+ combinedIndex2indices = {}
+ lastNamePrefixesCapped = set(self._getLastNameCapPrefixes())
+ k = 0
+ for first, i in nameParts[-2].iteritems():
+ capitalize = first in lastNamePrefixesCapped
+ for second, j in nameParts[-1].iteritems():
+ combinedLastName = first
+ if capitalize:
+ combinedLastName += second.capitalize()
+ else:
+ combinedLastName += second
+ combinedNameParts[-1][combinedLastName] = k
+ combinedIndex2indices[k] = (i, j)
+ k += 1
+
+ pattern = self._computeWithNameParts(nameStr, combinedNameParts)
+ if pattern:
+ combinedIndex = pattern[-1]
+ pattern = pattern[:-1]
+ pattern.append(-1)
+ pattern.append(-1)
+ if combinedIndex != -1:
+ pattern[-2] = combinedIndex2indices[combinedIndex][0]
+ pattern[-1] = combinedIndex2indices[combinedIndex][1]
+ return pattern
diff --git a/otp/namepanel/__init__.py b/otp/namepanel/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/otpbase/OTPBase.py b/otp/otpbase/OTPBase.py
new file mode 100644
index 0000000..ad40f58
--- /dev/null
+++ b/otp/otpbase/OTPBase.py
@@ -0,0 +1,229 @@
+from direct.showbase.ShowBase import ShowBase
+from pandac.PandaModules import Camera, TPLow, VBase4, ColorWriteAttrib, Filename, getModelPath, NodePath
+import OTPRender
+import time
+import math
+import re
+
+class OTPBase(ShowBase):
+
+ def __init__(self, windowType = None):
+ self.wantEnviroDR = False
+ ShowBase.__init__(self, windowType=windowType)
+ if config.GetBool('want-phase-checker', 0):
+ from direct.showbase import Loader
+ Loader.phaseChecker = self.loaderPhaseChecker
+ self.errorAccumulatorBuffer = ''
+ taskMgr.add(self.delayedErrorCheck, 'delayedErrorCheck', priority=10000)
+ self.idTags = config.GetBool('want-id-tags', 0)
+ if not self.idTags:
+ del self.idTags
+ self.wantNametags = self.config.GetBool('want-nametags', 1)
+ self.slowCloseShard = self.config.GetBool('slow-close-shard', 0)
+ self.slowCloseShardDelay = self.config.GetFloat('slow-close-shard-delay', 10.0)
+ self.fillShardsToIdealPop = self.config.GetBool('fill-shards-to-ideal-pop', 1)
+ self.logPrivateInfo = self.config.GetBool('log-private-info', __dev__)
+ self.wantDynamicShadows = 1
+ self.stereoEnabled = False
+ self.enviroDR = None
+ self.enviroCam = None
+ self.pixelZoomSetup = False
+ self.gameOptionsCode = ''
+ self.locationCode = ''
+ self.locationCodeChanged = time.time()
+ if base.cam:
+ if self.wantEnviroDR:
+ base.cam.node().setCameraMask(OTPRender.MainCameraBitmask)
+ else:
+ base.cam.node().setCameraMask(OTPRender.MainCameraBitmask | OTPRender.EnviroCameraBitmask)
+ taskMgr.setupTaskChain('net')
+ return
+
+ def setTaskChainNetThreaded(self):
+ if base.config.GetBool('want-threaded-network', 0):
+ taskMgr.setupTaskChain('net', numThreads=1, frameBudget=0.001, threadPriority=TPLow)
+
+ def setTaskChainNetNonthreaded(self):
+ taskMgr.setupTaskChain('net', numThreads=0, frameBudget=-1)
+
+ def toggleStereo(self):
+ self.stereoEnabled = not self.stereoEnabled
+ if self.stereoEnabled:
+ if not base.win.isStereo():
+ base.win.setRedBlueStereo(True, ColorWriteAttrib.CRed, ColorWriteAttrib.CGreen | ColorWriteAttrib.CBlue)
+ if self.wantEnviroDR:
+ self.setupEnviroCamera()
+ return
+ mainDR = base.camNode.getDisplayRegion(0)
+ if self.stereoEnabled:
+ if not mainDR.isStereo():
+ base.win.removeDisplayRegion(mainDR)
+ mainDR = base.win.makeStereoDisplayRegion()
+ mainDR.getRightEye().setClearDepthActive(True)
+ mainDR.setCamera(base.cam)
+ elif mainDR.isStereo():
+ base.win.removeDisplayRegion(mainDR)
+ mainDR = base.win.makeMonoDisplayRegion()
+ mainDR.setCamera(base.cam)
+
+ def setupEnviroCamera(self):
+ clearColor = VBase4(0, 0, 0, 1)
+ if self.enviroDR:
+ clearColor = self.enviroDR.getClearColor()
+ self.win.removeDisplayRegion(self.enviroDR)
+ if not self.enviroCam:
+ self.enviroCam = self.cam.attachNewNode(Camera('enviroCam'))
+ mainDR = self.camNode.getDisplayRegion(0)
+ if self.stereoEnabled:
+ self.enviroDR = self.win.makeStereoDisplayRegion()
+ if not mainDR.isStereo():
+ self.win.removeDisplayRegion(mainDR)
+ mainDR = self.win.makeStereoDisplayRegion()
+ mainDR.setCamera(self.cam)
+ ml = mainDR.getLeftEye()
+ mr = mainDR.getRightEye()
+ el = self.enviroDR.getLeftEye()
+ er = self.enviroDR.getRightEye()
+ el.setSort(-8)
+ ml.setSort(-6)
+ er.setSort(-4)
+ er.setClearDepthActive(True)
+ mr.setSort(-2)
+ mr.setClearDepthActive(False)
+ else:
+ self.enviroDR = self.win.makeMonoDisplayRegion()
+ if mainDR.isStereo():
+ self.win.removeDisplayRegion(mainDR)
+ mainDR = self.win.makeMonoDisplayRegion()
+ mainDR.setCamera(self.cam)
+ self.enviroDR.setSort(-10)
+ self.enviroDR.setClearColor(clearColor)
+ self.win.setClearColor(clearColor)
+ self.enviroDR.setCamera(self.enviroCam)
+ self.enviroCamNode = self.enviroCam.node()
+ self.enviroCamNode.setLens(self.cam.node().getLens())
+ self.enviroCamNode.setCameraMask(OTPRender.EnviroCameraBitmask)
+ render.hide(OTPRender.EnviroCameraBitmask)
+ self.camList.append(self.enviroCam)
+ self.backgroundDrawable = self.enviroDR
+ self.enviroDR.setTextureReloadPriority(-10)
+ if self.pixelZoomSetup:
+ self.setupAutoPixelZoom()
+
+ def setupAutoPixelZoom(self):
+ self.win.setPixelZoom(1)
+ self.enviroDR.setPixelZoom(1)
+ if not self.stereoEnabled:
+ self.enviroDR.setClearColorActive(True)
+ self.enviroDR.setClearDepthActive(True)
+ self.win.setClearColorActive(False)
+ self.win.setClearDepthActive(False)
+ self.backgroundDrawable = self.enviroDR
+ else:
+ self.enviroDR.setClearColorActive(False)
+ self.enviroDR.setClearDepthActive(False)
+ self.enviroDR.getRightEye().setClearDepthActive(True)
+ self.win.setClearColorActive(True)
+ self.win.setClearDepthActive(True)
+ self.backgroundDrawable = self.win
+ self.pixelZoomSetup = True
+ self.targetPixelZoom = 1.0
+ self.pixelZoomTask = None
+ self.pixelZoomCamHistory = 2.0
+ self.pixelZoomCamMovedList = []
+ self.pixelZoomStarted = None
+ flag = self.config.GetBool('enable-pixel-zoom', True)
+ self.enablePixelZoom(flag)
+ return
+
+ def enablePixelZoom(self, flag):
+ if not self.backgroundDrawable.supportsPixelZoom():
+ flag = False
+ self.pixelZoomEnabled = flag
+ taskMgr.remove('chasePixelZoom')
+ if flag:
+ taskMgr.add(self.__chasePixelZoom, 'chasePixelZoom', priority=-52)
+ else:
+ self.backgroundDrawable.setPixelZoom(1)
+
+ def __chasePixelZoom(self, task):
+ now = globalClock.getFrameTime()
+ pos = base.cam.getNetTransform().getPos()
+ prevPos = base.cam.getNetPrevTransform().getPos()
+ d2 = (pos - prevPos).lengthSquared()
+ if d2:
+ d = math.sqrt(d2)
+ self.pixelZoomCamMovedList.append((now, d))
+ while self.pixelZoomCamMovedList and self.pixelZoomCamMovedList[0][0] < now - self.pixelZoomCamHistory:
+ del self.pixelZoomCamMovedList[0]
+
+ dist = sum(map(lambda pair: pair[1], self.pixelZoomCamMovedList))
+ speed = dist / self.pixelZoomCamHistory
+ if speed < 5:
+ self.backgroundDrawable.setPixelZoom(4)
+ self.pixelZoomStart = None
+ elif speed > 10:
+ if self.pixelZoomStart == None:
+ self.pixelZoomStart = now
+ elapsed = now - self.pixelZoomStart
+ if elapsed > 10:
+ self.backgroundDrawable.setPixelZoom(16)
+ elif elapsed > 5:
+ self.backgroundDrawable.setPixelZoom(8)
+ return task.cont
+
+ def getShardPopLimits(self):
+ return (300, 600, 1200)
+
+ def setLocationCode(self, locationCode):
+ if locationCode != self.locationCode:
+ self.locationCode = locationCode
+ self.locationCodeChanged = time.time()
+
+ def delayedErrorCheck(self, task):
+ if self.errorAccumulatorBuffer:
+ buffer = self.errorAccumulatorBuffer
+ self.errorAccumulatorBuffer = ''
+ self.notify.error('\nAccumulated Phase Errors!:\n %s' % buffer)
+ return task.cont
+
+ def loaderPhaseChecker(self, path, loaderOptions):
+ if 'audio/' in path:
+ return 1
+ file = Filename(path)
+ if not file.getExtension():
+ file.setExtension('bam')
+ mp = getModelPath()
+ path = mp.findFile(file).cStr()
+ if not path:
+ return
+ match = re.match('.*phase_([^/]+)/', path)
+ if not match:
+ if 'dmodels' in path:
+ return
+ else:
+ self.errorAccumulatorBuffer += 'file not in phase (%s, %s)\n' % (file, path)
+ return
+ basePhase = float(match.groups()[0])
+ if not launcher.getPhaseComplete(basePhase):
+ self.errorAccumulatorBuffer += 'phase is not loaded for this model %s\n' % path
+ model = loader.loader.loadSync(Filename(path), loaderOptions)
+ if model:
+ model = NodePath(model)
+ for tex in model.findAllTextures():
+ texPath = tex.getFullpath().cStr()
+ match = re.match('.*phase_([^/]+)/', texPath)
+ if match:
+ texPhase = float(match.groups()[0])
+ if texPhase > basePhase:
+ self.errorAccumulatorBuffer += 'texture phase is higher than the models (%s, %s)\n' % (path, texPath)
+
+ def getRepository(self):
+ return self.cr
+
+ def openMainWindow(self, *args, **kw):
+ result = ShowBase.openMainWindow(self, *args, **kw)
+ if result:
+ self.wantEnviroDR = not self.win.getGsg().isHardware() or config.GetBool('want-background-region', 1)
+ self.backgroundDrawable = self.win
+ return result
diff --git a/otp/otpbase/OTPGlobals.py b/otp/otpbase/OTPGlobals.py
new file mode 100644
index 0000000..cf14243
--- /dev/null
+++ b/otp/otpbase/OTPGlobals.py
@@ -0,0 +1,357 @@
+from pandac.PandaModules import *
+QuietZone = 1
+UberZone = 2
+WallBitmask = BitMask32(1)
+FloorBitmask = BitMask32(2)
+CameraBitmask = BitMask32(4)
+CameraTransparentBitmask = BitMask32(8)
+SafetyNetBitmask = BitMask32(512)
+SafetyGateBitmask = BitMask32(1024)
+GhostBitmask = BitMask32(2048)
+PathFindingBitmask = BitMask32.bit(29)
+OriginalCameraFov = 52.0
+DefaultCameraFov = 52.0
+DefaultCameraFar = 400.0
+DefaultCameraNear = 1.0
+AICollisionPriority = 10
+AICollMovePriority = 8
+MaxFriends = 50
+MaxPlayerFriends = 300
+MaxBackCatalog = 48
+FriendChat = 1
+CommonChat = 1
+SuperChat = 2
+MaxCustomMessages = 25
+SPInvalid = 0
+SPHidden = 1
+SPRender = 2
+SPDynamic = 5
+CENormal = 0
+CEBigHead = 1
+CESmallHead = 2
+CEBigLegs = 3
+CESmallLegs = 4
+CEBigToon = 5
+CESmallToon = 6
+CEFlatPortrait = 7
+CEFlatProfile = 8
+CETransparent = 9
+CENoColor = 10
+CEInvisible = 11
+CEPumpkin = 12
+CEBigWhite = 13
+CESnowMan = 14
+CEGreenToon = 15
+CEGhost = 'g'
+BigToonScale = 1.5
+SmallToonScale = 0.5
+DisconnectUnknown = 0
+DisconnectBookExit = 1
+DisconnectCloseWindow = 2
+DisconnectPythonError = 3
+DisconnectSwitchShards = 4
+DisconnectGraphicsError = 5
+DisconnectReasons = {DisconnectUnknown: 'unknown',
+ DisconnectBookExit: 'book exit',
+ DisconnectCloseWindow: 'closed window',
+ DisconnectPythonError: 'python error',
+ DisconnectSwitchShards: 'switch shards',
+ DisconnectGraphicsError: 'graphics error'}
+DatabaseDialogTimeout = 20.0
+DatabaseGiveupTimeout = 45.0
+PeriodTimerWarningTime = (600, 300, 60)
+WalkCutOff = 0.5
+RunCutOff = 8.0
+FloorOffset = 0.025
+AvatarDefaultRadius = 1
+InterfaceFont = None
+InterfaceFontPath = None
+SignFont = None
+SignFontPath = None
+FancyFont = None
+FancyFontPath = None
+NametagFonts = {}
+NametagFontPaths = {}
+DialogClass = None
+GlobalDialogClass = None
+ProductPrefix = None
+
+def getInterfaceFont():
+ global InterfaceFontPath
+ global InterfaceFont
+ if InterfaceFont == None:
+ if InterfaceFontPath == None:
+ InterfaceFont = TextNode.getDefaultFont()
+ else:
+ InterfaceFont = loader.loadFont(InterfaceFontPath, lineHeight=1.0)
+ return InterfaceFont
+
+
+def setInterfaceFont(path):
+ global InterfaceFontPath
+ global InterfaceFont
+ InterfaceFontPath = path
+ InterfaceFont = None
+ return
+
+
+def getSignFont():
+ global SignFont
+ global SignFontPath
+ if SignFont == None:
+ if SignFontPath == None:
+ InterfaceFont = TextNode.getDefaultFont()
+ SignFont = TextNode.getDefaultFont()
+ else:
+ SignFont = loader.loadFont(SignFontPath, lineHeight=1.0)
+ return SignFont
+
+
+def setSignFont(path):
+ global SignFontPath
+ SignFontPath = path
+
+
+def getFancyFont():
+ global FancyFontPath
+ global FancyFont
+ if FancyFont == None:
+ if FancyFontPath == None:
+ InterfaceFont = TextNode.getDefaultFont()
+ FancyFont = TextNode.getDefaultFont()
+ else:
+ FancyFont = loader.loadFont(FancyFontPath, lineHeight=1.0)
+ return FancyFont
+
+
+def setFancyFont(path):
+ global FancyFontPath
+ FancyFontPath = path
+
+
+def getNametagFont(index):
+ global NametagFontPaths
+ global NametagFonts
+ if not NametagFonts.has_key(index) or NametagFonts[index] == None:
+ if not NametagFontPaths.has_key(index) or NametagFontPaths[index] == None:
+ InterfaceFont = TextNode.getDefaultFont()
+ NametagFonts[index] = TextNode.getDefaultFont()
+ else:
+ NametagFonts[index] = loader.loadFont(NametagFontPaths[index], lineHeight=1.0)
+ return NametagFonts[index]
+
+
+def setNametagFont(index, path):
+ NametagFontPaths[index] = path
+
+
+def getDialogClass():
+ global DialogClass
+ if DialogClass == None:
+ from otp.otpgui.OTPDialog import OTPDialog
+ DialogClass = OTPDialog
+ return DialogClass
+
+
+def getGlobalDialogClass():
+ global GlobalDialogClass
+ if DialogClass == None:
+ from otp.otpgui.OTPDialog import GlobalDialog
+ GlobalDialogClass = GlobalDialog
+ return GlobalDialogClass
+
+
+def setDialogClasses(dialogClass, globalDialogClass):
+ global DialogClass
+ global GlobalDialogClass
+ DialogClass = dialogClass
+ GlobalDialogClass = globalDialogClass
+
+
+def getDefaultProductPrefix():
+ global ProductPrefix
+ return ProductPrefix
+
+
+def setDefaultProductPrefix(prefix):
+ global ProductPrefix
+ ProductPrefix = prefix
+
+
+NetworkLatency = 1.0
+maxLoginWidth = 9.1
+STAND_INDEX = 0
+WALK_INDEX = 1
+RUN_INDEX = 2
+REVERSE_INDEX = 3
+STRAFE_LEFT_INDEX = 4
+STRAFE_RIGHT_INDEX = 5
+ToonStandableGround = 0.707
+ToonSpeedFactor = 1.25
+ToonForwardSpeed = 16.0 * ToonSpeedFactor
+ToonJumpForce = 24.0
+ToonReverseSpeed = 8.0 * ToonSpeedFactor
+ToonRotateSpeed = 80.0 * ToonSpeedFactor
+ToonForwardSlowSpeed = 6.0
+ToonJumpSlowForce = 4.0
+ToonReverseSlowSpeed = 2.5
+ToonRotateSlowSpeed = 33.0
+MickeySpeed = 5.0
+MinnieSpeed = 3.2
+DonaldSpeed = 3.68
+GoofySpeed = 5.2
+PlutoSpeed = 5.5
+ThinkPosHotkey = 'shift-f1'
+PlaceMarkerHotkey = 'f2'
+FriendsListHotkey = 'f7'
+StickerBookHotkey = 'f8'
+OptionsPageHotkey = 'escape'
+ScreenshotHotkey = 'f9'
+SynchronizeHotkey = 'shift-f6'
+QuestsHotkeyOn = 'end'
+QuestsHotkeyOff = 'end-up'
+InventoryHotkeyOn = 'home'
+InventoryHotkeyOff = 'home-up'
+MapHotkeyOn = 'delete'
+MapHotkeyOff = 'delete-up'
+DetectGarbageHotkey = 'shift-f11'
+PrintCamPosHotkey = 'f12'
+GlobalDialogColor = (1,
+ 1,
+ 0.75,
+ 1)
+DefaultBackgroundColor = (0.3,
+ 0.3,
+ 0.3,
+ 1)
+toonBodyScales = {'mouse': 0.6,
+ 'cat': 0.73,
+ 'duck': 0.66,
+ 'rabbit': 0.74,
+ 'horse': 0.85,
+ 'dog': 0.85,
+ 'monkey': 0.68,
+ 'bear': 0.85,
+ 'pig': 0.77}
+toonHeadScales = {'mouse': Point3(1.0),
+ 'cat': Point3(1.0),
+ 'duck': Point3(1.0),
+ 'rabbit': Point3(1.0),
+ 'horse': Point3(1.0),
+ 'dog': Point3(1.0),
+ 'monkey': Point3(1.0),
+ 'bear': Point3(1.0),
+ 'pig': Point3(1.0)}
+legHeightDict = {'s': 1.5,
+ 'm': 2.0,
+ 'l': 2.75}
+torsoHeightDict = {'s': 1.5,
+ 'm': 1.75,
+ 'l': 2.25,
+ 'ss': 1.5,
+ 'ms': 1.75,
+ 'ls': 2.25,
+ 'sd': 1.5,
+ 'md': 1.75,
+ 'ld': 2.25}
+headHeightDict = {'dls': 0.75,
+ 'dss': 0.5,
+ 'dsl': 0.5,
+ 'dll': 0.75,
+ 'cls': 0.75,
+ 'css': 0.5,
+ 'csl': 0.5,
+ 'cll': 0.75,
+ 'hls': 0.75,
+ 'hss': 0.5,
+ 'hsl': 0.5,
+ 'hll': 0.75,
+ 'mls': 0.75,
+ 'mss': 0.5,
+ 'rls': 0.75,
+ 'rss': 0.5,
+ 'rsl': 0.5,
+ 'rll': 0.75,
+ 'fls': 0.75,
+ 'fss': 0.5,
+ 'fsl': 0.5,
+ 'fll': 0.75,
+ 'pls': 0.75,
+ 'pss': 0.5,
+ 'psl': 0.5,
+ 'pll': 0.75,
+ 'bls': 0.75,
+ 'bss': 0.5,
+ 'bsl': 0.5,
+ 'bll': 0.75,
+ 'sls': 0.75,
+ 'sss': 0.5,
+ 'ssl': 0.5,
+ 'sll': 0.75}
+RandomButton = 'Randomize'
+TypeANameButton = 'Type Name'
+PickANameButton = 'Pick-A-Name'
+NameShopSubmitButton = 'Submit'
+RejectNameText = 'That name is not allowed. Please try again.'
+WaitingForNameSubmission = 'Submitting your name...'
+NameShopNameMaster = 'NameMasterEnglish.txt'
+NameShopPay = 'Subscribe Now!'
+NameShopPlay = 'Free Trial'
+NameShopOnlyPaid = 'Only paid users\nmay name their Toons.\nUntil you subscribe\nyour name will be\n'
+NameShopContinueSubmission = 'Continue Submission'
+NameShopChooseAnother = 'Choose Another Name'
+NameShopToonCouncil = 'The Toon Council\nwill review your\nname. ' + 'Review may\ntake a few days.\nWhile you wait\nyour name will be\n '
+PleaseTypeName = 'Please type your name:'
+AllNewNames = 'All new names\nmust be approved\nby the Toon Council.'
+NameShopNameRejected = 'The name you\nsubmitted has\nbeen rejected.'
+NameShopNameAccepted = 'Congratulations!\nThe name you\nsubmitted has\nbeen accepted!'
+NoPunctuation = "You can't use punctuation marks in your name!"
+PeriodOnlyAfterLetter = 'You can use a period in your name, but only after a letter.'
+ApostropheOnlyAfterLetter = 'You can use an apostrophe in your name, but only after a letter.'
+NoNumbersInTheMiddle = 'Numeric digits may not appear in the middle of a word.'
+ThreeWordsOrLess = 'Your name must be three words or fewer.'
+CopyrightedNames = ('mickey',
+ 'mickey mouse',
+ 'mickeymouse',
+ 'minnie',
+ 'minnie mouse',
+ 'minniemouse',
+ 'donald',
+ 'donald duck',
+ 'donaldduck',
+ 'pluto',
+ 'goofy')
+GuildUpdateMembersEvent = 'guildUpdateMembersEvent'
+GuildInvitationEvent = 'guildInvitationEvent'
+GuildAcceptInviteEvent = 'guildAcceptInviteEvent'
+GuildRejectInviteEvent = 'guildRejectInviteEvent'
+AvatarFriendAddEvent = 'avatarFriendAddEvent'
+AvatarNewFriendAddEvent = 'avatarNewFriendAddEvent'
+AvatarFriendUpdateEvent = 'avatarFriendUpdateEvent'
+AvatarFriendRemoveEvent = 'avatarFriendRemoveEvent'
+PlayerFriendAddEvent = 'playerFriendAddEvent'
+PlayerFriendUpdateEvent = 'playerFriendUpdateEvent'
+PlayerFriendRemoveEvent = 'playerFriendRemoveEvent'
+AvatarFriendConsideringEvent = 'avatarFriendConsideringEvent'
+AvatarFriendInvitationEvent = 'avatarFriendInvitationEvent'
+AvatarFriendRejectInviteEvent = 'avatarFriendRejectInviteEvent'
+AvatarFriendRetractInviteEvent = 'avatarFriendRetractInviteEvent'
+AvatarFriendRejectRemoveEvent = 'avatarFriendRejectRemoveEvent'
+PlayerFriendInvitationEvent = 'playerFriendInvitationEvent'
+PlayerFriendRejectInviteEvent = 'playerFriendRejectInviteEvent'
+PlayerFriendRetractInviteEvent = 'playerFriendRetractInviteEvent'
+PlayerFriendRejectRemoveEvent = 'playerFriendRejectRemoveEvent'
+PlayerFriendNewSecretEvent = 'playerFriendNewSecretEvent'
+PlayerFriendRejectNewSecretEvent = 'playerFriendRejectNewSecretEvent'
+PlayerFriendRejectUseSecretEvent = 'playerFriendRejectUseSecretEvent'
+WhisperIncomingEvent = 'whisperIncomingEvent'
+ChatFeedback_PassedBlacklist = 32
+ChatFeedback_Whitelist = 64
+ChatFeedback_OpenChat = 128
+AccessUnknown = 0
+AccessVelvetRope = 1
+AccessFull = 2
+AccessInvalid = 3
+AvatarPendingCreate = -1
+AvatarSlotUnavailable = -2
+AvatarSlotAvailable = -3
diff --git a/otp/otpbase/OTPLauncherGlobals.py b/otp/otpbase/OTPLauncherGlobals.py
new file mode 100644
index 0000000..832f622
--- /dev/null
+++ b/otp/otpbase/OTPLauncherGlobals.py
@@ -0,0 +1,7 @@
+NonErrorExitStateStart = 100
+ExitPurchase = 100
+ExitSetParentPassword = 101
+ExitEnableChat = 102
+ExitTimeExpired = 103
+ExitUpsell = 104
+NonErrorExitStateEnd = 199
diff --git a/otp/otpbase/OTPLocalizer.py b/otp/otpbase/OTPLocalizer.py
new file mode 100644
index 0000000..b651766
--- /dev/null
+++ b/otp/otpbase/OTPLocalizer.py
@@ -0,0 +1,45 @@
+from pandac.libpandaexpressModules import *
+import string
+import types
+try:
+ language = getConfigExpress().GetString('language', 'english')
+ checkLanguage = getConfigExpress().GetBool('check-language', 0)
+except:
+ language = simbase.config.GetString('language', 'english')
+ checkLanguage = simbase.config.GetBool('check-language', 0)
+
+def getLanguage():
+ return language
+
+
+print 'OTPLocalizer: Running in language: %s' % language
+if language == 'english':
+ _languageModule = 'otp.otpbase.OTPLocalizer' + string.capitalize(language)
+else:
+ checkLanguage = 1
+ _languageModule = 'otp.otpbase.OTPLocalizer_' + language
+print 'from ' + _languageModule + ' import *'
+from otp.otpbase.OTPLocalizerEnglish import *
+if checkLanguage:
+ l = {}
+ g = {}
+ englishModule = __import__('otp.otpbase.OTPLocalizerEnglish', g, l)
+ foreignModule = __import__(_languageModule, g, l)
+ for key, val in englishModule.__dict__.items():
+ if not foreignModule.__dict__.has_key(key):
+ print 'WARNING: Foreign module: %s missing key: %s' % (_languageModule, key)
+ locals()[key] = val
+ elif isinstance(val, types.DictType):
+ fval = foreignModule.__dict__.get(key)
+ for dkey, dval in val.items():
+ if not fval.has_key(dkey):
+ print 'WARNING: Foreign module: %s missing key: %s.%s' % (_languageModule, key, dkey)
+ fval[dkey] = dval
+
+ for dkey in fval.keys():
+ if not val.has_key(dkey):
+ print 'WARNING: Foreign module: %s extra key: %s.%s' % (_languageModule, key, dkey)
+
+ for key in foreignModule.__dict__.keys():
+ if not englishModule.__dict__.has_key(key):
+ print 'WARNING: Foreign module: %s extra key: %s' % (_languageModule, key)
diff --git a/otp/otpbase/OTPLocalizerEnglish.py b/otp/otpbase/OTPLocalizerEnglish.py
new file mode 100644
index 0000000..41a6819
--- /dev/null
+++ b/otp/otpbase/OTPLocalizerEnglish.py
@@ -0,0 +1,2893 @@
+import string
+from otp.otpbase.OTPLocalizerEnglishProperty import *
+lTheBrrrgh = 'The Brrrgh'
+lDaisyGardens = 'Daisy Gardens'
+lDonaldsDock = "Donald's Dock"
+lDonaldsDreamland = "Donald's Dreamland"
+lMinniesMelodyland = "Minnie's Melodyland"
+lToontownCentral = 'Toontown Central'
+lGoofySpeedway = 'Goofy Speedway'
+lOutdoorZone = "Chip 'n Dale's Acorn Acres"
+lGolfZone = "Chip 'n Dale's MiniGolf"
+lCancel = 'Cancel'
+lClose = 'Close'
+lOK = 'OK'
+lNext = 'Next'
+lNo = 'No'
+lQuit = 'Quit'
+lYes = 'Yes'
+Cog = 'Cog'
+Cogs = 'Cogs'
+DialogOK = lOK
+DialogCancel = lCancel
+DialogYes = lYes
+DialogNo = lNo
+DialogDoNotShowAgain = 'Do Not\nShow Again'
+WhisperNoLongerFriend = '%s left your friends list.'
+WhisperNowSpecialFriend = '%s is now your True Friend!'
+WhisperComingToVisit = '%s is coming to visit you.'
+WhisperFailedVisit = '%s tried to visit you.'
+WhisperTargetLeftVisit = '%s has gone somewhere else. Try again!'
+WhisperGiveupVisit = "%s couldn't find you because you're moving around!"
+WhisperIgnored = '%s is ignoring you!'
+TeleportGreeting = 'Hi, %s.'
+WhisperFriendComingOnline = '%s is coming online!'
+WhisperFriendLoggedOut = '%s has logged out.'
+WhisperPlayerOnline = '%s logged into %s'
+WhisperPlayerOffline = '%s is offline.'
+WhisperUnavailable = 'That player is no longer available for whispers.'
+DialogSpecial = 'ooo'
+DialogExclamation = '!'
+DialogQuestion = '?'
+ChatInputNormalSayIt = 'Say It'
+ChatInputNormalCancel = lCancel
+ChatInputNormalWhisper = 'Whisper'
+ChatInputWhisperLabel = 'To %s'
+SCEmoteNoAccessMsg = 'You do not have access\nto this emotion yet.'
+SCEmoteNoAccessOK = lOK
+ParentLogin = 'Parent Login'
+ParentPassword = 'Parent Account Password'
+ChatGarblerDefault = ['blah']
+ChatManagerChat = 'Chat'
+ChatManagerWhisperTo = 'Whisper to:'
+ChatManagerWhisperToName = 'Whisper To:\n%s'
+ChatManagerCancel = lCancel
+ChatManagerWhisperOffline = '%s is offline.'
+OpenChatWarning = 'To become True Friends with somebody, click on them, and select "True Friends" from the detail panel.\n\nSpeedChat Plus can also be enabled, which allow users to chat by typing words found in the Disney SpeedChat Plus dictionary.\n\nTo activate these features or to learn more, exit Toontown and then click on Membership and select Manage Account. Log in to edit your "Community Settings."\n\nIf you are under 18, you need a Parent Account to manage these settings.'
+OpenChatWarningOK = lOK
+UnpaidChatWarning = 'Once you have subscribed, you can use this button to chat with your friends using the keyboard. Until then, you should chat with other Toons using SpeedChat.'
+UnpaidChatWarningPay = 'Subscribe'
+UnpaidChatWarningContinue = 'Continue Free Trial'
+PaidNoParentPasswordWarning = 'Use this button to chat with your friends by using the keyboard, enable it through your Account Manager on the Toontown Web site. Until then, you can chat by using SpeedChat.'
+UnpaidNoParentPasswordWarning = 'This is for SpeedChat Plus, which allows users to chat by typing words found in the Disney SpeedChat Plus dictionary. To activate this feature, exit Toontown and click on Membership. Select Manage Account and log in to edit your "Community Settings." If you are under 18, you need a Parent Account to manage these settings.'
+PaidNoParentPasswordWarningSet = 'Update Chat Settings'
+PaidNoParentPasswordWarningContinue = 'Continue Playing Game'
+PaidParentPasswordUKWarning = 'Once you have Enabled Chat, you can enable this button to chat with your friends using the keyboard. Until then, you should chat with other Toons using SpeedChat.'
+PaidParentPasswordUKWarningSet = 'Enable Chat Now!'
+PaidParentPasswordUKWarningContinue = 'Continue Playing Game'
+NoSecretChatWarningTitle = 'Parental Controls'
+NoSecretChatWarning = 'To chat with a friend, the True Friends feature must first be enabled. Kids, have your parent visit the Toontown Web site to learn about True Friends.'
+RestrictedSecretChatWarning = 'To get or enter a True Friend Code, log in with the Parent Account. You can disable this prompt by changing your True Friends options.'
+NoSecretChatWarningOK = lOK
+NoSecretChatWarningCancel = lCancel
+NoSecretChatWarningWrongPassword = "That's not the correct Parent Account. Please log in with the Parent Account that is linked to this account."
+NoSecretChatAtAllTitle = 'Open Chat With True Friends'
+NoSecretChatAtAll = 'Open Chat with True Friends allows real-life friends to chat openly with each other by means of a True Friend Code that must be shared outside of the game.\n\nTo activate these features or to learn more, exit Toontown and then click on Membership and select Manage Account. Log in to edit your "Community Settings." If you are under 18, you need a Parent Account to manage these settings.'
+NoSecretChatAtAllAndNoWhitelistTitle = 'Chat button'
+NoSecretChatAtAllAndNoWhitelist = 'You can use the blue Chat button to communicate with other Toons by using Speechat Plus or Open Chat with True Friends.\n\nSpeedchat Plus is a form of type chat that allows users to communicate by using the Disney SpeedChat Plus dictionary.\n\nOpen Chat with True Friends allows real-life friends to chat openly with each other by means of a True Friend Code that must be shared outside of the game.\n\nTo activate these features or to learn more, exit Toontown and then click on Membership and select Manage Account. Log in to edit your "Community Settings." If you are under 18, you need a Parent Account to manage these settings.'
+NoSecretChatAtAllOK = lOK
+ChangeSecretFriendsOptions = 'Change True Friends Options'
+ChangeSecretFriendsOptionsWarning = '\nPlease enter the Parent Account Password to change your True Friends options.'
+ActivateChatTitle = 'True Friends Options'
+WhisperToFormat = 'To %s %s'
+WhisperToFormatName = 'To %s'
+WhisperFromFormatName = '%s whispers'
+ThoughtOtherFormatName = '%s thinks'
+ThoughtSelfFormatName = 'You think'
+from pandac.PandaModules import TextProperties
+from pandac.PandaModules import TextPropertiesManager
+shadow = TextProperties()
+shadow.setShadow(-0.025, -0.025)
+shadow.setShadowColor(0, 0, 0, 1)
+TextPropertiesManager.getGlobalPtr().setProperties('shadow', shadow)
+red = TextProperties()
+red.setTextColor(1, 0, 0, 1)
+TextPropertiesManager.getGlobalPtr().setProperties('red', red)
+green = TextProperties()
+green.setTextColor(0, 1, 0, 1)
+TextPropertiesManager.getGlobalPtr().setProperties('green', green)
+yellow = TextProperties()
+yellow.setTextColor(1, 1, 0, 1)
+TextPropertiesManager.getGlobalPtr().setProperties('yellow', yellow)
+midgreen = TextProperties()
+midgreen.setTextColor(0.2, 1, 0.2, 1)
+TextPropertiesManager.getGlobalPtr().setProperties('midgreen', midgreen)
+blue = TextProperties()
+blue.setTextColor(0, 0, 1, 1)
+TextPropertiesManager.getGlobalPtr().setProperties('blue', blue)
+white = TextProperties()
+white.setTextColor(1, 1, 1, 1)
+TextPropertiesManager.getGlobalPtr().setProperties('white', white)
+black = TextProperties()
+black.setTextColor(0, 0, 0, 1)
+TextPropertiesManager.getGlobalPtr().setProperties('black', black)
+grey = TextProperties()
+grey.setTextColor(0.5, 0.5, 0.5, 1)
+TextPropertiesManager.getGlobalPtr().setProperties('grey', grey)
+ActivateChat = "True Friends allows one member to chat with another member only by means of a True Friend Code that must be communicated outside of the game. True Friends is not moderated or supervised.\n\nPlease choose one of Toontown's True Friends options:\n\n \x01shadow\x01No True Friends\x02 - Ability to make True Friends is disabled.\n This offers the highest level of control.\n\n \x01shadow\x01Restricted True Friends\x02 - Requires the Parent Account Password to make\n each new True Friend.\n\n \x01shadow\x01Unrestricted True Friends\x02 - Once enabled with the Parent Account Password,\n it is not required to supply the Parent Account Password to make each new\n True Friend. \x01red\x01This option is not recommended for children under 13.\x02\n\n\n\n\n\n\nBy enabling the True Friends feature, you acknowledge that there are some risks inherent in the True Friends feature and that you have been informed of, and agree to accept, any such risks."
+ActivateChatYes = 'Update'
+ActivateChatNo = lCancel
+ActivateChatMoreInfo = 'More Info'
+ActivateChatPrivacyPolicy = 'Privacy Policy'
+ActivateChatPrivacyPolicy_Button1A = 'Version 1'
+ActivateChatPrivacyPolicy_Button1K = 'Version 1'
+ActivateChatPrivacyPolicy_Button2A = 'Version 2'
+ActivateChatPrivacyPolicy_Button2K = 'Version 2'
+PrivacyPolicyText_1A = [' ']
+PrivacyPolicyText_1K = [' ']
+PrivacyPolicyText_2A = [' ']
+PrivacyPolicyText_2K = [' ']
+PrivacyPolicyText_Intro = [' ']
+PrivacyPolicyClose = lClose
+SecretFriendsInfoPanelOk = lOK
+SecretFriendsInfoPanelClose = lClose
+SecretFriendsInfoPanelText = ['\nThe Open Chat with True Friends Feature\n\nThe Open Chat with True Friends feature enables a member to chat directly with another member within Disney\'s Toontown Online (the "Service") once the members establish a True Friends connection. When your child attempts to use the Open Chat with True Friends feature, we will require that you indicate your consent to your child\'s use of this feature by entering your Parent Account Password. Here is a detailed description of the process of creating an Open Chat with True Friends connection between members whom we will call "Sally" and "Mike."\n1. Sally\'s parent and Mike\'s parent each enable the Open Chat with True Friends feature by entering their respective Parent Account Passwords either (a) in the Account Options areas within the Service, or (b) when prompted within the game by a Parental Controls pop-up.\n2. Sally requests a True Friend Code (described below) from within the Service.\n',
+ "\n3. Sally's True Friend Code is communicated to Mike outside of the Service. (Sally's True Friend Code may be communicated to Mike either directly by Sally, or indirectly through Sally's disclosure of the True Friend Code to another person.)\n4. Mike submits Sally's True Friend Code to the Service within 48 hours of the time that Sally requested the True Friend Code from the Service.\n5. The Service then notifies Mike that Sally has become Mike's True Friend. The Service similarly notifies Sally that Mike has become Sally's True Friend.\n6. Sally and Mike can now open chat directly with each other until either one chooses to terminate the other as a True Friend, or until the Open Chat with True Friends feature is disabled for either Sally or Mike by their respective parent. The True Friends connection can thus be disabled anytime by either: (a) a member removing the True Friend from his or her friends list (as described in the Service); or, (b) the parent of that member disabling the Open Chat with ",
+ "\nTrue Friends feature by going to the Account Options area within the Service and following the steps set forth there.\n\nA True Friend Code is a computer-generated random code assigned to a particular member. The True Friend Code must be used to activate a True Friend connection within 48 hours of the time that the member requests the True Friend Code; otherwise, the True Friend Code expires and cannot be used. Moreover, a single True Friend Code can only be used to establish one True Friend connection. To make additional True Friend connections, a member must request an additional True Friend Code for each additional True Friend.\n\nTrue Friendships do not transfer. For example, if Sally becomes a True Friend of Mike, and Mike becomes a True Friend of Jessica, Sally does not automatically become Jessica's True Friend. In order for Sally and Jessica to\n",
+ '\nbecome True Friends, one of them must request a new True Friend Code from the Service and communicate it to the other.\n\nTrue Friends communicate with one another in a free-form interactive open chat. The content of this chat is directly entered by the participating member and is processed through the Service, which is operated by the Walt Disney Internet Group ("WDIG"), 500 S. Buena Vista St., Burbank, CA 91521-7691. While we advise members not to exchange personal information such as first and last names, e-mail addresses, postal addresses, or phone numbers while using Open Chat with True Friends, we cannot guarantee that such exchanges of personal information will not happen. Although the True Friends chat is automatically filtered for most bad words, Open Chat with True Friends may be moderated, and Disney reserves the right to moderate any part of the Service that Disney,\n',
+ "\nin its sole and absolute discretion, deems necessary. However, because Open Chat with True Friends will not always be moderated, if the Parent Account allows a child to use his or her account with the Open Chat with True Friends feature enabled, we strongly encourage parents to supervise their child or children while they play in the Service. By enabling the Open Chat with True Friends feature, the Parent Account acknowledges that there are some risks inherent in the Open Chat with True Friends feature and that the Parent Account has been informed of, and agrees to accept, any such risks, whether foreseeable or otherwise. \n\nWDIG does not use the content of True Friends chat for any purpose other than communicating that content to the member's true friend, and does not disclose that content to any third party except: (1) if required by law, for example, to comply with a court order or subpoena; (2) to enforce the Terms of Use\n",
+ "\napplicable to the Service (which may be accessed on the home page of the Service); or, (3) to protect the safety and security of Members of the Service and the Service itself. In accordance with the Children's Online Privacy Protection Act, we are prohibited from conditioning, and do not condition, a child's participation in any activity (including Open Chat with True Friends) on the child's disclosing more personal information than is reasonably necessary to participate in such activity.\n\nIn addition, as noted above, we recognize the right of a parent to refuse to permit us to continue to allow a child to use the True Friends feature. By enabling the Open Chat with True Friends feature, you acknowledge that there are some risks inherent in the ability of members to open chat with one another through the Open Chat with True Friends feature, and that you have been informed of, and agree to accept, any such risks, whether foreseeable or otherwise.\n"]
+LeaveToPay = 'Click Purchase to exit the game and buy a Membership at toontown.com'
+LeaveToPayYes = 'Purchase'
+LeaveToPayNo = lCancel
+LeaveToSetParentPassword = 'In order to set parent account password, the game will exit to the Toontown website.'
+LeaveToSetParentPasswordYes = 'Set Password'
+LeaveToSetParentPasswordNo = lCancel
+LeaveToEnableChatUK = 'In order to enable chat, the game will exit to the Toontown website.'
+LeaveToEnableChatUKYes = 'Enable Chat'
+LeaveToEnableChatUKNo = lCancel
+ChatMoreInfoOK = lOK
+SecretChatDeactivated = 'The "True Friends" feature has been disabled.'
+RestrictedSecretChatActivated = 'The "Restricted True Friends" feature has been enabled!'
+SecretChatActivated = 'The "Unrestricted True Friends" feature has been enabled!'
+SecretChatActivatedOK = lOK
+SecretChatActivatedChange = 'Change Options'
+ProblemActivatingChat = 'Oops! We were unable to activate the "True Friends" chat feature.\n\n%s\n\nPlease try again later.'
+ProblemActivatingChatOK = lOK
+MultiPageTextFrameNext = lNext
+MultiPageTextFramePrev = 'Previous'
+MultiPageTextFramePage = 'Page %s/%s'
+GuiScreenToontownUnavailable = 'The server appears to be temporarily unavailable, still trying...'
+GuiScreenCancel = lCancel
+CreateAccountScreenUserName = 'Account Name'
+CreateAccountScreenPassword = 'Password'
+CreateAccountScreenConfirmPassword = 'Confirm Password'
+CreateAccountScreenCancel = lCancel
+CreateAccountScreenSubmit = 'Submit'
+CreateAccountScreenConnectionErrorSuffix = '.\n\nPlease try again later.'
+CreateAccountScreenNoAccountName = 'Please enter an account name.'
+CreateAccountScreenAccountNameTooShort = 'Your account name must be at least %s characters long. Please try again.'
+CreateAccountScreenPasswordTooShort = 'Your password must be at least %s characters long. Please try again.'
+CreateAccountScreenPasswordMismatch = 'The passwords you typed did not match. Please try again.'
+CreateAccountScreenUserNameTaken = 'That user name is already taken. Please try again.'
+CreateAccountScreenInvalidUserName = 'Invalid user name.\nPlease try again.'
+CreateAccountScreenUserNameNotFound = 'User name not found.\nPlease try again or create a new account.'
+CRConnecting = 'Connecting...'
+CRNoConnectTryAgain = 'Could not connect to %s:%s. Try again?'
+CRNoConnectProxyNoPort = 'Could not connect to %s:%s.\n\nYou are communicating to the internet via a proxy, but your proxy does not permit connections on port %s.\n\nYou must open up this port, or disable your proxy, in order to play. If your proxy has been provided by your ISP, you must contact your ISP to request them to open up this port.'
+CRMissingGameRootObject = 'Missing some root game objects. (May be a failed network connection).\n\nTry again?'
+CRNoDistrictsTryAgain = 'No Districts are available. Try again?'
+CRRejectRemoveAvatar = 'The avatar was not able to be deleted, try again another time.'
+CRLostConnection = 'Your internet connection to the servers has been unexpectedly broken.'
+CRBootedReasons = {1: 'An unexpected problem has occurred. Your connection has been lost, but you should be able to connect again and go right back into the game.',
+ 100: 'You have been disconnected because someone else just logged in using your account on another computer.',
+ 120: 'You have been disconnected because of a problem with your authorization to use keyboard chat.',
+ 122: 'There has been an unexpected problem logging you in. Please contact customer support.',
+ 125: 'Your installed files appear to be invalid. Please use the Play button on the official website to run.',
+ 126: 'You are not authorized to use administrator privileges.',
+ 127: 'A problem has occurred with your Toon. Please contact Member Services via phone or email and reference Error Code 127. Thank you.',
+ 151: 'You have been logged out by an administrator working on the servers.',
+ 152: "There has been a reported violation of our Terms of Use connected to '%(name)s'. For more details, please review the message sent to the e-mail address associated with '%(name)s'.",
+ 153: 'The district you were playing on has been reset. Everyone who was playing on that district has been disconnected. However, you should be able to connect again and go right back into the game.',
+ 288: 'Sorry, you have used up all of your available minutes this month.',
+ 349: 'Sorry, you have used up all of your available minutes this month.'}
+CRBootedReasonUnknownCode = 'An unexpected problem has occurred (error code %s). Your connection has been lost, but you should be able to connect again and go right back into the game.'
+CRTryConnectAgain = '\n\nTry to connect again?'
+CRToontownUnavailable = 'The server appears to be temporarily unavailable, still trying...'
+CRToontownUnavailableCancel = lCancel
+CRNameCongratulations = 'CONGRATULATIONS!!'
+CRNameAccepted = 'Your name has been\napproved by the Toon Council.\n\nFrom this day forth\nyou will be named\n"%s"'
+CRServerConstantsProxyNoPort = 'Unable to contact %s.\n\nYou are communicating to the internet via a proxy, but your proxy does not permit connections on port %s.\n\nYou must open up this port, or disable your proxy, in order to play. If your proxy has been provided by your ISP, you must contact your ISP to request them to open up this port.'
+CRServerConstantsProxyNoCONNECT = 'Unable to contact %s.\n\nYou are communicating to the internet via a proxy, but your proxy does not support the CONNECT method.\n\nYou must enable this capability, or disable your proxy, in order to play. If your proxy has been provided by your ISP, you must contact your ISP to request them to enable this capability.'
+CRServerConstantsTryAgain = 'Unable to contact %s.\n\nThe account server might be temporarily down, or there might be some problem with your internet connection.\n\nTry again?'
+CRServerDateTryAgain = 'Could not get server date from %s. Try again?'
+AfkForceAcknowledgeMessage = 'Your toon got sleepy and went to bed.'
+PeriodTimerWarning = 'Your available time is almost over!'
+PeriodForceAcknowledgeMessage = 'Sorry, you have used up all of your available time. Please exit to purchase more.'
+CREnteringToontown = 'Entering...'
+DownloadWatcherUpdate = 'Downloading %s'
+DownloadWatcherInitializing = 'Download Initializing...'
+LoginScreenUserName = 'Account Name'
+LoginScreenPassword = 'Password'
+LoginScreenLogin = 'Login'
+LoginScreenCreateAccount = 'Create Account'
+LoginScreenQuit = lQuit
+LoginScreenLoginPrompt = 'Please enter a user name and password.'
+LoginScreenBadPassword = 'Bad password.\nPlease try again.'
+LoginScreenInvalidUserName = 'Invalid user name.\nPlease try again.'
+LoginScreenUserNameNotFound = 'User name not found.\nPlease try again or create a new account.'
+LoginScreenPeriodTimeExpired = 'Sorry, you have used up all of your available time.'
+LoginScreenNoNewAccounts = 'Sorry, we are not accepting new accounts at this time.'
+LoginScreenTryAgain = 'Try Again'
+DialogSpecial = 'ooo'
+DialogExclamation = '!'
+DialogQuestion = '?'
+DialogLength1 = 6
+DialogLength2 = 12
+DialogLength3 = 20
+GlobalSpeedChatName = 'SpeedChat'
+SCMenuPromotion = 'PROMOTIONAL'
+SCMenuElection = 'ELECTION'
+SCMenuEmotions = 'EMOTIONS'
+SCMenuCustom = 'MY PHRASES'
+SCMenuResistance = 'UNITE!'
+SCMenuPets = 'PETS'
+SCMenuPetTricks = 'TRICKS'
+SCMenuCog = 'COG SPEAK'
+SCMenuHello = 'HELLO'
+SCMenuBye = 'GOODBYE'
+SCMenuHappy = 'HAPPY'
+SCMenuSad = 'SAD'
+SCMenuFriendly = 'FRIENDLY'
+SCMenuSorry = 'SORRY'
+SCMenuStinky = 'STINKY'
+SCMenuPlaces = 'PLACES'
+SCMenuToontasks = 'TOONTASKS'
+SCMenuBattle = 'BATTLE'
+SCMenuGagShop = 'GAG SHOP'
+SCMenuFactory = 'FACTORY'
+SCMenuKartRacing = 'RACING'
+SCMenuFactoryMeet = 'MEET'
+SCMenuCFOBattle = 'C.F.O.'
+SCMenuCFOBattleCranes = 'CRANES'
+SCMenuCFOBattleGoons = 'GOONS'
+SCMenuCJBattle = 'CHIEF JUSTICE'
+SCMenuCEOBattle = 'C.E.O.'
+SCMenuGolf = 'GOLF'
+SCMenuWhiteList = 'WHITELIST'
+SCMenuPlacesPlayground = 'PLAYGROUND'
+SCMenuPlacesEstate = 'ESTATE'
+SCMenuPlacesCogs = 'COGS'
+SCMenuPlacesWait = 'WAIT'
+SCMenuFriendlyYou = 'You...'
+SCMenuFriendlyILike = 'I like...'
+SCMenuPlacesLetsGo = "Let's go..."
+SCMenuToontasksMyTasks = 'MY TASKS'
+SCMenuToontasksYouShouldChoose = 'I think you should choose...'
+SCMenuToontasksINeedMore = 'I need more...'
+SCMenuBattleGags = 'GAGS'
+SCMenuBattleTaunts = 'TAUNTS'
+SCMenuBattleStrategy = 'STRATEGY'
+SCMenuBoardingGroup = 'BOARDING'
+SCMenuParties = 'PARTIES'
+SCMenuAprilToons = "APRIL TOONS'"
+SCMenuSingingGroup = 'SINGING'
+SCMenuCarol = 'CAROLING'
+SCMenuSillyHoliday = 'SILLY METER'
+SCMenuVictoryParties = 'VICTORY PARTIES'
+SCMenuSellbotNerf = 'STORM SELLBOT'
+SCMenuJellybeanJam = 'JELLYBEAN WEEK'
+SCMenuHalloween = 'HALLOWEEN'
+SCMenuWinter = 'WINTER'
+SCMenuSellbotInvasion = 'SELLBOT INVASION'
+SCMenuFieldOffice = 'FIELD OFFICES'
+SCMenuIdesOfMarch = 'GREEN'
+FriendSecretNeedsPasswordWarningTitle = 'Parental Controls'
+FriendSecretNeedsParentLoginWarning = 'To get or enter a True Friend Code, log in with the Parent Account. You can disable this prompt by changing your True Friend options.'
+FriendSecretNeedsPasswordWarning = 'To get or enter a True Friend Code, you must enter the Parent Account Password. You can disable this prompt by changing your True Friends options.'
+FriendSecretNeedsPasswordWarningOK = lOK
+FriendSecretNeedsPasswordWarningCancel = lCancel
+FriendSecretNeedsPasswordWarningWrongUsername = "That's not the correct username. Please enter the username of the parental account. This is not the same username used to play the game."
+FriendSecretNeedsPasswordWarningWrongPassword = "That's not the correct password. Please enter the password of the parental account. This is not the same password used to play the game."
+FriendSecretIntro = "If you are playing Disney's Toontown Online with someone you know in the real world, you can become True Friends. You can chat using the keyboard with your True Friends. Other Toons won't understand what you're saying.\n\nYou do this by getting a True Friend Code. Tell the True Friend Code to your friend, but not to anyone else. When your friend types in your True Friend Code on his or her screen, you'll be True Friends in Toontown!"
+FriendSecretGetSecret = 'Get a True Friend Code'
+FriendSecretEnterSecret = 'If you have a True Friend Code from someone you know, type it here.'
+FriendSecretOK = lOK
+FriendSecretEnter = 'Enter True Friend Code'
+FriendSecretCancel = lCancel
+FriendSecretGettingSecret = 'Getting True Friend Code. . .'
+FriendSecretGotSecret = "Here is your new True Friend Code. Be sure to write it down!\n\nYou may give this True Friend Code to one person only. Once someone types in your True Friend Code, it will not work for anyone else. If you want to give a True Friend Code to more than one person, get another True Friend Code.\n\nThe True Friend Code will only work for the next two days. Your friend will have to type it in before it goes away, or it won't work.\n\nYour True Friend Code is:"
+FriendSecretTooMany = "Sorry, you can't have any more True Friend Codes today. You've already had more than your fair share!\n\nTry again tomorrow."
+FriendSecretTryingSecret = 'Trying True Friend Code. . .'
+FriendSecretEnteredSecretSuccess = 'You are now True Friends with %s!'
+FriendSecretTimeOut = 'Sorry, secrets are not working right now.'
+FriendSecretEnteredSecretUnknown = "That's not anyone's True Friend Code. Are you sure you spelled it correctly?\n\nIf you did type it correctly, it may have expired. Ask your friend to get a new True Friend Code for you (or get a new one yourself and give it to your friend)."
+FriendSecretEnteredSecretFull = "You can't be friends with %s because one of you has too many friends on your friends list."
+FriendSecretEnteredSecretFullNoName = "You can't be friends because one of you has too many friends on your friends list."
+FriendSecretEnteredSecretSelf = 'You just typed in your own True Friend Code! Now no one else can use that True Friend Code.'
+FriendSecretEnteredSecretWrongProduct = "You have entered the wrong type of True Friend Code.\nThis game uses codes that begin with '%s'."
+FriendSecretNowFriends = 'You are now True Friends with %s!'
+FriendSecretNowFriendsNoName = 'You are now True Friends!'
+FriendSecretDetermineSecret = 'What type of True Friend would you like to make?'
+FriendSecretDetermineSecretAvatar = 'Avatar'
+FriendSecretDetermineSecretAvatarRollover = 'A friend only in this game'
+FriendSecretDetermineSecretAccount = 'Account'
+FriendSecretDetermineSecretAccountRollover = 'A friend across the Disney.com network'
+GuildMemberTitle = 'Member Options'
+GuildMemberPromote = 'Make Officer'
+GuildMemberPromoteInvite = 'Make Veteran'
+GuildMemberDemoteInvite = 'Demote to Veteran'
+GuildMemberGM = 'Make Guildmaster'
+GuildMemberGMConfirm = 'Confirm'
+GuildMemberDemote = 'Demote to Member'
+GuildMemberKick = 'Remove Member'
+GuildMemberCancel = lCancel
+GuildMemberOnline = 'has come online.'
+GuildMemberOffline = 'has gone offline.'
+GuildPrefix = '(G):'
+GuildNewMember = 'New Guild Member'
+GuildMemberUnknown = 'Unknown'
+GuildMemberGMMessage = 'Warning! Would you like to give up leadership of your guild and make %s your guild master?\n\nYou will become an officer'
+GuildInviteeOK = lOK
+GuildInviteeNo = lNo
+GuildInviteeInvitation = '%s is inviting you to join %s.'
+GuildRedeemErrorInvalidToken = 'Sorry, that code is invalid. Please try again.'
+GuildRedeemErrorGuildFull = 'Sorry, this guild has too many members already.'
+FriendInviteeTooManyFriends = '%s would like to be your friend, but you already have too many friends on your list!'
+FriendInviteeInvitation = '%s would like to be your friend.'
+FriendInviteeInvitationPlayer = "%s's player would like to be your friend."
+FriendNotifictation = '%s is now your friend.'
+FriendInviteeOK = lOK
+FriendInviteeNo = lNo
+GuildInviterWentAway = '%s is no longer present.'
+GuildInviterAlready = '%s is already in a guild.'
+GuildInviterBusy = '%s is busy right now.'
+GuildInviterNotYet = 'Invite %s to join your guild?'
+GuildInviterCheckAvailability = 'Inviting %s to join your guild.'
+GuildInviterOK = lOK
+GuildInviterNo = lNo
+GuildInviterCancel = lCancel
+GuildInviterYes = lYes
+GuildInviterTooFull = 'Guild has reached maximum size.'
+GuildInviterNo = lNo
+GuildInviterClickToon = 'Click on the pirate you would like to invite.'
+GuildInviterTooMany = 'This is a bug'
+GuildInviterNotAvailable = '%s is busy right now; try again later.'
+GuildInviterGuildSaidNo = '%s has declined your guild invitation.'
+GuildInviterAlreadyInvited = '%s has already been invited.'
+GuildInviterEndGuildship = 'Remove %s from the guild?'
+GuildInviterFriendsNoMore = '%s has left the guild.'
+GuildInviterSelf = 'You are already in the guild!'
+GuildInviterIgnored = '%s is ignoring you.'
+GuildInviterAsking = 'Asking %s to join the guild.'
+GuildInviterGuildSaidYes = '%s has joined the guild!'
+GuildInviterFriendKickedOut = '%s has kicked out %s from the Guild.'
+GuildInviterFriendKickedOutP = '%s have kicked out %s from the Guild.'
+GuildInviterFriendInvited = '%s has invited %s to the Guild.'
+GuildInviterFriendInvitedP = '%s have invited %s to the Guild.'
+GuildInviterFriendPromoted = '%s has promoted %s to the rank of %s.'
+GuildInviterFriendPromotedP = '%s have promoted %s to the rank of %s.'
+GuildInviterFriendDemoted = '%s has demoted %s to the rank of %s.'
+GuildInviterFriendDemotedP = '%s have demoted %s to the rank of %s.'
+GuildInviterFriendPromotedGM = '%s has named %s as the new %s'
+GuildInviterFriendPromotedGMP = '%s have named %s as the new %s'
+GuildInviterFriendDemotedGM = '%s has been named by %s as the new GuildMaster who became the rank of %s'
+GuildInviterFriendDemotedGMP = '%s have been named by %s as the new GuildMaster who beaome the rank of %s'
+FriendOnline = 'has come online.'
+FriendOffline = 'has gone offline.'
+FriendInviterOK = lOK
+FriendInviterCancel = lCancel
+FriendInviterStopBeingFriends = 'Stop being friends'
+FriendInviterConfirmRemove = 'Remove'
+FriendInviterYes = lYes
+FriendInviterNo = lNo
+FriendInviterClickToon = 'Click on the toon you would like to make friends with.'
+FriendInviterTooMany = 'You have too many friends on your list to add another one now. You will have to remove some friends if you want to make friends with %s.'
+FriendInviterToonTooMany = 'You have too many toon friends on your list to add another one now. You will have to remove some toon friends if you want to make friends with %s.'
+FriendInviterPlayerTooMany = 'You have too many player friends on your list to add another one now. You will have to remove some player friends if you want to make friends with %s.'
+FriendInviterNotYet = 'Would you like to make friends with %s?'
+FriendInviterCheckAvailability = 'Seeing if %s is available.'
+FriendInviterNotAvailable = '%s is busy right now; try again later.'
+FriendInviterCantSee = 'This only works if you can see %s.'
+FriendInviterNotOnline = 'This only works if %s is online'
+FriendInviterNotOpen = '%s does not have open chat, use secrets to make friends'
+FriendInviterWentAway = '%s went away.'
+FriendInviterAlready = '%s is already your friend.'
+FriendInviterAlreadyInvited = '%s has already been invited.'
+FriendInviterAskingCog = 'Asking %s to be your friend.'
+FriendInviterAskingPet = '%s jumps around, runs in circles and licks your face.'
+FriendInviterAskingMyPet = '%s is already your BEST friend.'
+FriendInviterEndFriendship = 'Are you sure you want to stop being friends with %s?'
+FriendInviterFriendsNoMore = '%s is no longer your friend.'
+FriendInviterSelf = "You are already 'friends' with yourself!"
+FriendInviterIgnored = '%s is ignoring you.'
+FriendInviterAsking = 'Asking %s to be your friend.'
+FriendInviterFriendSaidYes = 'You are now friends with %s!'
+FriendInviterPlayerFriendSaidYes = "You are now friends with %s's player, %s!"
+FriendInviterFriendSaidNo = '%s said no, thank you.'
+FriendInviterFriendSaidNoNewFriends = "%s isn't looking for new friends right now."
+FriendInviterOtherTooMany = '%s has too many friends already!'
+FriendInviterMaybe = '%s was unable to answer.'
+FriendInviterDown = 'Cannot make friends now.'
+TalkGuild = 'G'
+TalkParty = 'P'
+TalkPVP = 'PVP'
+AntiSpamInChat = '***Spamming***'
+IgnoreConfirmOK = lOK
+IgnoreConfirmCancel = lCancel
+IgnoreConfirmYes = lYes
+IgnoreConfirmNo = lNo
+IgnoreConfirmNotYet = 'Would you like to Ignore %s?'
+IgnoreConfirmAlready = 'You are already ignoring %s.'
+IgnoreConfirmSelf = 'You cannot ignore yourself!'
+IgnoreConfirmNewIgnore = 'You are ignoring %s.'
+IgnoreConfirmEndIgnore = 'You are no longer ignoring %s.'
+IgnoreConfirmRemoveIgnore = 'Stop ignoring %s?'
+EmoteList = ['Wave',
+ 'Happy',
+ 'Sad',
+ 'Angry',
+ 'Sleepy',
+ 'Shrug',
+ 'Dance',
+ 'Think',
+ 'Bored',
+ 'Applause',
+ 'Cringe',
+ 'Confused',
+ 'Belly Flop',
+ 'Bow',
+ 'Banana Peel',
+ 'Resistance Salute',
+ 'Laugh',
+ lYes,
+ lNo,
+ lOK,
+ 'Surprise',
+ 'Cry',
+ 'Delighted',
+ 'Furious',
+ 'Laugh']
+EmoteWhispers = ['%s waves.',
+ '%s is happy.',
+ '%s is sad.',
+ '%s is angry.',
+ '%s is sleepy.',
+ '%s shrugs.',
+ '%s dances.',
+ '%s thinks.',
+ '%s is bored.',
+ '%s applauds.',
+ '%s cringes.',
+ '%s is confused.',
+ '%s does a belly flop.',
+ '%s bows to you.',
+ '%s slips on a banana peel.',
+ '%s gives the resistance salute.',
+ '%s laughs.',
+ "%s says '" + lYes + "'.",
+ "%s says '" + lNo + "'.",
+ "%s says '" + lOK + "'.",
+ '%s is surprised.',
+ '%s is crying.',
+ '%s is delighted.',
+ '%s is furious.',
+ '%s is laughing.']
+EmoteFuncDict = {'Wave': 0,
+ 'Happy': 1,
+ 'Sad': 2,
+ 'Angry': 3,
+ 'Sleepy': 4,
+ 'Shrug': 5,
+ 'Dance': 6,
+ 'Think': 7,
+ 'Bored': 8,
+ 'Applause': 9,
+ 'Cringe': 10,
+ 'Confused': 11,
+ 'Belly Flop': 12,
+ 'Bow': 13,
+ 'Banana Peel': 14,
+ 'Resistance Salute': 15,
+ 'Laugh': 16,
+ lYes: 17,
+ lNo: 18,
+ lOK: 19,
+ 'Surprise': 20,
+ 'Cry': 21,
+ 'Delighted': 22,
+ 'Furious': 23,
+ 'Laugh': 24,
+ 'Sing Note G1': 25,
+ 'Sing Note A': 26,
+ 'Sing Note B': 27,
+ 'Sing Note C': 28,
+ 'Sing Note D': 29,
+ 'Sing Note E': 30,
+ 'Sing Note F': 31,
+ 'Sing Note G2': 32}
+SuitBrushOffs = {'f': ["I'm late for a meeting."],
+ 'p': ['Push off.'],
+ 'ym': ['Yes Man says NO.'],
+ None: ["It's my day off.",
+ "I believe you're in the wrong office.",
+ 'Have your people call my people.',
+ "You're in no position to meet with me.",
+ 'Talk to my assistant.']}
+SuitFaceoffTaunts = {'b': ['Do you have a donation for me?',
+ "I'm going to make you a sore loser.",
+ "I'm going to leave you high and dry.",
+ 'I\'m "A Positive" I\'m going to win.',
+ '"O" don\'t be so "Negative".',
+ "I'm surprised you found me, I'm very mobile.",
+ "I'm going to need to do a quick count on you.",
+ "You're soon going to need a cookie and some juice.",
+ "When I'm through you'll need to lie down.",
+ 'This will only hurt for a second.',
+ "I'm going to make you dizzy.",
+ "Good timing, I'm a pint low."],
+ 'm': ["You don't know who you're mingling with.",
+ 'Ever mingle with the likes of me?',
+ 'Good, it takes two to mingle.',
+ "Let's mingle.",
+ 'This looks like a good place to mingle.',
+ "Well,isn't this cozy?",
+ "You're mingling with defeat.",
+ "I'm going to mingle in your business.",
+ "Are you sure you're ready to mingle?"],
+ 'ms': ['Get ready for a shake down.',
+ 'You had better move out of the way.',
+ 'Move it or lose it.',
+ "I believe it's my move.",
+ 'This should shake you up.',
+ 'Prepare to be moved.',
+ "I'm ready to make my move.",
+ "Watch out toon, you're on shaky ground.",
+ 'This should be a moving moment.',
+ 'I feel moved to defeat you.',
+ 'Are you shaking yet?'],
+ 'hh': ["I'm way ahead of you.",
+ "You're headed for big trouble.",
+ "You'll wish this was all in your head.",
+ "Oh good, I've been hunting for you.",
+ "I'll have your head for this.",
+ 'Heads up!',
+ "Looks like you've got a head for trouble.",
+ 'Headed my way?',
+ 'A perfect trophy for my collection.',
+ 'You are going to have such a headache.',
+ "Don't lose your head over me."],
+ 'tbc': ["Watch out, I'm gouda getcha.",
+ 'You can call me Jack.',
+ 'Are you sure? I can be such a Muenster at times.',
+ 'Well finally, I was afraid you were stringing me along.',
+ "I'm going to cream you.",
+ "Don't you think I've aged well?",
+ "I'm going to make mozzarella outta ya.",
+ "I've been told I'm very strong.",
+ 'Careful, I know your expiration date.',
+ "Watch out, I'm a whiz at this game.",
+ 'Beating you will be a brieeze.'],
+ 'cr': ['RAID!',
+ "You don't fit in my corporation.",
+ 'Prepare to be raided.',
+ "Looks like you're primed for a take-over.",
+ 'That is not proper corporate attire.',
+ "You're looking rather vulnerable.",
+ 'Time to sign over your assets.',
+ "I'm on a toon removal crusade.",
+ 'You are defenseless against my ideas.',
+ "Relax, you'll find this is for the best."],
+ 'mh': ['Are you ready for my take?',
+ 'Lights, camera, action!',
+ "Let's start rolling.",
+ 'Today the role of defeated toon, will be played by - YOU!',
+ 'This scene will go on the cutting room floor.',
+ 'I already know my motivation for this scene.',
+ 'Are you ready for your final scene?',
+ "I'm ready to roll your end credits.",
+ 'I told you not to call me.',
+ "Let's get on with the show.",
+ "There's no business like it!",
+ "I hope you don't forget your lines."],
+ 'nc': ['Looks like your number is up.',
+ 'I hope you prefer extra crunchy.',
+ "Now you're really in a crunch.",
+ 'Is it time for crunch already?',
+ "Let's do crunch.",
+ 'Where would you like to have your crunch today?',
+ "You've given me something to crunch on.",
+ 'This will not be smooth.',
+ 'Go ahead, try and take a number.',
+ 'I could do with a nice crunch about now.'],
+ 'ls': ["It's time to collect on your loan.",
+ "You've been on borrowed time.",
+ 'Your loan is now due.',
+ 'Time to pay up.',
+ 'Well you asked for an advance and you got it.',
+ "You're going to pay for this.",
+ "It's pay back time.",
+ 'Can you lend me an ear?',
+ "Good thing you're here, I'm in a frenzy.",
+ 'Shall we have a quick bite?',
+ 'Let me take a bite at it.'],
+ 'mb': ['Time to bring in the big bags.',
+ 'I can bag this.',
+ 'Paper or plastic?',
+ 'Do you have your baggage claim?',
+ "Remember, money won't make you happy.",
+ 'Careful, I have some serious baggage.',
+ "You're about to have money trouble.",
+ 'Money will make your world go around.',
+ "I'm too rich for your blood.",
+ 'You can never have too much money!'],
+ 'rb': ["You've been robbed.",
+ "I'll rob you of this victory.",
+ "I'm a royal pain!",
+ 'Hope you can grin and baron.',
+ "You'll need to report this robbery.",
+ "Stick 'em up.",
+ "I'm a noble adversary.",
+ "I'm going to take everything you have.",
+ 'You could call this neighborhood robbery.',
+ 'You should know not to talk to strangers.'],
+ 'bs': ['Never turn your back on me.',
+ "You won't be coming back.",
+ 'Take that back or else!',
+ "I'm good at cutting costs.",
+ 'I have lots of back up.',
+ "There's no backing down now.",
+ "I'm the best and I can back that up.",
+ 'Whoa, back up there toon.',
+ 'Let me get your back.',
+ "You're going to have a stabbing headache soon.",
+ 'I have perfect puncture.'],
+ 'bw': ["Don't brush me aside.",
+ 'You make my hair curl.',
+ 'I can make this permanent if you want.',
+ "It looks like you're going to have some split ends.",
+ "You can't handle the truth.",
+ "I think it's your turn to be dyed.",
+ "I'm so glad you're on time for your cut.",
+ "You're in big trouble.",
+ "I'm going to wig out on you.",
+ "I'm a big deal little toon."],
+ 'le': ["Careful, my legal isn't very tender.",
+ 'I soar, then I score.',
+ "I'm bringing down the law on you.",
+ 'You should know, I have some killer instincts.',
+ "I'm going to give you legal nightmares.",
+ "You won't win this battle.",
+ 'This is so much fun it should be illegal.',
+ "Legally, you're too small to fight me.",
+ 'There is no limit to my talons.',
+ "I call this a citizen's arrest."],
+ 'sd': ["You'll never know when I'll stop.",
+ 'Let me take you for a spin.',
+ 'The doctor will see you now.',
+ "I'm going to put you into a spin.",
+ 'You look like you need a doctor.',
+ 'The doctor is in, the Toon is out.',
+ "You won't like my spin on this.",
+ 'You are going to spin out of control.',
+ 'Care to take a few turns with me?',
+ 'I have my own special spin on the subject.'],
+ 'f': ["I'm gonna tell the boss about you!",
+ "I may be just a flunky - But I'm real spunky.",
+ "I'm using you to step up the corporate ladder.",
+ "You're not going to like the way I work.",
+ 'The boss is counting on me to stop you.',
+ "You're going to look good on my resume.",
+ "You'll have to go through me first.",
+ "Let's see how you rate my job performance.",
+ 'I excel at Toon disposal.',
+ "You're never going to meet my boss.",
+ "I'm sending you back to the Playground."],
+ 'p': ["I'm gonna rub you out!",
+ "Hey, you can't push me around.",
+ "I'm No.2!",
+ "I'm going to scratch you out.",
+ "I'll have to make my point more clear.",
+ 'Let me get right to the point.',
+ "Let's hurry, I bore easily.",
+ 'I hate it when things get dull.',
+ 'So you want to push your luck?',
+ 'Did you pencil me in?',
+ 'Careful, I may leave a mark.'],
+ 'ym': ["I'm positive you're not going to like this.",
+ "I don't know the meaning of no.",
+ 'Want to meet? I say yes, anytime.',
+ 'You need some positive enforcement.',
+ "I'm going to make a positive impression.",
+ "I haven't been wrong yet.",
+ "Yes, I'm ready for you.",
+ 'Are you positive you want to do this?',
+ "I'll be sure to end this on a positive note.",
+ "I'm confirming our meeting time.",
+ "I won't take no for an answer."],
+ 'mm': ["I'm going to get into your business!",
+ 'Sometimes big hurts come in small packages.',
+ 'No job is too small for me.',
+ "I want the job done right, so I'll do it myself.",
+ 'You need someone to manage your assets.',
+ 'Oh good, a project.',
+ "Well, you've managed to find me.",
+ 'I think you need some managing.',
+ "I'll take care of you in no time.",
+ "I'm watching every move you make.",
+ 'Are you sure you want to do this?',
+ "We're going to do this my way.",
+ "I'm going to be breathing down your neck.",
+ 'I can be very intimidating.'],
+ 'ds': ["You're going down!",
+ 'Your options are shrinking.',
+ 'Expect diminishing returns.',
+ "You've just become expendable.",
+ "Don't ask me to lay off.",
+ 'I might have to make a few cutbacks.',
+ 'Things are looking down for you.',
+ 'Why do you look so down?'],
+ 'cc': ['Surprised to hear from me?',
+ 'You rang?',
+ 'Are you ready to accept my charges?',
+ 'This caller always collects.',
+ "I'm one smooth operator.",
+ "Hold the phone -- I'm here.",
+ 'Have you been waiting for my call?',
+ "I was hoping you'd answer my call.",
+ "I'm going to cause a ringing sensation.",
+ 'I always make my calls direct.',
+ 'Boy, did you get your wires crossed.',
+ 'This call is going to cost you.',
+ "You've got big trouble on the line."],
+ 'tm': ['I plan on making this inconvenient for you.',
+ 'Can I interest you in an insurance plan?',
+ 'You should have missed my call.',
+ "You won't be able to get rid of me now.",
+ 'This a bad time? Good.',
+ 'I was planning on running into you.',
+ 'I will be reversing the charges for this call.',
+ 'I have some costly items for you today.',
+ 'Too bad for you - I make house calls.',
+ "I'm prepared to close this deal quickly.",
+ "I'm going to use up a lot of your resources."],
+ 'nd': ['In my opinion, your name is mud.',
+ "I hope you don't mind if I drop your name.",
+ "Haven't we met before?",
+ "Let's hurry, I'm having lunch with 'Mr. Hollywood.'",
+ "Have I mentioned I know 'The Mingler?'",
+ "You'll never forget me.",
+ 'I know all the right people to bring you down.',
+ "I think I'll just drop in.",
+ "I'm in the mood to drop some Toons.",
+ "You name it, I've dropped it."],
+ 'gh': ['Put it there, Toon.',
+ "Let's shake on it.",
+ "I'm going to enjoy this.",
+ "You'll notice I have a very firm grip.",
+ "Let's seal the deal.",
+ "Let's get right to the business at hand.",
+ "Off handedly I'd say, you're in trouble.",
+ "You'll find I'm a handful.",
+ 'I can be quite handy.',
+ "I'm a very hands-on kinda guy.",
+ 'Would you like some hand-me-downs?',
+ 'Let me show you some of my handiwork.',
+ 'I think the handwriting is on the wall.'],
+ 'sc': ['I will make short work of you.',
+ "You're about to have money trouble.",
+ "You're about to be overcharged.",
+ 'This will be a short-term assignment.',
+ "I'll be done with you in short order.",
+ "You'll soon experience a shortfall.",
+ "Let's make this a short stop.",
+ "I think you've come up short.",
+ 'I have a short temper for Toons.',
+ "I'll be with you shortly.",
+ "You're about to be shorted."],
+ 'pp': ['This is going to sting a little.',
+ "I'm going to give you a pinch for luck.",
+ "You don't want to press your luck with me.",
+ "I'm going to put a crimp in your smile.",
+ 'Perfect, I have an opening for you.',
+ 'Let me add my two cents.',
+ "I've been asked to pinch-hit.",
+ "I'll prove you're not dreaming.",
+ 'Heads you lose, tails I win.',
+ 'A Penny for your gags.'],
+ 'tw': ['Things are about to get very tight.',
+ "That's Mr. Tightwad to you.",
+ "I'm going to cut off your funding.",
+ 'Is this the best deal you can offer?',
+ "Let's get going - time is money.",
+ "You'll find I'm very tightfisted.",
+ "You're in a tight spot.",
+ 'Prepare to walk a tight rope.',
+ 'I hope you can afford this.',
+ "I'm going to make this a tight squeeze.",
+ "I'm going to make a big dent in your budget."],
+ 'bc': ['I enjoy subtracting Toons.',
+ 'You can count on me to make you pay.',
+ 'Bean there, done that.',
+ 'I can hurt you where it counts.',
+ 'I make every bean count.',
+ 'Your expense report is overdue.',
+ 'Time for an audit.',
+ "Let's step into my office.",
+ 'Where have you bean?',
+ "I've bean waiting for you.",
+ "I'm going to bean you."],
+ 'bf': ["Looks like you've hit rock bottom.",
+ "I'm ready to feast.",
+ "I'm a sucker for Toons.",
+ 'Oh goody, lunch time.',
+ 'Perfect timing, I need a quick bite.',
+ "I'd like some feedback on my performance.",
+ "Let's talk about the bottom line.",
+ "You'll find my talents are bottomless.",
+ 'Good, I need a little pick-me-up.',
+ "I'd love to have you for lunch."],
+ 'tf': ["It's time to face-off!",
+ 'You had better face up to defeat.',
+ 'Prepare to face your worst nightmare!',
+ "Face it, I'm better than you.",
+ 'Two heads are better than one.',
+ 'It takes two to tango, you wanna tango?',
+ "You're in for two times the trouble.",
+ 'Which face would you like to defeat you?',
+ "I'm 'two' much for you.",
+ "You don't know who you're facing.",
+ 'Are you ready to face your doom?'],
+ 'dt': ["I'm gonna give you double the trouble.",
+ 'See if you can stop my double cross.',
+ 'I serve a mean double-\x04DECKER.',
+ "It's time to do some double-dealing.",
+ 'I plan to do some double DIPPING.',
+ "You're not going to like my double play.",
+ 'You may want to double think this.',
+ 'Get ready for a double TAKE.',
+ 'You may want to double up against me.',
+ 'Doubles anyone??'],
+ 'ac': ["I'm going to chase you out of town!",
+ 'Do you hear a siren?',
+ "I'm going to enjoy this.",
+ 'I love the thrill of the chase.',
+ 'Let me give you the run down.',
+ 'Do you have insurance?',
+ 'I hope you brought a stretcher with you.',
+ 'I doubt you can keep up with me.',
+ "It's all uphill from here.",
+ "You're going to need some urgent care soon.",
+ 'This is no laughing matter.',
+ "I'm going to give you the business."]}
+SpeedChatStaticTextCommon = {1: lYes,
+ 2: lNo,
+ 3: lOK,
+ 4: 'SPEEDCHAT PLUS'}
+SpeedChatStaticTextToontown = {100: 'Hi!',
+ 101: 'Hello!',
+ 102: 'Hi there!',
+ 103: 'Hey!',
+ 104: 'Howdy!',
+ 105: 'Hi everybody!',
+ 106: 'Welcome to Toontown!',
+ 107: "What's up?",
+ 108: 'How are you doing?',
+ 109: 'Hello?',
+ 200: 'Bye!',
+ 201: 'Later!',
+ 202: 'See ya!',
+ 203: 'Have a nice day!',
+ 204: 'Have fun!',
+ 205: 'Good luck!',
+ 206: "I'll be right back.",
+ 207: 'I need to go.',
+ 208: "I'll be back later!",
+ 209: 'I only have a few minutes.',
+ 300: ':-)',
+ 301: 'Yay!',
+ 302: 'Hooray!',
+ 303: 'Cool!',
+ 304: 'Woo hoo!',
+ 305: 'Yeah!',
+ 306: 'Ha ha!',
+ 307: 'Hee hee!',
+ 308: 'Wow!',
+ 309: 'Great!',
+ 310: 'Whee!',
+ 311: 'Oh boy!',
+ 312: 'Whoopee!',
+ 313: 'Yippee!',
+ 314: 'Yee hah!',
+ 315: 'Toontastic!',
+ 400: ':-(',
+ 401: 'Oh no!',
+ 402: 'Uh oh!',
+ 403: 'Rats!',
+ 404: 'Drat!',
+ 405: 'Ouch!',
+ 406: 'Oof!',
+ 407: 'No!!!',
+ 408: 'Yikes!',
+ 409: 'Huh?',
+ 410: 'I need more Laff points.',
+ 500: 'Thanks!',
+ 501: 'No problem.',
+ 502: "You're welcome!",
+ 503: 'Any time!',
+ 504: 'No thank you.',
+ 505: 'Good teamwork!',
+ 506: 'That was fun!',
+ 507: 'Please be my friend!',
+ 508: "Let's work together!",
+ 509: 'You guys are great!',
+ 510: 'Are you new here?',
+ 511: 'Did you win?',
+ 512: 'I think this is too risky for you.',
+ 513: 'Would you like some help?',
+ 514: 'Can you help me?',
+ 515: 'Have you been here before?',
+ 600: 'You look nice.',
+ 601: 'You are awesome!',
+ 602: 'You rock!',
+ 603: 'You are a genius!',
+ 700: 'I like your name.',
+ 701: 'I like your look.',
+ 702: 'I like your shirt.',
+ 703: 'I like your skirt.',
+ 704: 'I like your shorts.',
+ 705: 'I like this game!',
+ 800: 'Sorry!',
+ 801: 'Oops!',
+ 802: "Sorry, I'm busy fighting Cogs!",
+ 803: "Sorry, I'm busy getting Jellybeans!",
+ 804: "Sorry, I'm busy completing a ToonTask!",
+ 805: 'Sorry, I had to leave unexpectedly.',
+ 806: 'Sorry, I was delayed.',
+ 807: "Sorry, I can't.",
+ 808: "I couldn't wait any longer.",
+ 809: "I can't understand you.",
+ 810: 'Use the %s.' % GlobalSpeedChatName,
+ 811: "Sorry, I'm busy fishing!",
+ 812: "Sorry, I'm in a building!",
+ 813: "Sorry, I'm helping a friend!",
+ 814: "Sorry, I'm busy kart racing!",
+ 815: "Sorry, I'm busy gardening!",
+ 816: "I can't get on the elevator now.",
+ 817: "Sorry, I'm busy golfing!",
+ 818: 'Sorry, my Friends List is full.',
+ 900: 'Hey!',
+ 901: 'Please go away!',
+ 902: 'Stop that!',
+ 903: "That wasn't nice!",
+ 904: "Don't be mean!",
+ 905: 'You stink!',
+ 906: 'Send a bug report.',
+ 907: "I'm stuck.",
+ 1000: "Let's go!",
+ 1001: 'Can you teleport to me?',
+ 1002: 'Shall we go?',
+ 1003: 'Where should we go?',
+ 1004: 'Which way?',
+ 1005: 'This way.',
+ 1006: 'Follow me.',
+ 1007: 'Wait for me!',
+ 1008: "Let's wait for my friend.",
+ 1009: "Let's find other Toons.",
+ 1010: 'Wait here.',
+ 1011: 'Wait a minute.',
+ 1012: 'Meet here.',
+ 1013: 'Can you come to my house?',
+ 1014: "Don't wait for me.",
+ 1015: 'Wait!',
+ 1016: 'Come check out my garden.',
+ 1017: "Let's catch the next one.",
+ 1100: "Let's go on the trolley!",
+ 1101: "Let's go back to the playground!",
+ 1102: "Let's go fight the %s!" % Cogs,
+ 1103: "Let's go take over a %s building!" % Cog,
+ 1104: "Let's go in the elevator!",
+ 1105: "Let's go to %s!" % lToontownCentral,
+ 1106: "Let's go to %s!" % lDonaldsDock,
+ 1107: "Let's go to %s!" % lMinniesMelodyland,
+ 1108: "Let's go to %s!" % lDaisyGardens,
+ 1109: "Let's go to %s!" % lTheBrrrgh,
+ 1110: "Let's go to %s!" % lDonaldsDreamland,
+ 1111: "Let's go to %s!" % lGoofySpeedway,
+ 1112: "Let's go to my house!",
+ 1113: "Let's go to your house!",
+ 1114: "Let's go to Sellbot HQ!",
+ 1115: "Let's go fight the VP!",
+ 1116: "Let's go in the Factory!",
+ 1117: "Let's go fishing!",
+ 1118: "Let's go fishing at my house!",
+ 1119: "Let's go to Cashbot HQ!",
+ 1120: "Let's go fight the CFO!",
+ 1121: "Let's go in the Mint!",
+ 1122: "Let's go to Lawbot HQ!",
+ 1123: "Let's go fight the Chief Justice!",
+ 1124: "Let's go in the District Attorney's Office!",
+ 1125: "Let's go to %s!" % lOutdoorZone,
+ 1126: "Let's go to %s!" % lGolfZone,
+ 1127: "Let's go to Bossbot HQ!",
+ 1128: "Let's go fight the CEO!",
+ 1129: "Let's go in the Cog Golf Courses!",
+ 1130: "Let's go take over a Field Office!",
+ 1200: 'What ToonTask are you working on?',
+ 1201: "Let's work on that.",
+ 1202: "This isn't what I'm looking for.",
+ 1203: "I'm going to look for that.",
+ 1204: "It isn't on this street.",
+ 1205: "I haven't found it yet.",
+ 1206: 'I need more Merits.',
+ 1207: 'I need more Sellbot Suit Parts.',
+ 1208: "This isn't what you need.",
+ 1209: 'I found what you need.',
+ 1210: 'I need more Cogbucks.',
+ 1211: 'I need more Jury Notices.',
+ 1212: 'I need more Stock Options.',
+ 1213: 'I need more Cashbot Suit Parts.',
+ 1214: 'I need more Lawbot Suit Parts.',
+ 1215: 'I need more Bossbot Suit Parts.',
+ 1299: 'I need to get a ToonTask.',
+ 1300: 'I think you should choose Toon-up.',
+ 1301: 'I think you should choose Sound.',
+ 1302: 'I think you should choose Drop.',
+ 1303: 'I think you should choose Trap.',
+ 1304: 'I think you should choose Lure.',
+ 1400: 'Hurry!',
+ 1401: 'Nice shot!',
+ 1402: 'Nice gag!',
+ 1403: 'Missed me!',
+ 1404: 'You did it!',
+ 1405: 'We did it!',
+ 1406: 'Bring it on!',
+ 1407: 'Piece of cake!',
+ 1408: 'That was easy!',
+ 1409: 'Run!',
+ 1410: 'Help!',
+ 1411: 'Phew!',
+ 1412: 'We are in trouble.',
+ 1413: 'I need more gags.',
+ 1414: 'I need a Toon-Up.',
+ 1415: 'You should pass.',
+ 1416: 'We can do this!',
+ 1500: "Let's use toon-up!",
+ 1501: "Let's use trap!",
+ 1502: "Let's use lure!",
+ 1503: "Let's use sound!",
+ 1504: "Let's use throw!",
+ 1505: "Let's use squirt!",
+ 1506: "Let's use drop!",
+ 1520: 'Rock and roll!',
+ 1521: "That's gotta hurt.",
+ 1522: 'Catch!',
+ 1523: 'Special delivery!',
+ 1524: 'Are you still here?',
+ 1525: "I'm SO scared!",
+ 1526: "That's going to leave a mark!",
+ 1550: "I'm going to use trap.",
+ 1551: "I'm going to use lure.",
+ 1552: "I'm going to use drop.",
+ 1553: 'You should use a different gag.',
+ 1554: "Let's all go for the same Cog.",
+ 1555: 'You should choose a different Cog.',
+ 1556: 'Go for the weakest Cog first.',
+ 1557: 'Go for the strongest Cog first.',
+ 1558: 'Save your powerful gags.',
+ 1559: "Don't use sound on lured Cogs.",
+ 1600: 'I have enough gags.',
+ 1601: 'I need more jellybeans.',
+ 1602: 'Me too.',
+ 1603: 'Hurry up!',
+ 1604: 'One more?',
+ 1605: 'Play again?',
+ 1606: "Let's play again.",
+ 1700: "Let's split up.",
+ 1701: "Let's stay together.",
+ 1702: "Let's battle the Cogs.",
+ 1703: 'Step on the switch.',
+ 1704: 'Go through the door.',
+ 1803: "I'm in the Front Entrance.",
+ 1804: "I'm in the Lobby.",
+ 1805: "I'm in the hallway outside the Lobby.",
+ 1806: "I'm in the hallway outside the Lobby.",
+ 1807: "I'm in the Gear Room.",
+ 1808: "I'm in the Boiler Room.",
+ 1809: "I'm on the East Catwalk.",
+ 1810: "I'm in the Paint Mixer.",
+ 1811: "I'm in the Paint Mixer Storage Room.",
+ 1812: "I'm on the West Silo Catwalk.",
+ 1813: "I'm in the Pipe Room.",
+ 1814: "I'm on the stairs to the Pipe Room.",
+ 1815: "I'm in the Duct Room.",
+ 1816: "I'm in the Side Entrance.",
+ 1817: "I'm in Stomper Alley.",
+ 1818: "I'm outside the Lava Room.",
+ 1819: "I'm in the Lava Room.",
+ 1820: "I'm in the Lava Storage Room.",
+ 1821: "I'm on the West Catwalk.",
+ 1822: "I'm in the Oil Room.",
+ 1823: "I'm on the Warehouse Lookout.",
+ 1824: "I'm in the Warehouse.",
+ 1825: "I'm outside the Paint Mixer.",
+ 1827: "I'm outside the Oil Room.",
+ 1830: "I'm in the East Silo Control Room.",
+ 1831: "I'm in the West Silo Control Room.",
+ 1832: "I'm in the Center Silo Control Room.",
+ 1833: "I'm at the East Silo.",
+ 1834: "I'm on the West Silo.",
+ 1835: "I'm on the Center Silo.",
+ 1836: "I'm on the West Silo.",
+ 1837: "I'm at the East Silo.",
+ 1838: "I'm on the East Silo Catwalk.",
+ 1840: "I'm on top of the West Silo.",
+ 1841: "I'm on top of the East Silo.",
+ 1860: "I'm on the West Silo Elevator.",
+ 1861: "I'm on the East Silo Elevator.",
+ 1903: "Let's meet in the Front Entrance.",
+ 1904: "Let's meet in the Lobby.",
+ 1905: "Let's meet in the hallway outside the Lobby.",
+ 1906: "Let's meet in the hallway outside the Lobby.",
+ 1907: "Let's meet in the Gear Room.",
+ 1908: "Let's meet in the Boiler Room.",
+ 1909: "Let's meet on the East Catwalk.",
+ 1910: "Let's meet in the Paint Mixer.",
+ 1911: "Let's meet in the Paint Mixer Storage Room.",
+ 1912: "Let's meet on the West Silo Catwalk.",
+ 1913: "Let's meet in the Pipe Room.",
+ 1914: "Let's meet on the stairs to the Pipe Room.",
+ 1915: "Let's meet in the Duct Room.",
+ 1916: "Let's meet in the Side Entrance.",
+ 1917: "Let's meet in Stomper Alley.",
+ 1918: "Let's meet outside the Lava Room.",
+ 1919: "Let's meet in the Lava Room.",
+ 1920: "Let's meet in the Lava Storage Room.",
+ 1921: "Let's meet on the West Catwalk.",
+ 1922: "Let's meet in the Oil Room.",
+ 1923: "Let's meet on the Warehouse Lookout.",
+ 1924: "Let's meet in the Warehouse.",
+ 1925: "Let's meet outside the Paint Mixer.",
+ 1927: "Let's meet outside the Oil Room.",
+ 1930: "Let's meet in the East Silo Control Room.",
+ 1931: "Let's meet in the West Silo Control Room.",
+ 1932: "Let's meet in the Center Silo Control Room.",
+ 1933: "Let's meet at the East Silo.",
+ 1934: "Let's meet on the West Silo.",
+ 1935: "Let's meet on the Center Silo.",
+ 1936: "Let's meet on the West Silo.",
+ 1937: "Let's meet at the East Silo.",
+ 1938: "Let's meet on the East Silo Catwalk.",
+ 1940: "Let's meet on top of the West Silo.",
+ 1941: "Let's meet on top of the East Silo.",
+ 1960: "Let's meet on the West Silo Elevator.",
+ 1961: "Let's meet on the East Silo Elevator.",
+ 2000: 'Purple',
+ 2001: 'Blue',
+ 2002: 'Cyan',
+ 2003: 'Teal',
+ 2004: 'Green',
+ 2005: 'Yellow',
+ 2006: 'Orange',
+ 2007: 'Red',
+ 2008: 'Pink',
+ 2009: 'Brown',
+ 2100: 'Please operate the crane.',
+ 2101: 'May I operate the crane?',
+ 2102: 'I need practice operating the crane.',
+ 2103: 'Pick up a disabled goon.',
+ 2104: 'Throw the goon at the CFO.',
+ 2105: 'Throw a safe now!',
+ 2106: "Don't throw a safe now!",
+ 2107: 'A safe will knock off his helmet.',
+ 2108: 'A safe will become his new helmet.',
+ 2109: "I can't reach any safes.",
+ 2110: "I can't reach any goons.",
+ 2120: 'Please disable the goons.',
+ 2121: 'I would rather disable goons.',
+ 2122: 'I need practice disabling goons.',
+ 2123: 'Please stay nearby.',
+ 2124: 'Keep moving.',
+ 2125: 'I need to keep moving.',
+ 2126: 'Look for someone who needs help.',
+ 2130: 'Please save the treasures.',
+ 2131: 'Take the treasures.',
+ 2132: 'I need treasures!',
+ 2133: 'Look out!',
+ 2200: 'You need to hit the scale.',
+ 2201: 'I will hit the scale.',
+ 2202: 'I need help with the scale!',
+ 2203: 'You need to stun the Cogs.',
+ 2204: 'I will stun the Cogs.',
+ 2205: 'I need help with the Cogs!',
+ 2206: 'I need more evidence!',
+ 2207: "I'm shooting for chairs in the top row.",
+ 2208: "I'm shooting for chairs in the bottom row.",
+ 2209: "Move out of the way! We can't hit the pan.",
+ 2210: "I'll do Toon-Ups for us.",
+ 2211: "I don't have any bonus weight.",
+ 2212: 'I have a bonus weight of 1.',
+ 2213: 'I have a bonus weight of 2.',
+ 2214: 'I have a bonus weight of 3.',
+ 2215: 'I have a bonus weight of 4.',
+ 2216: 'I have a bonus weight of 5.',
+ 2217: 'I have a bonus weight of 6.',
+ 2218: 'I have a bonus weight of 7.',
+ 2219: 'I have a bonus weight of 8.',
+ 2220: 'I have a bonus weight of 9.',
+ 2221: 'I have a bonus weight of 10.',
+ 2222: 'I have a bonus weight of 11.',
+ 2223: 'I have a bonus weight of 12.',
+ 2300: 'You feed the Cogs on the left.',
+ 2301: "I'll feed the Cogs on the left.",
+ 2302: 'You feed the Cogs on the right.',
+ 2303: "I'll feed the Cogs on the right.",
+ 2304: 'You feed the Cogs in the front.',
+ 2305: "I'll feed the Cogs in the front.",
+ 2306: 'You feed the Cogs in the back.',
+ 2307: "I'll feed the Cogs in the back.",
+ 2308: 'You use the seltzer bottle.',
+ 2309: "I'll use the seltzer bottle.",
+ 2310: 'You use the golf tee.',
+ 2311: "I'll use the golf tee.",
+ 2312: "I'll serve this table.",
+ 2313: 'Can you serve this table?',
+ 2314: 'Feed the same cog again.',
+ 2315: 'Hurry, your cog is hungry!',
+ 2316: 'Please save the snacks for sadder toons.',
+ 2317: 'Take the snacks before they fall.',
+ 3010: 'Anyone want to race?',
+ 3020: "Let's race!",
+ 3030: 'Want to race?',
+ 3040: "Let's show off our karts!",
+ 3050: "I don't have enough tickets.",
+ 3060: "Let's race again!",
+ 3061: 'Want to race again?',
+ 3150: 'I need to go to the Kart Shop.',
+ 3160: "Let's go to the Race Tracks!",
+ 3170: "Let's go to Pit Row to show off our karts!",
+ 3180: "I'm going to Pit Row to show off my kart!",
+ 3190: 'Meet me at the Race Tracks!',
+ 3110: 'Meet up near the Kart Shop!',
+ 3130: 'Where should we meet?',
+ 3200: 'Where do you want to race?',
+ 3201: "Let's pick a different race.",
+ 3210: "Let's do a practice race.",
+ 3211: "Let's do a battle race.",
+ 3220: 'I like the Screwball Stadium race!',
+ 3221: 'I like the Rustic Raceway race!',
+ 3222: 'I like the City Circuit race!',
+ 3223: 'I like the Corkscrew Coliseum race!',
+ 3224: 'I like the Airborne Acres race!',
+ 3225: 'I like the Blizzard Boulevard race!',
+ 3230: "Let's race in the Screwball Stadium!",
+ 3231: "Let's race on the Rustic Raceway!",
+ 3232: "Let's race on the City Circuit!",
+ 3233: "Let's race in the Corkscrew Coliseum!",
+ 3234: "Let's race on the Airborne Acres!",
+ 3235: "Let's race on the Blizzard Boulevard!",
+ 3600: 'Which track do you want to race on?',
+ 3601: 'Pick a track!',
+ 3602: 'Can we race on a different track?',
+ 3603: "Let's pick a different track!",
+ 3640: 'I want to race on the first track!',
+ 3641: 'I want to race on the second track!',
+ 3642: 'I want to race on the third track!',
+ 3643: 'I want to race on the fourth track!',
+ 3660: "I don't want to race on the first track!",
+ 3661: "I don't want to race on the second track!",
+ 3662: "I don't want to race on the third track!",
+ 3663: "I don't want to race on the fourth track!",
+ 3300: 'Wow! You are FAST!',
+ 3301: "You're too fast for me!",
+ 3310: 'Good race!',
+ 3320: 'I really like your kart!',
+ 3330: 'Sweet ride!',
+ 3340: 'Your kart is cool!',
+ 3350: 'Your kart is awesome!',
+ 3360: 'Your kart is totally sweet!',
+ 3400: 'Too scared to race me?',
+ 3410: 'See you at the finish line!',
+ 3430: "I'm as fast as lightning!",
+ 3450: "You'll never catch me!",
+ 3451: "You'll never beat me!",
+ 3452: 'No one can beat my time!',
+ 3453: 'Hurry up slow pokes!',
+ 3460: 'Give me another shot!',
+ 3461: 'You got lucky!',
+ 3462: 'Ooooh! That was a close one!',
+ 3470: 'Wow, I thought you had me beat!',
+ 4000: "Let's play minigolf!",
+ 4001: "Let's play again!",
+ 4002: 'Want to golf?',
+ 4100: "Let's play 'Walk In The Par.'",
+ 4101: "Let's play 'Hole Some Fun.'",
+ 4102: "Let's play 'The Hole Kit and Caboodle.'",
+ 4103: 'That course is too easy.',
+ 4104: 'That course is too hard.',
+ 4105: 'That course is just right.',
+ 4200: 'Try standing more to the left.',
+ 4201: 'Try standing more to the right.',
+ 4202: 'Try standing right in the middle.',
+ 4203: 'Try hitting it harder.',
+ 4204: 'Try hitting it softer.',
+ 4205: 'Try aiming more to the left.',
+ 4206: 'Try aiming more to the right.',
+ 4207: 'Try aiming right down the middle.',
+ 4300: 'So close!',
+ 4301: 'What a great shot!',
+ 4302: 'That was a lucky shot.',
+ 4303: "I'll take a mulligan...",
+ 4304: "That's a gimme.",
+ 4305: 'Fore!',
+ 4306: 'Shhhh!',
+ 4307: 'Good game!',
+ 5000: "Let's form a Boarding Group.",
+ 5001: 'Join my Boarding Group.',
+ 5002: 'Can you invite me to your Boarding Group?',
+ 5003: "I'm already in a Boarding Group.",
+ 5004: 'Leave your Boarding Group.',
+ 5005: 'We are boarding now.',
+ 5006: 'Where are we going?',
+ 5007: 'Are we ready?',
+ 5008: "Let's Go!",
+ 5009: "Don't leave this area or you will leave the Boarding Group.",
+ 5100: "Let's go to the Front Three.",
+ 5101: "Let's go to the Middle Six.",
+ 5102: "Let's go to the Back Nine.",
+ 5103: "Let's go to the C.E.O. Battle.",
+ 5104: "Let's go to the Senior V.P Battle.",
+ 5105: "Let's go to the Front Entrance.",
+ 5106: "Let's go to the Side Entrance.",
+ 5107: "Let's go to the Coin Mint.",
+ 5108: "Let's go to the Dollar Mint.",
+ 5109: "Let's go to the Bullion Mint.",
+ 5110: "Let's go to the C.F.O. Battle.",
+ 5111: "Let's go to the Chief Justice Battle.",
+ 5112: "Let's go to the Lawbot A Office.",
+ 5113: "Let's go to the Lawbot B Office.",
+ 5114: "Let's go to the Lawbot C Office.",
+ 5115: "Let's go to the Lawbot D Office.",
+ 5200: "We're going to the Front Three.",
+ 5201: "We're going to the Middle Six.",
+ 5202: "We're going to the Back Nine.",
+ 5203: "We're going to the C.E.O. Battle.",
+ 5204: "We're going to the Senior V.P Battle.",
+ 5205: "We're going to the Front Entrance.",
+ 5206: "We're going to the Side Entrance.",
+ 5207: "We're going to the Coin Mint.",
+ 5208: "We're going to the Dollar Mint.",
+ 5209: "We're going to the Bullion Mint.",
+ 5210: "We're going to the C.F.O. Battle.",
+ 5211: "We're going to the Chief Justice Battle.",
+ 5212: "We're going to the Lawbot A Office.",
+ 5213: "We're going to the Lawbot B Office.",
+ 5214: "We're going to the Lawbot C Office.",
+ 5215: "We're going to the Lawbot D Office.",
+ 5300: "Let's go to a party.",
+ 5301: 'See you at the party!',
+ 5302: 'My party has started!',
+ 5303: 'Come to my party!',
+ 5304: 'Welcome to my party!',
+ 5305: 'This party rules!',
+ 5306: 'Your party is fun!',
+ 5307: "It's party time!",
+ 5308: 'Time is running out!',
+ 5309: 'No cogs allowed!',
+ 5310: 'I like this song!',
+ 5311: 'This music is great!',
+ 5312: 'Cannons are a blast!',
+ 5313: 'Watch me jump!',
+ 5314: 'Trampolines are fun!',
+ 5315: "Let's play Catch!",
+ 5316: "Let's dance!",
+ 5317: 'To the dance floor!',
+ 5318: "Let's play Tug of War!",
+ 5319: 'Start the fireworks!',
+ 5320: 'These fireworks are beautiful!',
+ 5321: 'Nice decorations.',
+ 5322: 'I wish I could eat this cake!',
+ 10000: 'The choice is yours!',
+ 10001: 'Who are you voting for?',
+ 10002: "I'm pickin' Chicken!",
+ 10003: 'Vote now! Vote Cow!',
+ 10004: 'Go bananas! Vote Monkey!',
+ 10005: 'Be a honey! Vote Bear!',
+ 10006: 'Think big! Vote Pig!',
+ 10007: "Vote Goat - and that's all she wrote!",
+ 20000: SuitBrushOffs[None][0],
+ 20001: SuitBrushOffs[None][1],
+ 20002: SuitBrushOffs[None][2],
+ 20003: SuitBrushOffs[None][3],
+ 20004: SuitBrushOffs[None][4],
+ 20005: SuitFaceoffTaunts['bf'][0],
+ 20006: SuitFaceoffTaunts['bf'][1],
+ 20007: SuitFaceoffTaunts['bf'][2],
+ 20008: SuitFaceoffTaunts['bf'][3],
+ 20009: SuitFaceoffTaunts['bf'][4],
+ 20010: SuitFaceoffTaunts['bf'][5],
+ 20011: SuitFaceoffTaunts['bf'][6],
+ 20012: SuitFaceoffTaunts['bf'][7],
+ 20013: SuitFaceoffTaunts['bf'][8],
+ 20014: SuitFaceoffTaunts['bf'][9],
+ 20015: SuitFaceoffTaunts['nc'][0],
+ 20016: SuitFaceoffTaunts['nc'][1],
+ 20017: SuitFaceoffTaunts['nc'][2],
+ 20018: SuitFaceoffTaunts['nc'][3],
+ 20019: SuitFaceoffTaunts['nc'][4],
+ 20020: SuitFaceoffTaunts['nc'][5],
+ 20021: SuitFaceoffTaunts['nc'][6],
+ 20022: SuitFaceoffTaunts['nc'][7],
+ 20023: SuitFaceoffTaunts['nc'][8],
+ 20024: SuitFaceoffTaunts['nc'][9],
+ 20025: SuitFaceoffTaunts['ym'][0],
+ 20026: SuitFaceoffTaunts['ym'][1],
+ 20027: SuitFaceoffTaunts['ym'][2],
+ 20028: SuitFaceoffTaunts['ym'][3],
+ 20029: SuitFaceoffTaunts['ym'][4],
+ 20030: SuitFaceoffTaunts['ym'][5],
+ 20031: SuitFaceoffTaunts['ym'][6],
+ 20032: SuitFaceoffTaunts['ym'][7],
+ 20033: SuitFaceoffTaunts['ym'][8],
+ 20034: SuitFaceoffTaunts['ym'][9],
+ 20035: SuitFaceoffTaunts['ym'][10],
+ 20036: SuitFaceoffTaunts['ms'][0],
+ 20037: SuitFaceoffTaunts['ms'][1],
+ 20038: SuitFaceoffTaunts['ms'][2],
+ 20039: SuitFaceoffTaunts['ms'][3],
+ 20040: SuitFaceoffTaunts['ms'][4],
+ 20041: SuitFaceoffTaunts['ms'][5],
+ 20042: SuitFaceoffTaunts['ms'][6],
+ 20043: SuitFaceoffTaunts['ms'][7],
+ 20044: SuitFaceoffTaunts['ms'][8],
+ 20045: SuitFaceoffTaunts['ms'][9],
+ 20046: SuitFaceoffTaunts['ms'][10],
+ 20047: SuitFaceoffTaunts['bc'][0],
+ 20048: SuitFaceoffTaunts['bc'][1],
+ 20049: SuitFaceoffTaunts['bc'][2],
+ 20050: SuitFaceoffTaunts['bc'][3],
+ 20051: SuitFaceoffTaunts['bc'][4],
+ 20052: SuitFaceoffTaunts['bc'][5],
+ 20053: SuitFaceoffTaunts['bc'][6],
+ 20054: SuitFaceoffTaunts['bc'][7],
+ 20055: SuitFaceoffTaunts['bc'][8],
+ 20056: SuitFaceoffTaunts['bc'][9],
+ 20057: SuitFaceoffTaunts['bc'][10],
+ 20058: SuitFaceoffTaunts['cc'][0],
+ 20059: SuitFaceoffTaunts['cc'][1],
+ 20060: SuitFaceoffTaunts['cc'][2],
+ 20061: SuitFaceoffTaunts['cc'][3],
+ 20062: SuitFaceoffTaunts['cc'][4],
+ 20063: SuitFaceoffTaunts['cc'][5],
+ 20064: SuitFaceoffTaunts['cc'][6],
+ 20065: SuitFaceoffTaunts['cc'][7],
+ 20066: SuitFaceoffTaunts['cc'][8],
+ 20067: SuitFaceoffTaunts['cc'][9],
+ 20068: SuitFaceoffTaunts['cc'][10],
+ 20069: SuitFaceoffTaunts['cc'][11],
+ 20070: SuitFaceoffTaunts['cc'][12],
+ 20071: SuitFaceoffTaunts['nd'][0],
+ 20072: SuitFaceoffTaunts['nd'][1],
+ 20073: SuitFaceoffTaunts['nd'][2],
+ 20074: SuitFaceoffTaunts['nd'][3],
+ 20075: SuitFaceoffTaunts['nd'][4],
+ 20076: SuitFaceoffTaunts['nd'][5],
+ 20077: SuitFaceoffTaunts['nd'][6],
+ 20078: SuitFaceoffTaunts['nd'][7],
+ 20079: SuitFaceoffTaunts['nd'][8],
+ 20080: SuitFaceoffTaunts['nd'][9],
+ 20081: SuitFaceoffTaunts['ac'][0],
+ 20082: SuitFaceoffTaunts['ac'][1],
+ 20083: SuitFaceoffTaunts['ac'][2],
+ 20084: SuitFaceoffTaunts['ac'][3],
+ 20085: SuitFaceoffTaunts['ac'][4],
+ 20086: SuitFaceoffTaunts['ac'][5],
+ 20087: SuitFaceoffTaunts['ac'][6],
+ 20088: SuitFaceoffTaunts['ac'][7],
+ 20089: SuitFaceoffTaunts['ac'][8],
+ 20090: SuitFaceoffTaunts['ac'][9],
+ 20091: SuitFaceoffTaunts['ac'][10],
+ 20092: SuitFaceoffTaunts['ac'][11],
+ 20093: SuitFaceoffTaunts['tf'][0],
+ 20094: SuitFaceoffTaunts['tf'][1],
+ 20095: SuitFaceoffTaunts['tf'][2],
+ 20096: SuitFaceoffTaunts['tf'][3],
+ 20097: SuitFaceoffTaunts['tf'][4],
+ 20098: SuitFaceoffTaunts['tf'][5],
+ 20099: SuitFaceoffTaunts['tf'][6],
+ 20100: SuitFaceoffTaunts['tf'][7],
+ 20101: SuitFaceoffTaunts['tf'][8],
+ 20102: SuitFaceoffTaunts['tf'][9],
+ 20103: SuitFaceoffTaunts['tf'][10],
+ 20104: SuitFaceoffTaunts['hh'][0],
+ 20105: SuitFaceoffTaunts['hh'][1],
+ 20106: SuitFaceoffTaunts['hh'][2],
+ 20107: SuitFaceoffTaunts['hh'][3],
+ 20108: SuitFaceoffTaunts['hh'][4],
+ 20109: SuitFaceoffTaunts['hh'][5],
+ 20110: SuitFaceoffTaunts['hh'][6],
+ 20111: SuitFaceoffTaunts['hh'][7],
+ 20112: SuitFaceoffTaunts['hh'][8],
+ 20113: SuitFaceoffTaunts['hh'][9],
+ 20114: SuitFaceoffTaunts['hh'][10],
+ 20115: SuitFaceoffTaunts['le'][0],
+ 20116: SuitFaceoffTaunts['le'][1],
+ 20117: SuitFaceoffTaunts['le'][2],
+ 20118: SuitFaceoffTaunts['le'][3],
+ 20119: SuitFaceoffTaunts['le'][4],
+ 20120: SuitFaceoffTaunts['le'][5],
+ 20121: SuitFaceoffTaunts['le'][6],
+ 20122: SuitFaceoffTaunts['le'][7],
+ 20123: SuitFaceoffTaunts['le'][8],
+ 20124: SuitFaceoffTaunts['le'][9],
+ 20125: SuitFaceoffTaunts['bs'][0],
+ 20126: SuitFaceoffTaunts['bs'][1],
+ 20127: SuitFaceoffTaunts['bs'][2],
+ 20128: SuitFaceoffTaunts['bs'][3],
+ 20129: SuitFaceoffTaunts['bs'][4],
+ 20130: SuitFaceoffTaunts['bs'][5],
+ 20131: SuitFaceoffTaunts['bs'][6],
+ 20132: SuitFaceoffTaunts['bs'][7],
+ 20133: SuitFaceoffTaunts['bs'][8],
+ 20134: SuitFaceoffTaunts['bs'][9],
+ 20135: SuitFaceoffTaunts['bs'][10],
+ 20136: SuitFaceoffTaunts['cr'][0],
+ 20137: SuitFaceoffTaunts['cr'][1],
+ 20138: SuitFaceoffTaunts['cr'][2],
+ 20139: SuitFaceoffTaunts['cr'][3],
+ 20140: SuitFaceoffTaunts['cr'][4],
+ 20141: SuitFaceoffTaunts['cr'][5],
+ 20142: SuitFaceoffTaunts['cr'][6],
+ 20143: SuitFaceoffTaunts['cr'][7],
+ 20144: SuitFaceoffTaunts['cr'][8],
+ 20145: SuitFaceoffTaunts['cr'][9],
+ 20146: SuitFaceoffTaunts['tbc'][0],
+ 20147: SuitFaceoffTaunts['tbc'][1],
+ 20148: SuitFaceoffTaunts['tbc'][2],
+ 20149: SuitFaceoffTaunts['tbc'][3],
+ 20150: SuitFaceoffTaunts['tbc'][4],
+ 20151: SuitFaceoffTaunts['tbc'][5],
+ 20152: SuitFaceoffTaunts['tbc'][6],
+ 20153: SuitFaceoffTaunts['tbc'][7],
+ 20154: SuitFaceoffTaunts['tbc'][8],
+ 20155: SuitFaceoffTaunts['tbc'][9],
+ 20156: SuitFaceoffTaunts['tbc'][10],
+ 20157: SuitFaceoffTaunts['ds'][0],
+ 20158: SuitFaceoffTaunts['ds'][1],
+ 20159: SuitFaceoffTaunts['ds'][2],
+ 20160: SuitFaceoffTaunts['ds'][3],
+ 20161: SuitFaceoffTaunts['ds'][4],
+ 20162: SuitFaceoffTaunts['ds'][5],
+ 20163: SuitFaceoffTaunts['ds'][6],
+ 20164: SuitFaceoffTaunts['ds'][7],
+ 20165: SuitFaceoffTaunts['gh'][0],
+ 20166: SuitFaceoffTaunts['gh'][1],
+ 20167: SuitFaceoffTaunts['gh'][2],
+ 20168: SuitFaceoffTaunts['gh'][3],
+ 20169: SuitFaceoffTaunts['gh'][4],
+ 20170: SuitFaceoffTaunts['gh'][5],
+ 20171: SuitFaceoffTaunts['gh'][6],
+ 20172: SuitFaceoffTaunts['gh'][7],
+ 20173: SuitFaceoffTaunts['gh'][8],
+ 20174: SuitFaceoffTaunts['gh'][9],
+ 20175: SuitFaceoffTaunts['gh'][10],
+ 20176: SuitFaceoffTaunts['gh'][11],
+ 20177: SuitFaceoffTaunts['gh'][12],
+ 20178: SuitFaceoffTaunts['pp'][0],
+ 20179: SuitFaceoffTaunts['pp'][1],
+ 20180: SuitFaceoffTaunts['pp'][2],
+ 20181: SuitFaceoffTaunts['pp'][3],
+ 20182: SuitFaceoffTaunts['pp'][4],
+ 20183: SuitFaceoffTaunts['pp'][5],
+ 20184: SuitFaceoffTaunts['pp'][6],
+ 20185: SuitFaceoffTaunts['pp'][7],
+ 20186: SuitFaceoffTaunts['pp'][8],
+ 20187: SuitFaceoffTaunts['pp'][9],
+ 20188: SuitFaceoffTaunts['b'][0],
+ 20189: SuitFaceoffTaunts['b'][1],
+ 20190: SuitFaceoffTaunts['b'][2],
+ 20191: SuitFaceoffTaunts['b'][3],
+ 20192: SuitFaceoffTaunts['b'][4],
+ 20193: SuitFaceoffTaunts['b'][5],
+ 20194: SuitFaceoffTaunts['b'][6],
+ 20195: SuitFaceoffTaunts['b'][7],
+ 20196: SuitFaceoffTaunts['b'][8],
+ 20197: SuitFaceoffTaunts['b'][9],
+ 20198: SuitFaceoffTaunts['b'][10],
+ 20199: SuitFaceoffTaunts['b'][11],
+ 20200: SuitFaceoffTaunts['f'][0],
+ 20201: SuitFaceoffTaunts['f'][1],
+ 20202: SuitFaceoffTaunts['f'][2],
+ 20203: SuitFaceoffTaunts['f'][3],
+ 20204: SuitFaceoffTaunts['f'][4],
+ 20205: SuitFaceoffTaunts['f'][5],
+ 20206: SuitFaceoffTaunts['f'][6],
+ 20207: SuitFaceoffTaunts['f'][7],
+ 20208: SuitFaceoffTaunts['f'][8],
+ 20209: SuitFaceoffTaunts['f'][9],
+ 20210: SuitFaceoffTaunts['f'][10],
+ 20211: SuitFaceoffTaunts['mm'][0],
+ 20212: SuitFaceoffTaunts['mm'][1],
+ 20213: SuitFaceoffTaunts['mm'][2],
+ 20214: SuitFaceoffTaunts['mm'][3],
+ 20215: SuitFaceoffTaunts['mm'][4],
+ 20216: SuitFaceoffTaunts['mm'][5],
+ 20217: SuitFaceoffTaunts['mm'][6],
+ 20218: SuitFaceoffTaunts['mm'][7],
+ 20219: SuitFaceoffTaunts['mm'][8],
+ 20220: SuitFaceoffTaunts['mm'][9],
+ 20221: SuitFaceoffTaunts['mm'][10],
+ 20222: SuitFaceoffTaunts['mm'][11],
+ 20223: SuitFaceoffTaunts['mm'][12],
+ 20224: SuitFaceoffTaunts['mm'][13],
+ 20225: SuitFaceoffTaunts['tw'][0],
+ 20226: SuitFaceoffTaunts['tw'][1],
+ 20227: SuitFaceoffTaunts['tw'][2],
+ 20228: SuitFaceoffTaunts['tw'][3],
+ 20229: SuitFaceoffTaunts['tw'][4],
+ 20230: SuitFaceoffTaunts['tw'][5],
+ 20231: SuitFaceoffTaunts['tw'][6],
+ 20232: SuitFaceoffTaunts['tw'][7],
+ 20233: SuitFaceoffTaunts['tw'][8],
+ 20234: SuitFaceoffTaunts['tw'][9],
+ 20235: SuitFaceoffTaunts['tw'][10],
+ 20236: SuitFaceoffTaunts['mb'][0],
+ 20237: SuitFaceoffTaunts['mb'][1],
+ 20238: SuitFaceoffTaunts['mb'][2],
+ 20239: SuitFaceoffTaunts['mb'][3],
+ 20240: SuitFaceoffTaunts['mb'][4],
+ 20241: SuitFaceoffTaunts['mb'][5],
+ 20242: SuitFaceoffTaunts['mb'][6],
+ 20243: SuitFaceoffTaunts['mb'][7],
+ 20244: SuitFaceoffTaunts['mb'][8],
+ 20245: SuitFaceoffTaunts['mb'][9],
+ 20246: SuitFaceoffTaunts['m'][0],
+ 20247: SuitFaceoffTaunts['m'][1],
+ 20248: SuitFaceoffTaunts['m'][2],
+ 20249: SuitFaceoffTaunts['m'][3],
+ 20250: SuitFaceoffTaunts['m'][4],
+ 20251: SuitFaceoffTaunts['m'][5],
+ 20252: SuitFaceoffTaunts['m'][6],
+ 20253: SuitFaceoffTaunts['m'][7],
+ 20254: SuitFaceoffTaunts['m'][8],
+ 20255: SuitFaceoffTaunts['mh'][0],
+ 20256: SuitFaceoffTaunts['mh'][1],
+ 20257: SuitFaceoffTaunts['mh'][2],
+ 20258: SuitFaceoffTaunts['mh'][3],
+ 20259: SuitFaceoffTaunts['mh'][4],
+ 20260: SuitFaceoffTaunts['mh'][5],
+ 20261: SuitFaceoffTaunts['mh'][6],
+ 20262: SuitFaceoffTaunts['mh'][7],
+ 20263: SuitFaceoffTaunts['mh'][8],
+ 20264: SuitFaceoffTaunts['mh'][9],
+ 20265: SuitFaceoffTaunts['mh'][10],
+ 20266: SuitFaceoffTaunts['mh'][11],
+ 20267: SuitFaceoffTaunts['dt'][0],
+ 20268: SuitFaceoffTaunts['dt'][1],
+ 20269: SuitFaceoffTaunts['dt'][2],
+ 20270: SuitFaceoffTaunts['dt'][3],
+ 20271: SuitFaceoffTaunts['dt'][4],
+ 20272: SuitFaceoffTaunts['dt'][5],
+ 20273: SuitFaceoffTaunts['dt'][6],
+ 20274: SuitFaceoffTaunts['dt'][7],
+ 20275: SuitFaceoffTaunts['dt'][8],
+ 20276: SuitFaceoffTaunts['dt'][9],
+ 20277: SuitFaceoffTaunts['p'][0],
+ 20278: SuitFaceoffTaunts['p'][1],
+ 20279: SuitFaceoffTaunts['p'][2],
+ 20280: SuitFaceoffTaunts['p'][3],
+ 20281: SuitFaceoffTaunts['p'][4],
+ 20282: SuitFaceoffTaunts['p'][5],
+ 20283: SuitFaceoffTaunts['p'][6],
+ 20284: SuitFaceoffTaunts['p'][7],
+ 20285: SuitFaceoffTaunts['p'][8],
+ 20286: SuitFaceoffTaunts['p'][9],
+ 20287: SuitFaceoffTaunts['p'][10],
+ 20288: SuitFaceoffTaunts['tm'][0],
+ 20289: SuitFaceoffTaunts['tm'][1],
+ 20290: SuitFaceoffTaunts['tm'][2],
+ 20291: SuitFaceoffTaunts['tm'][3],
+ 20292: SuitFaceoffTaunts['tm'][4],
+ 20293: SuitFaceoffTaunts['tm'][5],
+ 20294: SuitFaceoffTaunts['tm'][6],
+ 20295: SuitFaceoffTaunts['tm'][7],
+ 20296: SuitFaceoffTaunts['tm'][8],
+ 20297: SuitFaceoffTaunts['tm'][9],
+ 20298: SuitFaceoffTaunts['tm'][10],
+ 20299: SuitFaceoffTaunts['bw'][0],
+ 20300: SuitFaceoffTaunts['bw'][1],
+ 20301: SuitFaceoffTaunts['bw'][2],
+ 20302: SuitFaceoffTaunts['bw'][3],
+ 20303: SuitFaceoffTaunts['bw'][4],
+ 20304: SuitFaceoffTaunts['bw'][5],
+ 20305: SuitFaceoffTaunts['bw'][6],
+ 20306: SuitFaceoffTaunts['bw'][7],
+ 20307: SuitFaceoffTaunts['bw'][8],
+ 20308: SuitFaceoffTaunts['bw'][9],
+ 20309: SuitFaceoffTaunts['ls'][0],
+ 20310: SuitFaceoffTaunts['ls'][1],
+ 20311: SuitFaceoffTaunts['ls'][2],
+ 20312: SuitFaceoffTaunts['ls'][3],
+ 20313: SuitFaceoffTaunts['ls'][4],
+ 20314: SuitFaceoffTaunts['ls'][5],
+ 20315: SuitFaceoffTaunts['ls'][6],
+ 20316: SuitFaceoffTaunts['ls'][7],
+ 20317: SuitFaceoffTaunts['ls'][8],
+ 20318: SuitFaceoffTaunts['ls'][9],
+ 20319: SuitFaceoffTaunts['ls'][10],
+ 20320: SuitFaceoffTaunts['rb'][0],
+ 20321: SuitFaceoffTaunts['rb'][1],
+ 20322: SuitFaceoffTaunts['rb'][2],
+ 20323: SuitFaceoffTaunts['rb'][3],
+ 20324: SuitFaceoffTaunts['rb'][4],
+ 20325: SuitFaceoffTaunts['rb'][5],
+ 20326: SuitFaceoffTaunts['rb'][6],
+ 20327: SuitFaceoffTaunts['rb'][7],
+ 20328: SuitFaceoffTaunts['rb'][8],
+ 20329: SuitFaceoffTaunts['rb'][9],
+ 20330: SuitFaceoffTaunts['sc'][0],
+ 20331: SuitFaceoffTaunts['sc'][1],
+ 20332: SuitFaceoffTaunts['sc'][2],
+ 20333: SuitFaceoffTaunts['sc'][3],
+ 20334: SuitFaceoffTaunts['sc'][4],
+ 20335: SuitFaceoffTaunts['sc'][5],
+ 20336: SuitFaceoffTaunts['sc'][6],
+ 20337: SuitFaceoffTaunts['sc'][7],
+ 20338: SuitFaceoffTaunts['sc'][8],
+ 20339: SuitFaceoffTaunts['sc'][9],
+ 20340: SuitFaceoffTaunts['sc'][10],
+ 20341: SuitFaceoffTaunts['sd'][0],
+ 20342: SuitFaceoffTaunts['sd'][1],
+ 20343: SuitFaceoffTaunts['sd'][2],
+ 20344: SuitFaceoffTaunts['sd'][3],
+ 20345: SuitFaceoffTaunts['sd'][4],
+ 20346: SuitFaceoffTaunts['sd'][5],
+ 20347: SuitFaceoffTaunts['sd'][6],
+ 20348: SuitFaceoffTaunts['sd'][7],
+ 20349: SuitFaceoffTaunts['sd'][8],
+ 20350: SuitFaceoffTaunts['sd'][9],
+ 21000: 'Here boy!',
+ 21001: 'Here girl!',
+ 21002: 'Stay.',
+ 21003: 'Good boy!',
+ 21004: 'Good girl!',
+ 21005: 'Nice Doodle.',
+ 21006: "Please don't bother me.",
+ 21200: 'Jump!',
+ 21201: 'Beg!',
+ 21202: 'Play dead!',
+ 21203: 'Rollover!',
+ 21204: 'Backflip!',
+ 21205: 'Dance!',
+ 21206: 'Speak!',
+ 30100: "Happy April Toons' Week!",
+ 30101: "Welcome to my April Toons' Week party!",
+ 30102: 'The Silly Meter is back in Toon Hall!',
+ 30110: 'Mickey is in Daisy Gardens.',
+ 30111: 'Daisy is in Toontown Central.',
+ 30112: 'Minnie is in The Brrrgh.',
+ 30113: 'Pluto is in Melodyland.',
+ 30114: 'Donald is sleepwalking at the Speedway.',
+ 30115: 'Goofy is in Dreamland.',
+ 30120: 'Mickey is acting like Daisy!',
+ 30121: 'Daisy is acting like Mickey!',
+ 30122: 'Minnie is acting like Pluto!',
+ 30123: 'Pluto is acting like Minnie!',
+ 30124: 'Pluto is talking!',
+ 30125: 'Goofy is acting like Donald!',
+ 30126: 'Donald is dreaming he is Goofy!',
+ 30130: 'Watch how far I can jump.',
+ 30131: 'Wow, you jumped really far!',
+ 30132: 'Hey, Doodles can talk!',
+ 30133: 'Did your Doodle just talk?',
+ 30140: 'Things sure are silly around here!',
+ 30141: 'How sillier could things get?',
+ 30150: 'Operation: Storm Sellbot is here!',
+ 30151: 'Sellbot Towers had its power drained by Doodles!',
+ 30152: 'The VP had his power drained by Doodles!',
+ 30153: 'Everyone can fight the VP right now!',
+ 30154: "You don't need a Sellbot Disguise to fight the VP!",
+ 30155: 'You get a Rental Suit when you go into Sellbot Towers.',
+ 30156: 'Do you like my Rental Suit? Sorry about the safety pins!',
+ 30157: "It's best to have eight Toons to fight the VP.",
+ 30158: 'Will you help me fight the VP?',
+ 30159: 'Do you want to fight the VP with me?',
+ 30160: 'Would you like to join my Sellbot VP group?',
+ 30161: 'I am looking for a Toon with a Rental Suit to fight the VP.',
+ 30162: 'I have a Rental Suit, and am looking to fight the VP.',
+ 30163: 'Just walk through the doors to get your Rental Suit.',
+ 30164: 'Save your gags for the Cogs inside!',
+ 30165: 'We have to defeat these Cogs first!',
+ 30166: 'Bump the barrels to gag up.',
+ 30167: 'Bump the barrel to get a Toon-up.',
+ 30168: 'Now we have to fight some Skelecogs!',
+ 30169: "Jump up and touch the Toon's cage for pies!",
+ 30170: 'Now we fight the VP!',
+ 30171: 'Aim your pies by pressing the Delete button.',
+ 30172: "Two Toons should throw pies through the VP's open doors!",
+ 30173: "I'll stun the VP from the front.",
+ 30174: "I'll stun the VP from the back.",
+ 30175: 'Jump when the VP jumps!',
+ 30180: 'I got double jellybeans on the Trolley!',
+ 30181: 'I got double jellybeans from fishing!',
+ 30182: 'I got double jellybeans at a party!',
+ 30183: 'Jellybeans jellybeans jellybeans!',
+ 30184: "I'm really keen to earn a bean!",
+ 30185: "Don't be smelly, get beans of jelly!",
+ 30186: "I'm gonna adopt a Doodle with all these jellybeans!",
+ 30187: 'What am I gonna spend all these jellybeans on?',
+ 30188: "I'm gonna throw a huge party!",
+ 30189: "I'm gonna decorate my whole Estate!",
+ 30190: "I'm gonna buy a whole new wardrobe!",
+ 30191: 'Jellybeans, please!',
+ 30192: "Don't be mean, give a bean!",
+ 30193: 'Who wants jellybeans?',
+ 30194: 'Dance for jellybeans!',
+ 30200: 'Deck the halls... ',
+ 30201: 'Load some pies...',
+ 30202: 'Joyful toons...',
+ 30203: 'Snowman heads...',
+ 30204: "Toontown's merry...",
+ 30205: 'Lure good cheer...',
+ 30220: 'Deck the halls with seltzer spray!\nHappy Winter Holiday!',
+ 30221: 'Load some pies into your sleigh!\nHappy Winter Holiday!',
+ 30222: 'Joyful toons bring Cogs dismay!\nHappy Winter Holiday!',
+ 30223: 'Snowman heads are hot today!\nHappy Winter Holiday!',
+ 30224: "Toontown's merry, come what may!\nHappy Winter Holiday!",
+ 30225: 'Lure good cheer the Toontown way!\nHappy Winter Holiday!',
+ 30250: 'Boo!',
+ 30251: 'Happy Halloween!',
+ 30252: 'Spooky!',
+ 30275: 'Happy holidays!',
+ 30276: "Season's greetings!",
+ 30277: 'Have a Wonderful Winter!',
+ 30301: 'Have you seen the Silly Meter?',
+ 30302: 'The Silly Meter is in Toon Hall.',
+ 30303: 'Things sure are getting silly around here!',
+ 30304: 'I saw a fire hydrant moving!',
+ 30305: 'Toontown is coming to life!',
+ 30306: "Have you been to Flippy's new office?",
+ 30307: 'I caused a Silly Surge in battle!',
+ 30308: "Let's defeat some Cogs to make Toontown sillier!",
+ 30309: 'The Silly Meter is bigger and crazier than ever!',
+ 30310: 'Lots of hydrants have come alive!',
+ 30311: 'I saw a mail box moving!',
+ 30312: 'I watched a trash can wake up!',
+ 30313: 'How silly can it get?',
+ 30314: "What's going to happen next?",
+ 30315: 'Something silly, I bet!',
+ 30316: 'Have you caused a Silly Surge yet?',
+ 30317: "Let's defeat some Cogs to make Toontown sillier!",
+ 30318: 'Cog Invasion!',
+ 30319: 'Incoming!',
+ 30320: "Let's stop those Cogs!",
+ 30321: 'I miss the Silly Surges!',
+ 30322: "Let's go stop an Invasion!",
+ 30323: 'Toontown is sillier than ever now!',
+ 30324: 'Have you seen something come alive?',
+ 30325: 'My favorites are the fire hydrants!',
+ 30326: 'My favorites are the mailboxes!',
+ 30327: 'My favorites are the trash cans!',
+ 30328: 'Hooray! We stopped the Cog invasions!',
+ 30329: 'A hydrant helped me in battle!',
+ 30330: 'A hydrant boosted my Squirt Gags!',
+ 30331: 'A trash can boosted my Toon-Up Gags!',
+ 30332: 'A mailbox helped my Throw Gags!',
+ 30350: 'Welcome to my Victory Party!',
+ 30351: 'This is a great Victory Party!',
+ 30352: "We showed those Cogs who's boss!",
+ 30353: 'Good job helping end the Cog invasions!',
+ 30354: 'I bet this is driving the Cogs crazy!',
+ 30355: "Let's play Cog-O-War!",
+ 30356: 'My team won at Cog-O-War!',
+ 30357: "It's nice to have fire hydrants, trash cans, and mailboxes here!",
+ 30358: 'I like the balloon of the Doodle biting the Cog!',
+ 30359: 'I like the balloon of the Cog covered in ice cream!',
+ 30360: 'I like the wavy Cog that flaps his arms!',
+ 30361: "I jumped on a Cog's face!",
+ 30400: 'The Sellbots are invading!',
+ 30401: 'The V.P. was hopping mad about Operation: Storm Sellbot ...',
+ 30402: "He's sending the Sellbots in to invade Toontown!",
+ 30403: "Let's go fight some Sellbots!",
+ 30404: "There's a new kind of building in Toontown!",
+ 30405: 'Have you seen the Mover & Shaker Field Offices?',
+ 30406: 'The V.P. created them as a reward for the Movers & Shakers.',
+ 30407: "Let's go defeat a Field Office!",
+ 30408: 'I got an SOS Card for defeating a Field Office!',
+ 30409: 'Clear the map by exploring the maze.',
+ 30410: 'Destroy the Cogs by hitting them with water balloons!',
+ 30411: 'Movers & Shakers take two balloons to destroy.',
+ 30412: 'Look out for falling objects!',
+ 30413: 'Watch out for the Cogs!',
+ 30414: 'Collect Jokes to get a Toon-up at the end!',
+ 30415: 'When the room shakes, a Mover & Shaker is nearby.',
+ 30416: 'Defeat all four Movers & Shakers to open the exit!',
+ 30417: 'The exit is open!',
+ 30418: "It's the Boss!",
+ 30450: "It's easy to be green!",
+ 30451: 'Visit Green Bean Jeans and you can be green too!',
+ 30452: "It's on Oak Street in Daisy Gardens."}
+SpeedChatStaticTextPirates = {50001: 'Aye',
+ 50002: 'Nay',
+ 50003: 'Yes',
+ 50004: 'No',
+ 50005: 'Ok',
+ 50100: 'Gangway!',
+ 50101: 'Blimey!',
+ 50102: 'Well blow me down!',
+ 50103: 'Walk the plank!',
+ 50104: 'Dead men tell no tales....',
+ 50105: 'Shiver me timbers!',
+ 50106: "Salty as a Kraken's kiss.",
+ 50107: 'Treasure be the measure of our pleasure!',
+ 50108: "I don't fear death - I attune it.",
+ 50700: 'Ahoy!',
+ 50701: 'Ahoy, mate!',
+ 50702: 'Yo-Ho-Ho',
+ 50703: 'Avast!',
+ 50704: 'Hey Bucko.',
+ 50800: 'Until next time.',
+ 50801: 'May fair winds find ye.',
+ 50802: 'Godspeed.',
+ 50900: 'How are ye, mate?',
+ 50901: '',
+ 51000: "It's like the sky is raining gold doubloons!",
+ 51001: 'May a stiff wind be at our backs, the sun on our faces and our cannons fire true!',
+ 51100: 'I be sailing some rough waters today.',
+ 51200: 'Me apologies, mate.',
+ 51201: 'Sorry.',
+ 51202: 'Sorry, I was busy before.',
+ 51203: 'Sorry, I already have plans.',
+ 51204: "Sorry, I don't need to do that.",
+ 51300: 'Attack the weakest one!',
+ 51301: 'Attack the strongest one!',
+ 51302: 'Attack me target!',
+ 51303: 'I be needing help!',
+ 51304: "I can't do any damage!",
+ 51305: 'I think we be in trouble.',
+ 51306: 'Surround the most powerful one.',
+ 51307: 'We should retreat.',
+ 51308: 'Run for it!',
+ 51400: 'Fire a Broadside!',
+ 51401: 'Port Side! (left)',
+ 51402: 'Starboard Side! (right)',
+ 51403: 'Incoming!',
+ 51404: 'Come about!',
+ 51405: 'Broadside! Take Cover!',
+ 51406: 'To the Cannons!',
+ 51407: 'Open fire!',
+ 51408: 'Hold yer fire!',
+ 51409: 'Aim for the masts!',
+ 51410: 'Aim for the hull!',
+ 51411: 'Prepare to board!',
+ 51412: "She's coming about.",
+ 51413: 'Ramming speed!',
+ 51414: "We've got her on the run.",
+ 51415: 'We be taking on water!',
+ 51416: "We can't take anymore!",
+ 51417: "I don't have a shot!",
+ 51418: "Let's find port for repair.",
+ 51419: 'Man overboard!',
+ 51420: 'Enemy spotted.',
+ 51421: 'Handsomely now, mates!',
+ 50400: "Let's set sail.",
+ 50401: "Let's get out of here.",
+ 51500: "Let's sail to Port Royal.",
+ 51501: "Let's sail to Tortuga.",
+ 51502: "Let's sail to Padres Del Fuego.",
+ 51503: "Let's sail to Devil's Anvil.",
+ 51504: "Let's sail to Kingshead.",
+ 51505: "Let's sail to Isla Perdida.",
+ 51506: "Let's sail to Cuba.",
+ 51507: "Let's sail to Tormenta.",
+ 51508: "Let's sail to Outcast Isle.",
+ 51509: "Let's sail to Driftwood.",
+ 51510: "Let's sail to Cutthroat.",
+ 51511: "Let's sail to Rumrunner's Isle.",
+ 51512: "Let's sail to Isla Cangrejos.",
+ 51600: "Let's head into town.",
+ 51601: "Let's go to the docks.",
+ 51602: "Let's head to the tavern.",
+ 51800: "Let's go to Fort Charles.",
+ 51801: "Let's go to the Governor's Mansion.",
+ 52500: 'Where be I, mate?',
+ 51700: 'Yer already there.',
+ 51701: "I don't know.",
+ 51702: 'Yer on the wrong island.',
+ 51703: "That's in town.",
+ 51704: 'Look just outside of town.',
+ 51705: 'Ye will have to search through the jungle.',
+ 51706: 'Deeper inland.',
+ 51707: 'Oh, that be by the coast.',
+ 50200: 'Bilge rat!',
+ 50201: 'Scurvy dog!',
+ 50202: 'See ye in Davy Jones locker!',
+ 50203: 'Scoundrel!',
+ 50204: 'Landlubber!',
+ 50205: 'Addle-minded fool!',
+ 50206: 'You need a sharp sword and sharper wits.',
+ 50207: 'Ye be one doubloon short of a full hull mate!',
+ 50208: "Watch yer tongue or I'll pickle it with sea salt!",
+ 50209: 'Touch me loot and you get the boot!',
+ 50210: 'The horizon be as empty as yer head.',
+ 50211: "You're a canvas shy of a full sail, aren't ye mate?",
+ 50300: 'Fine shooting mate!',
+ 50301: 'A well placed blow!',
+ 50302: 'Nice shot!',
+ 50303: 'Well met!',
+ 50304: 'We showed them!',
+ 50305: 'Yer not so bad yerself!',
+ 50306: 'A fine plunder haul!',
+ 52400: 'May luck be my lady.',
+ 52401: 'I think these cards be marked!',
+ 52402: 'Blimey cheater!',
+ 51900: "That's a terrible flop!",
+ 51901: 'Trying to buy the hand, are ye?',
+ 51902: 'Ye be bluffing.',
+ 51903: "I don't think ye had it.",
+ 51904: 'Saved by the river.',
+ 52600: 'Hit me.',
+ 52601: 'Can I get another dealer?',
+ 53101: 'I caught a fish!',
+ 53102: 'I saw a Legendary Fish!',
+ 53103: 'What did you catch?',
+ 53104: 'This will make a whale of a tale!',
+ 53105: 'That was a beauty!',
+ 53106: 'Arr, the sea is treacherous today.',
+ 53107: 'What a bountiful haul of fish!',
+ 53110: 'Do you have the Legendary Lure?',
+ 53111: 'Have you ever caught a Legendary Fish?',
+ 53112: 'Can you sail on a fishing boat?',
+ 53113: 'Where is the Fishing Master?',
+ 53114: 'Have you completed your fish collection?',
+ 53120: 'Fire at my target!',
+ 53121: 'Fire at the ship closest to the shore!',
+ 53122: "There's a ship getting away!",
+ 53123: 'Fire at the big ships!',
+ 53124: 'Fire at the small ships!',
+ 53125: 'More are coming!',
+ 53126: "We're not going to last much longer!",
+ 53127: 'Shoot the barrels!',
+ 53128: "We've got new ammo!",
+ 53129: 'Sturdy defense, mates!',
+ 53141: 'Look at the potion I made!',
+ 53142: 'Have you completed your potion collection?',
+ 53143: 'Where is the Gypsy?',
+ 53144: 'What potion is that?',
+ 53145: 'This potion was easy enough.',
+ 53146: "This potion was hard brewin', I tell ye!",
+ 53160: 'We need someone to bilge pump!',
+ 53161: 'We need someone to scrub!',
+ 53162: 'We need someone to saw!',
+ 53163: 'We need someone to brace!',
+ 53164: 'We need someone to hammer!',
+ 53165: 'We need someone to patch!',
+ 53166: "I'll do it!",
+ 53167: "Keep it up, this ship won't repair itself!",
+ 53168: 'Great job repairing the ship!',
+ 52100: 'Want to group up?',
+ 52101: 'Join me crew?',
+ 52200: 'Fight some skeletons?',
+ 52201: 'Fight some crabs?',
+ 52300: "How 'bout a game of Mayhem?",
+ 52301: 'Join me Mayhem game.',
+ 52302: 'Want to start a Mayhem game?',
+ 52303: 'Want to start a team battle game?',
+ 52304: 'Join me team battle game.',
+ 52350: 'Join my Cannon Defense.',
+ 52351: 'Want to start a Cannon Defense?',
+ 52352: 'Can you lend me a hand with Repair?',
+ 52353: 'We need to Repair the ship now!',
+ 52354: 'Care to catch some fish?',
+ 52355: 'Want to go fishing with me?',
+ 52356: "Join me crew for some fishin'?",
+ 52357: 'Time to brew some potions!',
+ 52358: 'You should try your hand at brewing potions.',
+ 52000: '',
+ 52000: '',
+ 52700: '',
+ 53000: '',
+ 52800: '',
+ 52900: '',
+ 50500: '',
+ 50600: '',
+ 60100: 'Hi!',
+ 60101: 'Hello!',
+ 60102: 'Hey!',
+ 60103: 'Yo!',
+ 60104: 'Hi everybody!',
+ 60105: 'How are you doing?',
+ 60106: "What's Up?",
+ 60200: 'Bye!',
+ 60201: 'Later!',
+ 60202: 'See ya!',
+ 60203: "I'll be right back.",
+ 60204: 'I need to go.',
+ 60300: ':-)',
+ 60301: 'Cool!',
+ 60302: 'Yeah!',
+ 60303: 'Ha ha!',
+ 60304: 'Sweet!',
+ 60305: 'Yeah!',
+ 60306: 'That rocks!',
+ 60307: 'Funky!',
+ 60308: 'Awesome!',
+ 60309: 'Wow!',
+ 60400: ':-(',
+ 60401: 'Doh!',
+ 60402: 'Aw man!',
+ 60403: 'Ouch!',
+ 60404: 'Bummer!',
+ 60500: 'Where are you?',
+ 60501: "Let's go to the Gateway Store.",
+ 60502: "Let's go to the Disco Hall.",
+ 60503: "Let's go to Toontown.",
+ 60504: "Let's go to Pirates of the Carribean.",
+ 60505: 'Flip coin',
+ 60506: 'Dance',
+ 60507: 'Chant 1',
+ 60508: 'Chant 2',
+ 60509: 'Dance a jig',
+ 60510: 'Sleep',
+ 60511: 'Flex',
+ 60512: 'Play Lute',
+ 60513: 'Play Flute',
+ 60514: 'Frustrated',
+ 60515: 'Searching',
+ 60516: 'Yawn',
+ 60517: 'Kneel',
+ 60518: 'Sweep',
+ 60519: 'Primp',
+ 60520: 'Yawn',
+ 60521: 'Dance',
+ 60522: 'No',
+ 60523: 'Yes',
+ 60524: 'Laugh',
+ 60525: 'Clap',
+ 60526: 'Smile',
+ 60527: 'Anger',
+ 60528: 'Fear',
+ 60529: 'Sad',
+ 60530: 'Celebrate',
+ 60668: 'Celebrate',
+ 60669: 'Sleep',
+ 60602: 'Angry',
+ 60614: 'Clap',
+ 60622: 'Scared',
+ 60640: 'Laugh',
+ 60652: 'Sad',
+ 60657: 'Smile',
+ 60664: 'Wave',
+ 60665: 'Wink',
+ 60666: 'Yawn',
+ 60669: 'Sleep',
+ 60670: 'Dance',
+ 60676: 'Flirt',
+ 60677: 'Zombie dance',
+ 60678: 'Noisemaker',
+ 60671: "Hello, I'm a Pirate, and I'm here to steal your heart.",
+ 60672: "I just found the treasure I've been searching for.",
+ 60673: "If you were a booger, I'd pick you first.",
+ 60674: 'Come to Tortuga often?',
+ 60675: 'Do you have a map? I just keep getting lost in your eyes.',
+ 65000: 'Yes',
+ 65001: 'No',
+ 60909: 'Check Hand'}
+SpeedChatStaticText = SpeedChatStaticTextCommon
+Emotes_Root = 'EMOTES'
+Emotes_Dances = 'Dances'
+Emotes_General = 'General'
+Emotes_Music = 'Music'
+Emotes_Expressions = 'Emotions'
+Emote_ShipDenied = 'Cannot emote while sailing.'
+Emote_MoveDenied = 'Cannot emote while moving.'
+Emote_CombatDenied = 'Cannot emote while in combat.'
+Emote_CannonDenied = 'Cannot emote while using a cannon.'
+Emote_SwimDenied = 'Cannot emote while swimming.'
+Emote_ParlorGameDenied = 'Cannot emote while playing a parlor game.'
+Emotes = (60505,
+ 60506,
+ 60509,
+ 60510,
+ 60511,
+ 60516,
+ 60519,
+ 60520,
+ 60521,
+ 60522,
+ 60523,
+ 60524,
+ 60525,
+ 60526,
+ 60527,
+ 60528,
+ 60529,
+ 60530,
+ 60602,
+ 60607,
+ 60611,
+ 60614,
+ 60615,
+ 60622,
+ 60627,
+ 60629,
+ 60632,
+ 60636,
+ 60638,
+ 60640,
+ 60644,
+ 60652,
+ 60654,
+ 60657,
+ 60658,
+ 60663,
+ 60664,
+ 60665,
+ 60666,
+ 60668,
+ 60669,
+ 60612,
+ 60661,
+ 60645,
+ 60629,
+ 60641,
+ 60654,
+ 60630,
+ 60670,
+ 60633,
+ 60676,
+ 60677,
+ 65000,
+ 65001,
+ 60517,
+ 60678,
+ 60909)
+SCFactoryMeetMenuIndexes = (1903,
+ 1904,
+ 1906,
+ 1907,
+ 1908,
+ 1910,
+ 1913,
+ 1915,
+ 1916,
+ 1917,
+ 1919,
+ 1922,
+ 1923,
+ 1924,
+ 1932,
+ 1940,
+ 1941)
+CustomSCStrings = {10: 'Oh, well.',
+ 20: 'Why not?',
+ 30: 'Naturally!',
+ 40: "That's the way to do it.",
+ 50: 'Right on!',
+ 60: 'What up?',
+ 70: 'But of course!',
+ 80: 'Bingo!',
+ 90: "You've got to be kidding...",
+ 100: 'Sounds good to me.',
+ 110: "That's kooky!",
+ 120: 'Awesome!',
+ 130: 'For crying out loud!',
+ 140: "Don't worry.",
+ 150: 'Grrrr!',
+ 160: "What's new?",
+ 170: 'Hey, hey, hey!',
+ 180: 'See you tomorrow.',
+ 190: 'See you next time.',
+ 200: 'See ya later, alligator.',
+ 210: 'After a while, crocodile.',
+ 220: 'I need to go soon.',
+ 230: "I don't know about this!",
+ 240: "You're outta here!",
+ 250: 'Ouch, that really smarts!',
+ 260: 'Gotcha!',
+ 270: 'Please!',
+ 280: 'Thanks a million!',
+ 290: "You are stylin'!",
+ 300: 'Excuse me!',
+ 310: 'Can I help you?',
+ 320: "That's what I'm talking about!",
+ 330: "If you can't take the heat, stay out of the kitchen.",
+ 340: 'Well shiver me timbers!',
+ 350: "Well isn't that special!",
+ 360: 'Quit horsing around!',
+ 370: 'Cat got your tongue?',
+ 380: "You're in the dog house now!",
+ 390: 'Look what the cat dragged in.',
+ 400: 'I need to go see a Toon.',
+ 410: "Don't have a cow!",
+ 420: "Don't chicken out!",
+ 430: "You're a sitting duck.",
+ 440: 'Whatever!',
+ 450: 'Totally!',
+ 460: 'Sweet!',
+ 470: 'That rules!',
+ 480: 'Yeah, baby!',
+ 490: 'Catch me if you can!',
+ 500: 'You need to heal first.',
+ 510: 'You need more Laff Points.',
+ 520: "I'll be back in a minute.",
+ 530: "I'm hungry.",
+ 540: 'Yeah, right!',
+ 550: "I'm sleepy.",
+ 560: "I'm ready!",
+ 570: "I'm bored.",
+ 580: 'I love it!',
+ 590: 'That was exciting!',
+ 600: 'Jump!',
+ 610: 'Got gags?',
+ 620: "What's wrong?",
+ 630: 'Easy does it.',
+ 640: 'Slow and steady wins the race.',
+ 650: 'Touchdown!',
+ 660: 'Ready?',
+ 670: 'Set!',
+ 680: 'Go!',
+ 690: "Let's go this way!",
+ 700: 'You won!',
+ 710: 'I vote yes.',
+ 720: 'I vote no.',
+ 730: 'Count me in.',
+ 740: 'Count me out.',
+ 750: "Stay here, I'll be back.",
+ 760: 'That was quick!',
+ 770: 'Did you see that?',
+ 780: "What's that smell?",
+ 790: 'That stinks!',
+ 800: "I don't care.",
+ 810: 'Just what the doctor ordered.',
+ 820: "Let's get this party started!",
+ 830: 'This way everybody!',
+ 840: 'What in the world?',
+ 850: "The check's in the mail.",
+ 860: 'I heard that!',
+ 870: 'Are you talking to me?',
+ 880: "Thank you, I'll be here all week.",
+ 890: 'Hmm.',
+ 900: "I'll get this one.",
+ 910: 'I got it!',
+ 920: "It's mine!",
+ 930: 'Please, take it.',
+ 940: 'Stand back, this could be dangerous.',
+ 950: 'No worries!',
+ 960: 'Oh, my!',
+ 970: 'Whew!',
+ 980: 'Owoooo!',
+ 990: 'All Aboard!',
+ 1000: 'Hot Diggity Dog!',
+ 1010: 'Curiosity killed the cat.',
+ 2000: 'Act your age!',
+ 2010: 'Am I glad to see you!',
+ 2020: 'Be my guest.',
+ 2030: 'Been keeping out of trouble?',
+ 2040: 'Better late than never!',
+ 2050: 'Bravo!',
+ 2060: 'But seriously, folks...',
+ 2070: 'Care to join us?',
+ 2080: 'Catch you later!',
+ 2090: 'Changed your mind?',
+ 2100: 'Come and get it!',
+ 2110: 'Dear me!',
+ 2120: 'Delighted to make your acquaintance.',
+ 2130: "Don't do anything I wouldn't do!",
+ 2140: "Don't even think about it!",
+ 2150: "Don't give up the ship!",
+ 2160: "Don't hold your breath.",
+ 2170: "Don't ask.",
+ 2180: 'Easy for you to say.',
+ 2190: 'Enough is enough!',
+ 2200: 'Excellent!',
+ 2210: 'Fancy meeting you here!',
+ 2220: 'Give me a break.',
+ 2230: 'Glad to hear it.',
+ 2240: 'Go ahead, make my day!',
+ 2250: 'Go for it!',
+ 2260: 'Good job!',
+ 2270: 'Good to see you!',
+ 2280: 'Got to get moving.',
+ 2290: 'Got to hit the road.',
+ 2300: 'Hang in there.',
+ 2310: 'Hang on a second.',
+ 2320: 'Have a ball!',
+ 2330: 'Have fun!',
+ 2340: "Haven't got all day!",
+ 2350: 'Hold your horses!',
+ 2360: 'Horsefeathers!',
+ 2370: "I don't believe this!",
+ 2380: 'I doubt it.',
+ 2390: 'I owe you one.',
+ 2400: 'I read you loud and clear.',
+ 2410: 'I think so.',
+ 2420: 'I think you should pass.',
+ 2430: "I wish I'd said that.",
+ 2440: "I wouldn't if I were you.",
+ 2450: "I'd be happy to!",
+ 2460: "I'm helping my friend.",
+ 2470: "I'm here all week.",
+ 2480: 'Imagine that!',
+ 2490: 'In the nick of time...',
+ 2500: "It's not over 'til it's over.",
+ 2510: 'Just thinking out loud.',
+ 2520: 'Keep in touch.',
+ 2530: 'Lovely weather for ducks!',
+ 2540: 'Make it snappy!',
+ 2550: 'Make yourself at home.',
+ 2560: 'Maybe some other time.',
+ 2570: 'Mind if I join you?',
+ 2580: 'Nice place you have here.',
+ 2590: 'Nice talking to you.',
+ 2600: 'No doubt about it.',
+ 2610: 'No kidding!',
+ 2620: 'Not by a long shot.',
+ 2630: 'Of all the nerve!',
+ 2640: 'Okay by me.',
+ 2650: 'Righto.',
+ 2660: 'Say cheese!',
+ 2670: 'Say what?',
+ 2680: 'Tah-dah!',
+ 2690: 'Take it easy.',
+ 2700: 'Ta-ta for now!',
+ 2710: 'Thanks, but no thanks.',
+ 2720: 'That takes the cake!',
+ 2730: "That's funny.",
+ 2740: "That's the ticket!",
+ 2750: "There's a Cog invasion!",
+ 2760: 'Toodles.',
+ 2770: 'Watch out!',
+ 2780: 'Well done!',
+ 2790: "What's cooking?",
+ 2800: "What's happening?",
+ 2810: 'Works for me.',
+ 2820: 'Yes sirree.',
+ 2830: 'You betcha.',
+ 2840: 'You do the math.',
+ 2850: 'You leaving so soon?',
+ 2860: 'You make me laugh!',
+ 2870: 'You take right.',
+ 2880: "You're going down!",
+ 3000: 'Anything you say.',
+ 3010: 'Care if I join you?',
+ 3020: 'Check, please.',
+ 3030: "Don't be too sure.",
+ 3040: "Don't mind if I do.",
+ 3050: "Don't sweat it!",
+ 3060: "Don't you know it!",
+ 3070: "Don't mind me.",
+ 3080: 'Eureka!',
+ 3090: 'Fancy that!',
+ 3100: 'Forget about it!',
+ 3110: 'Going my way?',
+ 3120: 'Good for you!',
+ 3130: 'Good grief.',
+ 3140: 'Have a good one!',
+ 3150: 'Heads up!',
+ 3160: 'Here we go again.',
+ 3170: 'How about that!',
+ 3180: 'How do you like that?',
+ 3190: 'I believe so.',
+ 3200: 'I think not.',
+ 3210: "I'll get back to you.",
+ 3220: "I'm all ears.",
+ 3230: "I'm busy.",
+ 3240: "I'm not kidding!",
+ 3250: "I'm speechless.",
+ 3260: 'Keep smiling.',
+ 3270: 'Let me know!',
+ 3280: 'Let the pie fly!',
+ 3290: "Likewise, I'm sure.",
+ 3300: 'Look alive!',
+ 3310: 'My, how time flies.',
+ 3320: 'No comment.',
+ 3330: "Now you're talking!",
+ 3340: 'Okay by me.',
+ 3350: 'Pleased to meet you.',
+ 3360: 'Righto.',
+ 3370: 'Sure thing.',
+ 3380: 'Thanks a million.',
+ 3390: "That's more like it.",
+ 3400: "That's the stuff!",
+ 3410: 'Time for me to hit the hay.',
+ 3420: 'Trust me!',
+ 3430: 'Until next time.',
+ 3440: 'Wait up!',
+ 3450: 'Way to go!',
+ 3460: 'What brings you here?',
+ 3470: 'What happened?',
+ 3480: 'What now?',
+ 3490: 'You first.',
+ 3500: 'You take left.',
+ 3510: 'You wish!',
+ 3520: "You're toast!",
+ 3530: "You're too much!",
+ 4000: 'Toons rule!',
+ 4010: 'Cogs drool!',
+ 4020: 'Toons of the world unite!',
+ 4030: 'Howdy, partner!',
+ 4040: 'Much obliged.',
+ 4050: 'Get along, little doggie.',
+ 4060: "I'm going to hit the hay.",
+ 4070: "I'm chomping at the bit!",
+ 4080: "This town isn't big enough for the two of us!",
+ 4090: 'Saddle up!',
+ 4100: 'Draw!!!',
+ 4110: "There's gold in them there hills!",
+ 4120: 'Happy trails!',
+ 4130: 'This is where I ride off into the sunset...',
+ 4140: "Let's skedaddle!",
+ 4150: 'You got a bee in your bonnet?',
+ 4160: 'Lands sake!',
+ 4170: 'Right as rain.',
+ 4180: 'I reckon so.',
+ 4190: "Let's ride!",
+ 4200: 'Well, go figure!',
+ 4210: "I'm back in the saddle again!",
+ 4220: 'Round up the usual suspects.',
+ 4230: 'Giddyup!',
+ 4240: 'Reach for the sky.',
+ 4250: "I'm fixing to.",
+ 4260: 'Hold your horses!',
+ 4270: "I can't hit the broad side of a barn.",
+ 4280: "Y'all come back now.",
+ 4290: "It's a real barn burner!",
+ 4300: "Don't be a yellow belly.",
+ 4310: 'Feeling lucky?',
+ 4320: "What in Sam Hill's goin' on here?",
+ 4330: 'Shake your tail feathers!',
+ 4340: "Well, don't that take all.",
+ 4350: "That's a sight for sore eyes!",
+ 4360: 'Pickins is mighty slim around here.',
+ 4370: 'Take a load off.',
+ 4380: "Aren't you a sight!",
+ 4390: "That'll learn ya!",
+ 6000: 'I want candy!',
+ 6010: "I've got a sweet tooth.",
+ 6020: "That's half-baked.",
+ 6030: 'Just like taking candy from a baby!',
+ 6040: "They're cheaper by the dozen.",
+ 6050: 'Let them eat cake!',
+ 6060: "That's the icing on the cake.",
+ 6070: "You can't have your cake and eat it too.",
+ 6080: 'I feel like a kid in a candy store.',
+ 6090: 'Six of one, half a dozen of the other...',
+ 6100: "Let's keep it short and sweet.",
+ 6110: 'Keep your eye on the doughnut not the hole.',
+ 6120: "That's pie in the sky.",
+ 6130: "But it's wafer thin.",
+ 6140: "Let's gum up the works!",
+ 6150: "You're one tough cookie!",
+ 6160: "That's the way the cookie crumbles.",
+ 6170: 'Like water for chocolate.',
+ 6180: 'Are you trying to sweet talk me?',
+ 6190: 'A spoonful of sugar helps the medicine go down.',
+ 6200: 'You are what you eat!',
+ 6210: 'Easy as pie!',
+ 6220: "Don't be a sucker!",
+ 6230: 'Sugar and spice and everything nice.',
+ 6240: "It's like butter!",
+ 6250: 'The candyman can!',
+ 6260: 'We all scream for ice cream!',
+ 6270: "Let's not sugar coat it.",
+ 6280: 'Knock knock...',
+ 6290: "Who's there?",
+ 7000: 'Quit monkeying around!',
+ 7010: 'That really throws a monkey-wrench in things.',
+ 7020: 'Monkey see, monkey do.',
+ 7030: 'They made a monkey out of you.',
+ 7040: 'That sounds like monkey business.',
+ 7050: "I'm just monkeying with you.",
+ 7060: "Who's gonna be monkey in the middle?",
+ 7070: "That's a monkey off my back...",
+ 7080: 'This is more fun than a barrel of monkeys!',
+ 7090: "Well I'll be a monkey's uncle.",
+ 7100: "I've got monkeys on the brain.",
+ 7110: "What's with the monkey suit?",
+ 7120: 'Hear no evil.',
+ 7130: 'See no evil.',
+ 7140: 'Speak no evil.',
+ 7150: "Let's make like a banana and split.",
+ 7160: "It's a jungle out there.",
+ 7170: "You're the top banana.",
+ 7180: 'Cool bananas!',
+ 7190: "I'm going bananas!",
+ 7200: "Let's get into the swing of things!",
+ 7210: 'This place is swinging!',
+ 7220: "I'm dying on the vine.",
+ 7230: 'This whole affair has me up a tree.',
+ 7230: "Let's make like a tree and leave.",
+ 7240: "Jellybeans don't grow on trees!",
+ 10000: 'This place is a ghost town.',
+ 10001: 'Nice costume!',
+ 10002: 'I think this place is haunted.',
+ 10003: 'Trick or Treat!',
+ 10004: 'Boo!',
+ 10005: 'Happy Haunting!',
+ 10006: 'Happy Halloween!',
+ 10007: "It's time for me to turn into a pumpkin.",
+ 10008: 'Spooktastic!',
+ 10009: 'Spooky!',
+ 10010: "That's creepy!",
+ 10011: 'I hate spiders!',
+ 10012: 'Did you hear that?',
+ 10013: "You don't have a ghost of a chance!",
+ 10014: 'You scared me!',
+ 10015: "That's spooky!",
+ 10016: "That's freaky!",
+ 10017: 'That was strange....',
+ 10018: 'Skeletons in your closet?',
+ 10019: 'Did I scare you?',
+ 11000: 'Bah! Humbug!',
+ 11001: 'Better not pout!',
+ 11002: 'Brrr!',
+ 11003: 'Chill out!',
+ 11004: 'Come and get it!',
+ 11005: "Don't be a turkey.",
+ 11006: 'Gobble gobble!',
+ 11007: 'Happy holidays!',
+ 11008: 'Happy New Year!',
+ 11009: 'Happy Thanksgiving!',
+ 11010: 'Happy Turkey Day!',
+ 11011: 'Ho! Ho! Ho!',
+ 11012: 'It\'s "snow" problem.',
+ 11013: 'It\'s "snow" wonder.',
+ 11014: 'Let it snow!',
+ 11015: "Rake 'em in.",
+ 11016: "Season's greetings!",
+ 11017: 'Snow doubt about it!',
+ 11018: 'Snow far, snow good!',
+ 11019: 'Yule be sorry!',
+ 11020: 'Have a Wonderful Winter!',
+ 11021: 'The Holiday Party decorations are Toontastic!',
+ 11022: 'Toon Troopers are hosting Holiday Parties!',
+ 12000: 'Be mine!',
+ 12001: 'Be my sweetie!',
+ 12002: "Happy ValenToon's Day!",
+ 12003: 'Aww, how cute.',
+ 12004: "I'm sweet on you.",
+ 12005: "It's puppy love.",
+ 12006: 'Love ya!',
+ 12007: 'Will you be my ValenToon?',
+ 12008: 'You are a sweetheart.',
+ 12009: 'You are as sweet as pie.',
+ 12010: 'You are cute.',
+ 12011: 'You need a hug.',
+ 12012: 'Lovely!',
+ 12013: "That's darling!",
+ 12014: 'Roses are red...',
+ 12015: 'Violets are blue...',
+ 12016: "That's sweet!",
+ 12050: 'I LOVE busting Cogs!',
+ 12051: "You're dynamite!",
+ 12052: 'I only have hypno-eyes for you!',
+ 12053: "You're sweeter than a jellybean!",
+ 12054: "I'd LOVE for you to come to my ValenToon's party!",
+ 13000: "Top o' the mornin' to you!",
+ 13001: "Happy St. Patrick's Day!",
+ 13002: "You're not wearing green!",
+ 13003: "It's the luck of the Irish.",
+ 13004: "I'm green with envy.",
+ 13005: 'You lucky dog!',
+ 13006: "You're my four leaf clover!",
+ 13007: "You're my lucky charm!",
+ 14000: "Let's have a summer Estate party!",
+ 14001: "It's party time!",
+ 14002: 'Last one in the pond is a rotten Cog!',
+ 14003: 'Group Doodle training time!',
+ 14004: 'Doodle training time!',
+ 14005: 'Your Doodle is cool!',
+ 14006: 'What tricks can your Doodle do?',
+ 14007: 'Time for Cannon Pinball!',
+ 14008: 'Cannon Pinball rocks!',
+ 14009: 'Your Estate rocks!',
+ 14010: 'Your Garden is cool!',
+ 14011: 'Your Estate is cool!'}
+SCMenuCommonCogIndices = (20000, 20004)
+SCMenuCustomCogIndices = {'bf': (20005, 20014),
+ 'nc': (20015, 20024),
+ 'ym': (20025, 20035),
+ 'ms': (20036, 20046),
+ 'bc': (20047, 20057),
+ 'cc': (20058, 20070),
+ 'nd': (20071, 20080),
+ 'ac': (20081, 20092),
+ 'tf': (20093, 20103),
+ 'hh': (20104, 20114),
+ 'le': (20115, 20124),
+ 'bs': (20125, 20135),
+ 'cr': (20136, 20145),
+ 'tbc': (20146, 20156),
+ 'ds': (20157, 20164),
+ 'gh': (20165, 20177),
+ 'pp': (20178, 20187),
+ 'b': (20188, 20199),
+ 'f': (20200, 20210),
+ 'mm': (20211, 20224),
+ 'tw': (20225, 20235),
+ 'mb': (20236, 20245),
+ 'm': (20246, 20254),
+ 'mh': (20255, 20266),
+ 'dt': (20267, 20276),
+ 'p': (20277, 20287),
+ 'tm': (20288, 20298),
+ 'bw': (20299, 20308),
+ 'ls': (20309, 20319),
+ 'rb': (20320, 20329),
+ 'sc': (20330, 20331),
+ 'sd': (20341, 20350)}
+PSCMenuExpressions = 'EXPRESSIONS'
+PSCMenuGreetings = 'GREETINGS'
+PSCMenuGoodbyes = 'GOODBYES'
+PSCMenuFriendly = 'FRIENDLY'
+PSCMenuHappy = 'HAPPY'
+PSCMenuSad = 'SAD'
+PSCMenuSorry = 'SORRY'
+PSCMenuCombat = 'COMBAT'
+PSCMenuSeaCombat = 'SEA COMBAT'
+PSCMenuPlaces = 'PLACES'
+PSCMenuLetsSail = "LET'S SAIL..."
+PSCMenuLetsHeadTo = "LET'S HEAD TO..."
+PSCMenuHeadToPortRoyal = 'PORT ROYAL'
+PSCMenuWhereIs = 'WHERE IS ..?'
+PSCMenuWhereIsPortRoyal = 'PORT ROYAL'
+PSCMenuWhereIsTortuga = 'TORTUGA'
+PSCMenuWhereIsPadresDelFuego = 'PADRES DEL FUEGO'
+PSCMenuWhereIsLasPulgas = 'LAS PULGAS'
+PSCMenuWhereIsLosPadres = 'LOS PADRES'
+PSCMenuDirections = 'DIRECTIONS'
+PSCMenuInsults = 'INSULTS'
+PSCMenuCompliments = 'COMPLIMENTS'
+PSCMenuCardGames = 'CARD GAMES'
+PSCMenuPoker = 'POKER'
+PSCMenuBlackjack = 'BLACKJACK'
+PSCMenuMinigames = 'MINIGAMES'
+PSCMenuFishing = 'FISHING'
+PSCMenuCannonDefense = 'CANNON DEFENSE'
+PSCMenuPotions = 'POTION BREWING'
+PSCMenuRepair = 'REPAIR'
+PSCMenuInvitations = 'INVITATIONS'
+PSCMenuVersusPlayer = 'VERSUS'
+PSCMenuHunting = 'HUNTING'
+PSCMenuQuests = 'QUESTS'
+PSCMenuGM = 'GM'
+PSCMenuShips = 'SHIPS'
+PSCMenuAdventures = 'ADVENTURE'
+GWSCMenuHello = 'GREETINGS'
+GWSCMenuBye = 'GOODBYES'
+GWSCMenuHappy = 'HAPPY'
+GWSCMenuSad = 'SAD'
+GWSCMenuPlaces = 'PLACES'
+RandomButton = 'Randomize'
+TypeANameButton = 'Type Name'
+PickANameButton = 'Pick-A-Name'
+NameShopSubmitButton = 'Submit'
+RejectNameText = 'That name is not allowed. Please try again.'
+WaitingForNameSubmission = 'Submitting your name...'
+NameShopNameMaster = 'NameMasterEnglish.txt'
+NameShopPay = 'Subscribe'
+NameShopPlay = 'Free Trial'
+NameShopOnlyPaid = 'Only paid users\nmay name their Toons.\nUntil you subscribe\nyour name will be\n'
+NameShopContinueSubmission = 'Continue Submission'
+NameShopChooseAnother = 'Choose Another Name'
+NameShopToonCouncil = 'The Toon Council\nwill review your\nname. ' + 'Review may\ntake a few days.\nWhile you wait\nyour name will be\n '
+PleaseTypeName = 'Please type your name:'
+ToonAlreadyExists = '%s already exists'
+AllNewNames = 'All new names\nmust be approved\nby the Name Council.'
+NameShopNameRejected = 'The name you\nsubmitted has\nbeen rejected.'
+NameShopNameAccepted = 'Congratulations!\nThe name you\nsubmitted has\nbeen accepted!'
+NoPunctuation = "You can't use punctuation marks in your name!"
+PeriodOnlyAfterLetter = 'You can use a period in your name, but only after a letter.'
+ApostropheOnlyAfterLetter = 'You can use an apostrophe in your name, but only after a letter.'
+NoNumbersInTheMiddle = 'Numeric digits may not appear in the middle of a word.'
+ThreeWordsOrLess = 'Your name must be three words or fewer.'
+CopyrightedNames = ('mickey',
+ 'mickey mouse',
+ 'mickeymouse',
+ 'minnie',
+ 'minnie mouse',
+ 'minniemouse',
+ 'donald',
+ 'donald duck',
+ 'donaldduck',
+ 'pluto',
+ 'goofy')
+NCTooShort = 'That name is too short.'
+NCNoDigits = 'Your name cannot contain numbers.'
+NCNeedLetters = 'Each word in your name must contain some letters.'
+NCNeedVowels = 'Each word in your name must contain some vowels.'
+NCAllCaps = 'Your name cannot be all capital letters.'
+NCMixedCase = 'That name has too many capital letters.'
+NCBadCharacter = "Your name cannot contain the character '%s'"
+NCRepeatedChar = "Your name has too many of the character '%s'"
+NCGeneric = 'Sorry, that name will not work.'
+NCTooManyWords = 'Your name cannot be more than four words long.'
+NCDashUsage = "Dashes may only be used to connect two words together (like in 'Boo-Boo')."
+NCCommaEdge = 'Your name may not begin or end with a comma.'
+NCCommaAfterWord = 'You may not begin a word with a comma.'
+NCCommaUsage = 'That name does not use commas properly. Commas must join two words together, like in the name "Dr. Quack, MD". Commas must also be followed by a space.'
+NCPeriodUsage = 'That name does not use periods properly. Periods are only allowed in words like "Mr.", "Mrs.", "J.T.", etc.'
+NCApostrophes = 'That name has too many apostrophes.'
+AvatarDetailPanelOK = lOK
+AvatarDetailPanelCancel = lCancel
+AvatarDetailPanelClose = lClose
+AvatarDetailPanelLookup = 'Looking up details for %s.'
+AvatarDetailPanelFailedLookup = 'Unable to get details for %s.'
+AvatarDetailPanelPlayer = 'Player: %(player)s\nWorld: %(world)s\nLocation: %(location)s'
+AvatarDetailPanelOnline = 'District: %(district)s\nLocation: %(location)s'
+AvatarDetailPanelOffline = 'District: offline\nLocation: offline'
+AvatarPanelFriends = 'Friends'
+AvatarPanelWhisper = 'Whisper'
+AvatarPanelSecrets = 'True Friends'
+AvatarPanelGoTo = 'Go To'
+AvatarPanelIgnore = 'Ignore'
+AvatarPanelStopIgnore = 'Stop Ignoring'
+AvatarPanelEndIgnore = 'End Ignore'
+AvatarPanelTrade = 'Trade'
+AvatarPanelCogLevel = 'Level: %s'
+AvatarPanelCogDetailClose = lClose
+TeleportPanelOK = lOK
+TeleportPanelCancel = lCancel
+TeleportPanelYes = lYes
+TeleportPanelNo = lNo
+TeleportPanelCheckAvailability = 'Trying to go to %s.'
+TeleportPanelNotAvailable = '%s is busy right now; try again later.'
+TeleportPanelIgnored = '%s is ignoring you.'
+TeleportPanelNotOnline = "%s isn't online right now."
+TeleportPanelWentAway = '%s went away.'
+TeleportPanelUnknownHood = "You don't know how to get to %s!"
+TeleportPanelUnavailableHood = '%s is not available right now; try again later.'
+TeleportPanelDenySelf = "You can't go to yourself!"
+TeleportPanelOtherShard = "%(avName)s is in district %(shardName)s, and you're in district %(myShardName)s. Do you want to switch to %(shardName)s?"
+KartRacingMenuSections = [-1,
+ 'PLACES',
+ 'RACES',
+ 'TRACKS',
+ 'COMPLIMENTS',
+ 'TAUNTS']
+AprilToonsMenuSections = [-1,
+ 'GREETINGS',
+ 'PLAYGROUNDS',
+ 'CHARACTERS',
+ 'ESTATES']
+SillyHolidayMenuSections = [-1, 'WORLD', 'BATTLE']
+CarolMenuSections = [-1]
+VictoryPartiesMenuSections = [-1, 'PARTY', 'ITEMS']
+GolfMenuSections = [-1,
+ 'COURSES',
+ 'TIPS',
+ 'COMMENTS']
+BoardingMenuSections = ['GROUP',
+ "Let's go to...",
+ "We're going to...",
+ -1]
+SellbotNerfMenuSections = [-1, 'GROUPING', 'SELLBOT TOWERS/VP']
+JellybeanJamMenuSections = ['GET JELLYBEANS', 'SPEND JELLYBEANS']
+WinterMenuSections = ['CAROLING', -1]
+HalloweenMenuSections = [-1]
+SingingMenuSections = [-1]
+WhiteListMenu = [-1, 'WHITELIST']
+SellbotInvasionMenuSections = [-1]
+SellbotFieldOfficeMenuSections = [-1, 'STRATEGY']
+IdesOfMarchMenuSections = [-1]
+TTAccountCallCustomerService = 'Please call Customer Service at %s.'
+TTAccountCustomerServiceHelp = '\nIf you need help, please call Customer Service at %s.'
+TTAccountIntractibleError = 'An error occurred.'
+
+def timeElapsedString(timeDelta):
+ timeDelta = abs(timeDelta)
+ if timeDelta.days > 0:
+ if timeDelta.days == 1:
+ return '1 day ago'
+ else:
+ return '%s days ago' % timeDelta.days
+ elif timeDelta.seconds / 3600 > 0:
+ if timeDelta.seconds / 3600 == 1:
+ return '1 hour ago'
+ else:
+ return '%s hours ago' % (timeDelta.seconds / 3600)
+ elif timeDelta.seconds / 60 < 2:
+ return '1 minute ago'
+ else:
+ return '%s minutes ago' % (timeDelta.seconds / 60)
diff --git a/otp/otpbase/OTPLocalizerEnglishProperty.py b/otp/otpbase/OTPLocalizerEnglishProperty.py
new file mode 100644
index 0000000..72c7f44
--- /dev/null
+++ b/otp/otpbase/OTPLocalizerEnglishProperty.py
@@ -0,0 +1,12 @@
+FSenterSecretTextPos = (0, 0, -0.25)
+FSgotSecretPos = (0, 0, 0.47)
+FSgetSecretButton = 0.06
+FSnextText = 1.0
+FSgetSecret = (1.55, 1, 1)
+FSok1 = (1.55, 1, 1)
+FSok2 = (0.6, 1, 1)
+FScancel = (0.6, 1, 1)
+LTPDdirectButtonYesText = 0.05
+LTPDdirectButtonNoText = 0.05
+LTPDdirectFrameText = 0.06
+SCOsubmenuOverlap = 2.0 / 3
diff --git a/otp/otpbase/OTPRender.py b/otp/otpbase/OTPRender.py
new file mode 100644
index 0000000..186d16f
--- /dev/null
+++ b/otp/otpbase/OTPRender.py
@@ -0,0 +1,48 @@
+from pandac.PandaModules import *
+MainCameraBitmask = BitMask32.bit(0)
+ReflectionCameraBitmask = BitMask32.bit(1)
+ShadowCameraBitmask = BitMask32.bit(2)
+SkyReflectionCameraBitmask = BitMask32.bit(3)
+GlowCameraBitmask = BitMask32.bit(4)
+EnviroCameraBitmask = BitMask32.bit(5)
+
+def setCameraBitmask(default, node_path, camera_bitmask, tag = None, tag_function = None, context = None):
+ if node_path:
+ show = default
+ if tag_function:
+ show = tag_function(default, tag, context)
+ if show:
+ node_path.show(camera_bitmask)
+ else:
+ node_path.hide(camera_bitmask)
+
+
+def renderReflection(default, node_path, tag = None, tag_function = None, context = None):
+ setCameraBitmask(default, node_path, ReflectionCameraBitmask, tag, tag_function, context)
+
+
+def renderShadow(default, node_path, tag = None, tag_function = None, context = None):
+ setCameraBitmask(default, node_path, ShadowCameraBitmask, tag, tag_function, context)
+
+
+def renderSkyReflection(default, node_path, tag = None, tag_function = None, context = None):
+ setCameraBitmask(default, node_path, SkyReflectionCameraBitmask, tag, tag_function, context)
+
+
+def renderGlow(default, node_path, tag = None, tag_function = None, context = None):
+ setCameraBitmask(default, node_path, GlowCameraBitmask, tag, tag_function, context)
+
+
+def setAdditiveEffect(node_path, tag = None, bin_name = None, lighting_on = False, reflect = False):
+ if node_path:
+ node_path.setTransparency(True)
+ node_path.setDepthWrite(False)
+ node_path.node().setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd))
+ if lighting_on == False:
+ node_path.setLightOff()
+ node_path.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.CRed | ColorWriteAttrib.CGreen | ColorWriteAttrib.CBlue))
+ if reflect == False:
+ renderReflection(False, node_path, tag, None)
+ if bin_name:
+ node_path.setBin(bin_name, 0)
+ return
diff --git a/otp/otpbase/OTPTimer.py b/otp/otpbase/OTPTimer.py
new file mode 100644
index 0000000..1ad6ffb
--- /dev/null
+++ b/otp/otpbase/OTPTimer.py
@@ -0,0 +1,113 @@
+from pandac.PandaModules import Vec4
+from direct.gui.DirectGui import DirectFrame, DGG
+from direct.task import Task
+from direct.showbase.PythonUtil import bound
+from otp.otpbase import OTPGlobals
+
+class OTPTimer(DirectFrame):
+ ClockImage = None
+ TimerId = 0
+
+ def __init__(self, useImage = True, highlightNearEnd = True):
+ if useImage:
+ image = self.getImage()
+ else:
+ image = None
+ DirectFrame.__init__(self, state=DGG.DISABLED, relief=None, scale=0.45, image=image, image_pos=(0, 0, 0), text='0', text_fg=(0, 0, 0, 1), text_font=OTPGlobals.getInterfaceFont(), text_pos=(-0.01, -0.15), text_scale=0.35)
+ self.initialiseoptions(OTPTimer)
+ self.timerId = OTPTimer.TimerId
+ OTPTimer.TimerId += 1
+ self.highlightNearEnd = highlightNearEnd
+ self.countdownTask = None
+ self.currentTime = 0
+ self.taskTime = 0.0
+ self.setFontColor(Vec4(0, 0, 0, 1))
+ return
+
+ def setFontColor(self, vColor):
+ self.vFontColor = vColor
+
+ def getImage(self):
+ if OTPTimer.ClockImage == None:
+ model = loader.loadModel('phase_3.5/models/gui/clock_gui')
+ OTPTimer.ClockImage = model.find('**/alarm_clock')
+ model.removeNode()
+ return OTPTimer.ClockImage
+
+ def posInTopRightCorner(self):
+ self.setPos(1.16, 0, 0.83)
+
+ def posBelowTopRightCorner(self):
+ self.setPos(1.16, 0, 0.58)
+
+ def posAboveShtikerBook(self):
+ self.setPos(1.16, 0, -.63)
+
+ def setTime(self, time):
+ time = bound(time, 0, 999)
+ if time == self.currentTime:
+ return
+ self.currentTime = time
+ timeStr = str(time)
+ timeStrLen = len(timeStr)
+ if timeStrLen == 1:
+ if time <= 5 and self.highlightNearEnd:
+ self.setTimeStr(timeStr, 0.34, (-0.025, -0.125), Vec4(1, 0, 0, 1))
+ else:
+ self.setTimeStr(timeStr, 0.34, (-0.025, -0.125))
+ elif timeStrLen == 2:
+ self.setTimeStr(timeStr, 0.27, (-0.025, -0.1))
+ elif timeStrLen == 3:
+ self.setTimeStr(timeStr, 0.2, (-0.01, -0.08))
+
+ def setTimeStr(self, timeStr, scale = 0.2, pos = (-0.01, -0.08), fg = None):
+ self['text'] = ''
+ self['text_fg'] = fg or self.vFontColor
+ self['text_scale'] = scale
+ self['text_pos'] = pos
+ self['text'] = timeStr
+
+ def getElapsedTime(self):
+ return self.taskTime
+
+ def _timerTask(self, task):
+ countdownTime = int(task.duration - task.time)
+ self.setTime(countdownTime)
+ self.taskTime = task.time
+ if task.time >= task.duration:
+ self.timerExpired()
+ if task.callback:
+ task.callback()
+ return Task.done
+ else:
+ return Task.cont
+
+ def countdown(self, duration, callback = None):
+ self.countdownTask = Task.Task(self._timerTask)
+ self.countdownTask.duration = duration
+ self.countdownTask.callback = callback
+ taskMgr.remove('timerTask%s' % self.timerId)
+ return taskMgr.add(self.countdownTask, 'timerTask%s' % self.timerId)
+
+ def timerExpired(self):
+ pass
+
+ def stop(self):
+ if self.countdownTask:
+ taskMgr.remove(self.countdownTask)
+
+ def reset(self):
+ self.stop()
+ self.setTime(0)
+ taskMgr.remove('timerTask%s' % self.timerId)
+ self.taskTime = 0.0
+
+ def destroy(self):
+ self.reset()
+ self.countdownTask = None
+ DirectFrame.destroy(self)
+ return
+
+ def cleanup(self):
+ self.destroy()
+ self.notify.warning('Call destroy, not cleanup')
diff --git a/otp/otpbase/__init__.py b/otp/otpbase/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/otpgui/OTPDialog.py b/otp/otpgui/OTPDialog.py
new file mode 100644
index 0000000..80ffa7c
--- /dev/null
+++ b/otp/otpgui/OTPDialog.py
@@ -0,0 +1,113 @@
+from direct.gui.DirectGui import *
+from direct.directnotify import DirectNotifyGlobal
+import string
+from otp.otpbase import OTPGlobals
+from otp.otpbase import OTPLocalizer
+NoButtons = 0
+Acknowledge = 1
+CancelOnly = 2
+TwoChoice = 3
+YesNo = 4
+TwoChoiceCustom = 5
+
+class OTPDialog(DirectDialog):
+
+ def __init__(self, parent = None, style = NoButtons, **kw):
+ if parent == None:
+ parent = aspect2d
+ self.style = style
+ buttons = None
+ if self.style != NoButtons:
+ buttons = loader.loadModel(self.path)
+ if self.style == TwoChoiceCustom:
+ okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr'))
+ buttonImage = [okImageList, cancelImageList]
+ buttonValue = [DGG.DIALOG_OK, DGG.DIALOG_CANCEL]
+ if 'buttonText' in kw:
+ buttonText = kw['buttonText']
+ del kw['buttonText']
+ else:
+ buttonText = [OTPLocalizer.DialogOK, OTPLocalizer.DialogCancel]
+ elif self.style == TwoChoice:
+ okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr'))
+ buttonImage = [okImageList, cancelImageList]
+ buttonText = [OTPLocalizer.DialogOK, OTPLocalizer.DialogCancel]
+ buttonValue = [DGG.DIALOG_OK, DGG.DIALOG_CANCEL]
+ elif self.style == YesNo:
+ okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr'))
+ buttonImage = [okImageList, cancelImageList]
+ buttonText = [OTPLocalizer.DialogYes, OTPLocalizer.DialogNo]
+ buttonValue = [DGG.DIALOG_OK, DGG.DIALOG_CANCEL]
+ elif self.style == Acknowledge:
+ okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ buttonImage = [okImageList]
+ buttonText = [OTPLocalizer.DialogOK]
+ buttonValue = [DGG.DIALOG_OK]
+ elif self.style == CancelOnly:
+ cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr'))
+ buttonImage = [cancelImageList]
+ buttonText = [OTPLocalizer.DialogCancel]
+ buttonValue = [DGG.DIALOG_CANCEL]
+ elif self.style == NoButtons:
+ buttonImage = []
+ buttonText = []
+ buttonValue = []
+ else:
+ self.notify.error('No such style as: ' + str(self.style))
+ optiondefs = (('buttonImageList', buttonImage, DGG.INITOPT),
+ ('buttonTextList', buttonText, DGG.INITOPT),
+ ('buttonValueList', buttonValue, DGG.INITOPT),
+ ('buttonPadSF', 2.2, DGG.INITOPT),
+ ('text_font', DGG.getDefaultFont(), None),
+ ('text_wordwrap', 12, None),
+ ('text_scale', 0.07, None),
+ ('buttonSize', (-.05, 0.05, -.05, 0.05), None),
+ ('button_pad', (0, 0), None),
+ ('button_relief', None, None),
+ ('button_text_pos', (0, -0.1), None),
+ ('fadeScreen', 0.5, None),
+ ('image_color', OTPGlobals.GlobalDialogColor, None))
+ self.defineoptions(kw, optiondefs)
+ DirectDialog.__init__(self, parent)
+ self.initialiseoptions(OTPDialog)
+ if buttons != None:
+ buttons.removeNode()
+ return
+
+
+class GlobalDialog(OTPDialog):
+ notify = DirectNotifyGlobal.directNotify.newCategory('GlobalDialog')
+
+ def __init__(self, message = '', doneEvent = None, style = NoButtons, okButtonText = OTPLocalizer.DialogOK, cancelButtonText = OTPLocalizer.DialogCancel, **kw):
+ if not hasattr(self, 'path'):
+ self.path = 'phase_3/models/gui/dialog_box_buttons_gui'
+ if doneEvent == None and style != NoButtons:
+ self.notify.error('Boxes with buttons must specify a doneEvent.')
+ self.__doneEvent = doneEvent
+ if style == NoButtons:
+ buttonText = []
+ elif style == Acknowledge:
+ buttonText = [okButtonText]
+ elif style == CancelOnly:
+ buttonText = [cancelButtonText]
+ else:
+ buttonText = [okButtonText, cancelButtonText]
+ optiondefs = (('dialogName', 'globalDialog', DGG.INITOPT),
+ ('buttonTextList', buttonText, DGG.INITOPT),
+ ('text', message, None),
+ ('command', self.handleButton, None))
+ self.defineoptions(kw, optiondefs)
+ OTPDialog.__init__(self, style=style)
+ self.initialiseoptions(GlobalDialog)
+ return
+
+ def handleButton(self, value):
+ if value == DGG.DIALOG_OK:
+ self.doneStatus = 'ok'
+ messenger.send(self.__doneEvent)
+ elif value == DGG.DIALOG_CANCEL:
+ self.doneStatus = 'cancel'
+ messenger.send(self.__doneEvent)
diff --git a/otp/otpgui/__init__.py b/otp/otpgui/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/snapshot/SnapshotDispatcher.py b/otp/snapshot/SnapshotDispatcher.py
new file mode 100644
index 0000000..cbc751c
--- /dev/null
+++ b/otp/snapshot/SnapshotDispatcher.py
@@ -0,0 +1,5 @@
+from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
+from direct.directnotify.DirectNotifyGlobal import directNotify
+
+class SnapshotDispatcher(DistributedObjectGlobal):
+ pass
diff --git a/otp/snapshot/SnapshotRenderer.py b/otp/snapshot/SnapshotRenderer.py
new file mode 100644
index 0000000..fa69b32
--- /dev/null
+++ b/otp/snapshot/SnapshotRenderer.py
@@ -0,0 +1,5 @@
+from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
+from direct.directnotify.DirectNotifyGlobal import directNotify
+
+class SnapshotRenderer(DistributedObjectGlobal):
+ pass
diff --git a/otp/snapshot/__init__.py b/otp/snapshot/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/speedchat/ColorSpace.py b/otp/speedchat/ColorSpace.py
new file mode 100644
index 0000000..854b19d
--- /dev/null
+++ b/otp/speedchat/ColorSpace.py
@@ -0,0 +1,61 @@
+import math
+
+def rgb2hsv(r, g, b):
+ _min = float(min(r, g, b))
+ _max = float(max(r, g, b))
+ v = _max
+ delta = _max - _min
+ if delta != 0.0:
+ s = delta / _max
+ else:
+ s = 0.0
+ h = -1.0
+ return (h, s, v)
+ if r == _max:
+ h = (g - b) / delta
+ elif g == _max:
+ h = 2.0 + (b - r) / delta
+ else:
+ h = 4.0 + (r - g) / delta
+ h *= 60.0
+ if h < 0.0:
+ h += 360.0
+ return (h, s, v)
+
+
+def hsv2rgb(h, s, v):
+ if s == 0.0:
+ return (v, v, v)
+ h %= 360.0
+ h /= 60.0
+ i = int(math.floor(h))
+ f = h - i
+ p = v * (1.0 - s)
+ q = v * (1.0 - s * f)
+ t = v * (1.0 - s * (1.0 - f))
+ if i == 0:
+ return (v, t, p)
+ elif i == 1:
+ return (q, v, p)
+ elif i == 2:
+ return (p, v, t)
+ elif i == 3:
+ return (p, q, v)
+ elif i == 4:
+ return (t, p, v)
+ else:
+ return (v, p, q)
+
+
+def rgb2yuv(r, g, b):
+ y = 0.299 * r + 0.587 * g + 0.114 * b
+ u = -.169 * r - 0.331 * g + 0.5 * b + 0.5
+ v = 0.5 * r - 0.419 * g - 0.081 * b + 0.5
+ return tuple(map(lambda x: min(max(x, 0), 1), (y, u, v)))
+
+
+def yuv2rgb(y, u, v):
+ r = y - 0.0009267 * (u - 0.5) + 1.4016868 * (v - 0.5)
+ g = y - 0.3436954 * (u - 0.5) - 0.714169 * (v - 0.5)
+ b = y + 1.7721604 * (u - 0.5) + 0.0009902 * (v - 0.5)
+ return tuple(map(lambda x: min(max(x, 0), 1), (r, g, b)))
diff --git a/otp/speedchat/SCColorScheme.py b/otp/speedchat/SCColorScheme.py
new file mode 100644
index 0000000..7b2050b
--- /dev/null
+++ b/otp/speedchat/SCColorScheme.py
@@ -0,0 +1,79 @@
+from ColorSpace import *
+
+class SCColorScheme:
+
+ def __init__(self, arrowColor = (0.5, 0.5, 1), rolloverColor = (0.53, 0.9, 0.53), frameColor = None, pressedColor = None, menuHolderActiveColor = None, emoteIconColor = None, textColor = (0, 0, 0), emoteIconDisabledColor = (0.5, 0.5, 0.5), textDisabledColor = (0.4, 0.4, 0.4), alpha = 0.95):
+
+ def scaleColor(color, s):
+ y, u, v = rgb2yuv(*color)
+ return yuv2rgb(y * s, u, v)
+
+ def scaleIfNone(color, srcColor, s):
+ if color is not None:
+ return color
+ else:
+ return scaleColor(srcColor, s)
+ return
+
+ self.__arrowColor = arrowColor
+ self.__rolloverColor = rolloverColor
+ self.__frameColor = frameColor
+ if self.__frameColor is None:
+ h, s, v = rgb2hsv(*arrowColor)
+ self.__frameColor = hsv2rgb(h, 0.2 * s, v)
+ h, s, v = rgb2hsv(*self.__frameColor)
+ self.__frameColor = hsv2rgb(h, 0.5 * s, v)
+ self.__pressedColor = scaleIfNone(pressedColor, self.__rolloverColor, 0.92)
+ self.__menuHolderActiveColor = scaleIfNone(menuHolderActiveColor, self.__rolloverColor, 0.84)
+ self.__emoteIconColor = emoteIconColor
+ if self.__emoteIconColor is None:
+ h, s, v = rgb2hsv(*self.__rolloverColor)
+ self.__emoteIconColor = hsv2rgb(h, 1.0, 0.8 * v)
+ self.__emoteIconDisabledColor = emoteIconDisabledColor
+ self.__textColor = textColor
+ self.__textDisabledColor = textDisabledColor
+ self.__alpha = alpha
+ return
+
+ def getArrowColor(self):
+ return self.__arrowColor
+
+ def getRolloverColor(self):
+ return self.__rolloverColor
+
+ def getFrameColor(self):
+ return self.__frameColor
+
+ def getPressedColor(self):
+ return self.__pressedColor
+
+ def getMenuHolderActiveColor(self):
+ return self.__menuHolderActiveColor
+
+ def getEmoteIconColor(self):
+ return self.__emoteIconColor
+
+ def getTextColor(self):
+ return self.__textColor
+
+ def getEmoteIconDisabledColor(self):
+ return self.__emoteIconDisabledColor
+
+ def getTextDisabledColor(self):
+ return self.__textDisabledColor
+
+ def getAlpha(self):
+ return self.__alpha
+
+ def __str__(self):
+ members = ('arrowColor', 'rolloverColor', 'frameColor', 'pressedColor', 'menuHolderActiveColor', 'emoteIconColor', 'textColor', 'emoteIconDisabledColor', 'textDisabledColor', 'alpha')
+ result = ''
+ for member in members:
+ result += '%s = %s' % (member, self.__dict__['_%s__%s' % (self.__class__.__name__, member)])
+ if member is not members[-1]:
+ result += '\n'
+
+ return result
+
+ def __repr__(self):
+ return str(self)
diff --git a/otp/speedchat/SCConstants.py b/otp/speedchat/SCConstants.py
new file mode 100644
index 0000000..2be1e50
--- /dev/null
+++ b/otp/speedchat/SCConstants.py
@@ -0,0 +1,2 @@
+SCMenuFinalizePriority = 48
+SCElementFinalizePriority = 47
diff --git a/otp/speedchat/SCCustomMenu.py b/otp/speedchat/SCCustomMenu.py
new file mode 100644
index 0000000..10e5fe1
--- /dev/null
+++ b/otp/speedchat/SCCustomMenu.py
@@ -0,0 +1,24 @@
+from SCMenu import SCMenu
+from SCCustomTerminal import SCCustomTerminal
+from otp.otpbase.OTPLocalizer import CustomSCStrings
+
+class SCCustomMenu(SCMenu):
+
+ def __init__(self):
+ SCMenu.__init__(self)
+ self.accept('customMessagesChanged', self.__customMessagesChanged)
+ self.__customMessagesChanged()
+
+ def destroy(self):
+ SCMenu.destroy(self)
+
+ def __customMessagesChanged(self):
+ self.clearMenu()
+ try:
+ lt = base.localAvatar
+ except:
+ return
+
+ for msgIndex in lt.customMessages:
+ if CustomSCStrings.has_key(msgIndex):
+ self.append(SCCustomTerminal(msgIndex))
diff --git a/otp/speedchat/SCCustomTerminal.py b/otp/speedchat/SCCustomTerminal.py
new file mode 100644
index 0000000..d04a521
--- /dev/null
+++ b/otp/speedchat/SCCustomTerminal.py
@@ -0,0 +1,18 @@
+from SCTerminal import SCTerminal
+from otp.otpbase.OTPLocalizer import CustomSCStrings
+SCCustomMsgEvent = 'SCCustomMsg'
+
+def decodeSCCustomMsg(textId):
+ return CustomSCStrings.get(textId, None)
+
+
+class SCCustomTerminal(SCTerminal):
+
+ def __init__(self, textId):
+ SCTerminal.__init__(self)
+ self.textId = textId
+ self.text = CustomSCStrings[self.textId]
+
+ def handleSelect(self):
+ SCTerminal.handleSelect(self)
+ messenger.send(self.getEventName(SCCustomMsgEvent), [self.textId])
diff --git a/otp/speedchat/SCDecoders.py b/otp/speedchat/SCDecoders.py
new file mode 100644
index 0000000..39357c7
--- /dev/null
+++ b/otp/speedchat/SCDecoders.py
@@ -0,0 +1,3 @@
+from SCStaticTextTerminal import decodeSCStaticTextMsg
+from SCCustomTerminal import decodeSCCustomMsg
+from SCEmoteTerminal import decodeSCEmoteWhisperMsg
diff --git a/otp/speedchat/SCElement.py b/otp/speedchat/SCElement.py
new file mode 100644
index 0000000..8a2af34
--- /dev/null
+++ b/otp/speedchat/SCElement.py
@@ -0,0 +1,174 @@
+from pandac.PandaModules import *
+from direct.gui.DirectGui import *
+from direct.task import Task
+from SCConstants import *
+from SCObject import SCObject
+from direct.showbase.PythonUtil import boolEqual
+from otp.otpbase import OTPGlobals
+
+class SCElement(SCObject, NodePath):
+ font = OTPGlobals.getInterfaceFont()
+ SerialNum = 0
+
+ def __init__(self, parentMenu = None):
+ SCObject.__init__(self)
+ self.SerialNum = SCElement.SerialNum
+ SCElement.SerialNum += 1
+ node = hidden.attachNewNode('SCElement%s' % self.SerialNum)
+ NodePath.__init__(self, node)
+ self.FinalizeTaskName = 'SCElement%s_Finalize' % self.SerialNum
+ self.parentMenu = parentMenu
+ self.__active = 0
+ self.__viewable = 1
+ self.lastWidth = 0
+ self.lastHeight = 0
+ self.setDimensions(0, 0)
+ self.padX = 0.25
+ self.padZ = 0.1
+
+ def destroy(self):
+ if self.isActive():
+ self.exitActive()
+ SCObject.destroy(self)
+ if hasattr(self, 'button'):
+ self.button.destroy()
+ del self.button
+ self.parentMenu = None
+ self.detachNode()
+ return
+
+ def setParentMenu(self, parentMenu):
+ self.parentMenu = parentMenu
+
+ def getParentMenu(self):
+ return self.parentMenu
+
+ def getDisplayText(self):
+ self.notify.error('getDisplayText is pure virtual, derived class must override')
+
+ def onMouseEnter(self, event):
+ if self.parentMenu is not None:
+ self.parentMenu.memberGainedInputFocus(self)
+ return
+
+ def onMouseLeave(self, event):
+ if self.parentMenu is not None:
+ self.parentMenu.memberLostInputFocus(self)
+ return
+
+ def onMouseClick(self, event):
+ pass
+
+ def enterActive(self):
+ self.__active = 1
+
+ def exitActive(self):
+ self.__active = 0
+
+ def isActive(self):
+ return self.__active
+
+ def hasStickyFocus(self):
+ return 0
+
+ def setViewable(self, viewable):
+ if not boolEqual(self.__viewable, viewable):
+ self.__viewable = viewable
+ if self.parentMenu is not None:
+ self.parentMenu.memberViewabilityChanged(self)
+ return
+
+ def isViewable(self):
+ return self.__viewable
+
+ def getMinDimensions(self):
+ text = TextNode('SCTemp')
+ text.setFont(SCElement.font)
+ dText = self.getDisplayText()
+ text.setText(dText)
+ bounds = text.getCardActual()
+ width = abs(bounds[1] - bounds[0]) + self.padX
+ height = abs(bounds[3] - bounds[2]) + 2.0 * self.padZ
+ return (width, height)
+
+ def setDimensions(self, width, height):
+ self.width = float(width)
+ self.height = float(height)
+ if (self.lastWidth, self.lastHeight) != (self.width, self.height):
+ self.invalidate()
+
+ def invalidate(self):
+ SCObject.invalidate(self)
+ parentMenu = self.getParentMenu()
+ if parentMenu is not None:
+ if not parentMenu.isFinalizing():
+ parentMenu.invalidate()
+ return
+
+ def enterVisible(self):
+ SCObject.enterVisible(self)
+ self.privScheduleFinalize()
+
+ def exitVisible(self):
+ SCObject.exitVisible(self)
+ self.privCancelFinalize()
+
+ def privScheduleFinalize(self):
+
+ def finalizeElement(task, self = self):
+ if self.parentMenu is not None:
+ if self.parentMenu.isDirty():
+ return Task.done
+ self.finalize()
+ return Task.done
+
+ taskMgr.remove(self.FinalizeTaskName)
+ taskMgr.add(finalizeElement, self.FinalizeTaskName, priority=SCElementFinalizePriority)
+
+ def privCancelFinalize(self):
+ taskMgr.remove(self.FinalizeTaskName)
+
+ def finalize(self, dbArgs = {}):
+ if not self.isDirty():
+ return
+ SCObject.finalize(self)
+ if hasattr(self, 'button'):
+ self.button.destroy()
+ del self.button
+ halfHeight = self.height / 2.0
+ textX = 0
+ if dbArgs.has_key('text_align'):
+ if dbArgs['text_align'] == TextNode.ACenter:
+ textX = self.width / 2.0
+ args = {'text': self.getDisplayText(),
+ 'frameColor': (0, 0, 0, 0),
+ 'rolloverColor': self.getColorScheme().getRolloverColor() + (1,),
+ 'pressedColor': self.getColorScheme().getPressedColor() + (1,),
+ 'text_font': OTPGlobals.getInterfaceFont(),
+ 'text_align': TextNode.ALeft,
+ 'text_fg': self.getColorScheme().getTextColor() + (1,),
+ 'text_pos': (textX, -.25 - halfHeight, 0),
+ 'relief': DGG.FLAT,
+ 'pressEffect': 0}
+ args.update(dbArgs)
+ rolloverColor = args['rolloverColor']
+ pressedColor = args['pressedColor']
+ del args['rolloverColor']
+ del args['pressedColor']
+ btn = DirectButton(parent=self, frameSize=(0,
+ self.width,
+ -self.height,
+ 0), **args)
+ btn.frameStyle[DGG.BUTTON_ROLLOVER_STATE].setColor(*rolloverColor)
+ btn.frameStyle[DGG.BUTTON_DEPRESSED_STATE].setColor(*pressedColor)
+ btn.updateFrameStyle()
+ btn.bind(DGG.ENTER, self.onMouseEnter)
+ btn.bind(DGG.EXIT, self.onMouseLeave)
+ btn.bind(DGG.B1PRESS, self.onMouseClick)
+ self.button = btn
+ self.lastWidth = self.width
+ self.lastHeight = self.height
+ self.validate()
+
+ def __str__(self):
+ return '%s: %s' % (self.__class__.__name__, self.getDisplayText())
diff --git a/otp/speedchat/SCEmoteMenu.py b/otp/speedchat/SCEmoteMenu.py
new file mode 100644
index 0000000..c3514f9
--- /dev/null
+++ b/otp/speedchat/SCEmoteMenu.py
@@ -0,0 +1,23 @@
+from SCMenu import SCMenu
+from SCEmoteTerminal import SCEmoteTerminal
+
+class SCEmoteMenu(SCMenu):
+
+ def __init__(self):
+ SCMenu.__init__(self)
+ self.accept('emotesChanged', self.__emoteAccessChanged)
+ self.__emoteAccessChanged()
+
+ def destroy(self):
+ SCMenu.destroy(self)
+
+ def __emoteAccessChanged(self):
+ self.clearMenu()
+ try:
+ lt = base.localAvatar
+ except:
+ return
+
+ for i in range(len(lt.emoteAccess)):
+ if lt.emoteAccess[i]:
+ self.append(SCEmoteTerminal(i))
diff --git a/otp/speedchat/SCEmoteTerminal.py b/otp/speedchat/SCEmoteTerminal.py
new file mode 100644
index 0000000..0dc1c66
--- /dev/null
+++ b/otp/speedchat/SCEmoteTerminal.py
@@ -0,0 +1,97 @@
+from direct.gui.DirectGui import *
+from SCTerminal import SCTerminal
+from otp.otpbase.OTPLocalizer import EmoteList, EmoteWhispers
+from otp.avatar import Emote
+SCEmoteMsgEvent = 'SCEmoteMsg'
+SCEmoteNoAccessEvent = 'SCEmoteNoAccess'
+
+def decodeSCEmoteWhisperMsg(emoteId, avName):
+ if emoteId >= len(EmoteWhispers):
+ return None
+ return EmoteWhispers[emoteId] % avName
+
+
+class SCEmoteTerminal(SCTerminal):
+
+ def __init__(self, emoteId):
+ SCTerminal.__init__(self)
+ self.emoteId = emoteId
+ if not self.__ltHasAccess():
+ self.text = '?'
+ else:
+ self.text = EmoteList[self.emoteId]
+
+ def __ltHasAccess(self):
+ try:
+ lt = base.localAvatar
+ return lt.emoteAccess[self.emoteId]
+ except:
+ return 0
+
+ def __emoteEnabled(self):
+ if self.isWhispering():
+ return 1
+ return Emote.globalEmote.isEnabled(self.emoteId)
+
+ def finalize(self, dbArgs = {}):
+ if not self.isDirty():
+ return
+ args = {}
+ if not self.__ltHasAccess() or not self.__emoteEnabled():
+ args.update({'rolloverColor': (0, 0, 0, 0),
+ 'pressedColor': (0, 0, 0, 0),
+ 'rolloverSound': None,
+ 'text_fg': self.getColorScheme().getTextDisabledColor() + (1,)})
+ if not self.__ltHasAccess():
+ args.update({'text_align': TextNode.ACenter})
+ elif not self.__emoteEnabled():
+ args.update({'clickSound': None})
+ self.lastEmoteEnableState = self.__emoteEnabled()
+ args.update(dbArgs)
+ SCTerminal.finalize(self, dbArgs=args)
+ return
+
+ def __emoteEnableStateChanged(self):
+ if self.isDirty():
+ self.notify.info("skipping __emoteEnableStateChanged; we're marked as dirty")
+ return
+ elif not hasattr(self, 'button'):
+ self.notify.error('SCEmoteTerminal is not marked as dirty, but has no button!')
+ btn = self.button
+ if self.__emoteEnabled():
+ rolloverColor = self.getColorScheme().getRolloverColor() + (1,)
+ pressedColor = self.getColorScheme().getPressedColor() + (1,)
+ btn.frameStyle[DGG.BUTTON_ROLLOVER_STATE].setColor(*rolloverColor)
+ btn.frameStyle[DGG.BUTTON_DEPRESSED_STATE].setColor(*pressedColor)
+ btn.updateFrameStyle()
+ btn['text_fg'] = self.getColorScheme().getTextColor() + (1,)
+ btn['rolloverSound'] = DGG.getDefaultRolloverSound()
+ btn['clickSound'] = DGG.getDefaultClickSound()
+ else:
+ btn.frameStyle[DGG.BUTTON_ROLLOVER_STATE].setColor(0, 0, 0, 0)
+ btn.frameStyle[DGG.BUTTON_DEPRESSED_STATE].setColor(0, 0, 0, 0)
+ btn.updateFrameStyle()
+ btn['text_fg'] = self.getColorScheme().getTextDisabledColor() + (1,)
+ btn['rolloverSound'] = None
+ btn['clickSound'] = None
+ return
+
+ def enterVisible(self):
+ SCTerminal.enterVisible(self)
+ if self.__ltHasAccess():
+ if hasattr(self, 'lastEmoteEnableState'):
+ if self.lastEmoteEnableState != self.__emoteEnabled():
+ self.invalidate()
+ if not self.isWhispering():
+ self.accept(Emote.globalEmote.EmoteEnableStateChanged, self.__emoteEnableStateChanged)
+
+ def exitVisible(self):
+ SCTerminal.exitVisible(self)
+ self.ignore(Emote.globalEmote.EmoteEnableStateChanged)
+
+ def handleSelect(self):
+ if not self.__ltHasAccess():
+ messenger.send(self.getEventName(SCEmoteNoAccessEvent))
+ elif self.__emoteEnabled():
+ SCTerminal.handleSelect(self)
+ messenger.send(self.getEventName(SCEmoteMsgEvent), [self.emoteId])
diff --git a/otp/speedchat/SCGMTextTerminal.py b/otp/speedchat/SCGMTextTerminal.py
new file mode 100644
index 0000000..5063e3d
--- /dev/null
+++ b/otp/speedchat/SCGMTextTerminal.py
@@ -0,0 +1,15 @@
+from SCTerminal import SCTerminal
+from otp.speedchat import SpeedChatGMHandler
+SCGMTextMsgEvent = 'SCGMTextMsg'
+
+class SCGMTextTerminal(SCTerminal):
+
+ def __init__(self, textId):
+ SCTerminal.__init__(self)
+ gmHandler = SpeedChatGMHandler.SpeedChatGMHandler()
+ self.textId = textId
+ self.text = gmHandler.getPhrase(textId)
+
+ def handleSelect(self):
+ SCTerminal.handleSelect(self)
+ messenger.send(self.getEventName(SCGMTextMsgEvent), [self.textId])
diff --git a/otp/speedchat/SCMenu.py b/otp/speedchat/SCMenu.py
new file mode 100644
index 0000000..73857d0
--- /dev/null
+++ b/otp/speedchat/SCMenu.py
@@ -0,0 +1,454 @@
+from pandac.PandaModules import *
+from direct.gui.DirectGui import *
+from direct.task import Task
+from SCConstants import *
+from direct.interval.IntervalGlobal import *
+from SCObject import SCObject
+from direct.showbase.PythonUtil import makeTuple
+import types
+
+class SCMenu(SCObject, NodePath):
+ config = getConfigShowbase()
+ SpeedChatRolloverTolerance = config.GetFloat('speedchat-rollover-tolerance', 0.08)
+ WantFade = config.GetBool('want-speedchat-fade', 0)
+ FadeDuration = config.GetFloat('speedchat-fade-duration', 0.2)
+ SerialNum = 0
+ BackgroundModelName = None
+ GuiModelName = None
+
+ def __init__(self, holder = None):
+ SCObject.__init__(self)
+ self.SerialNum = SCMenu.SerialNum
+ SCMenu.SerialNum += 1
+ node = hidden.attachNewNode('SCMenu%s' % self.SerialNum)
+ NodePath.__init__(self, node)
+ self.setHolder(holder)
+ self.FinalizeTaskName = 'SCMenu%s_Finalize' % self.SerialNum
+ self.ActiveMemberSwitchTaskName = 'SCMenu%s_SwitchActiveMember' % self.SerialNum
+ self.bg = loader.loadModel(self.BackgroundModelName)
+
+ def findNodes(names, model = self.bg):
+ results = []
+ for name in names:
+ for nm in makeTuple(name):
+ node = model.find('**/%s' % nm)
+ if not node.isEmpty():
+ results.append(node)
+ break
+
+ return results
+
+ self.bgTop, self.bgBottom, self.bgLeft, self.bgRight, self.bgMiddle, self.bgTopLeft, self.bgBottomLeft, self.bgTopRight, self.bgBottomRight = findNodes([('top', 'top1'),
+ 'bottom',
+ 'left',
+ 'right',
+ 'middle',
+ 'topLeft',
+ 'bottomLeft',
+ 'topRight',
+ 'bottomRight'])
+ self.bg.reparentTo(self, -1)
+ self.__members = []
+ self.activeMember = None
+ self.activeCandidate = None
+ self.fadeIval = None
+ self.width = 1
+ self.inFinalize = 0
+ return
+
+ def destroy(self):
+ self.stopFade()
+ SCObject.destroy(self)
+ del self.bgTop
+ del self.bgBottom
+ del self.bgLeft
+ del self.bgRight
+ del self.bgMiddle
+ del self.bgBottomLeft
+ del self.bgTopRight
+ del self.bgBottomRight
+ self.bg.removeNode()
+ del self.bg
+ self.holder = None
+ for member in self.__members:
+ member.destroy()
+
+ del self.__members
+ self.removeNode()
+ taskMgr.remove(self.FinalizeTaskName)
+ taskMgr.remove(self.ActiveMemberSwitchTaskName)
+ return
+
+ def clearMenu(self):
+ while len(self):
+ item = self[0]
+ del self[0]
+ item.destroy()
+
+ def rebuildFromStructure(self, structure, title = None):
+ self.clearMenu()
+ if title:
+ holder = self.getHolder()
+ if holder:
+ holder.setTitle(title)
+ self.appendFromStructure(structure)
+
+ def appendFromStructure(self, structure):
+ from SpeedChatTypes import SCMenuHolder, SCStaticTextTerminal, SCGMTextTerminal
+ from otp.otpbase import OTPLocalizer
+
+ def addChildren(menu, childList):
+ for child in childList:
+ emote = None
+ if type(child) == type({}):
+ item = child.keys()[0]
+ emote = child[item]
+ child = item
+ if type(child) == type(0):
+ terminal = SCStaticTextTerminal(child)
+ if emote is not None:
+ terminal.setLinkedEmote(emote)
+ menu.append(terminal)
+ elif type(child) == type([]):
+ if type(child[0]) == type(''):
+ holderTitle = child[0]
+ subMenu = SCMenu()
+ subMenuChildren = child[1:]
+ else:
+ menuType, holderTitle = child[0], child[1]
+ subMenu = menuType()
+ subMenuChildren = child[2:]
+ if emote:
+ print 'warning: tried to link emote %s to a menu holder' % emote
+ holder = SCMenuHolder(holderTitle, menu=subMenu)
+ menu.append(holder)
+ addChildren(subMenu, subMenuChildren)
+ elif type(child) == type('') and child[:2] == 'gm':
+ terminal = SCGMTextTerminal(child)
+ menu.append(terminal)
+ else:
+ raise 'error parsing speedchat structure. invalid child: %s' % child
+
+ return
+
+ addChildren(self, structure)
+ addChildren = None
+ return
+
+ def fadeFunc(self, t):
+ cs = self.getColorScale()
+ self.setColorScale(cs[0], cs[1], cs[2], t)
+
+ def stopFade(self):
+ if self.fadeIval is not None:
+ self.fadeIval.pause()
+ self.fadeIval = None
+ return
+
+ def enterVisible(self):
+ SCObject.enterVisible(self)
+ self.privScheduleFinalize()
+ for member in self:
+ if member.isViewable():
+ if not member.isVisible():
+ member.enterVisible()
+
+ self.childHasFaded = 0
+ alreadyFaded = 0
+ parentMenu = None
+ if self.holder is not None:
+ if self.holder.parentMenu is not None:
+ parentMenu = self.holder.parentMenu
+ alreadyFaded = parentMenu.childHasFaded
+ if SCMenu.WantFade:
+ if alreadyFaded:
+ self.fadeFunc(1.0)
+ else:
+ self.stopFade()
+ self.fadeIval = LerpFunctionInterval(self.fadeFunc, fromData=0.0, toData=1.0, duration=SCMenu.FadeDuration)
+ self.fadeIval.play()
+ if parentMenu is not None:
+ parentMenu.childHasFaded = 1
+ return
+
+ def exitVisible(self):
+ SCObject.exitVisible(self)
+ self.stopFade()
+ self.privCancelFinalize()
+ self.__cancelActiveMemberSwitch()
+ self.__setActiveMember(None)
+ for member in self:
+ if member.isVisible():
+ member.exitVisible()
+
+ return
+
+ def setHolder(self, holder):
+ self.holder = holder
+
+ def getHolder(self):
+ return self.holder
+
+ def isTopLevel(self):
+ return self.holder == None
+
+ def memberSelected(self, member):
+ self.__cancelActiveMemberSwitch()
+ self.__setActiveMember(member)
+
+ def __setActiveMember(self, member):
+ if self.activeMember is member:
+ return
+ if self.activeMember is not None:
+ self.activeMember.exitActive()
+ self.activeMember = member
+ if self.activeMember is not None:
+ self.activeMember.reparentTo(self)
+ self.activeMember.enterActive()
+ return
+
+ def memberGainedInputFocus(self, member):
+ self.__cancelActiveMemberSwitch()
+ if member is self.activeMember:
+ return
+ if self.activeMember is None or SCMenu.SpeedChatRolloverTolerance == 0 or member.posInParentMenu < self.activeMember.posInParentMenu:
+ self.__setActiveMember(member)
+ else:
+
+ def doActiveMemberSwitch(task, self = self, member = member):
+ self.activeCandidate = None
+ self.__setActiveMember(member)
+ return Task.done
+
+ minFrameRate = 1.0 / SCMenu.SpeedChatRolloverTolerance
+ if globalClock.getAverageFrameRate() > minFrameRate:
+ taskMgr.doMethodLater(SCMenu.SpeedChatRolloverTolerance, doActiveMemberSwitch, self.ActiveMemberSwitchTaskName)
+ self.activeCandidate = member
+ else:
+ self.__setActiveMember(member)
+ return
+
+ def __cancelActiveMemberSwitch(self):
+ taskMgr.remove(self.ActiveMemberSwitchTaskName)
+ self.activeCandidate = None
+ return
+
+ def memberLostInputFocus(self, member):
+ if member is self.activeCandidate:
+ self.__cancelActiveMemberSwitch()
+ if member is not self.activeMember:
+ pass
+ elif not member.hasStickyFocus():
+ self.__setActiveMember(None)
+ return
+
+ def memberViewabilityChanged(self, member):
+ self.invalidate()
+
+ def invalidate(self):
+ SCObject.invalidate(self)
+ if self.isVisible():
+ self.privScheduleFinalize()
+
+ def privScheduleFinalize(self):
+
+ def finalizeMenu(task, self = self):
+ self.finalize()
+ return Task.done
+
+ taskMgr.remove(self.FinalizeTaskName)
+ taskMgr.add(finalizeMenu, self.FinalizeTaskName, priority=SCMenuFinalizePriority)
+
+ def privCancelFinalize(self):
+ taskMgr.remove(self.FinalizeTaskName)
+
+ def isFinalizing(self):
+ return self.inFinalize
+
+ def finalize(self):
+ if not self.isDirty():
+ return
+ self.inFinalize = 1
+ SCObject.finalize(self)
+ visibleMembers = []
+ for member in self:
+ if member.isViewable():
+ visibleMembers.append(member)
+ member.reparentTo(self)
+ else:
+ member.reparentTo(hidden)
+ if self.activeMember is member:
+ self.__setActiveMember(None)
+
+ maxWidth = 0.0
+ maxHeight = 0.0
+ for member in visibleMembers:
+ width, height = member.getMinDimensions()
+ maxWidth = max(maxWidth, width)
+ maxHeight = max(maxHeight, height)
+
+ holder = self.getHolder()
+ if holder is not None:
+ widthToCover = holder.getMinSubmenuWidth()
+ maxWidth = max(maxWidth, widthToCover)
+ memberWidth, memberHeight = maxWidth, maxHeight
+ self.width = maxWidth
+ for i in xrange(len(visibleMembers)):
+ member = visibleMembers[i]
+ member.setPos(0, 0, -i * maxHeight)
+ member.setDimensions(memberWidth, memberHeight)
+ member.finalize()
+
+ if len(visibleMembers) > 0:
+ z1 = visibleMembers[0].getZ(aspect2d)
+ visibleMembers[0].setZ(-maxHeight)
+ z2 = visibleMembers[0].getZ(aspect2d)
+ visibleMembers[0].setZ(0)
+ actualHeight = (z2 - z1) * len(visibleMembers)
+ bottomZ = self.getZ(aspect2d) + actualHeight
+ if bottomZ < -1.0:
+ overlap = bottomZ - -1.0
+ self.setZ(aspect2d, self.getZ(aspect2d) - overlap)
+ if self.getZ(aspect2d) > 1.0:
+ self.setZ(aspect2d, 1.0)
+ sX = memberWidth
+ sZ = memberHeight * len(visibleMembers)
+ self.bgMiddle.setScale(sX, 1, sZ)
+ self.bgTop.setScale(sX, 1, 1)
+ self.bgBottom.setScale(sX, 1, 1)
+ self.bgLeft.setScale(1, 1, sZ)
+ self.bgRight.setScale(1, 1, sZ)
+ self.bgBottomLeft.setZ(-sZ)
+ self.bgBottom.setZ(-sZ)
+ self.bgTopRight.setX(sX)
+ self.bgRight.setX(sX)
+ self.bgBottomRight.setX(sX)
+ self.bgBottomRight.setZ(-sZ)
+ sB = 0.15
+ self.bgTopLeft.setSx(aspect2d, sB)
+ self.bgTopLeft.setSz(aspect2d, sB)
+ self.bgBottomRight.setSx(aspect2d, sB)
+ self.bgBottomRight.setSz(aspect2d, sB)
+ self.bgBottomLeft.setSx(aspect2d, sB)
+ self.bgBottomLeft.setSz(aspect2d, sB)
+ self.bgTopRight.setSx(aspect2d, sB)
+ self.bgTopRight.setSz(aspect2d, sB)
+ self.bgTop.setSz(aspect2d, sB)
+ self.bgBottom.setSz(aspect2d, sB)
+ self.bgLeft.setSx(aspect2d, sB)
+ self.bgRight.setSx(aspect2d, sB)
+ r, g, b = self.getColorScheme().getFrameColor()
+ a = self.getColorScheme().getAlpha()
+ self.bg.setColorScale(r, g, b, a)
+ if self.activeMember is not None:
+ self.activeMember.reparentTo(self)
+ self.validate()
+ self.inFinalize = 0
+ return
+
+ def append(self, element):
+ if isinstance(self.__members, types.TupleType):
+ self.__members = list(self.__members)
+ self.__members.append(element)
+ self.privMemberListChanged(added=[element])
+
+ def extend(self, elements):
+ self += elements
+
+ def index(self, element):
+ return self.__members.index(element)
+
+ def __len__(self):
+ return len(self.__members)
+
+ def __getitem__(self, index):
+ return self.__members[index]
+
+ def __setitem__(self, index, value):
+ if isinstance(self.__members, types.TupleType):
+ self.__members = list(self.__members)
+ removedMember = self.__members[index]
+ self.__members[index] = value
+ self.privMemberListChanged(added=[value], removed=[removedMember])
+
+ def __delitem__(self, index):
+ if isinstance(self.__members, types.TupleType):
+ self.__members = list(self.__members)
+ removedMember = self.__members[index]
+ del self.__members[index]
+ self.privMemberListChanged(removed=[removedMember])
+
+ def __getslice__(self, i, j):
+ if isinstance(self.__members, types.TupleType):
+ self.__members = list(self.__members)
+ return self.__members[i:j]
+
+ def __setslice__(self, i, j, s):
+ if isinstance(self.__members, types.TupleType):
+ self.__members = list(self.__members)
+ removedMembers = self.__members[i:j]
+ self.__members[i:j] = list(s)
+ self.privMemberListChanged(added=list(s), removed=removedMembers)
+
+ def __delslice__(self, i, j):
+ if isinstance(self.__members, types.TupleType):
+ self.__members = list(self.__members)
+ removedMembers = self.__members[i:j]
+ del self.__members[i:j]
+ self.privMemberListChanged(removed=removedMembers)
+
+ def __iadd__(self, other):
+ if isinstance(self.__members, types.TupleType):
+ self.__members = list(self.__members)
+ if isinstance(other, SCMenu):
+ otherMenu = other
+ other = otherMenu.__members
+ del otherMenu[:]
+ self.__members += list(other)
+ self.privMemberListChanged(added=list(other))
+ return self
+
+ def privMemberListChanged(self, added = None, removed = None):
+ if removed is not None:
+ for element in removed:
+ if element is self.activeMember:
+ self.__setActiveMember(None)
+ if element.getParentMenu() is self:
+ if element.isVisible():
+ element.exitVisible()
+ element.setParentMenu(None)
+ element.reparentTo(hidden)
+
+ if added is not None:
+ for element in added:
+ self.privAdoptSCObject(element)
+ element.setParentMenu(self)
+
+ if self.holder is not None:
+ self.holder.updateViewability()
+ for i in range(len(self.__members)):
+ self.__members[i].posInParentMenu = i
+
+ self.invalidate()
+ return
+
+ def privSetSettingsRef(self, settingsRef):
+ SCObject.privSetSettingsRef(self, settingsRef)
+ for member in self:
+ member.privSetSettingsRef(settingsRef)
+
+ def invalidateAll(self):
+ SCObject.invalidateAll(self)
+ for member in self:
+ member.invalidateAll()
+
+ def finalizeAll(self):
+ SCObject.finalizeAll(self)
+ for member in self:
+ member.finalizeAll()
+
+ def getWidth(self):
+ return self.width
+
+ def __str__(self):
+ return '%s: menu%s' % (self.__class__.__name__, self.SerialNum)
diff --git a/otp/speedchat/SCMenuHolder.py b/otp/speedchat/SCMenuHolder.py
new file mode 100644
index 0000000..c613d05
--- /dev/null
+++ b/otp/speedchat/SCMenuHolder.py
@@ -0,0 +1,178 @@
+from pandac.PandaModules import *
+from direct.gui.DirectGui import *
+from SCObject import SCObject
+from SCElement import SCElement
+from SCMenu import SCMenu
+import types
+
+class SCMenuHolder(SCElement):
+ N = 0.9
+ DefaultFrameColor = (0,
+ 0,
+ 0,
+ 1.0 - N)
+ del N
+ MenuColorScaleDown = 0.95
+
+ def __init__(self, title, menu = None):
+ SCElement.__init__(self)
+ self.title = title
+ scGui = loader.loadModel(SCMenu.GuiModelName)
+ self.scArrow = scGui.find('**/chatArrow')
+ self.menu = None
+ self.setMenu(menu)
+ return
+
+ def destroy(self):
+ if self.menu is not None:
+ self.menu.destroy()
+ self.menu = None
+ SCElement.destroy(self)
+ return
+
+ def setTitle(self, title):
+ self.title = title
+ self.invalidate()
+
+ def getTitle(self):
+ return self.title
+
+ def setMenu(self, menu):
+ if self.menu is not None:
+ self.menu.destroy()
+ self.menu = menu
+ if self.menu is not None:
+ self.privAdoptSCObject(self.menu)
+ self.menu.setHolder(self)
+ self.menu.reparentTo(self, 1)
+ self.menu.hide()
+ self.updateViewability()
+ return
+
+ def getMenu(self):
+ return self.menu
+
+ def showMenu(self):
+ if self.menu is not None:
+ cS = SCMenuHolder.MenuColorScaleDown
+ self.menu.setColorScale(cS, cS, cS, 1)
+ self.menu.enterVisible()
+ self.menu.show()
+ return
+
+ def hideMenu(self):
+ if self.menu is not None:
+ self.menu.hide()
+ self.menu.exitVisible()
+ return
+
+ def getMenuOverlap(self):
+ if self.parentMenu.isTopLevel():
+ return self.getTopLevelOverlap()
+ else:
+ return self.getSubmenuOverlap()
+
+ def getMenuOffset(self):
+ xOffset = self.width * (1.0 - self.getMenuOverlap())
+ return Point3(xOffset, 0, 0)
+
+ def onMouseClick(self, event):
+ SCElement.enterActive(self)
+ self.parentMenu.memberSelected(self)
+
+ def enterActive(self):
+ SCElement.enterActive(self)
+ self.showMenu()
+ if hasattr(self, 'button'):
+ r, g, b = self.getColorScheme().getMenuHolderActiveColor()
+ a = self.getColorScheme().getAlpha()
+ self.button.frameStyle[DGG.BUTTON_READY_STATE].setColor(r, g, b, a)
+ self.button.updateFrameStyle()
+ else:
+ self.notify.warning('SCMenuHolder has no button (has finalize been called?).')
+
+ def exitActive(self):
+ SCElement.exitActive(self)
+ self.hideMenu()
+ self.button.frameStyle[DGG.BUTTON_READY_STATE].setColor(*SCMenuHolder.DefaultFrameColor)
+ self.button.updateFrameStyle()
+
+ def getDisplayText(self):
+ return self.title
+
+ def updateViewability(self):
+ if self.menu is None:
+ self.setViewable(0)
+ return
+ isViewable = False
+ for child in self.menu:
+ if child.isViewable():
+ isViewable = True
+ break
+
+ self.setViewable(isViewable)
+ return
+
+ def getMinSubmenuWidth(self):
+ parentMenu = self.getParentMenu()
+ if parentMenu is None:
+ myWidth, myWeight = self.getMinDimensions()
+ else:
+ myWidth = parentMenu.getWidth()
+ return 0.15 + myWidth * self.getMenuOverlap()
+
+ def getMinDimensions(self):
+ width, height = SCElement.getMinDimensions(self)
+ width += 1.0
+ return (width, height)
+
+ def invalidate(self):
+ SCElement.invalidate(self)
+ if self.menu is not None:
+ self.menu.invalidate()
+ return
+
+ def finalize(self, dbArgs = {}):
+ if not self.isDirty():
+ return
+ r, g, b = self.getColorScheme().getArrowColor()
+ a = self.getColorScheme().getAlpha()
+ self.scArrow.setColorScale(r, g, b, a)
+ if self.menu is not None:
+ self.menu.setPos(self.getMenuOffset())
+ if self.isActive():
+ r, g, b = self.getColorScheme().getMenuHolderActiveColor()
+ a = self.getColorScheme().getAlpha()
+ frameColor = (r,
+ g,
+ b,
+ a)
+ else:
+ frameColor = SCMenuHolder.DefaultFrameColor
+ args = {'image': self.scArrow,
+ 'image_pos': (self.width - 0.5, 0, -self.height * 0.5),
+ 'frameColor': frameColor}
+ args.update(dbArgs)
+ SCElement.finalize(self, dbArgs=args)
+ return
+
+ def hasStickyFocus(self):
+ return 1
+
+ def privSetSettingsRef(self, settingsRef):
+ SCObject.privSetSettingsRef(self, settingsRef)
+ if self.menu is not None:
+ self.menu.privSetSettingsRef(settingsRef)
+ return
+
+ def invalidateAll(self):
+ SCObject.invalidateAll(self)
+ if self.menu is not None:
+ self.menu.invalidateAll()
+ return
+
+ def finalizeAll(self):
+ SCObject.finalizeAll(self)
+ if self.menu is not None:
+ self.menu.finalizeAll()
+ return
diff --git a/otp/speedchat/SCObject.py b/otp/speedchat/SCObject.py
new file mode 100644
index 0000000..5b37ba7
--- /dev/null
+++ b/otp/speedchat/SCObject.py
@@ -0,0 +1,68 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.showbase.DirectObject import DirectObject
+
+class SCObject(DirectObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('SpeedChat')
+
+ def __init__(self):
+ self.settingsRef = None
+ self.__visible = 0
+ self.__dirty = 1
+ return
+
+ def destroy(self):
+ self.ignoreAll()
+ if self.isVisible():
+ self.exitVisible()
+
+ def enterVisible(self):
+ self.__visible = 1
+
+ def exitVisible(self):
+ self.__visible = 0
+
+ def isVisible(self):
+ return self.__visible
+
+ def invalidate(self):
+ self.__dirty = 1
+
+ def isDirty(self):
+ return self.__dirty
+
+ def validate(self):
+ self.__dirty = 0
+
+ def finalize(self):
+ pass
+
+ def getEventName(self, name):
+ return '%s%s' % (self.settingsRef.eventPrefix, name)
+
+ def getColorScheme(self):
+ return self.settingsRef.colorScheme
+
+ def isWhispering(self):
+ return self.settingsRef.whisperMode
+
+ def getSubmenuOverlap(self):
+ return self.settingsRef.submenuOverlap
+
+ def getTopLevelOverlap(self):
+ if self.settingsRef.topLevelOverlap is None:
+ return self.getSubmenuOverlap()
+ else:
+ return self.settingsRef.topLevelOverlap
+ return
+
+ def privSetSettingsRef(self, settingsRef):
+ self.settingsRef = settingsRef
+
+ def privAdoptSCObject(self, scObj):
+ scObj.privSetSettingsRef(self.settingsRef)
+
+ def invalidateAll(self):
+ self.invalidate()
+
+ def finalizeAll(self):
+ self.finalize()
diff --git a/otp/speedchat/SCSettings.py b/otp/speedchat/SCSettings.py
new file mode 100644
index 0000000..74379f8
--- /dev/null
+++ b/otp/speedchat/SCSettings.py
@@ -0,0 +1,14 @@
+from SCColorScheme import SCColorScheme
+from otp.otpbase import OTPLocalizer
+
+class SCSettings:
+
+ def __init__(self, eventPrefix, whisperMode = 0, colorScheme = None, submenuOverlap = OTPLocalizer.SCOsubmenuOverlap, topLevelOverlap = None):
+ self.eventPrefix = eventPrefix
+ self.whisperMode = whisperMode
+ if colorScheme is None:
+ colorScheme = SCColorScheme()
+ self.colorScheme = colorScheme
+ self.submenuOverlap = submenuOverlap
+ self.topLevelOverlap = topLevelOverlap
+ return
diff --git a/otp/speedchat/SCStaticTextTerminal.py b/otp/speedchat/SCStaticTextTerminal.py
new file mode 100644
index 0000000..e725098
--- /dev/null
+++ b/otp/speedchat/SCStaticTextTerminal.py
@@ -0,0 +1,18 @@
+from SCTerminal import SCTerminal
+from otp.otpbase.OTPLocalizer import SpeedChatStaticText
+SCStaticTextMsgEvent = 'SCStaticTextMsg'
+
+def decodeSCStaticTextMsg(textId):
+ return SpeedChatStaticText.get(textId, None)
+
+
+class SCStaticTextTerminal(SCTerminal):
+
+ def __init__(self, textId):
+ SCTerminal.__init__(self)
+ self.textId = textId
+ self.text = SpeedChatStaticText[self.textId]
+
+ def handleSelect(self):
+ SCTerminal.handleSelect(self)
+ messenger.send(self.getEventName(SCStaticTextMsgEvent), [self.textId])
diff --git a/otp/speedchat/SCTerminal.py b/otp/speedchat/SCTerminal.py
new file mode 100644
index 0000000..09782be
--- /dev/null
+++ b/otp/speedchat/SCTerminal.py
@@ -0,0 +1,168 @@
+from SCElement import SCElement
+from SCObject import SCObject
+from SCMenu import SCMenu
+from direct.fsm.StatePush import StateVar, FunctionCall
+from direct.showbase.DirectObject import DirectObject
+from otp.avatar import Emote
+SCTerminalSelectedEvent = 'SCTerminalSelected'
+SCTerminalLinkedEmoteEvent = 'SCTerminalLinkedEmoteEvent'
+SCWhisperModeChangeEvent = 'SCWhisperModeChange'
+
+class SCTerminal(SCElement):
+
+ def __init__(self, linkedEmote = None):
+ SCElement.__init__(self)
+ self.setLinkedEmote(linkedEmote)
+ scGui = loader.loadModel(SCMenu.GuiModelName)
+ self.emotionIcon = scGui.find('**/emotionIcon')
+ self.setDisabled(False)
+ self.__numCharges = -1
+ self._handleWhisperModeSV = StateVar(False)
+ self._handleWhisperModeFC = None
+ return
+
+ def destroy(self):
+ self._handleWhisperModeSV.set(False)
+ if self._handleWhisperModeFC:
+ self._handleWhisperModeFC.destroy()
+ self._handleWhisperModeSV.destroy()
+ SCElement.destroy(self)
+
+ def privSetSettingsRef(self, settingsRef):
+ SCElement.privSetSettingsRef(self, settingsRef)
+ if self._handleWhisperModeFC is None:
+ self._handleWhisperModeFC = FunctionCall(self._handleWhisperModeSVChanged, self._handleWhisperModeSV)
+ self._handleWhisperModeFC.pushCurrentState()
+ self._handleWhisperModeSV.set(self.settingsRef is not None and not self.isWhisperable())
+ return
+
+ def _handleWhisperModeSVChanged(self, handleWhisperMode):
+ if handleWhisperMode:
+ self._wmcListener = DirectObject()
+ self._wmcListener.accept(self.getEventName(SCWhisperModeChangeEvent), self._handleWhisperModeChange)
+ elif hasattr(self, '_wmcListener'):
+ self._wmcListener.ignoreAll()
+ del self._wmcListener
+ self.invalidate()
+
+ def _handleWhisperModeChange(self, whisperMode):
+ self.invalidate()
+
+ def handleSelect(self):
+ messenger.send(self.getEventName(SCTerminalSelectedEvent))
+ if self.hasLinkedEmote() and self.linkedEmoteEnabled():
+ messenger.send(self.getEventName(SCTerminalLinkedEmoteEvent), [self.linkedEmote])
+
+ def isWhisperable(self):
+ return True
+
+ def getLinkedEmote(self):
+ return self.linkedEmote
+
+ def setLinkedEmote(self, linkedEmote):
+ self.linkedEmote = linkedEmote
+ self.invalidate()
+
+ def hasLinkedEmote(self):
+ return self.linkedEmote is not None
+
+ def linkedEmoteEnabled(self):
+ if Emote.globalEmote:
+ return Emote.globalEmote.isEnabled(self.linkedEmote)
+
+ def getCharges(self):
+ return self.__numCharges
+
+ def setCharges(self, nCharges):
+ self.__numCharges = nCharges
+ if nCharges is 0:
+ self.setDisabled(True)
+
+ def isDisabled(self):
+ return self.__disabled or self.isWhispering() and not self.isWhisperable()
+
+ def setDisabled(self, bDisabled):
+ self.__disabled = bDisabled
+
+ def onMouseClick(self, event):
+ if not self.isDisabled():
+ SCElement.onMouseClick(self, event)
+ self.handleSelect()
+
+ def getMinDimensions(self):
+ width, height = SCElement.getMinDimensions(self)
+ if self.hasLinkedEmote():
+ width += 1.3
+ return (width, height)
+
+ def finalize(self, dbArgs = {}):
+ if not self.isDirty():
+ return
+ args = {}
+ if self.hasLinkedEmote():
+ self.lastEmoteIconColor = self.getEmoteIconColor()
+ self.emotionIcon.setColorScale(*self.lastEmoteIconColor)
+ args.update({'image': self.emotionIcon,
+ 'image_pos': (self.width - 0.6, 0, -self.height * 0.5)})
+ if self.isDisabled():
+ args.update({'rolloverColor': (0, 0, 0, 0),
+ 'pressedColor': (0, 0, 0, 0),
+ 'rolloverSound': None,
+ 'clickSound': None,
+ 'text_fg': self.getColorScheme().getTextDisabledColor() + (1,)})
+ args.update(dbArgs)
+ SCElement.finalize(self, dbArgs=args)
+ return
+
+ def getEmoteIconColor(self):
+ if self.linkedEmoteEnabled() and not self.isWhispering():
+ r, g, b = self.getColorScheme().getEmoteIconColor()
+ else:
+ r, g, b = self.getColorScheme().getEmoteIconDisabledColor()
+ return (r,
+ g,
+ b,
+ 1)
+
+ def updateEmoteIcon(self):
+ if hasattr(self, 'button'):
+ self.lastEmoteIconColor = self.getEmoteIconColor()
+ for i in range(self.button['numStates']):
+ self.button['image%s_image' % i].setColorScale(*self.lastEmoteIconColor)
+
+ else:
+ self.invalidate()
+
+ def enterVisible(self):
+ SCElement.enterVisible(self)
+ if hasattr(self, 'lastEmoteIconColor'):
+ if self.getEmoteIconColor() != self.lastEmoteIconColor:
+ self.invalidate()
+
+ def handleWhisperModeChange(whisperMode, self = self):
+ if self.hasLinkedEmote():
+ if self.isVisible() and not self.isWhispering():
+ self.updateEmoteIcon()
+
+ self.accept(self.getEventName(SCWhisperModeChangeEvent), handleWhisperModeChange)
+
+ def handleEmoteEnableStateChange(self = self):
+ if self.hasLinkedEmote():
+ if self.isVisible() and not self.isWhispering():
+ self.updateEmoteIcon()
+
+ if self.hasLinkedEmote():
+ if Emote.globalEmote:
+ self.accept(Emote.globalEmote.EmoteEnableStateChanged, handleEmoteEnableStateChange)
+
+ def exitVisible(self):
+ SCElement.exitVisible(self)
+ self.ignore(self.getEventName(SCWhisperModeChangeEvent))
+ if Emote.globalEmote:
+ self.ignore(Emote.globalEmote.EmoteEnableStateChanged)
+
+ def getDisplayText(self):
+ if self.getCharges() is not -1:
+ return self.text + ' (%s)' % self.getCharges()
+ else:
+ return self.text
diff --git a/otp/speedchat/SpeedChat.py b/otp/speedchat/SpeedChat.py
new file mode 100644
index 0000000..d2741dc
--- /dev/null
+++ b/otp/speedchat/SpeedChat.py
@@ -0,0 +1,66 @@
+from direct.showbase.PythonUtil import boolEqual
+from SpeedChatTypes import *
+from SCSettings import SCSettings
+from SCTerminal import SCWhisperModeChangeEvent
+from otp.otpbase import OTPLocalizer
+
+class SpeedChat(SCMenu):
+
+ def __init__(self, name = '', structure = None, backgroundModelName = None, guiModelName = None):
+ SCMenu.BackgroundModelName = backgroundModelName
+ SCMenu.GuiModelName = guiModelName
+ SCMenu.__init__(self)
+ self.name = name
+ self.settings = SCSettings(eventPrefix=self.name)
+ self.privSetSettingsRef(self.settings)
+ if structure is not None:
+ self.rebuildFromStructure(structure)
+ self._lastTransform = None
+ return
+
+ def destroy(self):
+ if self.isVisible():
+ self.exitVisible()
+ self._lastTransform = None
+ SCMenu.destroy(self)
+ return
+
+ def __str__(self):
+ return "%s: '%s'" % (self.__class__.__name__, self.name)
+
+ def enter(self):
+ self._detectTransformChange()
+ self.enterVisible()
+
+ def exit(self):
+ self.exitVisible()
+
+ def _detectTransformChange(self):
+ newTransform = self.getTransform(aspect2d)
+ if self._lastTransform is not None:
+ if newTransform != self._lastTransform:
+ self.invalidateAll()
+ self._lastTransform = newTransform
+ return
+
+ def setWhisperMode(self, whisperMode):
+ if not boolEqual(self.settings.whisperMode, whisperMode):
+ self.settings.whisperMode = whisperMode
+ messenger.send(self.getEventName(SCWhisperModeChangeEvent), [whisperMode])
+
+ def setColorScheme(self, colorScheme):
+ self.settings.colorScheme = colorScheme
+ self.invalidateAll()
+
+ def setSubmenuOverlap(self, submenuOverlap):
+ self.settings.submenuOverlap = submenuOverlap
+ self.invalidateAll()
+
+ def setTopLevelOverlap(self, topLevelOverlap):
+ self.settings.topLevelOverlap = topLevelOverlap
+ self.invalidateAll()
+
+ def finalizeAll(self):
+ self.notify.debug('finalizing entire SpeedChat tree')
+ self._detectTransformChange()
+ SCMenu.finalizeAll(self)
diff --git a/otp/speedchat/SpeedChatGMHandler.py b/otp/speedchat/SpeedChatGMHandler.py
new file mode 100644
index 0000000..35b30e2
--- /dev/null
+++ b/otp/speedchat/SpeedChatGMHandler.py
@@ -0,0 +1,47 @@
+from pandac.PandaModules import *
+from direct.showbase import DirectObject
+from otp.otpbase import OTPLocalizer
+
+class SpeedChatGMHandler(DirectObject.DirectObject):
+ scStructure = None
+ scList = {}
+
+ def __init__(self):
+ if SpeedChatGMHandler.scStructure is None:
+ self.generateSCStructure()
+ return
+
+ def generateSCStructure(self):
+ SpeedChatGMHandler.scStructure = [OTPLocalizer.PSCMenuGM]
+ phraseCount = 0
+ numGMCategories = base.config.GetInt('num-gm-categories', 0)
+ for i in range(0, numGMCategories):
+ categoryName = base.config.GetString('gm-category-%d' % i, '')
+ if categoryName == '':
+ continue
+ categoryStructure = [categoryName]
+ numCategoryPhrases = base.config.GetInt('gm-category-%d-phrases' % i, 0)
+ for j in range(0, numCategoryPhrases):
+ phrase = base.config.GetString('gm-category-%d-phrase-%d' % (i, j), '')
+ if phrase != '':
+ idx = 'gm%d' % phraseCount
+ SpeedChatGMHandler.scList[idx] = phrase
+ categoryStructure.append(idx)
+ phraseCount += 1
+
+ SpeedChatGMHandler.scStructure.append(categoryStructure)
+
+ numGMPhrases = base.config.GetInt('num-gm-phrases', 0)
+ for i in range(0, numGMPhrases):
+ phrase = base.config.GetString('gm-phrase-%d' % i, '')
+ if phrase != '':
+ idx = 'gm%d' % phraseCount
+ SpeedChatGMHandler.scList[idx] = phrase
+ SpeedChatGMHandler.scStructure.append(idx)
+ phraseCount += 1
+
+ def getStructure(self):
+ return SpeedChatGMHandler.scStructure
+
+ def getPhrase(self, id):
+ return SpeedChatGMHandler.scList[id]
diff --git a/otp/speedchat/SpeedChatGlobals.py b/otp/speedchat/SpeedChatGlobals.py
new file mode 100644
index 0000000..379d997
--- /dev/null
+++ b/otp/speedchat/SpeedChatGlobals.py
@@ -0,0 +1,6 @@
+from SCTerminal import SCTerminalSelectedEvent
+from SCTerminal import SCTerminalLinkedEmoteEvent
+from SCStaticTextTerminal import SCStaticTextMsgEvent
+from SCGMTextTerminal import SCGMTextMsgEvent
+from SCCustomTerminal import SCCustomMsgEvent
+from SCEmoteTerminal import SCEmoteMsgEvent, SCEmoteNoAccessEvent
diff --git a/otp/speedchat/SpeedChatTypes.py b/otp/speedchat/SpeedChatTypes.py
new file mode 100644
index 0000000..17423ce
--- /dev/null
+++ b/otp/speedchat/SpeedChatTypes.py
@@ -0,0 +1,12 @@
+from SCObject import SCObject
+from SCMenu import SCMenu
+from SCElement import SCElement
+from SCMenuHolder import SCMenuHolder
+from SCTerminal import SCTerminal
+from SCCustomMenu import SCCustomMenu
+from SCEmoteMenu import SCEmoteMenu
+from SCStaticTextTerminal import SCStaticTextTerminal
+from SCGMTextTerminal import SCGMTextTerminal
+from SCCustomTerminal import SCCustomTerminal
+from SCEmoteTerminal import SCEmoteTerminal
+from SCColorScheme import SCColorScheme
diff --git a/otp/speedchat/__init__.py b/otp/speedchat/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/status/StatusDatabase.py b/otp/status/StatusDatabase.py
new file mode 100644
index 0000000..75083a8
--- /dev/null
+++ b/otp/status/StatusDatabase.py
@@ -0,0 +1,53 @@
+import datetime
+import time
+from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from otp.otpbase import OTPLocalizer
+
+class StatusDatabase(DistributedObjectGlobal):
+ notify = directNotify.newCategory('StatusDatabase')
+
+ def __init__(self, cr):
+ DistributedObjectGlobal.__init__(self, cr)
+ self.avatarData = {}
+ self.avatarQueue = []
+ self.avatarRequestTaskName = 'StatusDataBase_RequestAvatarLastOnline'
+ self.avatarRetreiveTaskName = 'StatusDataBase_GetAvatarLastOnline'
+ self.avatarDoneTaskName = 'StatusDataBase GotAvatarData'
+
+ def requestOfflineAvatarStatus(self, avIds):
+ self.notify.debugCall()
+ self.sendUpdate('requestOfflineAvatarStatus', [avIds])
+
+ def queueOfflineAvatarStatus(self, avIds):
+ for avId in avIds:
+ if avId not in self.avatarQueue:
+ self.avatarQueue.append(avId)
+
+ while taskMgr.hasTaskNamed(self.avatarRequestTaskName):
+ taskMgr.remove(self.avatarRequestTaskName)
+
+ task = taskMgr.doMethodLater(1.0, self.requestAvatarQueue, self.avatarRequestTaskName)
+
+ def requestAvatarQueue(self, task):
+ self.sendUpdate('requestOfflineAvatarStatus', [self.avatarQueue])
+ self.avatarQueue = []
+
+ def recvOfflineAvatarStatus(self, avId, lastOnline):
+ self.notify.debugCall()
+ self.notify.debug('Got an update for offline avatar %s who was last online %s' % (avId, self.lastOnlineString(lastOnline)))
+ self.avatarData[avId] = lastOnline
+ while taskMgr.hasTaskNamed(self.avatarRetreiveTaskName):
+ taskMgr.remove(self.avatarRetreiveTaskName)
+
+ task = taskMgr.doMethodLater(1.0, self.announceNewAvatarData, self.avatarRetreiveTaskName)
+
+ def announceNewAvatarData(self, task):
+ messenger.send(self.avatarDoneTaskName)
+
+ def lastOnlineString(self, timestamp):
+ if timestamp == 0:
+ return ''
+ now = datetime.datetime.utcnow()
+ td = abs(now - datetime.datetime.fromtimestamp(timestamp))
+ return OTPLocalizer.timeElapsedString(td)
diff --git a/otp/status/__init__.py b/otp/status/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/uberdog/AccountDetailRecord.py b/otp/uberdog/AccountDetailRecord.py
new file mode 100644
index 0000000..938135c
--- /dev/null
+++ b/otp/uberdog/AccountDetailRecord.py
@@ -0,0 +1,80 @@
+from otp.otpbase import OTPGlobals
+
+class SubDetailRecord:
+
+ def __init__(self):
+ self.subId = 0
+ self.subOwnerId = 0
+ self.subName = ''
+ self.subActive = ''
+ self.subAccess = ''
+ self.subLevel = 0
+ self.subNumAvatars = 0
+ self.subNumConcur = 0
+ self.subFounder = 0
+
+ def __str__(self):
+ s = ''
+ s += '========== Sub %d ==========\n' % self.subId
+ s += 'Sub Id: %d\n' % self.subId
+ s += 'Sub Owner Id: %d\n' % self.subOwnerId
+ s += 'Sub Name: %s\n' % self.subName
+ s += 'Sub Active: %s\n' % self.subActive
+ s += 'Sub Access: %s\n' % self.subAccess
+ s += 'Sub Level: %d\n' % self.subLevel
+ s += 'Sub MaxAvatars: %d\n' % self.subNumAvatars
+ s += 'Sub Concurrent: %d\n' % self.subNumConcur
+ s += 'Sub Founder: %d\n' % self.subFounder
+ return s
+
+
+class AccountDetailRecord:
+
+ def __init__(self):
+ self.openChatEnabled = False
+ self.createFriendsWithChat = False
+ self.chatCodeCreation = False
+ self.piratesAccess = OTPGlobals.AccessUnknown
+ self.familyAccountId = 0
+ self.playerAccountId = 0
+ self.playerName = ''
+ self.playerNameApproved = False
+ self.maxAvatars = 0
+ self.numFamilyMembers = 0
+ self.familyMembers = []
+ self.numSubs = 0
+ self.subDetails = {}
+ self.maxAvatarSlots = 0
+ self.WLChatEnabled = False
+
+ def getMaxNumAvatars(self, subId):
+ subDetails = self.subDetails.get(subId)
+ if subDetails:
+ return subDetails.subNumAvatars
+ else:
+ return 0
+
+ def canOpenChatAndNotGetBooted(self):
+ return self.openChatEnabled or self.createFriendsWithChat
+
+ def __str__(self):
+ s = '========== Account %s ==========\n' % self.playerAccountId
+ s += 'OpenChatEnabled: %s\n' % self.openChatEnabled
+ s += 'WLChatEnabled: %s\n' % self.WLChatEnabled
+ s += 'CreateFriendsWithChat: %s\n' % self.createFriendsWithChat
+ s += 'ChatCodeCreation: %s\n' % self.chatCodeCreation
+ s += 'PiratesAccess: %s\n' % self.piratesAccess
+ s += 'FamilyAccountId: %d\n' % self.familyAccountId
+ s += 'PlayerAccountId: %d\n' % self.playerAccountId
+ s += 'PlayerName: %s\n' % self.playerName
+ s += 'AccountNameApproved: %d\n' % self.playerNameApproved
+ s += 'MaxAvatars: %d\n' % self.maxAvatars
+ s += 'MaxAvatarSlots: %d\n' % self.maxAvatarSlots
+ s += 'NumFamilyMembers: %d\n' % self.numFamilyMembers
+ s += 'FamilyMembers: %s\n' % self.familyMembers
+ s += 'NumSubs: %s\n' % self.numSubs
+ for subDetails in self.subDetails.values():
+ s += str(subDetails)
+
+ s += '================================\n'
+ return s
diff --git a/otp/uberdog/DistributedChatManager.py b/otp/uberdog/DistributedChatManager.py
new file mode 100644
index 0000000..e61801e
--- /dev/null
+++ b/otp/uberdog/DistributedChatManager.py
@@ -0,0 +1,84 @@
+from direct.distributed.DistributedObject import DistributedObject
+from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
+from pandac.PandaModules import *
+from otp.otpbase import OTPGlobals
+
+class DistributedChatManager(DistributedObjectGlobal):
+
+ def __init__(self, cr):
+ DistributedObjectGlobal.__init__(self, cr)
+ self.notify.warning('ChatManager going online')
+
+ def delete(self):
+ self.ignoreAll()
+ self.notify.warning('ChatManager going offline')
+ self.cr.chatManager = None
+ DistributedObjectGlobal.delete(self)
+ return
+
+ def online(self):
+ pass
+
+ def adminChat(self, aboutId, message):
+ self.notify.warning('Admin Chat(%s): %s' % (aboutId, message))
+ messenger.send('adminChat', [aboutId, message])
+
+ def sendChatTo(self, message, chatFlags):
+ self.sendUpdate('chatTo', [message, chatFlags])
+
+ def chatFrom(self, fromId, message, chatFlags):
+ pass
+
+ def sendSpeedChatTo(self, msgIndex):
+ self.sendUpdate('speedChatTo', [msgIndex])
+
+ def speedChatFrom(self, fromId, msgIndex):
+ pass
+
+ def sendSpeedChatCustomTo(self, msgIndex):
+ self.sendUpdate('speedChatCustomTo', [msgIndex])
+
+ def speedChatCustomFrom(self, fromId, msgIndex):
+ pass
+
+ def sendWhisperTo(self, toId, message):
+ self.sendUpdate('whisperTo', [toId, message])
+
+ def whisperFrom(self, fromId, message):
+ if base.cr.wantSwitchboardHacks:
+ print 'received whisper on avatar: %s' % message
+ whisper = WhisperPopup(message, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
+ whisper.manage(base.marginManager)
+
+ def sendWhisperSCTo(self, toId, msgIndex):
+ self.sendUpdate('whisperSCTo', [toId, msgIndex])
+
+ def whisperSCFrom(self, fromId, msgIndex):
+ pass
+
+ def sendWhisperSCCustomTo(self, toId, msgIndex):
+ self.sendUpdate('whisperSCCustomTo', [toId, msgIndex])
+
+ def whisperSCCustomFrom(self, fromId, msgIndex):
+ pass
+
+ def sendWhisperSCEmoteTo(self, toId, emoteId):
+ self.sendUpdate('whisperSCEmoteTo', [toId, emoteId])
+
+ def whisperSCEmoteFrom(self, fromId, emoteId):
+ pass
+
+ def whisperIgnored(self, fromId):
+ pass
+
+ def sendCrewChatTo(self, message):
+ self.sendUpdate('crewChatTo', [message])
+
+ def crewChatFrom(self, fromId, message):
+ pass
+
+ def sendGuildChatTo(self, message):
+ self.sendUpdate('guildChatTo', [message])
+
+ def guildChatFrom(self, fromId, message):
+ pass
diff --git a/otp/uberdog/OtpAvatarManager.py b/otp/uberdog/OtpAvatarManager.py
new file mode 100644
index 0000000..eb30bb7
--- /dev/null
+++ b/otp/uberdog/OtpAvatarManager.py
@@ -0,0 +1,86 @@
+from cPickle import loads, dumps
+from direct.distributed import DistributedObject
+from direct.directnotify import DirectNotifyGlobal
+notify = DirectNotifyGlobal.directNotify.newCategory('AvatarManager')
+
+class OtpAvatarManager(DistributedObject.DistributedObject):
+ notify = notify
+ OnlineEvent = 'GlobalAvatarManagerOnline'
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.avatars = {}
+
+ def delete(self):
+ self.ignoreAll()
+ self.cr.avatarManager = None
+ DistributedObject.DistributedObject.delete(self)
+ return
+
+ def online(self):
+ messenger.send(OtpAvatarManager.OnlineEvent)
+
+ def sendRequestAvatarList(self):
+ self.sendUpdate('requestAvatarList', [0])
+
+ def rejectAvatarList(self, result):
+ messenger.send('avatarListFailed', [result])
+
+ def avatarListResponse(self, pickleData):
+ avatars = loads(pickleData)
+ messenger.send('avatarList', [avatars])
+
+ def rejectCreateAvatar(self, result):
+ messenger.send('createdNewAvatarFailed', [result])
+
+ def createAvatarResponse(self, avatarId, subId, access, founder):
+ self.notify.info('new avatarId: %s subId: %s access: %s founder: %s' % (avatarId,
+ subId,
+ access,
+ founder))
+ messenger.send('createdNewAvatar', [avatarId, subId])
+
+ def sendRequestRemoveAvatar(self, avatarId, subId, confirmPassword):
+ self.sendUpdate('requestRemoveAvatar', [0,
+ avatarId,
+ subId,
+ confirmPassword])
+
+ def rejectRemoveAvatar(self, reasonId):
+ messenger.send('rejectRemoveAvatar', [reasonId])
+
+ def removeAvatarResponse(self, avatarId, subId):
+ messenger.send('removeAvatarResponse', [avatarId, subId])
+
+ def sendRequestShareAvatar(self, avatarId, subId, shared):
+ self.sendUpdate('requestShareAvatar', [0,
+ avatarId,
+ subId,
+ shared])
+
+ def rejectShareAvatar(self, reasonId):
+ messenger.send('rejectShareAvatar', [reasonId])
+
+ def shareAvatarResponse(self, avatarId, subId, shared):
+ messenger.send('shareAvatarResponse', [avatarId, subId, shared])
+
+ def sendRequestAvatarSlot(self, subId, slot):
+ self.sendUpdate('requestAvatarSlot', [0, subId, slot])
+
+ def rejectAvatarSlot(self, reasonId, subId, slot):
+ messenger.send('rejectAvatarSlot', [reasonId, subId, slot])
+
+ def avatarSlotResponse(self, subId, slot):
+ messenger.send('avatarSlotResponse', [subId, slot])
+
+ def sendRequestPlayAvatar(self, avatarId, subId):
+ self.sendUpdate('requestPlayAvatar', [0, avatarId, subId])
+
+ def rejectPlayAvatar(self, reasonId, avatarId):
+ messenger.send('rejectPlayAvatar', [reasonId, avatarId])
+
+ def playAvatarResponse(self, avatarId, subId, access, founder):
+ messenger.send('playAvatarResponse', [avatarId,
+ subId,
+ access,
+ founder])
diff --git a/otp/uberdog/RejectCode.py b/otp/uberdog/RejectCode.py
new file mode 100644
index 0000000..654ec00
--- /dev/null
+++ b/otp/uberdog/RejectCode.py
@@ -0,0 +1,78 @@
+
+
+class RejectCode:
+ TIMEOUT = 65535
+ NO_GUILD = 10000
+ MAY_NOT_JOIN_GUILD = 10001
+ MAY_NOT_INVITE_GUILD = 10002
+ NO_MEMBERSHIP = 10003
+ GUILD_FULL = 10004
+ LEAVE_CURRENT_GUILD_FIRST = 10005
+ ALREADY_IN_GUILD = 10006
+ JOIN_OWN_GUILD = 10007
+ NOT_IN_A_GUILD = 10008
+ OTHER_AVATAR_NO_MEMBERSHIP = 10009
+ OTHER_AVATAR_NO_GUILD = 10010
+ OTHER_AVATAR_NO_GUILD_FOUND = 10011
+ ALREADY_HAS_GUILD = 10012
+ MAY_NOT_CREATE_GUILD = 10013
+ BUSY = 10014
+ NO_CREW = 20000
+ MAY_NOT_JOIN_CREW = 20001
+ JOIN_OWN_CREW = 20002
+ NOT_IN_A_CREW = 20003
+ MAY_NOT_INVITE_CREW = 20004
+ ALREADY_IN_CREW = 20005
+ ALREADY_INVITED_TO_CREW = 20006
+ NOT_CAPTAIN_OF_CREW = 20007
+ NO_SELF_CREW_INVITE = 20008
+ CREW_INVITATION_DECLINED = 20009
+ ALREADY_LOCKED = 20010
+ NOT_LOCKED = 20011
+ NOT_YOUR_LOCK = 20012
+ YOUR_CREW_IS_LOCKED = 20013
+ OTHER_CREW_IS_LOCKED = 20014
+ NOT_IN_YOUR_CREW = 20015
+ NO_TRADE = 30000
+ NOT_YOUR_TRADE = 30001
+ TRADE_NOT_ACTIVE = 30002
+ AVATAR_NOT_HERE = 30003
+ MAY_NOT_TRADE = 30004
+ NO_SELF_TRADE = 30005
+ OTHER_AVATAR_NOT_HERE = 30006
+ OTHER_AVATAR_MAY_NOT_TRADE = 30007
+ NO_INVENTORY = 40000
+ CONTENT_TYPE_NOT_ALLOWED = 40001
+ HAS_NONE_OF_THAT_TYPE = 40002
+ NO_MORE_SLOTS = 40003
+ SLOT_TYPE_NOT_FOUND = 40004
+ OVERFLOW = 40005
+ UNDERFLOW = 40006
+ MULTIPLE_OF_SAME_ITEM = 40007
+ MAY_NOT_DESTROY = 40008
+ NO_REMOVE = 40009
+ NOT_YOUR_DO_ID = 40010
+ NO_AVATAR = 50000
+ NOT_YOUR_AVATAR = 50001
+ BAD_DELETE_PASSWORD = 50002
+ MAY_NOT_LIST_AVATARS = 50003
+ MAY_NOT_CREATE_AVATAR = 50004
+ MAY_NOT_REMOVE_AVATAR = 50005
+ SLOT_OUT_OF_RANGE = 50006
+ SLOT_TAKEN = 50007
+ INVALID_ACCOUNT = 50008
+ AVATAR_ONLINE = 50009
+ MAX_AVATAR_LIMIT = 50010
+ NO_LOCK_ON_SLOT = 50011
+ NO_FRIENDS_LIST = 60000
+ FRIENDS_LIST_NOT_HANDY = 60001
+ INVITEE_NOT_ONLINE = 60002
+ ALREADY_INVITED = 60003
+ ALREADY_YOUR_FRIEND = 60004
+ FRIENDS_LIST_FULL = 60005
+ OTHER_FRIENDS_LIST_FULL = 60006
+ ALREADY_NOT_YOUR_FRIEND = 60007
+ INVITATION_DECLINED = 60008
+ NOT_YOUR_FRIENDS_LIST = 60009
+ ALREADY_FRIENDS_WITH_SELF = 60010
+ MAY_NOT_OPEN_INVITE = 60011
diff --git a/otp/uberdog/SpeedchatRelay.py b/otp/uberdog/SpeedchatRelay.py
new file mode 100644
index 0000000..30d7997
--- /dev/null
+++ b/otp/uberdog/SpeedchatRelay.py
@@ -0,0 +1,26 @@
+from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from otp.otpbase import OTPGlobals
+from otp.uberdog import SpeedchatRelayGlobals
+
+class SpeedchatRelay(DistributedObjectGlobal):
+
+ def __init__(self, cr):
+ DistributedObjectGlobal.__init__(self, cr)
+
+ def sendSpeedchat(self, receiverId, messageIndex):
+ self.sendSpeedchatToRelay(receiverId, SpeedchatRelayGlobals.NORMAL, [messageIndex])
+
+ def sendSpeedchatCustom(self, receiverId, messageIndex):
+ self.sendSpeedchatToRelay(receiverId, SpeedchatRelayGlobals.CUSTOM, [messageIndex])
+
+ def sendSpeedchatEmote(self, receiverId, messageIndex):
+ self.sendSpeedchatToRelay(receiverId, SpeedchatRelayGlobals.EMOTE, [messageIndex])
+
+ def sendSpeedchatToRelay(self, receiverId, speedchatType, parameters):
+ self.sendUpdate('forwardSpeedchat', [receiverId,
+ speedchatType,
+ parameters,
+ base.cr.accountDetailRecord.playerAccountId,
+ base.cr.accountDetailRecord.playerName + ' RHFM',
+ 0])
diff --git a/otp/uberdog/SpeedchatRelayGlobals.py b/otp/uberdog/SpeedchatRelayGlobals.py
new file mode 100644
index 0000000..7521e43
--- /dev/null
+++ b/otp/uberdog/SpeedchatRelayGlobals.py
@@ -0,0 +1,5 @@
+NORMAL = 0
+CUSTOM = 1
+EMOTE = 2
+PIRATES_QUEST = 3
+TOONTOWN_QUEST = 4
diff --git a/otp/uberdog/__init__.py b/otp/uberdog/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/otp/web/SettingsMgr.py b/otp/web/SettingsMgr.py
new file mode 100644
index 0000000..18ff3c7
--- /dev/null
+++ b/otp/web/SettingsMgr.py
@@ -0,0 +1,41 @@
+from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from otp.web.SettingsMgrBase import SettingsMgrBase
+
+class SettingsMgr(DistributedObjectGlobal, SettingsMgrBase):
+ notify = directNotify.newCategory('SettingsMgr')
+
+ def announceGenerate(self):
+ DistributedObjectGlobal.announceGenerate(self)
+ SettingsMgrBase.announceGenerate(self)
+ if not self.cr.isLive():
+ self._sracs = None
+ if self.cr.isConnected():
+ self._scheduleChangedSettingRequest()
+ self._crConnectEvent = self.cr.getConnectedEvent()
+ self.accept(self._crConnectEvent, self._handleConnected)
+ return
+
+ def _handleConnected(self):
+ self._scheduleChangedSettingRequest()
+
+ def _scheduleChangedSettingRequest(self):
+ if self._sracs:
+ self._sracs.destroy()
+ self._sracs = FrameDelayedCall('requestAllChangedSettings', self.sendRequestAllChangedSettings)
+
+ def delete(self):
+ self.ignore(self._crConnectEvent)
+ if self._sracs:
+ self._sracs.destroy()
+ SettingsMgrBase.delete(self)
+ DistributedObjectGlobal.delete(self)
+
+ def sendRequestAllChangedSettings(self):
+ self.sendUpdate('requestAllChangedSettings', [])
+
+ def settingChange(self, settingName, valueStr):
+ if valueStr == self._getCurrentValueRepr(settingName):
+ return
+ self.notify.info('got setting change: %s -> %s' % (settingName, valueStr))
+ self._changeSetting(settingName, valueStr)
diff --git a/otp/web/SettingsMgrBase.py b/otp/web/SettingsMgrBase.py
new file mode 100644
index 0000000..6e2004c
--- /dev/null
+++ b/otp/web/SettingsMgrBase.py
@@ -0,0 +1,65 @@
+from direct.directnotify.DirectNotifyGlobal import directNotify
+
+class SettingsMgrBase:
+ notify = directNotify.newCategory('SettingsMgrBase')
+
+ def announceGenerate(self):
+ self._settings = {}
+ self._originalValueReprs = {}
+ self._currentValueReprs = {}
+ self._initSettings()
+
+ def delete(self):
+ del self._settings
+
+ def _initSettings(self):
+ pass
+
+ def _iterSettingNames(self):
+ for name in self._settings.iterkeys():
+ yield name
+
+ def _addSettings(self, *settings):
+ for setting in settings:
+ self._addSetting(setting)
+
+ def _addSetting(self, setting):
+ name = setting.getName()
+ if name in self._settings:
+ self.notify.error('duplicate setting "%s"' % name)
+ self._settings[name] = setting
+ self._originalValueReprs[name] = repr(setting.getValue())
+ self._currentValueReprs[name] = repr(setting.getValue())
+
+ def _getOriginalValueRepr(self, settingName):
+ return self._originalValueReprs.get(settingName)
+
+ def _getCurrentValueRepr(self, settingName):
+ return self._currentValueReprs.get(settingName)
+
+ def _removeSetting(self, setting):
+ del self._settings[setting.getName()]
+ del self._originalValueReprs[setting.getName()]
+ del self._currentValueReprs[setting.getName()]
+
+ def _getSetting(self, settingName):
+ return self._settings[settingName]
+
+ def _isSettingModified(self, settingName):
+ return self._getOriginalValueRepr(settingName) != self._getCurrentValueRepr(settingName)
+
+ def _changeSetting(self, settingName, valueStr):
+ try:
+ val = eval(valueStr)
+ except:
+ self.notify.warning('error evaling "%s" for setting "%s"' % (valueStr, settingName))
+ return
+
+ try:
+ setting = self._getSetting(settingName)
+ except:
+ self.notify.warning('unknown setting %s' % settingName)
+ return
+
+ setting.setValue(val)
+ self._currentValueReprs[settingName] = valueStr
diff --git a/otp/web/__init__.py b/otp/web/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/toontown/__init__.py b/toontown/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/toontown/ai/CrashedLeaderBoardDecorator.py b/toontown/ai/CrashedLeaderBoardDecorator.py
new file mode 100644
index 0000000..7a592c4
--- /dev/null
+++ b/toontown/ai/CrashedLeaderBoardDecorator.py
@@ -0,0 +1,46 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed.ClockDelta import *
+from direct.interval.IntervalGlobal import *
+import HolidayDecorator
+from toontown.toonbase import ToontownGlobals
+from pandac.PandaModules import Vec4, loadDNAFile, CSDefault, TransformState, NodePath, TransparencyAttrib
+from toontown.hood import GSHood
+
+class CrashedLeaderBoardDecorator(HolidayDecorator.HolidayDecorator):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CrashedLeaderBoardDecorator')
+
+ def __init__(self):
+ HolidayDecorator.HolidayDecorator.__init__(self)
+
+ def decorate(self):
+ self.updateHoodDNAStore()
+ self.swapIval = self.getSwapVisibleIval()
+ if self.swapIval:
+ self.swapIval.start()
+ holidayIds = base.cr.newsManager.getDecorationHolidayId()
+ if ToontownGlobals.CRASHED_LEADERBOARD not in holidayIds:
+ return
+ if base.config.GetBool('want-crashedLeaderBoard-Smoke', 1):
+ self.startSmokeEffect()
+
+ def startSmokeEffect(self):
+ if isinstance(base.cr.playGame.getPlace().loader.hood, GSHood.GSHood):
+ base.cr.playGame.getPlace().loader.startSmokeEffect()
+
+ def stopSmokeEffect(self):
+ if isinstance(base.cr.playGame.getPlace().loader.hood, GSHood.GSHood):
+ base.cr.playGame.getPlace().loader.stopSmokeEffect()
+
+ def undecorate(self):
+ if base.config.GetBool('want-crashedLeaderBoard-Smoke', 1):
+ self.stopSmokeEffect()
+ holidayIds = base.cr.newsManager.getDecorationHolidayId()
+ if len(holidayIds) > 0:
+ self.decorate()
+ return
+ storageFile = base.cr.playGame.hood.storageDNAFile
+ if storageFile:
+ loadDNAFile(self.dnaStore, storageFile, CSDefault)
+ self.swapIval = self.getSwapVisibleIval()
+ if self.swapIval:
+ self.swapIval.start()
diff --git a/toontown/ai/DatabaseObject.py b/toontown/ai/DatabaseObject.py
new file mode 100644
index 0000000..e685a01
--- /dev/null
+++ b/toontown/ai/DatabaseObject.py
@@ -0,0 +1,213 @@
+from pandac.PandaModules import *
+from ToontownAIMsgTypes import *
+from direct.directnotify.DirectNotifyGlobal import *
+from toontown.toon import DistributedToonAI
+from direct.distributed.PyDatagram import PyDatagram
+from direct.distributed.PyDatagramIterator import PyDatagramIterator
+import types
+
+class DatabaseObject:
+ notify = directNotify.newCategory('DatabaseObject')
+ notify.setInfo(0)
+
+ def __init__(self, air, doId = None, doneEvent = 'DatabaseObject'):
+ self.air = air
+ self.doId = doId
+ self.values = {}
+ self.gotDataHandler = None
+ self.doneEvent = doneEvent
+ return
+
+ def readToon(self, fields = None):
+ toon = DistributedToonAI.DistributedToonAI(self.air)
+ self.readObject(toon, fields)
+ return toon
+
+ if simbase.wantPets:
+
+ def readPet(self):
+ from toontown.pets import DistributedPetAI
+ pet = DistributedPetAI.DistributedPetAI(self.air)
+ self.readObject(pet, None)
+ return pet
+
+ def readPetProxy(self):
+ from toontown.pets import DistributedPetProxyAI
+ petProxy = DistributedPetProxyAI.DistributedPetProxyAI(self.air)
+ self.readObject(petProxy, None)
+ return petProxy
+
+ def readObject(self, do, fields = None):
+ self.do = do
+ className = do.__class__.__name__
+ self.dclass = self.air.dclassesByName[className]
+ self.gotDataHandler = self.fillin
+ if fields != None:
+ self.getFields(fields)
+ else:
+ self.getFields(self.getDatabaseFields(self.dclass))
+ return
+
+ def storeObject(self, do, fields = None):
+ self.do = do
+ className = do.__class__.__name__
+ self.dclass = self.air.dclassesByName[className]
+ if fields != None:
+ self.reload(self.do, self.dclass, fields)
+ else:
+ dbFields = self.getDatabaseFields(self.dclass)
+ self.reload(self.do, self.dclass, dbFields)
+ values = self.values
+ if fields != None:
+ values = {}
+ for field in fields:
+ if self.values.has_key(field):
+ values[field] = self.values[field]
+ else:
+ self.notify.warning('Field %s not defined.' % field)
+
+ self.setFields(values)
+ return
+
+ def getFields(self, fields):
+ context = self.air.dbObjContext
+ self.air.dbObjContext += 1
+ self.air.dbObjMap[context] = self
+ dg = PyDatagram()
+ dg.addServerHeader(DBSERVER_ID, self.air.ourChannel, DBSERVER_GET_STORED_VALUES)
+ dg.addUint32(context)
+ dg.addUint32(self.doId)
+ dg.addUint16(len(fields))
+ for f in fields:
+ dg.addString(f)
+
+ self.air.send(dg)
+
+ def getFieldsResponse(self, di):
+ objId = di.getUint32()
+ if objId != self.doId:
+ self.notify.warning('Unexpected doId %d' % objId)
+ return
+ count = di.getUint16()
+ fields = []
+ for i in range(count):
+ name = di.getString()
+ fields.append(name)
+
+ retCode = di.getUint8()
+ if retCode != 0:
+ self.notify.warning('Failed to retrieve data for object %d' % self.doId)
+ else:
+ values = []
+ for i in range(count):
+ value = di.getString()
+ values.append(value)
+
+ for i in range(count):
+ found = di.getUint8()
+ if not found:
+ self.notify.info('field %s is not found' % fields[i])
+ try:
+ del self.values[fields[i]]
+ except:
+ pass
+
+ else:
+ self.values[fields[i]] = PyDatagram(values[i])
+
+ self.notify.info('got data for %d' % self.doId)
+ if self.gotDataHandler != None:
+ self.gotDataHandler(self.do, self.dclass)
+ self.gotDataHandler = None
+ if self.doneEvent != None:
+ messenger.send(self.doneEvent, [self, retCode])
+ return
+
+ def setFields(self, values):
+ dg = PyDatagram()
+ dg.addServerHeader(DBSERVER_ID, self.air.ourChannel, DBSERVER_SET_STORED_VALUES)
+ dg.addUint32(self.doId)
+ dg.addUint16(len(values))
+ items = values.items()
+ for field, value in items:
+ dg.addString(field)
+
+ for field, value in items:
+ dg.addString(value.getMessage())
+
+ self.air.send(dg)
+
+ def getDatabaseFields(self, dclass):
+ fields = []
+ for i in range(dclass.getNumInheritedFields()):
+ dcf = dclass.getInheritedField(i)
+ af = dcf.asAtomicField()
+ if af:
+ if af.isDb():
+ fields.append(af.getName())
+
+ return fields
+
+ def fillin(self, do, dclass):
+ do.doId = self.doId
+ for field, value in self.values.items():
+ if field == 'setZonesVisited' and value.getLength() == 1:
+ self.notify.warning('Ignoring broken setZonesVisited')
+ else:
+ dclass.directUpdate(do, field, value)
+
+ def reload(self, do, dclass, fields):
+ self.doId = do.doId
+ self.values = {}
+ for fieldName in fields:
+ field = dclass.getFieldByName(fieldName)
+ if field == None:
+ self.notify.warning('No definition for %s' % fieldName)
+ else:
+ dg = PyDatagram()
+ packOk = dclass.packRequiredField(dg, do, field)
+ self.values[fieldName] = dg
+
+ return
+
+ def createObject(self, objectType):
+ values = {}
+ for key, value in values.items():
+ values[key] = PyDatagram(str(value))
+
+ context = self.air.dbObjContext
+ self.air.dbObjContext += 1
+ self.air.dbObjMap[context] = self
+ self.createObjType = objectType
+ dg = PyDatagram()
+ dg.addServerHeader(DBSERVER_ID, self.air.ourChannel, DBSERVER_CREATE_STORED_OBJECT)
+ dg.addUint32(context)
+ dg.addString('')
+ dg.addUint16(objectType)
+ dg.addUint16(len(values))
+ for field in values.keys():
+ dg.addString(field)
+
+ for value in values.values():
+ dg.addString(value.getMessage())
+
+ self.air.send(dg)
+
+ def handleCreateObjectResponse(self, di):
+ retCode = di.getUint8()
+ if retCode != 0:
+ self.notify.warning('Database object %s create failed' % self.createObjType)
+ else:
+ del self.createObjType
+ self.doId = di.getUint32()
+ if self.doneEvent != None:
+ messenger.send(self.doneEvent, [self, retCode])
+ return
+
+ def deleteObject(self):
+ self.notify.warning('deleting object %s' % self.doId)
+ dg = PyDatagram()
+ dg.addServerHeader(DBSERVER_ID, self.air.ourChannel, DBSERVER_DELETE_STORED_OBJECT)
+ dg.addUint32(self.doId)
+ dg.addUint32(3735928559L)
+ self.air.send(dg)
diff --git a/toontown/ai/DistributedBlackCatMgr.py b/toontown/ai/DistributedBlackCatMgr.py
new file mode 100644
index 0000000..a39a37e
--- /dev/null
+++ b/toontown/ai/DistributedBlackCatMgr.py
@@ -0,0 +1,51 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from direct.interval.IntervalGlobal import *
+from toontown.effects import DustCloud
+
+def getDustCloudIval(toon):
+ dustCloud = DustCloud.DustCloud(fBillboard=0)
+ dustCloud.setBillboardAxis(2.0)
+ dustCloud.setZ(3)
+ dustCloud.setScale(0.4)
+ dustCloud.createTrack()
+ toon.laffMeter.color = toon.style.getBlackColor()
+ return Sequence(Wait(0.5), Func(dustCloud.reparentTo, toon), dustCloud.track, Func(dustCloud.detachNode), Func(toon.laffMeter.adjustFace, toon.hp, toon.maxHp))
+
+
+class DistributedBlackCatMgr(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBlackCatMgr')
+ ActivateEvent = 'DistributedBlackCatMgr-activate'
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+
+ def setAvId(self, avId):
+ self.avId = avId
+
+ def announceGenerate(self):
+ DistributedBlackCatMgr.notify.debug('announceGenerate')
+ DistributedObject.DistributedObject.announceGenerate(self)
+ self.acceptOnce(DistributedBlackCatMgr.ActivateEvent, self.doBlackCatTransformation)
+ self.dustCloudIval = None
+ return
+
+ def delete(self):
+ if self.dustCloudIval:
+ self.dustCloudIval.finish()
+ del self.dustCloudIval
+ self.ignore(DistributedBlackCatMgr.ActivateEvent)
+ DistributedObject.DistributedObject.delete(self)
+
+ def doBlackCatTransformation(self):
+ DistributedBlackCatMgr.notify.debug('doBlackCatTransformation')
+ toon = base.cr.doId2do[self.avId]
+ if not toon:
+ DistributedBlackCatMgr.notify.warning("couldn't find Toon %s" % self.avId)
+ return
+ if toon.style.getAnimal() != 'cat':
+ DistributedBlackCatMgr.notify.warning('not a cat: %s' % self.avId)
+ return
+ self.sendUpdate('doBlackCatTransformation', [])
+ self.dustCloudIval = getDustCloudIval(toon)
+ self.dustCloudIval.start()
diff --git a/toontown/ai/DistributedGreenToonEffectMgr.py b/toontown/ai/DistributedGreenToonEffectMgr.py
new file mode 100644
index 0000000..2046332
--- /dev/null
+++ b/toontown/ai/DistributedGreenToonEffectMgr.py
@@ -0,0 +1,33 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from direct.interval.IntervalGlobal import *
+from otp.speedchat import SpeedChatGlobals
+from toontown.toonbase import TTLocalizer
+
+class DistributedGreenToonEffectMgr(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGreenToonEffectMgr')
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+
+ def phraseSaid(phraseId):
+ greenPhrase = 30450
+ if phraseId == greenPhrase:
+ self.addGreenToonEffect()
+
+ self.accept(SpeedChatGlobals.SCStaticTextMsgEvent, phraseSaid)
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ DistributedGreenToonEffectMgr.notify.debug('announceGenerate')
+
+ def delete(self):
+ self.ignore(SpeedChatGlobals.SCStaticTextMsgEvent)
+ DistributedObject.DistributedObject.delete(self)
+
+ def addGreenToonEffect(self):
+ DistributedGreenToonEffectMgr.notify.debug('addGreenToonEffect')
+ av = base.localAvatar
+ self.sendUpdate('addGreenToonEffect', [])
+ msgTrack = Sequence(Func(av.setSystemMessage, 0, TTLocalizer.GreenToonEffectMsg))
+ msgTrack.start()
diff --git a/toontown/ai/DistributedHydrantZeroMgr.py b/toontown/ai/DistributedHydrantZeroMgr.py
new file mode 100644
index 0000000..f951882
--- /dev/null
+++ b/toontown/ai/DistributedHydrantZeroMgr.py
@@ -0,0 +1,30 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from toontown.ai import DistributedPhaseEventMgr
+
+class DistributedHydrantZeroMgr(DistributedPhaseEventMgr.DistributedPhaseEventMgr):
+ neverDisable = 1
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedHydrantZeroMgr')
+
+ def __init__(self, cr):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.__init__(self, cr)
+ cr.hydrantZeroMgr = self
+
+ def announceGenerate(self):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.announceGenerate(self)
+ messenger.send('hydrantZeroIsRunning', [self.isRunning])
+
+ def delete(self):
+ self.notify.debug('deleting hydrantzeromgr')
+ messenger.send('hydrantZeroIsRunning', [False])
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.delete(self)
+ if hasattr(self.cr, 'hydrantZeroMgr'):
+ del self.cr.hydrantZeroMgr
+
+ def setCurPhase(self, newPhase):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.setCurPhase(self, newPhase)
+ messenger.send('hydrantZeroPhase', [newPhase])
+
+ def setIsRunning(self, isRunning):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.setIsRunning(self, isRunning)
+ messenger.send('hydrantZeroIsRunning', [isRunning])
diff --git a/toontown/ai/DistributedMailboxZeroMgr.py b/toontown/ai/DistributedMailboxZeroMgr.py
new file mode 100644
index 0000000..71058c1
--- /dev/null
+++ b/toontown/ai/DistributedMailboxZeroMgr.py
@@ -0,0 +1,30 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from toontown.ai import DistributedPhaseEventMgr
+
+class DistributedMailboxZeroMgr(DistributedPhaseEventMgr.DistributedPhaseEventMgr):
+ neverDisable = 1
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMailboxZeroMgr')
+
+ def __init__(self, cr):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.__init__(self, cr)
+ cr.mailboxZeroMgr = self
+
+ def announceGenerate(self):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.announceGenerate(self)
+ messenger.send('mailboxZeroIsRunning', [self.isRunning])
+
+ def delete(self):
+ self.notify.debug('deleting mailboxzeromgr')
+ messenger.send('mailboxZeroIsRunning', [False])
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.delete(self)
+ if hasattr(self.cr, 'mailboxZeroMgr'):
+ del self.cr.mailboxZeroMgr
+
+ def setCurPhase(self, newPhase):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.setCurPhase(self, newPhase)
+ messenger.send('mailboxZeroPhase', [newPhase])
+
+ def setIsRunning(self, isRunning):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.setIsRunning(self, isRunning)
+ messenger.send('mailboxZeroIsRunning', [isRunning])
diff --git a/toontown/ai/DistributedPhaseEventMgr.py b/toontown/ai/DistributedPhaseEventMgr.py
new file mode 100644
index 0000000..5ec42fe
--- /dev/null
+++ b/toontown/ai/DistributedPhaseEventMgr.py
@@ -0,0 +1,32 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+import datetime
+
+class DistributedPhaseEventMgr(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPhaseEventMgr')
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.holidayDates = []
+
+ def setIsRunning(self, isRunning):
+ self.isRunning = isRunning
+
+ def setNumPhases(self, numPhases):
+ self.numPhases = numPhases
+
+ def setCurPhase(self, curPhase):
+ self.curPhase = curPhase
+
+ def getIsRunning(self):
+ return self.isRunning
+
+ def getNumPhases(self):
+ return self.numPhases
+
+ def getCurPhase(self):
+ return self.curPhase
+
+ def setDates(self, holidayDates):
+ for holidayDate in holidayDates:
+ self.holidayDates.append(datetime.datetime(holidayDate[0], holidayDate[1], holidayDate[2], holidayDate[3], holidayDate[4], holidayDate[5]))
diff --git a/toontown/ai/DistributedPolarPlaceEffectMgr.py b/toontown/ai/DistributedPolarPlaceEffectMgr.py
new file mode 100644
index 0000000..0c30fee
--- /dev/null
+++ b/toontown/ai/DistributedPolarPlaceEffectMgr.py
@@ -0,0 +1,33 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from direct.interval.IntervalGlobal import *
+from otp.speedchat import SpeedChatGlobals
+from toontown.toonbase import TTLocalizer
+
+class DistributedPolarPlaceEffectMgr(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPolarPlaceEffectMgr')
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+
+ def phraseSaid(phraseId):
+ helpPhrase = 104
+ if phraseId == helpPhrase:
+ self.addPolarPlaceEffect()
+
+ self.accept(SpeedChatGlobals.SCStaticTextMsgEvent, phraseSaid)
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ DistributedPolarPlaceEffectMgr.notify.debug('announceGenerate')
+
+ def delete(self):
+ self.ignore(SpeedChatGlobals.SCStaticTextMsgEvent)
+ DistributedObject.DistributedObject.delete(self)
+
+ def addPolarPlaceEffect(self):
+ DistributedPolarPlaceEffectMgr.notify.debug('addResitanceEffect')
+ av = base.localAvatar
+ self.sendUpdate('addPolarPlaceEffect', [])
+ msgTrack = Sequence(Func(av.setSystemMessage, 0, TTLocalizer.PolarPlaceEffect1), Wait(2), Func(av.setSystemMessage, 0, TTLocalizer.PolarPlaceEffect2), Wait(4), Func(av.setSystemMessage, 0, TTLocalizer.PolarPlaceEffect3))
+ msgTrack.start()
diff --git a/toontown/ai/DistributedResistanceEmoteMgr.py b/toontown/ai/DistributedResistanceEmoteMgr.py
new file mode 100644
index 0000000..cec26df
--- /dev/null
+++ b/toontown/ai/DistributedResistanceEmoteMgr.py
@@ -0,0 +1,36 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from direct.interval.IntervalGlobal import *
+from otp.speedchat import SpeedChatGlobals
+from otp.otpbase.OTPLocalizerEnglish import EmoteFuncDict
+from toontown.toonbase import TTLocalizer
+RESIST_INDEX = EmoteFuncDict['Resistance Salute']
+
+class DistributedResistanceEmoteMgr(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedResistanceEmoteMgr')
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+
+ def phraseSaid(phraseId):
+ helpPhrase = 513
+ if phraseId == helpPhrase:
+ self.addResistanceEmote()
+
+ self.accept(SpeedChatGlobals.SCStaticTextMsgEvent, phraseSaid)
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ DistributedResistanceEmoteMgr.notify.debug('announceGenerate')
+
+ def delete(self):
+ self.ignore(SpeedChatGlobals.SCStaticTextMsgEvent)
+ DistributedObject.DistributedObject.delete(self)
+
+ def addResistanceEmote(self):
+ DistributedResistanceEmoteMgr.notify.debug('addResitanceEmote')
+ av = base.localAvatar
+ if not av.emoteAccess[RESIST_INDEX]:
+ self.sendUpdate('addResistanceEmote', [])
+ msgTrack = Sequence(Wait(1), Func(av.setSystemMessage, 0, TTLocalizer.ResistanceEmote1), Wait(3), Func(av.setSystemMessage, 0, TTLocalizer.ResistanceEmote2), Wait(4), Func(av.setSystemMessage, 0, TTLocalizer.ResistanceEmote3))
+ msgTrack.start()
diff --git a/toontown/ai/DistributedScavengerHuntTarget.py b/toontown/ai/DistributedScavengerHuntTarget.py
new file mode 100644
index 0000000..3406e2a
--- /dev/null
+++ b/toontown/ai/DistributedScavengerHuntTarget.py
@@ -0,0 +1,40 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from otp.speedchat import SpeedChatGlobals
+
+class DistributedScavengerHuntTarget(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedScavengerHuntTarget')
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+
+ def setupListenerDetails(self):
+ self.triggered = False
+ self.triggerDelay = 15
+ self.accept(SpeedChatGlobals.SCCustomMsgEvent, self.phraseSaid)
+
+ def phraseSaid(self, phraseId):
+ self.notify.debug('Checking if phrase was said')
+ helpPhrase = 10003
+
+ def reset():
+ self.triggered = False
+
+ if phraseId == helpPhrase and not self.triggered:
+ self.triggered = True
+ self.attemptScavengerHunt()
+ taskMgr.doMethodLater(self.triggerDelay, reset, 'ScavengerHunt-phrase-reset', extraArgs=[])
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ DistributedScavengerHuntTarget.notify.debug('announceGenerate')
+ self.setupListenerDetails()
+
+ def delete(self):
+ self.ignoreAll()
+ taskMgr.remove('ScavengerHunt-phrase-reset')
+ DistributedObject.DistributedObject.delete(self)
+
+ def attemptScavengerHunt(self):
+ DistributedScavengerHuntTarget.notify.debug('attempScavengerHunt')
+ self.sendUpdate('attemptScavengerHunt', [])
diff --git a/toontown/ai/DistributedSillyMeterMgr.py b/toontown/ai/DistributedSillyMeterMgr.py
new file mode 100644
index 0000000..a45f474
--- /dev/null
+++ b/toontown/ai/DistributedSillyMeterMgr.py
@@ -0,0 +1,55 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from toontown.ai import DistributedPhaseEventMgr
+import time
+
+class DistributedSillyMeterMgr(DistributedPhaseEventMgr.DistributedPhaseEventMgr):
+ neverDisable = 1
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSillyMeterMgr')
+
+ def __init__(self, cr):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.__init__(self, cr)
+ cr.SillyMeterMgr = self
+
+ def announceGenerate(self):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.announceGenerate(self)
+ messenger.send('SillyMeterIsRunning', [self.isRunning])
+
+ def delete(self):
+ self.notify.debug('deleting SillyMetermgr')
+ messenger.send('SillyMeterIsRunning', [False])
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.delete(self)
+ if hasattr(self.cr, 'SillyMeterMgr'):
+ del self.cr.SillyMeterMgr
+
+ def setCurPhase(self, newPhase):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.setCurPhase(self, newPhase)
+ messenger.send('SillyMeterPhase', [newPhase])
+
+ def setIsRunning(self, isRunning):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.setIsRunning(self, isRunning)
+ messenger.send('SillyMeterIsRunning', [isRunning])
+
+ def getCurPhaseDuration(self):
+ if len(self.holidayDates) > 0:
+ startHolidayDate = self.holidayDates[self.curPhase]
+ if self.curPhase + 1 >= len(self.holidayDates):
+ self.notify.error('No end date for phase %' % self.curPhase)
+ return -1
+ else:
+ endHolidayDate = self.holidayDates[self.curPhase + 1]
+ startHolidayTime = time.mktime(startHolidayDate.timetuple())
+ endHolidayTime = time.mktime(endHolidayDate.timetuple())
+ holidayDuration = endHolidayTime - startHolidayTime
+ if holidayDuration < 0:
+ self.notify.error('Duration not set for phase %' % self.curPhase)
+ return -1
+ else:
+ return holidayDuration
+ else:
+ self.notify.warning('Phase dates not yet known')
+ return -1
+
+ def getCurPhaseStartDate(self):
+ if len(self.holidayDates) > 0:
+ return self.holidayDates[self.curPhase]
diff --git a/toontown/ai/DistributedTrashcanZeroMgr.py b/toontown/ai/DistributedTrashcanZeroMgr.py
new file mode 100644
index 0000000..9dd7a2e
--- /dev/null
+++ b/toontown/ai/DistributedTrashcanZeroMgr.py
@@ -0,0 +1,30 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from toontown.ai import DistributedPhaseEventMgr
+
+class DistributedTrashcanZeroMgr(DistributedPhaseEventMgr.DistributedPhaseEventMgr):
+ neverDisable = 1
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTrashcanZeroMgr')
+
+ def __init__(self, cr):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.__init__(self, cr)
+ cr.trashcanZeroMgr = self
+
+ def announceGenerate(self):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.announceGenerate(self)
+ messenger.send('trashcanZeroIsRunning', [self.isRunning])
+
+ def delete(self):
+ self.notify.debug('deleting trashcanzeromgr')
+ messenger.send('trashcanZeroIsRunning', [False])
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.delete(self)
+ if hasattr(self.cr, 'trashcanZeroMgr'):
+ del self.cr.trashcanZeroMgr
+
+ def setCurPhase(self, newPhase):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.setCurPhase(self, newPhase)
+ messenger.send('trashcanZeroPhase', [newPhase])
+
+ def setIsRunning(self, isRunning):
+ DistributedPhaseEventMgr.DistributedPhaseEventMgr.setIsRunning(self, isRunning)
+ messenger.send('trashcanZeroIsRunning', [isRunning])
diff --git a/toontown/ai/DistributedTrickOrTreatTarget.py b/toontown/ai/DistributedTrickOrTreatTarget.py
new file mode 100644
index 0000000..5791f6a
--- /dev/null
+++ b/toontown/ai/DistributedTrickOrTreatTarget.py
@@ -0,0 +1,22 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from otp.speedchat import SpeedChatGlobals
+import DistributedScavengerHuntTarget
+
+class DistributedTrickOrTreatTarget(DistributedScavengerHuntTarget.DistributedScavengerHuntTarget):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTrickOrTreatTarget')
+
+ def __init__(self, cr):
+ DistributedScavengerHuntTarget.DistributedScavengerHuntTarget.__init__(self, cr)
+
+ def phraseSaid(self, phraseId):
+ self.notify.debug('Checking if phrase was said')
+ helpPhrase = 10003
+
+ def reset():
+ self.triggered = False
+
+ if phraseId == helpPhrase and not self.triggered:
+ self.triggered = True
+ self.attemptScavengerHunt()
+ taskMgr.doMethodLater(self.triggerDelay, reset, 'ScavengerHunt-phrase-reset', extraArgs=[])
diff --git a/toontown/ai/DistributedWinterCarolingTarget.py b/toontown/ai/DistributedWinterCarolingTarget.py
new file mode 100644
index 0000000..ddcff72
--- /dev/null
+++ b/toontown/ai/DistributedWinterCarolingTarget.py
@@ -0,0 +1,29 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from toontown.speedchat.TTSCIndexedTerminal import TTSCIndexedMsgEvent
+import DistributedScavengerHuntTarget
+
+class DistributedWinterCarolingTarget(DistributedScavengerHuntTarget.DistributedScavengerHuntTarget):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedWinterCarolingTarget')
+
+ def __init__(self, cr):
+ DistributedScavengerHuntTarget.DistributedScavengerHuntTarget.__init__(self, cr)
+
+ def setupListenerDetails(self):
+ self.triggered = False
+ self.triggerDelay = 15
+ self.accept(TTSCIndexedMsgEvent, self.phraseSaid)
+
+ def phraseSaid(self, phraseId):
+ self.notify.debug('Checking if phrase was said')
+ helpPhrases = []
+ for i in range(6):
+ helpPhrases.append(30220 + i)
+
+ def reset():
+ self.triggered = False
+
+ if phraseId in helpPhrases and not self.triggered:
+ self.triggered = True
+ self.attemptScavengerHunt()
+ taskMgr.doMethodLater(self.triggerDelay, reset, 'ScavengerHunt-phrase-reset', extraArgs=[])
diff --git a/toontown/ai/HalloweenHolidayDecorator.py b/toontown/ai/HalloweenHolidayDecorator.py
new file mode 100644
index 0000000..1a71cdd
--- /dev/null
+++ b/toontown/ai/HalloweenHolidayDecorator.py
@@ -0,0 +1,109 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed.ClockDelta import *
+from direct.interval.IntervalGlobal import *
+import HolidayDecorator
+from toontown.toonbase import ToontownGlobals
+from toontown.safezone import Playground
+from toontown.town import Street
+from toontown.estate import Estate
+from pandac.PandaModules import Vec4, loadDNAFile, CSDefault, TransformState, NodePath, TransparencyAttrib
+
+class HalloweenHolidayDecorator(HolidayDecorator.HolidayDecorator):
+ notify = DirectNotifyGlobal.directNotify.newCategory('HalloweenHolidayDecorator')
+
+ def __init__(self):
+ HolidayDecorator.HolidayDecorator.__init__(self)
+
+ def __checkStreetValidity(self):
+ if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace() and isinstance(base.cr.playGame.getPlace(), Street.Street) and hasattr(base.cr.playGame.getPlace(), 'loader') and base.cr.playGame.getPlace().loader and hasattr(base.cr.playGame.getPlace().loader, 'geom') and base.cr.playGame.getPlace().loader.geom:
+ return True
+ else:
+ if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace():
+ self.notify.debug('Failed Street Check %s' % base.cr.playGame.getPlace())
+ else:
+ self.notify.debug('Failed Street Check')
+ return False
+
+ def __checkHoodValidity(self):
+ if (hasattr(base.cr.playGame, 'getPlace') and
+ base.cr.playGame.getPlace() and
+ (isinstance(base.cr.playGame.getPlace(), Playground.Playground) or
+ isinstance(base.cr.playGame.getPlace(), Estate.Estate)) and
+ hasattr(base.cr.playGame.getPlace(), 'loader') and
+ base.cr.playGame.getPlace().loader and
+ hasattr(base.cr.playGame.getPlace().loader, 'hood') and
+ base.cr.playGame.getPlace().loader.hood and
+ hasattr(base.cr.playGame.getPlace().loader.hood, 'loader') and
+ base.cr.playGame.getPlace().loader.hood.loader and
+ hasattr(base.cr.playGame.getPlace().loader.hood.loader, 'geom') and
+ base.cr.playGame.getPlace().loader.hood.loader.geom):
+ return True
+ else:
+ if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace():
+ self.notify.debug('Failed Hood Check %s' % base.cr.playGame.getPlace())
+ else:
+ self.notify.debug('Failed Hood Check')
+ return False
+
+ def __startSpookySky(self):
+ if (self.__checkHoodValidity() or self.__checkStreetValidity()) and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky:
+ base.cr.playGame.hood.startSpookySky()
+
+ def __stopSpookySky(self):
+ if (self.__checkHoodValidity() or self.__checkStreetValidity()) and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky:
+ base.cr.playGame.hood.endSpookySky()
+
+ def decorate(self):
+ self.updateHoodDNAStore()
+ self.swapIval = self.getSwapVisibleIval()
+ if self.swapIval:
+ self.swapIval.start()
+
+ def __lightDecorationOn__():
+ place = base.cr.playGame.getPlace()
+ if hasattr(place, 'halloweenLights'):
+ if not self.__checkStreetValidity():
+ return
+ else:
+ place.halloweenLights = place.loader.geom.findAllMatches('**/*light*')
+ place.halloweenLights += place.loader.geom.findAllMatches('**/*lamp*')
+ place.halloweenLights += place.loader.geom.findAllMatches('**/prop_snow_tree*')
+ for light in place.halloweenLights:
+ light.setColorScaleOff(0)
+
+ elif not self.__checkHoodValidity():
+ return
+ else:
+ place.loader.hood.halloweenLights = place.loader.hood.loader.geom.findAllMatches('**/*light*')
+ place.loader.hood.halloweenLights += place.loader.hood.loader.geom.findAllMatches('**/*lamp*')
+ place.loader.hood.halloweenLights += place.loader.hood.loader.geom.findAllMatches('**/prop_snow_tree*')
+ for light in place.loader.hood.halloweenLights:
+ light.setColorScaleOff(0)
+
+ holidayIds = base.cr.newsManager.getDecorationHolidayId()
+ if ToontownGlobals.HALLOWEEN_COSTUMES not in holidayIds and ToontownGlobals.SPOOKY_COSTUMES not in holidayIds:
+ return
+ if (self.__checkHoodValidity() or self.__checkStreetValidity()) and hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky:
+ preShow = Sequence(Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 1.5, Vec4(1, 1, 1, 0.25)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(0.55, 0.55, 0.65, 1)), Func(__lightDecorationOn__)), Func(self.__startSpookySky))
+ preShow.start()
+ distributedEstate = base.cr.doFind('DistributedEstate')
+ if distributedEstate:
+ distributedEstate.loadWitch()
+
+ def undecorate(self):
+ if (self.__checkHoodValidity() or self.__checkStreetValidity()) and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky:
+ postShow = Sequence(Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 1.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(1, 1, 1, 1))), Func(self.__stopSpookySky))
+ postShow.start()
+ distributedEstate = base.cr.doFind('DistributedEstate')
+ if distributedEstate:
+ distributedEstate.unloadWitch()
+ holidayIds = base.cr.newsManager.getDecorationHolidayId()
+ if len(holidayIds) > 0:
+ self.decorate()
+ return
+ storageFile = base.cr.playGame.hood.storageDNAFile
+ if storageFile:
+ loadDNAFile(self.dnaStore, storageFile, CSDefault)
+ self.swapIval = self.getSwapVisibleIval()
+ if self.swapIval:
+ self.swapIval.start()
diff --git a/toontown/ai/HolidayBaseAI.py b/toontown/ai/HolidayBaseAI.py
new file mode 100644
index 0000000..2578e36
--- /dev/null
+++ b/toontown/ai/HolidayBaseAI.py
@@ -0,0 +1,16 @@
+from direct.directnotify import DirectNotifyGlobal
+import random
+from direct.task import Task
+from toontown.effects import DistributedFireworkShowAI
+
+class HolidayBaseAI:
+
+ def __init__(self, air, holidayId):
+ self.air = air
+ self.holidayId = holidayId
+
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
diff --git a/toontown/ai/HolidayDecorator.py b/toontown/ai/HolidayDecorator.py
new file mode 100644
index 0000000..0f162d2
--- /dev/null
+++ b/toontown/ai/HolidayDecorator.py
@@ -0,0 +1,68 @@
+from toontown.toonbase import ToontownGlobals
+from direct.interval.IntervalGlobal import Parallel, Sequence, Func, Wait
+from pandac.PandaModules import Vec4, loadDNAFile, CSDefault, TransformState, NodePath, TransparencyAttrib
+
+class HolidayDecorator:
+
+ def __init__(self):
+ self.dnaStore = base.cr.playGame.dnaStore
+ self.swapIval = None
+ return
+
+ def exit(self):
+ if self.swapIval is not None and self.swapIval.isPlaying():
+ self.swapIval.finish()
+ return
+
+ def decorate(self):
+ self.updateHoodDNAStore()
+ self.swapIval = self.getSwapVisibleIval()
+ if self.swapIval:
+ self.swapIval.start()
+
+ def undecorate(self):
+ holidayIds = base.cr.newsManager.getDecorationHolidayId()
+ if len(holidayIds) > 0:
+ self.decorate()
+ return
+ storageFile = base.cr.playGame.hood.storageDNAFile
+ if storageFile:
+ loadDNAFile(self.dnaStore, storageFile, CSDefault)
+ self.swapIval = self.getSwapVisibleIval()
+ if self.swapIval:
+ self.swapIval.start()
+
+ def updateHoodDNAStore(self):
+ hood = base.cr.playGame.hood
+ holidayIds = base.cr.newsManager.getDecorationHolidayId()
+ for holiday in holidayIds:
+ for storageFile in hood.holidayStorageDNADict.get(holiday, []):
+ loadDNAFile(self.dnaStore, storageFile, CSDefault)
+
+ def getSwapVisibleIval(self, wait = 5.0, tFadeOut = 3.0, tFadeIn = 3.0):
+ loader = base.cr.playGame.hood.loader
+ npl = render.findAllMatches('**/=DNARoot=holiday_prop;+s')
+ p = Parallel()
+ for i in range(npl.getNumPaths()):
+ np = npl.getPath(i)
+ np.setTransparency(TransparencyAttrib.MDual, 1)
+ if not np.hasTag('DNACode'):
+ continue
+ dnaCode = np.getTag('DNACode')
+ dnaNode = self.dnaStore.findNode(dnaCode)
+ if dnaNode.isEmpty():
+ continue
+ newNP = dnaNode.copyTo(np.getParent())
+ newNP.setTag('DNARoot', 'holiday_prop')
+ newNP.setTag('DNACode', dnaCode)
+ newNP.setColorScale(1, 1, 1, 0)
+ newNP.setTransparency(TransparencyAttrib.MDual, 1)
+ if np.hasTag('transformIndex'):
+ index = int(np.getTag('transformIndex'))
+ transform = loader.holidayPropTransforms.get(index, TransformState.makeIdentity())
+ newNP.setTransform(NodePath(), transform)
+ newNP.setTag('transformIndex', `index`)
+ s = Sequence(Wait(wait), np.colorScaleInterval(tFadeOut, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeInOut'), Func(np.detachNode), Func(np.clearTransparency), newNP.colorScaleInterval(tFadeOut, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0), blendType='easeInOut'), Func(newNP.clearTransparency), Func(newNP.clearColorScale))
+ p.append(s)
+
+ return p
diff --git a/toontown/ai/NewsManager.py b/toontown/ai/NewsManager.py
new file mode 100644
index 0000000..54227ea
--- /dev/null
+++ b/toontown/ai/NewsManager.py
@@ -0,0 +1,666 @@
+from pandac.PandaModules import *
+from direct.distributed import DistributedObject
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import ToontownBattleGlobals
+from toontown.battle import SuitBattleGlobals
+from toontown.toonbase import TTLocalizer
+import HolidayDecorator
+import HalloweenHolidayDecorator
+import CrashedLeaderBoardDecorator
+from direct.interval.IntervalGlobal import *
+import calendar
+from copy import deepcopy
+from toontown.speedchat import TTSCJellybeanJamMenu
+decorationHolidays = [ToontownGlobals.WINTER_DECORATIONS,
+ ToontownGlobals.WACKY_WINTER_DECORATIONS,
+ ToontownGlobals.HALLOWEEN_PROPS,
+ ToontownGlobals.SPOOKY_PROPS,
+ ToontownGlobals.HALLOWEEN_COSTUMES,
+ ToontownGlobals.SPOOKY_COSTUMES,
+ ToontownGlobals.CRASHED_LEADERBOARD]
+promotionalSpeedChatHolidays = [ToontownGlobals.ELECTION_PROMOTION]
+
+class NewsManager(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('NewsManager')
+ neverDisable = 1
+ YearlyHolidayType = 1
+ OncelyHolidayType = 2
+ RelativelyHolidayType = 3
+ OncelyMultipleStartHolidayType = 4
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.population = 0
+ self.invading = 0
+ self.decorationHolidayIds = []
+ self.holidayDecorator = None
+ self.holidayIdList = []
+ base.cr.newsManager = self
+ base.localAvatar.inventory.setInvasionCreditMultiplier(1)
+ self.weeklyCalendarHolidays = []
+ return
+
+ def delete(self):
+ self.cr.newsManager = None
+ if self.holidayDecorator:
+ self.holidayDecorator.exit()
+ DistributedObject.DistributedObject.delete(self)
+ return
+
+ def setPopulation(self, population):
+ self.population = population
+ messenger.send('newPopulation', [population])
+
+ def getPopulation(self):
+ return population
+
+ def sendSystemMessage(self, message, style):
+ base.localAvatar.setSystemMessage(style, message)
+
+ def setInvasionStatus(self, msgType, cogType, numRemaining, skeleton):
+ self.notify.info('setInvasionStatus: msgType: %s cogType: %s, numRemaining: %s, skeleton: %s' % (msgType,
+ cogType,
+ numRemaining,
+ skeleton))
+ cogName = SuitBattleGlobals.SuitAttributes[cogType]['name']
+ cogNameP = SuitBattleGlobals.SuitAttributes[cogType]['pluralname']
+ if skeleton:
+ cogName = TTLocalizer.Skeleton
+ cogNameP = TTLocalizer.SkeletonP
+ if msgType == ToontownGlobals.SuitInvasionBegin:
+ msg1 = TTLocalizer.SuitInvasionBegin1
+ msg2 = TTLocalizer.SuitInvasionBegin2 % cogNameP
+ self.invading = 1
+ elif msgType == ToontownGlobals.SuitInvasionUpdate:
+ msg1 = TTLocalizer.SuitInvasionUpdate1 % numRemaining
+ msg2 = TTLocalizer.SuitInvasionUpdate2 % cogNameP
+ self.invading = 1
+ elif msgType == ToontownGlobals.SuitInvasionEnd:
+ msg1 = TTLocalizer.SuitInvasionEnd1 % cogName
+ msg2 = TTLocalizer.SuitInvasionEnd2
+ self.invading = 0
+ elif msgType == ToontownGlobals.SuitInvasionBulletin:
+ msg1 = TTLocalizer.SuitInvasionBulletin1
+ msg2 = TTLocalizer.SuitInvasionBulletin2 % cogNameP
+ self.invading = 1
+ else:
+ self.notify.warning('setInvasionStatus: invalid msgType: %s' % msgType)
+ return
+ if self.invading:
+ mult = ToontownBattleGlobals.getInvasionMultiplier()
+ else:
+ mult = 1
+ base.localAvatar.inventory.setInvasionCreditMultiplier(mult)
+ Sequence(Wait(1.0), Func(base.localAvatar.setSystemMessage, 0, msg1), Wait(5.0), Func(base.localAvatar.setSystemMessage, 0, msg2), name='newsManagerWait', autoPause=1).start()
+
+ def getInvading(self):
+ return self.invading
+
+ def startHoliday(self, holidayId):
+ if holidayId not in self.holidayIdList:
+ self.notify.info('setHolidayId: Starting Holiday %s' % holidayId)
+ self.holidayIdList.append(holidayId)
+ if holidayId in decorationHolidays:
+ self.decorationHolidayIds.append(holidayId)
+ if holidayId == ToontownGlobals.HALLOWEEN_PROPS:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addHalloweenMenu()
+ self.setHalloweenPropsHolidayStart()
+ elif holidayId == ToontownGlobals.SPOOKY_PROPS:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addHalloweenMenu()
+ self.setSpookyPropsHolidayStart()
+ elif holidayId == ToontownGlobals.WINTER_DECORATIONS:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addWinterMenu()
+ self.setWinterDecorationsStart()
+ elif holidayId == ToontownGlobals.WACKY_WINTER_DECORATIONS:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addWinterMenu()
+ self.setWackyWinterDecorationsStart()
+ if hasattr(base.cr.playGame, 'dnaStore') and hasattr(base.cr.playGame, 'hood') and hasattr(base.cr.playGame.hood, 'loader'):
+ if holidayId == ToontownGlobals.HALLOWEEN_COSTUMES or holidayId == ToontownGlobals.SPOOKY_COSTUMES:
+ self.holidayDecorator = HalloweenHolidayDecorator.HalloweenHolidayDecorator()
+ elif holidayId == ToontownGlobals.CRASHED_LEADERBOARD:
+ self.holidayDecorator = CrashedLeaderBoardDecorator.CrashedLeaderBoardDecorator()
+ else:
+ self.holidayDecorator = HolidayDecorator.HolidayDecorator()
+ self.holidayDecorator.decorate()
+ messenger.send('decorator-holiday-%d-starting' % holidayId)
+ elif holidayId in promotionalSpeedChatHolidays:
+ if hasattr(base, 'TTSCPromotionalMenu'):
+ base.TTSCPromotionalMenu.startHoliday(holidayId)
+ elif holidayId == ToontownGlobals.MORE_XP_HOLIDAY:
+ self.setMoreXpHolidayStart()
+ elif holidayId == ToontownGlobals.JELLYBEAN_DAY:
+ pass
+ elif holidayId == ToontownGlobals.CIRCUIT_RACING_EVENT:
+ self.setGrandPrixWeekendStart()
+ elif holidayId == ToontownGlobals.HYDRANT_ZERO_HOLIDAY:
+ self.setHydrantZeroHolidayStart()
+ elif holidayId == ToontownGlobals.APRIL_FOOLS_COSTUMES:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addAprilToonsMenu()
+ elif holidayId == ToontownGlobals.WINTER_CAROLING:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addCarolMenu()
+ self.setWinterCarolingStart()
+ elif holidayId == ToontownGlobals.WACKY_WINTER_CAROLING:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addCarolMenu()
+ elif holidayId == ToontownGlobals.VALENTINES_DAY:
+ messenger.send('ValentinesDayStart')
+ base.localAvatar.setSystemMessage(0, TTLocalizer.ValentinesDayStart)
+ elif holidayId == ToontownGlobals.SILLY_CHATTER_ONE:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addSillyPhaseOneMenu()
+ elif holidayId == ToontownGlobals.SILLY_CHATTER_TWO:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addSillyPhaseTwoMenu()
+ elif holidayId == ToontownGlobals.SILLY_CHATTER_THREE:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addSillyPhaseThreeMenu()
+ elif holidayId == ToontownGlobals.SILLY_CHATTER_FOUR:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addSillyPhaseFourMenu()
+ elif holidayId == ToontownGlobals.SILLY_CHATTER_FIVE:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addSillyPhaseFiveMenu()
+ elif holidayId == ToontownGlobals.VICTORY_PARTY_HOLIDAY:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addVictoryPartiesMenu()
+ elif holidayId == ToontownGlobals.SELLBOT_NERF_HOLIDAY:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setSellbotNerfHolidayStart()
+ base.localAvatar.chatMgr.chatInputSpeedChat.addSellbotNerfMenu()
+ elif holidayId == ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY or holidayId == ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addJellybeanJamMenu(TTSCJellybeanJamMenu.JellybeanJamPhases.TROLLEY)
+ elif holidayId == ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY or holidayId == ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY_MONTH:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addJellybeanJamMenu(TTSCJellybeanJamMenu.JellybeanJamPhases.FISHING)
+ elif holidayId == ToontownGlobals.JELLYBEAN_PARTIES_HOLIDAY:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setJellybeanPartiesHolidayStart()
+ elif holidayId == ToontownGlobals.JELLYBEAN_PARTIES_HOLIDAY_MONTH:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setJellybeanMonthHolidayStart()
+ elif holidayId == ToontownGlobals.BANK_UPGRADE_HOLIDAY:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setBankUpgradeHolidayStart()
+ elif holidayId == ToontownGlobals.BLACK_CAT_DAY:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setBlackCatHolidayStart()
+ elif holidayId == ToontownGlobals.SPOOKY_BLACK_CAT:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setSpookyBlackCatHolidayStart()
+ elif holidayId == ToontownGlobals.TOP_TOONS_MARATHON:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setTopToonsMarathonStart()
+ elif holidayId == ToontownGlobals.SELLBOT_INVASION:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addSellbotInvasionMenu()
+ elif holidayId == ToontownGlobals.SELLBOT_FIELD_OFFICE:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.addSellbotFieldOfficeMenu()
+ elif holidayId == ToontownGlobals.IDES_OF_MARCH:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setIdesOfMarchStart()
+ base.localAvatar.chatMgr.chatInputSpeedChat.addIdesOfMarchMenu()
+ elif holidayId == ToontownGlobals.EXPANDED_CLOSETS:
+ self.setExpandedClosetsStart()
+ elif holidayId == ToontownGlobals.KARTING_TICKETS_HOLIDAY:
+ self.setKartingTicketsHolidayStart()
+
+ def endHoliday(self, holidayId):
+ if holidayId in self.holidayIdList:
+ self.notify.info('setHolidayId: Ending Holiday %s' % holidayId)
+ self.holidayIdList.remove(holidayId)
+ if holidayId in self.decorationHolidayIds:
+ self.decorationHolidayIds.remove(holidayId)
+ if holidayId == ToontownGlobals.HALLOWEEN_PROPS:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeHalloweenMenu()
+ self.setHalloweenPropsHolidayEnd()
+ elif holidayId == ToontownGlobals.SPOOKY_PROPS:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeHalloweenMenu()
+ self.setSpookyPropsHolidayEnd()
+ elif holidayId == ToontownGlobals.WINTER_DECORATIONS:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeWinterMenu()
+ self.setWinterDecorationsEnd()
+ elif holidayId == ToontownGlobals.WACKY_WINTER_DECORATIONS:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeWinterMenu()
+ if hasattr(base.cr.playGame, 'dnaStore') and hasattr(base.cr.playGame, 'hood') and hasattr(base.cr.playGame.hood, 'loader'):
+ if holidayId == ToontownGlobals.HALLOWEEN_COSTUMES or holidayId == ToontownGlobals.SPOOKY_COSTUMES:
+ self.holidayDecorator = HalloweenHolidayDecorator.HalloweenHolidayDecorator()
+ elif holidayId == ToontownGlobals.CRASHED_LEADERBOARD:
+ self.holidayDecorator = CrashedLeaderBoardDecorator.CrashedLeaderBoardDecorator()
+ else:
+ self.holidayDecorator = HolidayDecorator.HolidayDecorator()
+ self.holidayDecorator.undecorate()
+ messenger.send('decorator-holiday-%d-ending' % holidayId)
+ elif holidayId in promotionalSpeedChatHolidays:
+ if hasattr(base, 'TTSCPromotionalMenu'):
+ base.TTSCPromotionalMenu.endHoliday(holidayId)
+ elif holidayId == ToontownGlobals.MORE_XP_HOLIDAY:
+ self.setMoreXpHolidayEnd()
+ elif holidayId == ToontownGlobals.JELLYBEAN_DAY:
+ pass
+ elif holidayId == ToontownGlobals.CIRCUIT_RACING_EVENT:
+ self.setGrandPrixWeekendEnd()
+ elif holidayId == ToontownGlobals.APRIL_FOOLS_COSTUMES:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeAprilToonsMenu()
+ elif holidayId == ToontownGlobals.VALENTINES_DAY:
+ messenger.send('ValentinesDayStop')
+ base.localAvatar.setSystemMessage(0, TTLocalizer.ValentinesDayEnd)
+ elif holidayId == ToontownGlobals.SILLY_CHATTER_ONE:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeSillyPhaseOneMenu()
+ elif holidayId == ToontownGlobals.SILLY_CHATTER_TWO:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeSillyPhaseTwoMenu()
+ elif holidayId == ToontownGlobals.SILLY_CHATTER_THREE:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeSillyPhaseThreeMenu()
+ elif holidayId == ToontownGlobals.SILLY_CHATTER_FOUR:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeSillyPhaseFourMenu()
+ elif holidayId == ToontownGlobals.SILLY_CHATTER_FIVE:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeSillyPhaseFiveMenu()
+ elif holidayId == ToontownGlobals.VICTORY_PARTY_HOLIDAY:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeVictoryPartiesMenu()
+ elif holidayId == ToontownGlobals.WINTER_CAROLING:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeCarolMenu()
+ elif holidayId == ToontownGlobals.WACKY_WINTER_CAROLING:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeCarolMenu()
+ elif holidayId == ToontownGlobals.SELLBOT_NERF_HOLIDAY:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setSellbotNerfHolidayEnd()
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeSellbotNerfMenu()
+ elif holidayId == ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY or holidayId == ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeJellybeanJamMenu()
+ elif holidayId == ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY or holidayId == ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY_MONTH:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeJellybeanJamMenu()
+ elif holidayId == ToontownGlobals.JELLYBEAN_PARTIES_HOLIDAY or holidayId == ToontownGlobals.JELLYBEAN_PARTIES_HOLIDAY_MONTH:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setJellybeanPartiesHolidayEnd()
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeJellybeanJamMenu()
+ elif holidayId == ToontownGlobals.BLACK_CAT_DAY:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setBlackCatHolidayEnd()
+ elif holidayId == ToontownGlobals.SPOOKY_BLACK_CAT:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setSpookyBlackCatHolidayEnd()
+ elif holidayId == ToontownGlobals.TOP_TOONS_MARATHON:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ self.setTopToonsMarathonEnd()
+ elif holidayId == ToontownGlobals.SELLBOT_INVASION:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeSellbotInvasionMenu()
+ elif holidayId == ToontownGlobals.SELLBOT_FIELD_OFFICE:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeSellbotFieldOfficeMenu()
+ elif holidayId == ToontownGlobals.IDES_OF_MARCH:
+ if hasattr(base, 'localAvatar') and base.localAvatar and hasattr(base.localAvatar, 'chatMgr') and base.localAvatar.chatMgr:
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeIdesOfMarchMenu()
+
+ def setHolidayIdList(self, holidayIdList):
+
+ def isEnding(id):
+ return id not in holidayIdList
+
+ def isStarting(id):
+ return id not in self.holidayIdList
+
+ toEnd = filter(isEnding, self.holidayIdList)
+ for endingHolidayId in toEnd:
+ self.endHoliday(endingHolidayId)
+
+ toStart = filter(isStarting, holidayIdList)
+ for startingHolidayId in toStart:
+ self.startHoliday(startingHolidayId)
+
+ messenger.send('setHolidayIdList', [holidayIdList])
+
+ def getDecorationHolidayId(self):
+ return self.decorationHolidayIds
+
+ def getHolidayIdList(self):
+ return self.holidayIdList
+
+ def setBingoWin(self, zoneId):
+ base.localAvatar.setSystemMessage(0, 'Bingo congrats!')
+
+ def setBingoStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.FishBingoStart)
+
+ def setBingoOngoing(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.FishBingoOngoing)
+
+ def setBingoEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.FishBingoEnd)
+
+ def setCircuitRaceStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.CircuitRaceStart)
+
+ def setCircuitRaceOngoing(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.CircuitRaceOngoing)
+
+ def setCircuitRaceEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.CircuitRaceEnd)
+
+ def setTrolleyHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.TrolleyHolidayStart)
+
+ def setTrolleyHolidayOngoing(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.TrolleyHolidayOngoing)
+
+ def setTrolleyHolidayEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.TrolleyHolidayEnd)
+
+ def setTrolleyWeekendStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.TrolleyWeekendStart)
+
+ def setTrolleyWeekendEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.TrolleyWeekendEnd)
+
+ def setRoamingTrialerWeekendStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.RoamingTrialerWeekendStart)
+ base.roamingTrialers = True
+
+ def setRoamingTrialerWeekendOngoing(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.RoamingTrialerWeekendOngoing)
+ base.roamingTrialers = True
+
+ def setRoamingTrialerWeekendEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.RoamingTrialerWeekendEnd)
+ base.roamingTrialers = False
+
+ def setMoreXpHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.MoreXpHolidayStart)
+
+ def setMoreXpHolidayOngoing(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.MoreXpHolidayOngoing)
+
+ def setMoreXpHolidayEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.MoreXpHolidayEnd)
+
+ def setJellybeanDayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.JellybeanDayHolidayStart)
+
+ def setJellybeanDayEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.JellybeanDayHolidayEnd)
+
+ def setGrandPrixWeekendStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.GrandPrixWeekendHolidayStart)
+
+ def setGrandPrixWeekendEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.GrandPrixWeekendHolidayEnd)
+
+ def setHydrantZeroHolidayStart(self):
+ messenger.send('HydrantZeroIsRunning', [True])
+
+ def setSellbotNerfHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.SellbotNerfHolidayStart)
+
+ def setSellbotNerfHolidayEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.SellbotNerfHolidayEnd)
+
+ def setJellybeanTrolleyHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.JellybeanTrolleyHolidayStart)
+
+ def setJellybeanTrolleyHolidayEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.JellybeanTrolleyHolidayEnd)
+
+ def setJellybeanFishingHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.JellybeanFishingHolidayStart)
+
+ def setJellybeanFishingHolidayEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.JellybeanFishingHolidayEnd)
+
+ def setJellybeanPartiesHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.JellybeanPartiesHolidayStart)
+
+ def setJellybeanMonthHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.JellybeanMonthHolidayStart)
+
+ def setJellybeanPartiesHolidayEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.JellybeanPartiesHolidayEnd)
+
+ def setBankUpgradeHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.BankUpgradeHolidayStart)
+
+ def setHalloweenPropsHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.HalloweenPropsHolidayStart)
+
+ def setHalloweenPropsHolidayEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.HalloweenPropsHolidayEnd)
+
+ def setSpookyPropsHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.SpookyPropsHolidayStart)
+
+ def setSpookyPropsHolidayEnd(self):
+ pass
+
+ def setBlackCatHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.BlackCatHolidayStart)
+
+ def setBlackCatHolidayEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.BlackCatHolidayEnd)
+
+ def setSpookyBlackCatHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.SpookyBlackCatHolidayStart)
+ for currToon in base.cr.toons.values():
+ currToon.setDNA(currToon.style.clone())
+
+ def setSpookyBlackCatHolidayEnd(self):
+ for currToon in base.cr.toons.values():
+ currToon.setDNA(currToon.style.clone())
+
+ def setTopToonsMarathonStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.TopToonsMarathonStart)
+
+ def setTopToonsMarathonEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.TopToonsMarathonEnd)
+
+ def setWinterDecorationsStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.WinterDecorationsStart)
+
+ def setWinterDecorationsEnd(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.WinterDecorationsEnd)
+
+ def setWackyWinterDecorationsStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.WackyWinterDecorationsStart)
+
+ def setWinterCarolingStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.WinterCarolingStart)
+
+ def setExpandedClosetsStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.ExpandedClosetsStart)
+
+ def setKartingTicketsHolidayStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.KartingTicketsHolidayStart)
+
+ def setIdesOfMarchStart(self):
+ base.localAvatar.setSystemMessage(0, TTLocalizer.IdesOfMarchStart)
+
+ def holidayNotify(self):
+ for id in self.holidayIdList:
+ if id == 19:
+ self.setBingoOngoing()
+ elif id == 20:
+ self.setCircuitRaceOngoing()
+ elif id == 21:
+ self.setTrolleyHolidayOngoing()
+ elif id == 22:
+ self.setRoamingTrialerWeekendOngoing()
+
+ def setWeeklyCalendarHolidays(self, weeklyCalendarHolidays):
+ self.weeklyCalendarHolidays = weeklyCalendarHolidays
+
+ def getHolidaysForWeekday(self, day):
+ result = []
+ for item in self.weeklyCalendarHolidays:
+ if item[1] == day:
+ result.append(item[0])
+
+ return result
+
+ def setYearlyCalendarHolidays(self, yearlyCalendarHolidays):
+ self.yearlyCalendarHolidays = yearlyCalendarHolidays
+
+ def getYearlyHolidaysForDate(self, theDate):
+ result = []
+ for item in self.yearlyCalendarHolidays:
+ if item[1][0] == theDate.month and item[1][1] == theDate.day:
+ newItem = [self.YearlyHolidayType] + list(item)
+ result.append(tuple(newItem))
+ continue
+ if item[2][0] == theDate.month and item[2][1] == theDate.day:
+ newItem = [self.YearlyHolidayType] + list(item)
+ result.append(tuple(newItem))
+
+ return result
+
+ def setMultipleStartHolidays(self, multipleStartHolidays):
+ self.multipleStartHolidays = multipleStartHolidays
+
+ def getMultipleStartHolidaysForDate(self, theDate):
+ result = []
+ for theHoliday in self.multipleStartHolidays:
+ times = theHoliday[1:]
+ tempTimes = times[0]
+ for startAndStopTimes in tempTimes:
+ startTime = startAndStopTimes[0]
+ endTime = startAndStopTimes[1]
+ if startTime[0] == theDate.year and startTime[1] == theDate.month and startTime[2] == theDate.day:
+ fakeOncelyHoliday = [theHoliday[0], startTime, endTime]
+ newItem = [self.OncelyMultipleStartHolidayType] + fakeOncelyHoliday
+ result.append(tuple(newItem))
+ continue
+ if endTime[0] == theDate.year and endTime[1] == theDate.month and endTime[2] == theDate.day:
+ fakeOncelyHoliday = [theHoliday[0], startTime, endTime]
+ newItem = [self.OncelyMultipleStartHolidayType] + fakeOncelyHoliday
+ result.append(tuple(newItem))
+
+ return result
+
+ def setOncelyCalendarHolidays(self, oncelyCalendarHolidays):
+ self.oncelyCalendarHolidays = oncelyCalendarHolidays
+
+ def getOncelyHolidaysForDate(self, theDate):
+ result = []
+ for item in self.oncelyCalendarHolidays:
+ if item[1][0] == theDate.year and item[1][1] == theDate.month and item[1][2] == theDate.day:
+ newItem = [self.OncelyHolidayType] + list(item)
+ result.append(tuple(newItem))
+ continue
+ if item[2][0] == theDate.year and item[2][1] == theDate.month and item[2][2] == theDate.day:
+ newItem = [self.OncelyHolidayType] + list(item)
+ result.append(tuple(newItem))
+
+ return result
+
+ def setRelativelyCalendarHolidays(self, relativelyCalendarHolidays):
+ self.relativelyCalendarHolidays = relativelyCalendarHolidays
+
+ def getRelativelyHolidaysForDate(self, theDate):
+ result = []
+ self.weekDaysInMonth = []
+ self.numDaysCorMatrix = [(28, 0), (29, 1), (30, 2), (31, 3)]
+
+ for i in range(7):
+ self.weekDaysInMonth.append((i, 4))
+
+ for holidayItem in self.relativelyCalendarHolidays:
+ item = deepcopy(holidayItem)
+
+ newItem = []
+ newItem.append(item[0])
+
+ i = 1
+ while i < len(item):
+ sRepNum = item[i][1]
+ sWeekday = item[i][2]
+ eWeekday = item[i+1][2]
+
+ while 1:
+ eRepNum = item[i+1][1]
+
+ self.initRepMatrix(theDate.year, item[i][0])
+ while self.weekDaysInMonth[sWeekday][1] < sRepNum:
+ sRepNum -= 1
+
+ sDay = self.dayForWeekday(theDate.year, item[i][0], sWeekday, sRepNum)
+
+ self.initRepMatrix(theDate.year, item[i+1][0])
+ while self.weekDaysInMonth[eWeekday][1] < eRepNum:
+ eRepNum -= 1
+
+ nDay = self.dayForWeekday(theDate.year, item[i+1][0], eWeekday, eRepNum)
+
+ if ((nDay > sDay and
+ item[i+1][0] == item[i][0] and
+ (item[i+1][1] - item[i][1]) <= (nDay - sDay + abs(eWeekday - sWeekday))/7) or
+ item[i+1][0] != item[i][0]):
+ break
+
+ if self.weekDaysInMonth[eWeekday][1] > eRepNum:
+ eRepNum += 1
+ else:
+ item[i+1][0] += 1
+ item[i+1][1] = 1
+
+ newItem.append([item[i][0], sDay, item[i][3], item[i][4], item[i][5]])
+
+ newItem.append([item[i+1][0], nDay, item[i+1][3], item[i+1][4], item[i+1][5]])
+
+ i += 2
+
+ if item[1][0] == theDate.month and newItem[1][1] == theDate.day:
+ nItem = [self.RelativelyHolidayType] + list(newItem)
+ result.append(tuple(nItem))
+ continue
+
+ if item[2][0] == theDate.month and newItem[2][1] == theDate.day:
+ nItem = [self.RelativelyHolidayType] + list(newItem)
+ result.append(tuple(nItem))
+
+ return result
+
+ def dayForWeekday(self, year, month, weekday, repNum):
+ monthDays = calendar.monthcalendar(year, month)
+ if monthDays[0][weekday] == 0:
+ repNum += 1
+ return monthDays[repNum - 1][weekday]
+
+ def initRepMatrix(self, year, month):
+ for i in range(7):
+ self.weekDaysInMonth[i] = (i, 4)
+
+ startingWeekDay, numDays = calendar.monthrange(year, month)
+ if startingWeekDay > 6:
+ import pdb
+ pdb.set_trace()
+ for i in range(4):
+ if numDays == self.numDaysCorMatrix[i][0]:
+ break
+
+ for j in range(self.numDaysCorMatrix[i][1]):
+ self.weekDaysInMonth[startingWeekDay] = (self.weekDaysInMonth[startingWeekDay][0], self.weekDaysInMonth[startingWeekDay][1] + 1)
+ startingWeekDay = (startingWeekDay + 1) % 7
+
+ def isHolidayRunning(self, holidayId):
+ result = holidayId in self.holidayIdList
+ return result
diff --git a/toontown/ai/ServerEventBuffer.py b/toontown/ai/ServerEventBuffer.py
new file mode 100644
index 0000000..047c209
--- /dev/null
+++ b/toontown/ai/ServerEventBuffer.py
@@ -0,0 +1,75 @@
+
+
+class ServerEventBuffer:
+
+ def __init__(self, air, name, avId, period = None):
+ self.air = air
+ self.name = name
+ self.avId = avId
+ if period is None:
+ period = 6 * 60.0
+ self.period = period
+ self.lastFlushTime = None
+ return
+
+ def destroy(self):
+ self.flush()
+
+ def flush(self):
+ self.lastFlushTime = None
+ return
+
+ def writeEvent(self, msg):
+ self.air.writeServerEvent(self.name, self.avId, msg)
+
+ def considerFlush(self):
+ if self.lastFlushTime is None:
+ self.lastFlushTime = globalClock.getFrameTime()
+ elif globalClock.getFrameTime() - self.lastFlushTime > self.period * 60.0:
+ self.flush()
+ return
+
+
+class ServerEventAccumulator(ServerEventBuffer):
+
+ def __init__(self, air, name, avId, period = None):
+ ServerEventBuffer.__init__(self, air, name, avId, period)
+ self.count = 0
+
+ def flush(self):
+ ServerEventBuffer.flush(self)
+ if not self.count:
+ return
+ self.writeEvent('%s' % self.count)
+ self.count = 0
+
+ def addEvent(self):
+ self.count += 1
+ self.considerFlush()
+
+
+class ServerEventMultiAccumulator(ServerEventBuffer):
+
+ def __init__(self, air, name, avId, period = None):
+ ServerEventBuffer.__init__(self, air, name, avId, period)
+ self.events = {}
+
+ def flush(self):
+ ServerEventBuffer.flush(self)
+ if not len(self.events):
+ return
+ msg = ''
+ eventNames = self.events.keys()
+ eventNames.sort()
+ for eventName in eventNames:
+ msg += '%s:%s' % (eventName, self.events[eventName])
+ if eventName != eventNames[-1]:
+ msg += ','
+
+ self.writeEvent(msg)
+ self.events = {}
+
+ def addEvent(self, eventName):
+ self.events.setdefault(eventName, 0)
+ self.events[eventName] += 1
+ self.considerFlush()
diff --git a/toontown/ai/ToonBarrier.py b/toontown/ai/ToonBarrier.py
new file mode 100644
index 0000000..14e18c6
--- /dev/null
+++ b/toontown/ai/ToonBarrier.py
@@ -0,0 +1,83 @@
+from otp.ai.AIBase import *
+from direct.task import Task
+from direct.showbase import DirectObject
+import random
+
+class ToonBarrier(DirectObject.DirectObject):
+ notify = directNotify.newCategory('ToonBarrier')
+
+ def __init__(self, name, uniqueName, avIdList, timeout, clearedFunc = None, timeoutFunc = None, doneFunc = None):
+ self.name = name
+ self.uniqueName = uniqueName + '-Barrier'
+ self.avIdList = avIdList[:]
+ self.pendingToons = self.avIdList[:]
+ self.timeout = timeout
+ self.clearedFunc = clearedFunc
+ self.timeoutFunc = timeoutFunc
+ self.doneFunc = doneFunc
+ if len(self.pendingToons) == 0:
+ self.notify.debug('%s: barrier with empty list' % self.uniqueName)
+ self.active = 0
+ if self.clearedFunc:
+ self.clearedFunc()
+ if self.doneFunc:
+ self.doneFunc(self.avIdList)
+ return
+ self.taskName = self.uniqueName + '-Timeout'
+ origTaskName = self.taskName
+ while taskMgr.hasTaskNamed(self.taskName):
+ self.taskName = origTaskName + '-' + str(random.randint(0, 10000))
+
+ taskMgr.doMethodLater(self.timeout, self.__timerExpired, self.taskName)
+ for avId in self.avIdList:
+ event = simbase.air.getAvatarExitEvent(avId)
+ self.acceptOnce(event, self.__handleUnexpectedExit, extraArgs=[avId])
+
+ self.notify.debug('%s: expecting responses from %s within %s seconds' % (self.uniqueName, self.avIdList, self.timeout))
+ self.active = 1
+
+ def cleanup(self):
+ if self.active:
+ taskMgr.remove(self.taskName)
+ self.active = 0
+ self.ignoreAll()
+
+ def clear(self, avId):
+ if avId not in self.pendingToons:
+ self.notify.warning('%s: tried to clear %s, who was not listed.' % (self.uniqueName, avId))
+ return
+ self.notify.debug('%s: clearing avatar %s' % (self.uniqueName, avId))
+ self.pendingToons.remove(avId)
+ if len(self.pendingToons) == 0:
+ self.notify.debug('%s: barrier cleared by %s' % (self.uniqueName, self.avIdList))
+ self.cleanup()
+ if self.clearedFunc:
+ self.clearedFunc()
+ if self.doneFunc:
+ self.doneFunc(self.avIdList)
+
+ def isActive(self):
+ return self.active
+
+ def getPendingToons(self):
+ return self.pendingToons[:]
+
+ def __timerExpired(self, task):
+ self.notify.warning('%s: timeout expired; responses not received from %s' % (self.uniqueName, self.pendingToons))
+ self.cleanup()
+ if self.timeoutFunc:
+ self.timeoutFunc(self.pendingToons[:])
+ if self.doneFunc:
+ clearedAvIds = self.avIdList[:]
+ for avId in self.pendingToons:
+ clearedAvIds.remove(avId)
+
+ self.doneFunc(clearedAvIds)
+ return Task.done
+
+ def __handleUnexpectedExit(self, avId):
+ if avId not in self.avIdList:
+ return
+ self.avIdList.remove(avId)
+ if avId in self.pendingToons:
+ self.clear(avId)
diff --git a/toontown/ai/ToontownAIMsgTypes.py b/toontown/ai/ToontownAIMsgTypes.py
new file mode 100644
index 0000000..25529fb
--- /dev/null
+++ b/toontown/ai/ToontownAIMsgTypes.py
@@ -0,0 +1,16 @@
+from otp.ai.AIMsgTypes import *
+TTAIMsgName2Id = {'DBSERVER_GET_ESTATE': 1040,
+ 'DBSERVER_GET_ESTATE_RESP': 1041,
+ 'PARTY_MANAGER_UD_TO_ALL_AI': 1042,
+ 'IN_GAME_NEWS_MANAGER_UD_TO_ALL_AI': 1043,
+ 'WHITELIST_MANAGER_UD_TO_ALL_AI': 1044}
+TTAIMsgId2Names = invertDictLossless(TTAIMsgName2Id)
+if not isClient():
+ print 'EXECWARNING ToontownAIMsgTypes: %s' % TTAIMsgName2Id
+ printStack()
+for name, value in TTAIMsgName2Id.items():
+ exec '%s = %s' % (name, value)
+
+del name
+del value
+DBSERVER_PET_OBJECT_TYPE = 5
diff --git a/toontown/ai/ToontownMagicWordManager.py b/toontown/ai/ToontownMagicWordManager.py
new file mode 100644
index 0000000..4654093
--- /dev/null
+++ b/toontown/ai/ToontownMagicWordManager.py
@@ -0,0 +1,840 @@
+from direct.interval.IntervalGlobal import *
+from direct.distributed import PyDatagram
+from direct.distributed.MsgTypes import MsgName2Id
+from pandac.PandaModules import *
+from direct.distributed import DistributedObject
+from toontown.toon import DistributedToon
+from direct.directnotify import DirectNotifyGlobal
+from toontown.town import TownBattleAttackPanel
+from toontown.suit import RoguesGallery
+from otp.avatar import Avatar
+from otp.chat import ChatManager
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import ToontownBattleGlobals
+import string
+from toontown.toon import Toon
+from direct.showbase import PythonUtil
+from toontown.suit import DistributedSuitPlanner
+from toontown.suit import DistributedBossCog
+from otp.otpbase import OTPGlobals
+from direct.distributed.ClockDelta import *
+from otp.ai import MagicWordManager
+from toontown.hood import ZoneUtil
+from toontown.battle import Fanfare
+from toontown.golf import GolfGlobals
+from toontown.distributed import ToontownDistrictStats
+from toontown.coderedemption import TTCodeRedemptionConsts
+from toontown.rpc import AwardManagerConsts
+if base.wantKarts:
+ from toontown.racing.KartDNA import *
+ from toontown.racing.KartShopGui import *
+if (__debug__):
+ import pdb
+
+class ToontownMagicWordManager(MagicWordManager.MagicWordManager):
+ notify = DirectNotifyGlobal.directNotify.newCategory('ToontownMagicWordManager')
+ neverDisable = 1
+ GameAvatarClass = DistributedToon.DistributedToon
+
+ def __init__(self, cr):
+ MagicWordManager.MagicWordManager.__init__(self, cr)
+ self.rogues = None
+ self.ruler = None
+ self.dbg_running_fast = 0
+ return
+
+ def generate(self):
+ MagicWordManager.MagicWordManager.generate(self)
+ self.accept('magicWord', self.b_setMagicWord)
+
+ def doLoginMagicWords(self):
+ MagicWordManager.MagicWordManager.doLoginMagicWords(self)
+ if base.config.GetBool('want-chat', 0):
+ self.d_setMagicWord('~chat', base.localAvatar.doId, 0)
+ if base.config.GetBool('want-run', 0) or base.config.GetBool('want-toontown-run', 0):
+ self.toggleRun()
+ if base.config.GetBool('immortal-mode', 0):
+ self.d_setMagicWord('~immortal', base.localAvatar.doId, 0)
+ mintFloor = base.config.GetInt('mint-floor', -1)
+ if mintFloor != -1:
+ self.d_setMagicWord('~mintFloor %s' % mintFloor, base.localAvatar.doId, 0)
+ mintId = base.config.GetInt('mint-id', -1)
+ if mintId != -1:
+ self.d_setMagicWord('~mint %s' % mintId, base.localAvatar.doId, 0)
+ autoRestock = base.config.GetInt('auto-restock', -1)
+ if autoRestock != -1:
+ self.d_setMagicWord('~autoRestock %s' % autoRestock, base.localAvatar.doId, 0)
+ autoRich = base.config.GetInt('auto-rich', -1)
+ if autoRich != -1:
+ self.d_setMagicWord('~autoRich %s' % autoRich, base.localAvatar.doId, 0)
+ autoResistanceRestock = base.config.GetInt('auto-resistance-restock', -1)
+ if autoResistanceRestock != -1:
+ self.d_setMagicWord('~autoResistanceRestock %s' % autoResistanceRestock, base.localAvatar.doId, 0)
+ autoRestockSOS = base.config.GetInt('auto-restock-sos', -1)
+ if autoRestockSOS != -1:
+ self.d_setMagicWord('~autoRestockSOS %s' % autoRestockSOS, base.localAvatar.doId, 0)
+ autoRestockPinkSlips = base.config.GetInt('auto-restock-pink-slips', -1)
+ if autoRestockPinkSlips != -1:
+ self.d_setMagicWord('~autoRestockPinkSlips %s' % autoRestockPinkSlips, base.localAvatar.doId, 0)
+ autoRestockSummons = base.config.GetInt('auto-restock-summons', -1)
+ if autoRestockSummons != -1:
+ self.d_setMagicWord('~autoRestockSummons %s' % autoRestockSummons, base.localAvatar.doId, 0)
+ paidStatus = base.config.GetString('force-paid-status', 'none')
+ if paidStatus != 'none':
+ self.d_setMagicWord('~setPaid %s' % choice(paidStatus == 'paid', 1, 0), localAvatar.doId, 0)
+ self.doConfigMagicWords()
+
+ def doConfigMagicWords(self):
+ autoMagicWords = base.config.GetString('auto-magic-words', '').split('|')
+ for command in autoMagicWords:
+ if command:
+ self.d_setMagicWord(command.strip(), base.localAvatar.doId, 0)
+
+ def disable(self):
+ self.ignore('magicWord')
+ if self.dbg_running_fast:
+ self.toggleRun()
+ MagicWordManager.MagicWordManager.disable(self)
+
+ def doMagicWord(self, word, avId, zoneId):
+ wordIs = Functor(self.wordIs, word)
+ if MagicWordManager.MagicWordManager.doMagicWord(self, word, avId, zoneId) == 1:
+ pass
+ elif wordIs('~fanfare'):
+ go = Fanfare.makeFanfareWithMessageImage(0, base.localAvatar, 1, "You just did a ~fanfare. Here's a rake.", Vec2(0, 0.2), 0.08, base.localAvatar.inventory.buttonLookup(1, 1), Vec3(0, 0, 0), 4)
+ Sequence(go[0], Func(go[1].show), LerpColorScaleInterval(go[1], duration=0.5, startColorScale=Vec4(1, 1, 1, 0), colorScale=Vec4(1, 1, 1, 1)), Wait(2), LerpColorScaleInterval(go[1], duration=0.5, startColorScale=Vec4(1, 1, 1, 1), colorScale=Vec4(1, 1, 1, 0)), Func(go[1].remove)).start()
+ elif wordIs('~endgame'):
+ print 'Requesting minigame abort...'
+ messenger.send('minigameAbort')
+ elif wordIs('~wingame'):
+ print 'Requesting minigame victory...'
+ messenger.send('minigameVictory')
+ elif wordIs('~walk'):
+ try:
+ fsm = base.cr.playGame.getPlace().fsm
+ fsm.forceTransition('walk')
+ except:
+ pass
+
+ elif wordIs('~movie'):
+ try:
+ fsm = base.cr.playGame.getPlace().fsm
+ fsm.forceTransition('movie')
+ except:
+ pass
+
+ elif wordIs('~sit'):
+ try:
+ base.cr.playGame.getPlace().fsm.request('sit')
+ except:
+ pass
+
+ elif wordIs('~rogues'):
+ suitname = None
+ if len(word) > 7:
+ suitname = word[7:].split(' ')[1]
+ self.rogues = RoguesGallery.RoguesGallery(suitname)
+ self.rogues.enter()
+ if suitname != None:
+ self.rogues.animate()
+ self.acceptOnce('mouse1', self.exit_rogues)
+ elif wordIs('~showPaths'):
+ for obj in self.cr.doId2do.values():
+ if isinstance(obj, DistributedSuitPlanner.DistributedSuitPlanner):
+ obj.showPaths()
+
+ place = base.cr.playGame.getPlace()
+ if hasattr(place, 'showPaths'):
+ place.showPaths()
+ elif wordIs('~hidePaths'):
+ for obj in self.cr.doId2do.values():
+ if isinstance(obj, DistributedSuitPlanner.DistributedSuitPlanner):
+ obj.hidePaths()
+
+ place = base.cr.playGame.getPlace()
+ if hasattr(place, 'hidePaths'):
+ place.hidePaths()
+ elif wordIs('~raceForever'):
+ base.raceForever = True
+ elif wordIs('~listen'):
+ base.localAvatar.garbleChat = 0
+ elif wordIs('~nochat') or wordIs('~chat') or wordIs('~superchat'):
+ base.localAvatar.garbleChat = 1
+ elif wordIs('~exec'):
+ ChatManager.ChatManager.execChat = 1
+ elif wordIs('~photoshoot'):
+ base.cr.playGame.hood.sky.hide()
+ base.cr.playGame.getPlace().loader.geom.hide()
+ base.win.setClearColor(VBase4(1, 1, 1, 1))
+ base.localAvatar.stopLookAroundNow()
+ base.localAvatar.stopBlink()
+ base.localAvatar.setNameVisible(0)
+ elif wordIs('~hideAttack'):
+ TownBattleAttackPanel.hideAttackPanel(1)
+ elif wordIs('~showAttack'):
+ TownBattleAttackPanel.hideAttackPanel(0)
+ elif wordIs('~collisions_on'):
+ base.localAvatar.collisionsOn()
+ elif wordIs('~collisions_off'):
+ base.localAvatar.collisionsOff()
+ elif wordIs('~battle_detect_off'):
+ from toontown.suit import DistributedSuit
+ DistributedSuit.ALLOW_BATTLE_DETECT = 0
+ elif wordIs('~battle_detect_on'):
+ from toontown.suit import DistributedSuit
+ DistributedSuit.ALLOW_BATTLE_DETECT = 1
+ elif wordIs('~battles'):
+ base.localAvatar.setWantBattles(not base.localAvatar.wantBattles)
+ if base.localAvatar.wantBattles:
+ response = 'battles ON'
+ else:
+ response = 'battles OFF'
+ self.setMagicWordResponse(response)
+ elif wordIs('~skipBattleMovie') or wordIs('~sbm'):
+ ToontownBattleGlobals.SkipMovie = not ToontownBattleGlobals.SkipMovie
+ if ToontownBattleGlobals.SkipMovie:
+ response = 'battle movies will be skipped'
+ else:
+ response = 'battle movies will be played'
+ self.setMagicWordResponse(response)
+ elif wordIs('~addCameraPosition'):
+ base.localAvatar.addCameraPosition()
+ elif wordIs('~removeCameraPosition'):
+ base.localAvatar.removeCameraPosition()
+ elif wordIs('~printCameraPosition'):
+ base.localAvatar.printCameraPosition(base.localAvatar.cameraIndex)
+ elif wordIs('~printCameraPositions'):
+ base.localAvatar.printCameraPositions()
+ elif wordIs('~worldCam') or wordIs('~wc'):
+ myCam = render.find('**/camera')
+ if not myCam.isEmpty():
+ camParent = myCam.getParent()
+ myCam.wrtReparentTo(render)
+ pos = myCam.getPos()
+ hpr = myCam.getHpr()
+ response = '(%.2f, %.2f, %.2f,) (%.2f, %.2f, %.2f)' % (pos[0],
+ pos[1],
+ pos[2],
+ hpr[0],
+ hpr[1],
+ hpr[2])
+ if not camParent.isEmpty():
+ myCam.wrtReparentTo(camParent)
+ self.setMagicWordResponse(response)
+ print response
+ elif wordIs('~sync'):
+ tm = base.cr.timeManager
+ if tm == None:
+ response = 'No TimeManager.'
+ self.setMagicWordResponse(response)
+ else:
+ tm.extraSkew = 0.0
+ skew = string.strip(word[5:])
+ if skew != '':
+ tm.extraSkew = float(skew)
+ globalClockDelta.clear()
+ tm.handleHotkey()
+ elif wordIs('~period'):
+ timeout = string.strip(word[7:])
+ if timeout != '':
+ seconds = int(timeout)
+ base.cr.stopPeriodTimer()
+ base.cr.resetPeriodTimer(seconds)
+ base.cr.startPeriodTimer()
+ if base.cr.periodTimerExpired:
+ response = 'Period timer has expired.'
+ elif base.cr.periodTimerStarted:
+ elapsed = globalClock.getFrameTime() - base.cr.periodTimerStarted
+ secondsRemaining = base.cr.periodTimerSecondsRemaining - elapsed
+ response = 'Period timer expires in %s seconds.' % int(secondsRemaining)
+ else:
+ response = 'Period timer not set.'
+ self.setMagicWordResponse(response)
+ elif wordIs('~net'):
+ if base.cr.networkPlugPulled():
+ base.cr.restoreNetworkPlug()
+ base.cr.startHeartbeat()
+ response = 'Network restored.'
+ else:
+ base.cr.pullNetworkPlug()
+ base.cr.stopHeartbeat()
+ response = 'Network disconnected.'
+ self.setMagicWordResponse(response)
+ elif wordIs('~lag'):
+ if not hasattr(base.cr, 'magicLag'):
+ base.cr.startDelay(0.1, 0.35)
+ base.cr.magicLag = None
+ response = 'Simulated Lag On'
+ else:
+ base.cr.stopDelay()
+ del base.cr.magicLag
+ response = 'Simulated Lag Off'
+ self.setMagicWordResponse(response)
+ elif wordIs('~endlessquietzone'):
+ base.endlessQuietZone = not base.endlessQuietZone
+ response = 'endless quiet zone %s' % choice(base.endlessQuietZone, 'ON', 'OFF')
+ self.setMagicWordResponse(response)
+ elif wordIs('~cogPageFull'):
+ base.localAvatar.suitPage.updateAllCogs(3)
+ elif wordIs('~mintId'):
+ args = word.split()
+ postName = 'mintIdOverride'
+ if len(args) < 2:
+ if bboard.has(postName):
+ bboard.remove(postName)
+ else:
+ try:
+ id = int(args[1])
+ foo = ToontownGlobals.MintNumRooms[id]
+ except:
+ pass
+ else:
+ bboard.post(postName, id)
+
+ elif wordIs('~mintRoom'):
+ args = word.split()
+ postName = 'mintRoom'
+ if len(args) < 2:
+ if bboard.has(postName):
+ bboard.remove(postName)
+ else:
+ try:
+ id = int(args[1])
+ except:
+ pass
+ else:
+ bboard.post(postName, id)
+
+ elif wordIs('~mintWarp'):
+ args = word.split()
+ if len(args) < 2:
+ self.setMagicWordResponse('Usage: ~mintWarp roomId')
+ return
+ try:
+ roomNum = int(args[1])
+ except:
+ self.setMagicWordResponse('roomId not found: %s' % args[1])
+ return
+
+ if not bboard.has('mint'):
+ self.setMagicWordResponse('not in a mint')
+ return
+ mint = bboard.get('mint')
+ if not mint.warpToRoom(roomNum):
+ self.setMagicWordResponse('invalid roomId or roomId not in this mint: %s' % args[1])
+ return
+ elif wordIs('~mintLayouts'):
+ from toontown.coghq import MintLayout
+ MintLayout.printAllCashbotInfo()
+ self.setMagicWordResponse('logged mint layouts')
+ elif wordIs('~edit'):
+ if not __dev__:
+ self.setMagicWordResponse('client not running in dev mode')
+ return
+ from otp.level import EditorGlobals
+ level = bboard.get(EditorGlobals.EditTargetPostName)
+ if level == None:
+ self.setMagicWordResponse('no level available for editing')
+ return
+ from toontown.coghq import DistributedInGameEditor
+ EditorGlobals.assertReadyToEdit()
+ editUsername = EditorGlobals.getEditUsername()
+ editors = base.cr.doFindAll('DistributedInGameEditor')
+ for e in editors:
+ if isinstance(e, DistributedInGameEditor.DistributedInGameEditor):
+ if e.getLevelDoId() == level.doId:
+ if e.editorIsLocalToon() or e.getEditUsername() == editUsername:
+ self.setMagicWordResponse("you ('%s') are already editing this level" % editUsername)
+ return
+
+ cmd = '~inGameEdit %s %s' % (level.doId, editUsername)
+ self.b_setMagicWord(cmd)
+ elif wordIs('~fshow'):
+ from toontown.coghq import DistributedFactory
+ factories = base.cr.doFindAll('DistributedFactory')
+ factory = None
+ for f in factories:
+ if isinstance(f, DistributedFactory.DistributedFactory):
+ factory = f
+ break
+
+ if factory is None:
+ self.setMagicWordResponse('factory not found')
+ return
+ factory.setColorZones(not factory.fColorZones)
+ elif wordIs('~fzone'):
+ args = word.split()
+ if len(args) < 2:
+ self.setMagicWordResponse('Usage: ~fzone ')
+ return
+ zoneId = int(args[1])
+ from toontown.coghq import DistributedFactory
+ factories = base.cr.doFindAll('DistributedFactory')
+ factory = None
+ for f in factories:
+ if isinstance(f, DistributedFactory.DistributedFactory):
+ factory = f
+ break
+
+ if factory is None:
+ self.setMagicWordResponse('factory not found')
+ return
+ factory.warpToZone(zoneId)
+ elif wordIs('~undead'):
+ try:
+ goons = base.cr.doFindAll('Goon')
+ for goon in goons:
+ goon.undead()
+
+ except:
+ self.notify.warning('Error in undead')
+
+ elif wordIs('~resyncGoons'):
+ try:
+ goons = base.cr.doFindAll('Goon')
+ for goon in goons:
+ goon.resync()
+
+ except:
+ self.notify.warning('Error in resyncing')
+
+ elif wordIs('~catalog'):
+ self.doCatalog(word)
+ elif wordIs('~petCam') and base.wantPets:
+ if hasattr(base, 'petCamPrevParent'):
+ base.cam.reparentTo(base.petCamPrevParent)
+ del base.petCamPrevParent
+ else:
+ petId = base.localAvatar.getPetId()
+ pet = self.cr.doId2do.get(petId)
+ if pet:
+ if not hasattr(pet, 'camNode'):
+ pet.camNode = pet.attachNewNode('camNode')
+ pet.camNode.setPos(0, 0, 2.5)
+ base.petCamPrevParent = base.cam.getParent()
+ base.cam.reparentTo(pet.camNode)
+ elif wordIs('~lockPet') and base.wantPets:
+ petId = base.localAvatar.getPetId()
+ pet = self.cr.doId2do.get(petId)
+ if pet:
+ if not pet.isLockedDown():
+ pet.lockPet()
+ elif wordIs('~unlockPet') and base.wantPets:
+ petId = base.localAvatar.getPetId()
+ pet = self.cr.doId2do.get(petId)
+ if pet:
+ if pet.isLockedDown():
+ pet.unlockPet()
+ elif wordIs('~resetPetTutorial') and base.wantPets:
+ base.localAvatar.setPetTutorialDone(False)
+ response = 'Pet Tutorial flag reset'
+ self.setMagicWordResponse(response)
+ elif wordIs('~bossBattle'):
+ self.doBossBattle(word)
+ elif wordIs('~RaceChat'):
+ base.localAvatar.chatMgr.chatInputSpeedChat.addKartRacingMenu()
+ elif wordIs('~BuyKart'):
+ if base.wantKarts:
+
+ def doShtikerLater(task):
+ base.localAvatar.addKartPage()
+ return 0
+
+ if base.localAvatar.hasKart():
+ response = 'Returning Kart %s' % base.localAvatar.getKartBodyType()
+ base.localAvatar.requestKartDNAFieldUpdate(KartDNA.bodyType, InvalidEntry)
+ self.setMagicWordResponse(response)
+ else:
+ base.localAvatar.requestKartDNAFieldUpdate(KartDNA.rimsType, getDefaultRim())
+ taskMgr.doMethodLater(1.0, doShtikerLater, 'doShtikerLater')
+ response = 'Kart %s has been purchased with body and accessory color %s.' % (word[9], getDefaultColor())
+ base.localAvatar.requestKartDNAFieldUpdate(KartDNA.bodyType, int(word[9]))
+ self.setMagicWordResponse(response)
+ else:
+ self.setMagicWordResponse('Enable wantKarts in Config.prc')
+ elif wordIs('~leaveRace'):
+ messenger.send('leaveRace')
+ elif wordIs('~kartParticles'):
+ b = ConfigVariableBool('want-kart-particles', 0)
+ b.setValue(not b)
+ elif wordIs('~gardenGame'):
+ messenger.send('gardenGame')
+ response = 'You must be on your estate'
+ self.setMagicWordResponse(response)
+ elif wordIs('~verboseState'):
+ base.localAvatar.verboseState()
+ elif wordIs('~golf'):
+ self.doGolf(word)
+ elif wordIs('~whiteList'):
+ base.localAvatar.chatMgr.chatInputSpeedChat.addWhiteList()
+ elif wordIs('~updateWhiteList'):
+ self.notify.info('Updating WhiteList')
+ base.whiteList.redownloadWhitelist()
+ elif wordIs('~noWhiteList'):
+ base.localAvatar.chatMgr.chatInputSpeedChat.removeWhiteList()
+ elif wordIs('~setPaid'):
+ args = word.split()
+ if len(args) > 1:
+ paid = int(args[1])
+ statusString = base.config.GetString('force-paid-status', 'none')
+ if paid:
+ paid = 1
+ if statusString != 'none':
+ if statusString == 'VELVET':
+ ConfigVariableString('force-paid-status').setValue('FULL')
+ elif statusString == 'unpaid':
+ ConfigVariableString('force-paid-status').setValue('paid')
+ base.cr.setIsPaid(1)
+ else:
+ paid = 0
+ if statusString != 'none':
+ if statusString == 'FULL':
+ ConfigVariableString('force-paid-status').setValue('VELVET')
+ elif statusString == 'paid':
+ ConfigVariableString('force-paid-status').setValue('unpaid')
+ base.cr.setIsPaid(0)
+ else:
+ return
+ elif wordIs('~party'):
+ self.doParty(word, avId, zoneId)
+ elif wordIs('~news'):
+ self.doNews(word, avId, zoneId)
+ elif wordIs('~bgui'):
+ if not hasattr(self, 'groupPanel'):
+ from toontown.toon import GroupPanel
+ self.groupPanel = GroupPanel.GroupPanel(base.localAvatar.boardingParty)
+ self.groupPanel.frame.show()
+ else:
+ self.groupPanel.frame.hide()
+ self.groupPanel.cleanup()
+ del self.groupPanel
+ elif wordIs('~generateOrder'):
+ args = word.split()
+ if len(args) > 1:
+ newOrder = int(args[1])
+ if newOrder >= 0 and newOrder <= 2:
+ datagram = PyDatagram.PyDatagram()
+ datagram.addUint16(MsgName2Id['CLIENT_CHANGE_GENERATE_ORDER'])
+ datagram.addUint32(newOrder)
+ base.cr.send(datagram)
+ response = 'changing generate order to %s' % newOrder
+ self.setMagicWordResponse(response)
+ else:
+ response = 'args: 0 default, 1 reversed, 2 shuffled'
+ self.setMagicWordResponse(response)
+ elif wordIs('~ruler'):
+ response = 'Each unit is equal to one foot'
+ self.setMagicWordResponse(response)
+ if self.ruler:
+ self.ruler.detachNode()
+ del self.ruler
+ self.ruler = loader.loadModel('phase_3/models/props/xyzAxis')
+ self.ruler.reparentTo(render)
+ self.ruler.setPos(base.localAvatar.getPos())
+ self.ruler.place()
+ elif wordIs('~toonIdTags'):
+ otherToons = base.cr.doFindAllOfType('DistributedToon')
+ if otherToons:
+ for toon in otherToons[0]:
+ toon.setNametagStyle(0)
+
+ messenger.send('nameTagShowAvId', [])
+ base.idTags = 1
+ elif wordIs('~code'):
+ args = word.split()
+ if len(args) > 1:
+ code = word[len(args[0]) + 1:]
+ base.codeRedemptionMgr.redeemCode(code, self._handleCodeRedemptionResponse)
+ response = 'sending code %s to server...' % code
+ else:
+ response = '~code '
+ self.setMagicWordResponse(response)
+ return
+
+ def _handleCodeRedemptionResponse(self, result, awardMgrResult):
+ if not result:
+ msg = 'code redeemed'
+ else:
+ if result != TTCodeRedemptionConsts.RedeemErrors.AwardCouldntBeGiven:
+ errMsg = TTCodeRedemptionConsts.RedeemErrorStrings[result]
+ else:
+ errMsg = AwardManagerConsts.GiveAwardErrorStrings[awardMgrResult]
+ msg = 'code NOT redeemed (%s)' % (errMsg,)
+ base.localAvatar.setChatAbsolute(msg, CFSpeech | CFTimeout)
+
+ def doParty(self, word, av, zoneId):
+ args = word.split()
+ response = None
+ action = None
+ if len(args) == 1:
+ return
+ action = args[1]
+ if action == 'plan':
+ base.localAvatar.aboutToPlanParty = True
+ base.localAvatar.creatingNewPartyWithMagicWord = False
+ elif action == 'new':
+ base.localAvatar.aboutToPlanParty = False
+ base.localAvatar.creatingNewPartyWithMagicWord = True
+ elif action == 'start':
+ base.localAvatar.creatingNewPartyWithMagicWord = False
+ base.localAvatar.aboutToPlanParty = False
+ hoodId = ToontownGlobals.PartyHood
+ ToontownDistrictStats.refresh('shardInfoUpdated')
+ curShardTuples = base.cr.listActiveShards()
+ lowestPop = 100000000000000000L
+ shardId = None
+ for shardInfo in curShardTuples:
+ pop = shardInfo[2]
+ if pop < lowestPop:
+ lowestPop = pop
+ shardId = shardInfo[0]
+
+ if shardId == base.localAvatar.defaultShard:
+ shardId = None
+ base.cr.playGame.getPlace().requestLeave({'loader': 'safeZoneLoader',
+ 'where': 'party',
+ 'how': 'teleportIn',
+ 'hoodId': hoodId,
+ 'zoneId': -1,
+ 'shardId': shardId,
+ 'avId': -1})
+ elif action == 'unreleasedClient':
+ newVal = base.cr.partyManager.toggleAllowUnreleasedClient()
+ response = 'Allow Unreleased Client = %s' % newVal
+ elif action == 'showdoid':
+ newVal = base.cr.partyManager.toggleShowDoid()
+ response = 'show doid = %s' % newVal
+ elif action == 'debugGrid':
+ newVal = not ConfigVariableBool('show-debug-party-grid')
+ ConfigVariableBool('show-debug-party-grid').setValue(newVal)
+ response = 'Grid: %s; re-enter party to see changes.' % newVal
+ if response is not None:
+ self.setMagicWordResponse(response)
+ return
+
+ def doCatalog(self, word):
+ args = word.split()
+ if len(args) == 1:
+ return
+ elif args[1] == 'reload':
+ phone = base.cr.doFind('phone')
+ if phone and phone.phoneGui:
+ phone.phoneGui.reload()
+ response = 'Reloaded catalog screen'
+ else:
+ response = 'Phone is not active.'
+ elif args[1] == 'dump':
+ if len(args) <= 2:
+ response = 'Specify output filename.'
+ else:
+ from toontown.catalog import CatalogGenerator
+ cg = CatalogGenerator.CatalogGenerator()
+ cg.outputSchedule(args[2])
+ response = 'Catalog schedule written to file %s.' % args[2]
+ else:
+ return
+ self.setMagicWordResponse(response)
+
+ def toggleRun(self):
+ if self.dbg_running_fast:
+ self.dbg_running_fast = 0
+ OTPGlobals.ToonForwardSpeed = self.save_fwdspeed
+ OTPGlobals.ToonReverseSpeed = self.save_revspeed
+ OTPGlobals.ToonRotateSpeed = self.save_rotspeed
+ base.localAvatar.setWalkSpeedNormal()
+ else:
+ self.dbg_running_fast = 1
+ self.save_fwdspeed = OTPGlobals.ToonForwardSpeed
+ self.save_revspeed = OTPGlobals.ToonReverseSpeed
+ self.save_rotspeed = OTPGlobals.ToonRotateSpeed
+ OTPGlobals.ToonForwardSpeed = 60
+ OTPGlobals.ToonReverseSpeed = 30
+ OTPGlobals.ToonRotateSpeed = 100
+ base.localAvatar.setWalkSpeedNormal()
+
+ def requestTeleport(self, loaderId, whereId, hoodId, zoneId, avId):
+ place = base.cr.playGame.getPlace()
+ if loaderId == '':
+ loaderId = ZoneUtil.getBranchLoaderName(zoneId)
+ if whereId == '':
+ whereId = ZoneUtil.getToonWhereName(zoneId)
+ if hoodId == 0:
+ hoodId = place.loader.hood.id
+ if avId == 0:
+ avId = -1
+ place.fsm.forceTransition('teleportOut', [{'loader': loaderId,
+ 'where': whereId,
+ 'how': 'teleportIn',
+ 'hoodId': hoodId,
+ 'zoneId': zoneId,
+ 'shardId': None,
+ 'avId': avId}])
+ return
+
+ def exit_rogues(self):
+ self.rogues.exit()
+ del self.rogues
+ self.rogues = None
+ return
+
+ def identifyDistributedObjects(self, name):
+ result = []
+ lowerName = string.lower(name)
+ for obj in base.cr.doId2do.values():
+ className = obj.__class__.__name__
+ try:
+ name = obj.getName()
+ except:
+ name = className
+
+ if string.lower(name) == lowerName or string.lower(className) == lowerName or string.lower(className) == 'distributed' + lowerName:
+ result.append((name, obj))
+
+ return result
+
+ def getCSBitmask(self, str):
+ words = string.lower(str).split()
+ if len(words) == 0:
+ return None
+ invalid = ''
+ bitmask = BitMask32.allOff()
+ for w in words:
+ if w == 'wall':
+ bitmask |= ToontownGlobals.WallBitmask
+ elif w == 'floor':
+ bitmask |= ToontownGlobals.FloorBitmask
+ elif w == 'cam':
+ bitmask |= ToontownGlobals.CameraBitmask
+ elif w == 'catch':
+ bitmask |= ToontownGlobals.CatchBitmask
+ elif w == 'ghost':
+ bitmask |= ToontownGlobals.GhostBitmask
+ elif w == 'furniture':
+ bitmask |= ToontownGlobals.FurnitureSideBitmask | ToontownGlobals.FurnitureTopBitmask | ToontownGlobals.FurnitureDragBitmask
+ elif w == 'furnitureside':
+ bitmask |= ToontownGlobals.FurnitureSideBitmask
+ elif w == 'furnituretop':
+ bitmask |= ToontownGlobals.FurnitureTopBitmask
+ elif w == 'furnituredrag':
+ bitmask |= ToontownGlobals.FurnitureDragBitmask
+ elif w == 'pie':
+ bitmask |= ToontownGlobals.PieBitmask
+ else:
+ invalid += ' ' + w
+
+ if invalid:
+ self.setMagicWordResponse('Unknown CS keyword(s): %s' % invalid)
+ return bitmask
+
+ def getFontByName(self, fontname):
+ if fontname == 'toon':
+ return ToontownGlobals.getToonFont()
+ elif fontname == 'building':
+ return ToontownGlobals.getBuildingNametagFont()
+ elif fontname == 'minnie':
+ return ToontownGlobals.getMinnieFont()
+ elif fontname == 'suit':
+ return ToontownGlobals.getSuitFont()
+ else:
+ return MagicWordManager.MagicWordManager.getFontByName(self, fontname)
+
+ def doBossBattle(self, word):
+ args = word.split()
+ bossCog = None
+ for distObj in self.cr.doId2do.values():
+ if isinstance(distObj, DistributedBossCog.DistributedBossCog):
+ bossCog = distObj
+ break
+
+ response = None
+ if len(args) == 1:
+ pass
+ elif args[1] == 'safe':
+ if len(args) <= 2:
+ flag = not bossCog.localToonIsSafe
+ else:
+ flag = int(args[2])
+ bossCog.localToonIsSafe = flag
+ if flag:
+ response = 'LocalToon is now safe from boss attacks'
+ else:
+ response = 'LocalToon is now vulnerable to boss attacks'
+ elif args[1] == 'stun':
+ bossCog.stunAllGoons()
+ elif args[1] == 'destroy':
+ bossCog.destroyAllGoons()
+ elif args[1] == 'avatarEnter':
+ bossCog.d_avatarEnter()
+ response = 'called d_avatarEnter'
+ if response:
+ self.setMagicWordResponse(response)
+ return
+
+ def doGolf(self, word):
+ args = word.split()
+ response = None
+ if len(args) == 1:
+ pass
+ elif args[1] == 'debugBarrier':
+ golfHole = base.cr.doFind('DistributedGolfHole')
+ if golfHole:
+ if hasattr(golfHole, 'golfBarrier') and not golfHole.golfBarrier.isEmpty():
+ if golfHole.golfBarrier.isHidden():
+ golfHole.golfBarrier.show()
+ response = 'showing golf barrier'
+ else:
+ golfHole.golfBarrier.hide()
+ response = 'hiding golf barrier'
+ else:
+ response = 'no golf hole'
+ elif args[1] == 'contact':
+ messenger.send('ode toggle contacts')
+ elif args[1] == 'power':
+ if len(args) > 2:
+ base.golfPower = args[2]
+ response = 'setting power to %s' % args[2]
+ else:
+ base.golfPower = None
+ response = 'unsetting power'
+ elif args[1] == 'heading':
+ if len(args) > 2:
+ golfHole = base.cr.doFind('DistributedGolfHole')
+ if golfHole:
+ golfHole.doMagicWordHeading(args[2])
+ response = 'setting heading to %s' % args[2]
+ else:
+ response = 'need heading parameter'
+ elif args[1] == 'list':
+ response = ''
+ for holeId in GolfGlobals.HoleInfo:
+ if holeId < 18:
+ response += '%d: %s\n' % (holeId, GolfGlobals.getHoleName(holeId))
+
+ elif args[1] == 'list2':
+ response = ''
+ for holeId in GolfGlobals.HoleInfo:
+ if holeId >= 18:
+ response += '%d: %s\n' % (holeId, GolfGlobals.getHoleName(holeId))
+
+ if response:
+ self.setMagicWordResponse(response)
+ return
+
+ def doNews(self, word, av, zoneId):
+ args = word.split()
+ response = None
+ action = None
+ if len(args) == 1:
+ return
+ action = args[1]
+ if action == 'frame':
+ NametagGlobals.setMasterArrowsOn(0)
+ from toontown.shtiker import InGameNewsFrame
+ base.newsFrame = InGameNewsFrame.InGameNewsFrame()
+ base.newsFrame.activate()
+ response = 'putting in game news direct frame up'
+ elif action == 'snapshot':
+ response = localAvatar.newsPage.doSnapshot()
+ if response is not None:
+ self.setMagicWordResponse(response)
+ return
diff --git a/toontown/ai/WelcomeValleyManager.py b/toontown/ai/WelcomeValleyManager.py
new file mode 100644
index 0000000..57bc3ba
--- /dev/null
+++ b/toontown/ai/WelcomeValleyManager.py
@@ -0,0 +1,38 @@
+from pandac.PandaModules import *
+from direct.distributed import DistributedObject
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownGlobals
+from direct.showbase import PythonUtil
+
+class WelcomeValleyManager(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('WelcomeValleyManager')
+ neverDisable = 1
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+
+ def generate(self):
+ if base.cr.welcomeValleyManager != None:
+ base.cr.welcomeValleyManager.delete()
+ base.cr.welcomeValleyManager = self
+ DistributedObject.DistributedObject.generate(self)
+ return
+
+ def disable(self):
+ self.ignore(ToontownGlobals.SynchronizeHotkey)
+ base.cr.welcomeValleyManager = None
+ DistributedObject.DistributedObject.disable(self)
+ return
+
+ def delete(self):
+ self.ignore(ToontownGlobals.SynchronizeHotkey)
+ base.cr.welcomeValleyManager = None
+ DistributedObject.DistributedObject.delete(self)
+ return
+
+ def requestZoneId(self, origZoneId, callback):
+ context = self.getCallbackContext(callback)
+ self.sendUpdate('requestZoneIdMessage', [origZoneId, context])
+
+ def requestZoneIdResponse(self, zoneId, context):
+ self.doCallbackContext(context, [zoneId])
diff --git a/toontown/ai/__init__.py b/toontown/ai/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/toontown/battle/BattleBase.py b/toontown/battle/BattleBase.py
new file mode 100644
index 0000000..338b250
--- /dev/null
+++ b/toontown/battle/BattleBase.py
@@ -0,0 +1,312 @@
+from pandac.PandaModules import *
+from toontown.toonbase.ToontownBattleGlobals import *
+from direct.task.Timer import *
+import math
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toon import NPCToons
+from toontown.toonbase import TTLocalizer
+TOON_ID_COL = 0
+TOON_TRACK_COL = 1
+TOON_LVL_COL = 2
+TOON_TGT_COL = 3
+TOON_HP_COL = 4
+TOON_ACCBONUS_COL = 5
+TOON_HPBONUS_COL = 6
+TOON_KBBONUS_COL = 7
+SUIT_DIED_COL = 8
+SUIT_REVIVE_COL = 9
+SUIT_ID_COL = 0
+SUIT_ATK_COL = 1
+SUIT_TGT_COL = 2
+SUIT_HP_COL = 3
+TOON_DIED_COL = 4
+SUIT_BEFORE_TOONS_COL = 5
+SUIT_TAUNT_COL = 6
+NO_ID = -1
+NO_ATTACK = -1
+UN_ATTACK = -2
+PASS_ATTACK = -3
+NO_TRAP = -1
+LURE_SUCCEEDED = -1
+PASS = 98
+SOS = 99
+NPCSOS = 97
+PETSOS = 96
+FIRE = 100
+HEAL = HEAL_TRACK
+TRAP = TRAP_TRACK
+LURE = LURE_TRACK
+SOUND = SOUND_TRACK
+THROW = THROW_TRACK
+SQUIRT = SQUIRT_TRACK
+DROP = DROP_TRACK
+TOON_ATTACK_TIME = 12.0
+SUIT_ATTACK_TIME = 12.0
+TOON_TRAP_DELAY = 0.8
+TOON_SOUND_DELAY = 1.0
+TOON_THROW_DELAY = 0.5
+TOON_THROW_SUIT_DELAY = 1.0
+TOON_SQUIRT_DELAY = 0.5
+TOON_SQUIRT_SUIT_DELAY = 1.0
+TOON_DROP_DELAY = 0.8
+TOON_DROP_SUIT_DELAY = 1.0
+TOON_RUN_T = 3.3
+TIMEOUT_PER_USER = 5
+TOON_FIRE_DELAY = 0.5
+TOON_FIRE_SUIT_DELAY = 1.0
+REWARD_TIMEOUT = 120
+FLOOR_REWARD_TIMEOUT = 4
+BUILDING_REWARD_TIMEOUT = 300
+try:
+ CLIENT_INPUT_TIMEOUT = base.config.GetFloat('battle-input-timeout', TTLocalizer.BBbattleInputTimeout)
+except:
+ CLIENT_INPUT_TIMEOUT = simbase.config.GetFloat('battle-input-timeout', TTLocalizer.BBbattleInputTimeout)
+
+def levelAffectsGroup(track, level):
+ return attackAffectsGroup(track, level)
+
+
+def attackAffectsGroup(track, level, type = None):
+ if track == NPCSOS or type == NPCSOS or track == PETSOS or type == PETSOS:
+ return 1
+ elif track >= 0 and track <= DROP_TRACK:
+ return AvPropTargetCat[AvPropTarget[track]][level]
+ else:
+ return 0
+
+
+def getToonAttack(id, track = NO_ATTACK, level = -1, target = -1):
+ return [id,
+ track,
+ level,
+ target,
+ [],
+ 0,
+ 0,
+ [],
+ 0,
+ 0]
+
+
+def getDefaultSuitAttacks():
+ suitAttacks = [[NO_ID,
+ NO_ATTACK,
+ -1,
+ [],
+ 0,
+ 0,
+ 0],
+ [NO_ID,
+ NO_ATTACK,
+ -1,
+ [],
+ 0,
+ 0,
+ 0],
+ [NO_ID,
+ NO_ATTACK,
+ -1,
+ [],
+ 0,
+ 0,
+ 0],
+ [NO_ID,
+ NO_ATTACK,
+ -1,
+ [],
+ 0,
+ 0,
+ 0]]
+ return suitAttacks
+
+
+def getDefaultSuitAttack():
+ return [NO_ID,
+ NO_ATTACK,
+ -1,
+ [],
+ 0,
+ 0,
+ 0]
+
+
+def findToonAttack(toons, attacks, track):
+ foundAttacks = []
+ for t in toons:
+ if attacks.has_key(t):
+ attack = attacks[t]
+ local_track = attack[TOON_TRACK_COL]
+ if track != NPCSOS and attack[TOON_TRACK_COL] == NPCSOS:
+ local_track = NPCToons.getNPCTrack(attack[TOON_TGT_COL])
+ if local_track == track:
+ if local_track == FIRE:
+ canFire = 1
+ for attackCheck in foundAttacks:
+ if attackCheck[TOON_TGT_COL] == attack[TOON_TGT_COL]:
+ canFire = 0
+
+ if canFire:
+ foundAttacks.append(attack)
+ else:
+ foundAttacks.append(attack)
+
+ def compFunc(a, b):
+ if a[TOON_LVL_COL] > b[TOON_LVL_COL]:
+ return 1
+ elif a[TOON_LVL_COL] < b[TOON_LVL_COL]:
+ return -1
+ return 0
+
+ foundAttacks.sort(compFunc)
+ return foundAttacks
+
+
+SERVER_BUFFER_TIME = 2.0
+SERVER_INPUT_TIMEOUT = CLIENT_INPUT_TIMEOUT + SERVER_BUFFER_TIME
+MAX_JOIN_T = TTLocalizer.BBbattleInputTimeout
+FACEOFF_TAUNT_T = 3.5
+FACEOFF_LOOK_AT_PROP_T = 6
+ELEVATOR_T = 4.0
+BATTLE_SMALL_VALUE = 1e-07
+MAX_EXPECTED_DISTANCE_FROM_BATTLE = 50.0
+
+class BattleBase:
+ notify = DirectNotifyGlobal.directNotify.newCategory('BattleBase')
+ suitPoints = (((Point3(0, 5, 0), 179),),
+ ((Point3(2, 5.3, 0), 170), (Point3(-2, 5.3, 0), 180)),
+ ((Point3(4, 5.2, 0), 170), (Point3(0, 6, 0), 179), (Point3(-4, 5.2, 0), 190)),
+ ((Point3(6, 4.4, 0), 160),
+ (Point3(2, 6.3, 0), 170),
+ (Point3(-2, 6.3, 0), 190),
+ (Point3(-6, 4.4, 0), 200)))
+ suitPendingPoints = ((Point3(-4, 8.2, 0), 190),
+ (Point3(0, 9, 0), 179),
+ (Point3(4, 8.2, 0), 170),
+ (Point3(8, 3.2, 0), 160))
+ toonPoints = (((Point3(0, -6, 0), 0),),
+ ((Point3(1.5, -6.5, 0), 5), (Point3(-1.5, -6.5, 0), -5)),
+ ((Point3(3, -6.75, 0), 5), (Point3(0, -7, 0), 0), (Point3(-3, -6.75, 0), -5)),
+ ((Point3(4.5, -7, 0), 10),
+ (Point3(1.5, -7.5, 0), 5),
+ (Point3(-1.5, -7.5, 0), -5),
+ (Point3(-4.5, -7, 0), -10)))
+ toonPendingPoints = ((Point3(-3, -8, 0), -5),
+ (Point3(0, -9, 0), 0),
+ (Point3(3, -8, 0), 5),
+ (Point3(5.5, -5.5, 0), 20))
+ posA = Point3(0, 10, 0)
+ posB = Point3(-7.071, 7.071, 0)
+ posC = Point3(-10, 0, 0)
+ posD = Point3(-7.071, -7.071, 0)
+ posE = Point3(0, -10, 0)
+ posF = Point3(7.071, -7.071, 0)
+ posG = Point3(10, 0, 0)
+ posH = Point3(7.071, 7.071, 0)
+ allPoints = (posA,
+ posB,
+ posC,
+ posD,
+ posE,
+ posF,
+ posG,
+ posH)
+ toonCwise = [posA,
+ posB,
+ posC,
+ posD,
+ posE]
+ toonCCwise = [posH,
+ posG,
+ posF,
+ posE]
+ suitCwise = [posE,
+ posF,
+ posG,
+ posH,
+ posA]
+ suitCCwise = [posD,
+ posC,
+ posB,
+ posA]
+ suitSpeed = 4.8
+ toonSpeed = 8.0
+
+ def __init__(self):
+ self.pos = Point3(0, 0, 0)
+ self.initialSuitPos = Point3(0, 1, 0)
+ self.timer = Timer()
+ self.resetLists()
+
+ def resetLists(self):
+ self.suits = []
+ self.pendingSuits = []
+ self.joiningSuits = []
+ self.activeSuits = []
+ self.luredSuits = []
+ self.suitGone = 0
+ self.toons = []
+ self.joiningToons = []
+ self.pendingToons = []
+ self.activeToons = []
+ self.runningToons = []
+ self.toonGone = 0
+ self.helpfulToons = []
+
+ def calcFaceoffTime(self, centerpos, suitpos):
+ facing = Vec3(centerpos - suitpos)
+ facing.normalize()
+ suitdest = Point3(centerpos - Point3(facing * 6.0))
+ dist = Vec3(suitdest - suitpos).length()
+ return dist / BattleBase.suitSpeed
+
+ def calcSuitMoveTime(self, pos0, pos1):
+ dist = Vec3(pos0 - pos1).length()
+ return dist / BattleBase.suitSpeed
+
+ def calcToonMoveTime(self, pos0, pos1):
+ dist = Vec3(pos0 - pos1).length()
+ return dist / BattleBase.toonSpeed
+
+ def buildJoinPointList(self, avPos, destPos, toon = 0):
+ minDist = 999999.0
+ nearestP = None
+ for p in BattleBase.allPoints:
+ dist = Vec3(avPos - p).length()
+ if dist < minDist:
+ nearestP = p
+ minDist = dist
+
+ self.notify.debug('buildJoinPointList() - avp: %s nearp: %s' % (avPos, nearestP))
+ dist = Vec3(avPos - destPos).length()
+ if dist < minDist:
+ self.notify.debug('buildJoinPointList() - destPos is nearest')
+ return []
+ if toon == 1:
+ if nearestP == BattleBase.posE:
+ self.notify.debug('buildJoinPointList() - posE')
+ plist = [BattleBase.posE]
+ elif BattleBase.toonCwise.count(nearestP) == 1:
+ self.notify.debug('buildJoinPointList() - clockwise')
+ index = BattleBase.toonCwise.index(nearestP)
+ plist = BattleBase.toonCwise[index + 1:]
+ else:
+ self.notify.debug('buildJoinPointList() - counter-clockwise')
+ index = BattleBase.toonCCwise.index(nearestP)
+ plist = BattleBase.toonCCwise[index + 1:]
+ elif nearestP == BattleBase.posA:
+ self.notify.debug('buildJoinPointList() - posA')
+ plist = [BattleBase.posA]
+ elif BattleBase.suitCwise.count(nearestP) == 1:
+ self.notify.debug('buildJoinPointList() - clockwise')
+ index = BattleBase.suitCwise.index(nearestP)
+ plist = BattleBase.suitCwise[index + 1:]
+ else:
+ self.notify.debug('buildJoinPointList() - counter-clockwise')
+ index = BattleBase.suitCCwise.index(nearestP)
+ plist = BattleBase.suitCCwise[index + 1:]
+ self.notify.debug('buildJoinPointList() - plist: %s' % plist)
+ return plist
+
+ def addHelpfulToon(self, toonId):
+ if toonId not in self.helpfulToons:
+ self.helpfulToons.append(toonId)
diff --git a/toontown/battle/BattleCalculatorAI.py b/toontown/battle/BattleCalculatorAI.py
new file mode 100644
index 0000000..f53bbe2
--- /dev/null
+++ b/toontown/battle/BattleCalculatorAI.py
@@ -0,0 +1,1607 @@
+from BattleBase import *
+from DistributedBattleAI import *
+from toontown.toonbase.ToontownBattleGlobals import *
+import random
+from toontown.suit import DistributedSuitBaseAI
+import SuitBattleGlobals, BattleExperienceAI
+from toontown.toon import NPCToons
+from toontown.pets import PetTricks, DistributedPetProxyAI
+from direct.showbase.PythonUtil import lerp
+
+class BattleCalculatorAI:
+ AccuracyBonuses = [
+ 0, 20, 40, 60]
+ DamageBonuses = [
+ 0, 20, 20, 20]
+ AttackExpPerTrack = [
+ 0, 10, 20, 30, 40, 50, 60]
+ NumRoundsLured = [
+ 2, 2, 3, 3, 4, 4, 15]
+ TRAP_CONFLICT = -2
+ APPLY_HEALTH_ADJUSTMENTS = 1
+ TOONS_TAKE_NO_DAMAGE = 0
+ CAP_HEALS = 1
+ CLEAR_SUIT_ATTACKERS = 1
+ SUITS_UNLURED_IMMEDIATELY = 1
+ CLEAR_MULTIPLE_TRAPS = 0
+ KBBONUS_LURED_FLAG = 0
+ KBBONUS_TGT_LURED = 1
+ notify = DirectNotifyGlobal.directNotify.newCategory('BattleCalculatorAI')
+ toonsAlwaysHit = simbase.config.GetBool('toons-always-hit', 0)
+ toonsAlwaysMiss = simbase.config.GetBool('toons-always-miss', 0)
+ toonsAlways5050 = simbase.config.GetBool('toons-always-5050', 0)
+ suitsAlwaysHit = simbase.config.GetBool('suits-always-hit', 0)
+ suitsAlwaysMiss = simbase.config.GetBool('suits-always-miss', 0)
+ immortalSuits = simbase.config.GetBool('immortal-suits', 0)
+ propAndOrganicBonusStack = simbase.config.GetBool('prop-and-organic-bonus-stack', 0)
+
+ def __init__(self, battle, tutorialFlag=0):
+ self.battle = battle
+ self.SuitAttackers = {}
+ self.currentlyLuredSuits = {}
+ self.successfulLures = {}
+ self.toonAtkOrder = []
+ self.toonHPAdjusts = {}
+ self.toonSkillPtsGained = {}
+ self.traps = {}
+ self.npcTraps = {}
+ self.suitAtkStats = {}
+ self.__clearBonuses(hp=1)
+ self.__clearBonuses(hp=0)
+ self.delayedUnlures = []
+ self.__skillCreditMultiplier = 1
+ self.tutorialFlag = tutorialFlag
+ self.trainTrapTriggered = False
+
+ def setSkillCreditMultiplier(self, mult):
+ self.__skillCreditMultiplier = 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 range(len(atkTargets)):
+ if self.__suitIsLured(atkTargets[i].getDoId()):
+ pass
+ else:
+ allLured = False
+
+ if allLured:
+ attack[TOON_ACCBONUS_COL] = 1
+ return (0, 0)
+ elif atkTrack == PETSOS:
+ return self.__calculatePetTrickSuccess(attack)
+ tgtDef = 0
+ numLured = 0
+ if atkTrack != HEAL:
+ for currTarget in atkTargets:
+ thisSuitDef = self.__targetDefense(currTarget, atkTrack)
+ if debug:
+ self.notify.debug('Examining suit def for toon attack: ' + str(thisSuitDef))
+ tgtDef = min(thisSuitDef, tgtDef)
+ if self.__suitIsLured(currTarget.getDoId()):
+ numLured += 1
+
+ trackExp = self.__toonTrackExp(attack[TOON_ID_COL], atkTrack)
+ for currOtherAtk in self.toonAtkOrder:
+ if currOtherAtk != attack[TOON_ID_COL]:
+ nextAttack = self.battle.toonAttacks[currOtherAtk]
+ nextAtkTrack = self.__getActualTrack(nextAttack)
+ if atkTrack == nextAtkTrack and attack[TOON_TGT_COL] == nextAttack[TOON_TGT_COL]:
+ currTrackExp = self.__toonTrackExp(nextAttack[TOON_ID_COL], atkTrack)
+ if debug:
+ self.notify.debug('Examining toon track exp bonus: ' + str(currTrackExp))
+ trackExp = max(currTrackExp, trackExp)
+
+ if debug:
+ if atkTrack == HEAL:
+ self.notify.debug('Toon attack is a heal, no target def used')
+ else:
+ self.notify.debug('Suit defense used for toon attack: ' + str(tgtDef))
+ self.notify.debug('Toon track exp bonus used for toon attack: ' + str(trackExp))
+ if attack[TOON_TRACK_COL] == NPCSOS:
+ randChoice = 0
+ else:
+ randChoice = random.randint(0, 99)
+ propAcc = AvPropAccuracy[atkTrack][atkLevel]
+ if 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 self.successfulLures.has_key(attack[TOON_TGT_COL]) or attackAffectsGroup(atkTrack, atkLevel, attack[TOON_TRACK_COL]))
+ if atkTrack == prevAtkTrack and (attack[TOON_TGT_COL] == prevAttack[TOON_TGT_COL] or lure):
+ if prevAttack[TOON_ACCBONUS_COL] == 1:
+ if debug:
+ self.notify.debug('DODGE: Toon attack track dodged')
+ elif prevAttack[TOON_ACCBONUS_COL] == 0:
+ if debug:
+ self.notify.debug('HIT: Toon attack track hit')
+ attack[TOON_ACCBONUS_COL] = prevAttack[TOON_ACCBONUS_COL]
+ return (not attack[TOON_ACCBONUS_COL], attackAcc)
+ atkAccResult = attackAcc
+ if debug:
+ self.notify.debug('setting atkAccResult to %d' % atkAccResult)
+ acc = attackAcc + self.__calcToonAccBonus(attackIndex)
+ if atkTrack != LURE and atkTrack != HEAL:
+ if atkTrack != DROP:
+ if numLured == len(atkTargets):
+ if debug:
+ self.notify.debug('all targets are lured, attack hits')
+ attack[TOON_ACCBONUS_COL] = 0
+ return (1, 100)
+ else:
+ luredRatio = float(numLured) / float(len(atkTargets))
+ accAdjust = 100 * luredRatio
+ if accAdjust > 0 and debug:
+ self.notify.debug(str(numLured) + ' out of ' + str(len(atkTargets)) + ' targets are lured, so adding ' + str(accAdjust) + ' to attack accuracy')
+ acc += accAdjust
+ elif numLured == len(atkTargets):
+ if debug:
+ self.notify.debug('all targets are lured, attack misses')
+ attack[TOON_ACCBONUS_COL] = 0
+ return (0, 0)
+ if acc > MaxToonAcc:
+ acc = MaxToonAcc
+ if randChoice < acc:
+ if debug:
+ self.notify.debug('HIT: Toon attack rolled' + str(randChoice) + 'to hit with an accuracy of' + str(acc))
+ attack[TOON_ACCBONUS_COL] = 0
+ else:
+ if debug:
+ self.notify.debug('MISS: Toon attack rolled' + str(randChoice) + 'to hit with an accuracy of' + str(acc))
+ attack[TOON_ACCBONUS_COL] = 1
+ return (not attack[TOON_ACCBONUS_COL], atkAccResult)
+
+ def __toonTrackExp(self, toonId, track):
+ toon = self.battle.getToon(toonId)
+ if toon != None:
+ toonExpLvl = toon.experience.getExpLevel(track)
+ exp = self.AttackExpPerTrack[toonExpLvl]
+ if track == HEAL:
+ exp = exp * 0.5
+ self.notify.debug('Toon track exp: ' + str(toonExpLvl) + ' and resulting acc bonus: ' + str(exp))
+ return exp
+ else:
+ return 0
+ return
+
+ def __toonCheckGagBonus(self, toonId, track, level):
+ toon = self.battle.getToon(toonId)
+ if toon != None:
+ return toon.checkGagBonus(track, level)
+ else:
+ return False
+ return
+
+ def __checkPropBonus(self, track):
+ result = False
+ if self.battle.getInteractivePropTrackBonus() == track:
+ result = True
+ return result
+
+ def __targetDefense(self, suit, atkTrack):
+ if atkTrack == HEAL:
+ return 0
+ suitDef = SuitBattleGlobals.SuitAttributes[suit.dna.name]['def'][suit.getLevel()]
+ return -suitDef
+
+ def __createToonTargetList(self, attackIndex):
+ attack = self.battle.toonAttacks[attackIndex]
+ atkTrack, atkLevel = self.__getActualTrackLevel(attack)
+ targetList = []
+ if atkTrack == NPCSOS:
+ return targetList
+ if not attackAffectsGroup(atkTrack, atkLevel, attack[TOON_TRACK_COL]):
+ if atkTrack == HEAL:
+ target = attack[TOON_TGT_COL]
+ else:
+ target = self.battle.findSuit(attack[TOON_TGT_COL])
+ if target != None:
+ targetList.append(target)
+ else:
+ if 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 self.traps.has_key(suitId):
+ 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 self.traps.has_key(suitId):
+ 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 self.traps.has_key(suitId):
+ 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 self.traps.has_key(id):
+ 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)
+ else:
+ if self.traps.has_key(suitId):
+ if self.traps[suitId][0] == self.TRAP_CONFLICT:
+ self.traps[suitId] = [
+ trapLvl, 0, npcDamage]
+ else:
+ if not self.__suitIsLured(suitId):
+ self.traps[suitId] = [
+ trapLvl, 0, npcDamage]
+
+ def __addSuitTrap(self, suitId, trapLvl, attackerId, npcDamage=0):
+ if npcDamage == 0:
+ if self.traps.has_key(suitId):
+ 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]
+ else:
+ if self.traps.has_key(suitId):
+ if self.traps[suitId][0] == self.TRAP_CONFLICT:
+ self.traps[suitId] = [
+ trapLvl, 0, npcDamage]
+ else:
+ if not self.__suitIsLured(suitId):
+ self.traps[suitId] = [
+ trapLvl, 0, npcDamage]
+
+ def __removeSuitTrap(self, suitId):
+ if self.traps.has_key(suitId):
+ 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 self.traps.has_key(suitId):
+ self.traps[suitId][1] = 0
+ return
+
+ def __trapCreator(self, suitId):
+ if self.traps.has_key(suitId):
+ 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 range(len(targetList)):
+ attackLevel = -1
+ attackTrack = None
+ attackDamage = 0
+ toonTarget = 0
+ targetLured = 0
+ if atkTrack == HEAL or atkTrack == PETSOS:
+ targetId = targetList[currTarget]
+ toonTarget = 1
+ else:
+ targetId = targetList[currTarget].getDoId()
+ if atkTrack == LURE:
+ if self.getSuitTrapType(targetId) == NO_TRAP:
+ if self.notify.getDebug():
+ self.notify.debug('Suit lured, but no trap exists')
+ if self.SUITS_UNLURED_IMMEDIATELY:
+ if not self.__suitIsLured(targetId, prevRound=1):
+ if not self.__combatantDead(targetId, toon=toonTarget):
+ validTargetAvail = 1
+ rounds = self.NumRoundsLured[atkLevel]
+ wakeupChance = 100 - atkAcc * 2
+ npcLurer = attack[TOON_TRACK_COL] == NPCSOS
+ currLureId = self.__addLuredSuitInfo(targetId, -1, rounds, wakeupChance, toonId, atkLevel, lureId=currLureId, npc=npcLurer)
+ if self.notify.getDebug():
+ self.notify.debug('Suit lured for ' + str(rounds) + ' rounds max with ' + str(wakeupChance) + '% chance to wake up each round')
+ targetLured = 1
+ else:
+ attackTrack = TRAP
+ if self.traps.has_key(targetId):
+ trapInfo = self.traps[targetId]
+ attackLevel = trapInfo[0]
+ else:
+ attackLevel = NO_TRAP
+ attackDamage = self.__suitTrapDamage(targetId)
+ trapCreatorId = self.__trapCreator(targetId)
+ if trapCreatorId > 0:
+ self.notify.debug('Giving trap EXP to toon ' + str(trapCreatorId))
+ self.__addAttackExp(attack, track=TRAP, level=attackLevel, attackerId=trapCreatorId)
+ self.__clearTrapCreator(trapCreatorId, targetId)
+ lureDidDamage = 1
+ if self.notify.getDebug():
+ self.notify.debug('Suit lured right onto a trap! (' + str(AvProps[attackTrack][attackLevel]) + ',' + str(attackLevel) + ')')
+ if not self.__combatantDead(targetId, toon=toonTarget):
+ validTargetAvail = 1
+ targetLured = 1
+ if not self.SUITS_UNLURED_IMMEDIATELY:
+ if not self.__suitIsLured(targetId, prevRound=1):
+ if not self.__combatantDead(targetId, toon=toonTarget):
+ validTargetAvail = 1
+ rounds = self.NumRoundsLured[atkLevel]
+ wakeupChance = 100 - atkAcc * 2
+ npcLurer = attack[TOON_TRACK_COL] == NPCSOS
+ currLureId = self.__addLuredSuitInfo(targetId, -1, rounds, wakeupChance, toonId, atkLevel, lureId=currLureId, npc=npcLurer)
+ if self.notify.getDebug():
+ self.notify.debug('Suit lured for ' + str(rounds) + ' rounds max with ' + str(wakeupChance) + '% chance to wake up each round')
+ targetLured = 1
+ if attackLevel != -1:
+ self.__addLuredSuitsDelayed(toonId, targetId)
+ if targetLured and (not self.successfulLures.has_key(targetId) or self.successfulLures.has_key(targetId) 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:
+ costToFire = 1
+ abilityToFire = toon.getPinkSlips()
+ toon.removePinkSlips(costToFire)
+ if costToFire > abilityToFire:
+ commentStr = 'Toon attempting to fire a %s cost cog with %s pinkslips' % (costToFire, abilityToFire)
+ simbase.air.writeServerEvent('suspicious', toonId, commentStr)
+ dislId = toon.DISLid
+ simbase.air.banManager.ban(toonId, dislId, commentStr)
+ print 'Not enough PinkSlips to fire cog - print a warning here'
+ else:
+ suit.skeleRevives = 0
+ attackDamage = suit.getHP()
+ 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 self.successfulLures.has_key(targetId) 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 range(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 range(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.suits.index(currTgt)
+ attackerId = self.toonAtkOrder[attackIndex]
+ attack = self.battle.toonAttacks[attackerId]
+ track = self.__getActualTrack(attack)
+ if hp:
+ if self.hpBonuses[tgtPos].has_key(track):
+ self.hpBonuses[tgtPos][track].append([attackIndex, dmg])
+ else:
+ self.hpBonuses[tgtPos][track] = [
+ [
+ attackIndex, dmg]]
+ elif self.__suitIsLured(currTgt.getDoId()):
+ if self.kbBonuses[tgtPos].has_key(track):
+ 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 range(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 self.SuitAttackers.has_key(suitId):
+ self.SuitAttackers[suitId] = {toonId: damage}
+ else:
+ if not self.SuitAttackers[suitId].has_key(toonId):
+ self.SuitAttackers[suitId][toonId] = damage
+ else:
+ if 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 self.traps.has_key(currTgt.doId):
+ 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 self.successfulLures.has_key(tgtId) 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 self.traps.has_key(currTgt.doId):
+ 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 self.SuitAttackers.has_key(suitId) 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
+ else:
+ if 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 range(len(targetList)):
+ toonId = targetList[currTarget]
+ toon = self.battle.getToon(toonId)
+ result = 0
+ if toon and toon.immortalMode:
+ result = 1
+ else:
+ if self.TOONS_TAKE_NO_DAMAGE:
+ result = 0
+ else:
+ if 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 self.toonHPAdjusts.has_key(toonDoId):
+ 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 self.suitAtkStats.has_key(toonId):
+ 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 range(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 range(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 range(longest):
+ self.battle.toonAttacks[t][TOON_HP_COL].append(-1)
+ self.battle.toonAttacks[t][TOON_KBBONUS_COL].append(-1)
+
+ for i in range(4):
+ for j in range(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
+
+ def __calculateFiredCogs():
+ import pdb
+ pdb.set_trace()
+
+ def toonLeftBattle(self, toonId):
+ if self.notify.getDebug():
+ self.notify.debug('toonLeftBattle()' + str(toonId))
+ if self.toonSkillPtsGained.has_key(toonId):
+ del self.toonSkillPtsGained[toonId]
+ if self.suitAtkStats.has_key(toonId):
+ del self.suitAtkStats[toonId]
+ if not self.CLEAR_SUIT_ATTACKERS:
+ oldSuitIds = []
+ for s in self.SuitAttackers.keys():
+ if self.SuitAttackers[s].has_key(toonId):
+ 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 self.SuitAttackers.has_key(suitId):
+ 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 = self.currentlyLuredSuits.has_key(suitId)
+ 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 self.currentlyLuredSuits.has_key(suitId):
+ lureInfo = self.currentlyLuredSuits[suitId]
+ if not lureInfo[3].has_key(lurer):
+ lureInfo[1] += maxRounds
+ if wakeChance < lureInfo[2]:
+ lureInfo[2] = wakeChance
+ lureInfo[3][lurer] = [
+ lureLvl, availLureId, credit]
+ else:
+ lurerInfo = {lurer: [lureLvl, availLureId, credit]}
+ self.currentlyLuredSuits[suitId] = [
+ currRounds, maxRounds, wakeChance, lurerInfo]
+ self.notify.debug('__addLuredSuitInfo: currLuredSuits -> %s' % repr(self.currentlyLuredSuits))
+ return availLureId
+
+ def __getLurers(self, suitId):
+ if self.__suitIsLured(suitId):
+ return self.currentlyLuredSuits[suitId][3].keys()
+ return []
+
+ def __getLuredExpInfo(self, suitId):
+ returnInfo = []
+ lurers = self.__getLurers(suitId)
+ if len(lurers) == 0:
+ return returnInfo
+ lurerInfo = self.currentlyLuredSuits[suitId][3]
+ for currLurer in lurers:
+ returnInfo.append([currLurer, lurerInfo[currLurer][0], lurerInfo[currLurer][1], lurerInfo[currLurer][2]])
+
+ return returnInfo
+
+ def __clearLurer(self, lurerId, lureId=-1):
+ luredSuits = self.currentlyLuredSuits.keys()
+ for currLured in luredSuits:
+ lurerInfo = self.currentlyLuredSuits[currLured][3]
+ lurers = lurerInfo.keys()
+ for currLurer in lurers:
+ if currLurer == lurerId and (lureId == -1 or lureId == lurerInfo[currLurer][1]):
+ del lurerInfo[currLurer]
+
+ def __setLuredMaxRounds(self, suitId, rounds):
+ if self.__suitIsLured(suitId):
+ self.currentlyLuredSuits[suitId][1] = rounds
+
+ def __setLuredWakeChance(self, suitId, chance):
+ if self.__suitIsLured(suitId):
+ self.currentlyLuredSuits[suitId][2] = chance
+
+ def __incLuredCurrRound(self, suitId):
+ if self.__suitIsLured(suitId):
+ self.currentlyLuredSuits[suitId][0] += 1
+
+ def __removeLured(self, suitId):
+ if self.__suitIsLured(suitId):
+ del self.currentlyLuredSuits[suitId]
+
+ def __luredMaxRoundsReached(self, suitId):
+ return self.__suitIsLured(suitId) and self.currentlyLuredSuits[suitId][0] >= self.currentlyLuredSuits[suitId][1]
+
+ def __luredWakeupTime(self, suitId):
+ return self.__suitIsLured(suitId) and self.currentlyLuredSuits[suitId][0] > 0 and random.randint(0, 99) < self.currentlyLuredSuits[suitId][2]
+
+ def itemIsCredit(self, track, level):
+ if track == PETSOS:
+ return 0
+ return level < self.creditLevel
+
+ def __getActualTrack(self, toonAttack):
+ if toonAttack[TOON_TRACK_COL] == NPCSOS:
+ track = NPCToons.getNPCTrack(toonAttack[TOON_TGT_COL])
+ if track != None:
+ return track
+ else:
+ self.notify.warning('No NPC with id: %d' % toonAttack[TOON_TGT_COL])
+ return toonAttack[TOON_TRACK_COL]
+
+ def __getActualTrackLevel(self, toonAttack):
+ if toonAttack[TOON_TRACK_COL] == NPCSOS:
+ track, level, hp = NPCToons.getNPCTrackLevelHp(toonAttack[TOON_TGT_COL])
+ if track != None:
+ return (track, level)
+ else:
+ self.notify.warning('No NPC with id: %d' % toonAttack[TOON_TGT_COL])
+ return (
+ toonAttack[TOON_TRACK_COL], toonAttack[TOON_LVL_COL])
+
+ def __getActualTrackLevelHp(self, toonAttack):
+ if toonAttack[TOON_TRACK_COL] == NPCSOS:
+ track, level, hp = NPCToons.getNPCTrackLevelHp(toonAttack[TOON_TGT_COL])
+ if track != None:
+ return (track, level, hp)
+ else:
+ self.notify.warning('No NPC with id: %d' % toonAttack[TOON_TGT_COL])
+ else:
+ if 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 simbase.air.doId2do.has_key(petProxyId):
+ 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 simbase.air.doId2do.has_key(petProxyId):
+ 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)
diff --git a/toontown/battle/BattleExperience.py b/toontown/battle/BattleExperience.py
new file mode 100644
index 0000000..61ce72f
--- /dev/null
+++ b/toontown/battle/BattleExperience.py
@@ -0,0 +1,22 @@
+from toontown.toonbase import ToontownBattleGlobals
+
+def genRewardDicts(entries):
+ toonRewardDicts = []
+ for toonId, origExp, earnedExp, origQuests, items, missedItems, origMerits, merits, parts in entries:
+ if toonId != -1:
+ dict = {}
+ toon = base.cr.doId2do.get(toonId)
+ if toon == None:
+ continue
+ dict['toon'] = toon
+ dict['origExp'] = origExp
+ dict['earnedExp'] = earnedExp
+ dict['origQuests'] = origQuests
+ dict['items'] = items
+ dict['missedItems'] = missedItems
+ dict['origMerits'] = origMerits
+ dict['merits'] = merits
+ dict['parts'] = parts
+ toonRewardDicts.append(dict)
+
+ return toonRewardDicts
diff --git a/toontown/battle/BattleExperienceAI.py b/toontown/battle/BattleExperienceAI.py
new file mode 100644
index 0000000..c70e991
--- /dev/null
+++ b/toontown/battle/BattleExperienceAI.py
@@ -0,0 +1,165 @@
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownBattleGlobals
+from toontown.suit import SuitDNA
+BattleExperienceAINotify = DirectNotifyGlobal.directNotify.newCategory('BattleExprienceAI')
+
+def getSkillGained(toonSkillPtsGained, toonId, track):
+ exp = 0
+ expList = toonSkillPtsGained.get(toonId, None)
+ if expList != None:
+ exp = expList[track]
+ return int(exp + 0.5)
+
+
+def getBattleExperience(numToons, activeToons, toonExp, toonSkillPtsGained, toonOrigQuests, toonItems, toonOrigMerits, toonMerits, toonParts, suitsKilled, helpfulToonsList=None):
+ if helpfulToonsList == None:
+ BattleExperienceAINotify.warning('=============\nERROR ERROR helpfulToons=None in assignRewards , tell Red')
+ p = []
+ for k in range(numToons):
+ toon = None
+ if k < len(activeToons):
+ toonId = activeToons[k]
+ toon = simbase.air.doId2do.get(toonId)
+ if toon == None:
+ p.append(-1)
+ p.append([0, 0, 0, 0, 0, 0, 0])
+ p.append([0, 0, 0, 0, 0, 0, 0])
+ p.append([])
+ p.append([])
+ p.append([])
+ p.append([0, 0, 0, 0])
+ p.append([0, 0, 0, 0])
+ p.append([0, 0, 0, 0])
+ else:
+ p.append(toonId)
+ origExp = toonExp[toonId]
+ earnedExp = []
+ for i in range(len(ToontownBattleGlobals.Tracks)):
+ earnedExp.append(getSkillGained(toonSkillPtsGained, toonId, i))
+
+ p.append(origExp)
+ p.append(earnedExp)
+ origQuests = toonOrigQuests.get(toonId, [])
+ p.append(origQuests)
+ items = toonItems.get(toonId, ([], []))
+ p.append(items[0])
+ p.append(items[1])
+ origMerits = toonOrigMerits.get(toonId, [])
+ p.append(origMerits)
+ merits = toonMerits.get(toonId, [0, 0, 0, 0])
+ p.append(merits)
+ parts = toonParts.get(toonId, [0, 0, 0, 0])
+ p.append(parts)
+
+ deathList = []
+ toonIndices = {}
+ for i in range(len(activeToons)):
+ toonIndices[activeToons[i]] = i
+
+ for deathRecord in suitsKilled:
+ level = deathRecord['level']
+ type = deathRecord['type']
+ if deathRecord['isVP'] or deathRecord['isCFO']:
+ level = 0
+ typeNum = SuitDNA.suitDepts.index(deathRecord['track'])
+ else:
+ typeNum = SuitDNA.suitHeadTypes.index(type)
+ involvedToonIds = deathRecord['activeToons']
+ toonBits = 0
+ for toonId in involvedToonIds:
+ if toonId in toonIndices:
+ toonBits |= 1 << toonIndices[toonId]
+
+ flags = 0
+ if deathRecord['isSkelecog']:
+ flags |= ToontownBattleGlobals.DLF_SKELECOG
+ if deathRecord['isForeman']:
+ flags |= ToontownBattleGlobals.DLF_FOREMAN
+ if deathRecord['isVP']:
+ flags |= ToontownBattleGlobals.DLF_VP
+ if deathRecord['isCFO']:
+ flags |= ToontownBattleGlobals.DLF_CFO
+ if deathRecord['isSupervisor']:
+ flags |= ToontownBattleGlobals.DLF_SUPERVISOR
+ if deathRecord['isVirtual']:
+ flags |= ToontownBattleGlobals.DLF_VIRTUAL
+ if 'hasRevies' in deathRecord and deathRecord['hasRevives']:
+ flags |= ToontownBattleGlobals.DLF_REVIVES
+ deathList.extend([typeNum, level, toonBits, flags])
+
+ p.append(deathList)
+ uberStats = getToonUberStatus(activeToons, numToons)
+ p.append(uberStats)
+ if helpfulToonsList == None:
+ helpfulToonsList = []
+ p.append(helpfulToonsList)
+ return p
+
+
+def getToonUberStatus(toons, numToons):
+ fieldList = []
+ uberIndex = ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1
+ for toonId in toons:
+ toonList = []
+ toon = simbase.air.doId2do.get(toonId)
+ if toon == None:
+ fieldList.append(-1)
+ else:
+ for trackIndex in range(ToontownBattleGlobals.MAX_TRACK_INDEX + 1):
+ toonList.append(toon.inventory.numItem(trackIndex, uberIndex))
+
+ fieldList.append(ToontownBattleGlobals.encodeUber(toonList))
+
+ lenDif = numToons - len(toons)
+ if lenDif > 0:
+ for index in range(lenDif):
+ fieldList.append(-1)
+
+ return fieldList
+
+
+def assignRewards(activeToons, toonSkillPtsGained, suitsKilled, zoneId, helpfulToons = None):
+ if helpfulToons == None:
+ BattleExperienceAINotify.warning('=============\nERROR ERROR helpfulToons=None in assignRewards , tell Red')
+ activeToonList = []
+ for t in activeToons:
+ toon = simbase.air.doId2do.get(t)
+ if toon != None:
+ activeToonList.append(toon)
+
+ for toon in activeToonList:
+ for i in range(len(ToontownBattleGlobals.Tracks)):
+ uberIndex = ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1
+ exp = getSkillGained(toonSkillPtsGained, toon.doId, i)
+ needed = ToontownBattleGlobals.Levels[i][ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1] + ToontownBattleGlobals.UberSkill
+ hasUber = 0
+ totalExp = exp + toon.experience.getExp(i)
+ if toon.inventory.numItem(i, uberIndex) > 0:
+ hasUber = 1
+ if totalExp >= needed or totalExp >= ToontownBattleGlobals.MaxSkill:
+ if toon.inventory.totalProps < toon.getMaxCarry() and not hasUber:
+ uberLevel = ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1
+ toon.inventory.addItem(i, uberLevel)
+ toon.experience.setExp(i, ToontownBattleGlobals.Levels[i][ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1])
+ else:
+ toon.experience.setExp(i, ToontownBattleGlobals.MaxSkill)
+ else:
+ if exp > 0:
+ newGagList = toon.experience.getNewGagIndexList(i, exp)
+ toon.experience.addExp(i, amount=exp)
+ toon.inventory.addItemWithList(i, newGagList)
+ toon.b_setExperience(toon.experience.makeNetString())
+ toon.d_setInventory(toon.inventory.makeNetString())
+ toon.b_setAnimState('victory', 1)
+
+ if simbase.air.config.GetBool('battle-passing-no-credit', True):
+ if helpfulToons and toon.doId in helpfulToons:
+ simbase.air.questManager.toonKilledCogs(toon, suitsKilled, zoneId, activeToonList)
+ simbase.air.cogPageManager.toonKilledCogs(toon, suitsKilled, zoneId)
+ else:
+ BattleExperienceAINotify.debug('toon=%d unhelpful not getting killed cog quest credit' % toon.doId)
+ else:
+ simbase.air.questManager.toonKilledCogs(toon, suitsKilled, zoneId, activeToonList)
+ simbase.air.cogPageManager.toonKilledCogs(toon, suitsKilled, zoneId)
+
+ return
diff --git a/toontown/battle/BattleManagerAI.py b/toontown/battle/BattleManagerAI.py
new file mode 100644
index 0000000..bff96cd
--- /dev/null
+++ b/toontown/battle/BattleManagerAI.py
@@ -0,0 +1,41 @@
+import DistributedBattleAI
+from direct.directnotify import DirectNotifyGlobal
+
+class BattleManagerAI:
+ notify = DirectNotifyGlobal.directNotify.newCategory('BattleManagerAI')
+
+ def __init__(self, air):
+ self.air = air
+ self.cellId2battle = {}
+ self.battleConstructor = DistributedBattleAI.DistributedBattleAI
+
+ def cellHasBattle(self, cellId):
+ return self.cellId2battle.has_key(cellId)
+
+ def getBattle(self, cellId):
+ if self.cellId2battle.has_key(cellId):
+ return self.cellId2battle[cellId]
+ return None
+
+ def newBattle(self, cellId, zoneId, pos, suit, toonId, finishCallback=None, maxSuits=4, interactivePropTrackBonus=-1):
+ if self.cellId2battle.has_key(cellId):
+ self.notify.info("A battle is already present in the suit's zone!")
+ if not self.requestBattleAddSuit(cellId, suit):
+ suit.flyAwayNow()
+ battle = self.cellId2battle[cellId]
+ battle.signupToon(toonId, pos[0], pos[1], pos[2])
+ else:
+ battle = self.battleConstructor(self.air, self, pos, suit, toonId, zoneId, finishCallback, maxSuits, interactivePropTrackBonus=interactivePropTrackBonus)
+ battle.generateWithRequired(zoneId)
+ battle.battleCellId = cellId
+ self.cellId2battle[cellId] = battle
+ return battle
+
+ def requestBattleAddSuit(self, cellId, suit):
+ return self.cellId2battle[cellId].suitRequestJoin(suit)
+
+ def destroy(self, battle):
+ cellId = battle.battleCellId
+ self.notify.debug('BattleManager - destroying battle %d' % cellId)
+ del self.cellId2battle[cellId]
+ battle.requestDelete()
diff --git a/toontown/battle/BattleParticles.py b/toontown/battle/BattleParticles.py
new file mode 100644
index 0000000..fb32da0
--- /dev/null
+++ b/toontown/battle/BattleParticles.py
@@ -0,0 +1,175 @@
+from direct.particles.ParticleEffect import *
+import os
+from direct.directnotify import DirectNotifyGlobal
+from direct.showbase import AppRunnerGlobal
+notify = DirectNotifyGlobal.directNotify.newCategory('BattleParticles')
+TutorialParticleEffects = ('gearExplosionBig.ptf', 'gearExplosionSmall.ptf', 'gearExplosion.ptf')
+ParticleNames = ('audit-div', 'audit-five', 'audit-four', 'audit-minus', 'audit-mult', 'audit-one', 'audit-plus', 'audit-six', 'audit-three', 'audit-two', 'blah', 'brainstorm-box', 'brainstorm-env', 'brainstorm-track', 'buzzwords-crash', 'buzzwords-inc', 'buzzwords-main', 'buzzwords-over', 'buzzwords-syn', 'confetti', 'doubletalk-double', 'doubletalk-dup', 'doubletalk-good', 'filibuster-cut', 'filibuster-fiscal', 'filibuster-impeach', 'filibuster-inc', 'jargon-brow', 'jargon-deep', 'jargon-hoop', 'jargon-ipo', 'legalese-hc', 'legalese-qpq', 'legalese-vd', 'mumbojumbo-boiler', 'mumbojumbo-creative', 'mumbojumbo-deben', 'mumbojumbo-high', 'mumbojumbo-iron', 'poundsign', 'schmooze-genius', 'schmooze-instant', 'schmooze-master', 'schmooze-viz', 'roll-o-dex', 'rollodex-card', 'dagger', 'fire', 'snow-particle', 'raindrop', 'gear', 'checkmark', 'dollar-sign', 'spark')
+particleModel = None
+particleSearchPath = None
+
+def loadParticles():
+ global particleModel
+ if particleModel == None:
+ particleModel = loader.loadModel('phase_3.5/models/props/suit-particles')
+ return
+
+
+def unloadParticles():
+ global particleModel
+ if particleModel != None:
+ particleModel.removeNode()
+ del particleModel
+ particleModel = None
+ return
+
+
+def getParticle(name):
+ if name in ParticleNames:
+ particle = particleModel.find('**/' + str(name))
+ return particle
+ else:
+ notify.warning('getParticle() - no name: %s' % name)
+ return None
+ return None
+
+
+def loadParticleFile(name):
+ global particleSearchPath
+ if particleSearchPath == None:
+ particleSearchPath = DSearchPath()
+ if AppRunnerGlobal.appRunner:
+ particleSearchPath.appendDirectory(Filename.expandFrom('$TT_3_5_ROOT/phase_3.5/etc'))
+ else:
+ basePath = os.path.expandvars('$TOONTOWN') or './toontown'
+ particleSearchPath.appendDirectory(Filename.fromOsSpecific(basePath + '/src/battle'))
+ particleSearchPath.appendDirectory(Filename.fromOsSpecific(basePath + '/src/safezone'))
+ particleSearchPath.appendDirectory(Filename('phase_3.5/etc'))
+ particleSearchPath.appendDirectory(Filename('phase_4/etc'))
+ particleSearchPath.appendDirectory(Filename('phase_5/etc'))
+ particleSearchPath.appendDirectory(Filename('phase_8/etc'))
+ particleSearchPath.appendDirectory(Filename('phase_9/etc'))
+ particleSearchPath.appendDirectory(Filename('.'))
+
+ pfile = Filename(name)
+ found = vfs.resolveFilename(pfile, particleSearchPath)
+ if not found:
+ notify.warning('loadParticleFile() - no path: %s' % name)
+ return
+ notify.debug('Loading particle file: %s' % pfile)
+ effect = ParticleEffect()
+ effect.loadConfig(pfile)
+ return effect
+
+
+def createParticleEffect(name = None, file = None, numParticles = None, color = None):
+ if not name:
+ fileName = file + '.ptf'
+ return loadParticleFile(fileName)
+ if name == 'GearExplosion':
+ return __makeGearExplosion(numParticles)
+ elif name == 'BigGearExplosion':
+ return __makeGearExplosion(numParticles, 'Big')
+ elif name == 'WideGearExplosion':
+ return __makeGearExplosion(numParticles, 'Wide')
+ elif name == 'BrainStorm':
+ return loadParticleFile('brainStorm.ptf')
+ elif name == 'BuzzWord':
+ return loadParticleFile('buzzWord.ptf')
+ elif name == 'Calculate':
+ return loadParticleFile('calculate.ptf')
+ elif name == 'Confetti':
+ return loadParticleFile('confetti.ptf')
+ elif name == 'DemotionFreeze':
+ return loadParticleFile('demotionFreeze.ptf')
+ elif name == 'DemotionSpray':
+ return loadParticleFile('demotionSpray.ptf')
+ elif name == 'DoubleTalkLeft':
+ return loadParticleFile('doubleTalkLeft.ptf')
+ elif name == 'DoubleTalkRight':
+ return loadParticleFile('doubleTalkRight.ptf')
+ elif name == 'FingerWag':
+ return loadParticleFile('fingerwag.ptf')
+ elif name == 'FiredFlame':
+ return loadParticleFile('firedFlame.ptf')
+ elif name == 'FreezeAssets':
+ return loadParticleFile('freezeAssets.ptf')
+ elif name == 'GlowerPower':
+ return loadParticleFile('glowerPowerKnives.ptf')
+ elif name == 'HotAir':
+ return loadParticleFile('hotAirSpray.ptf')
+ elif name == 'PoundKey':
+ return loadParticleFile('poundkey.ptf')
+ elif name == 'ShiftSpray':
+ return loadParticleFile('shiftSpray.ptf')
+ elif name == 'ShiftLift':
+ return __makeShiftLift()
+ elif name == 'Shred':
+ return loadParticleFile('shred.ptf')
+ elif name == 'Smile':
+ return loadParticleFile('smile.ptf')
+ elif name == 'SpriteFiredFlecks':
+ return loadParticleFile('spriteFiredFlecks.ptf')
+ elif name == 'Synergy':
+ return loadParticleFile('synergy.ptf')
+ elif name == 'Waterfall':
+ return loadParticleFile('waterfall.ptf')
+ elif name == 'PoundKey':
+ return loadParticleFile('poundkey.ptf')
+ elif name == 'RubOut':
+ return __makeRubOut(color)
+ elif name == 'SplashLines':
+ return loadParticleFile('splashlines.ptf')
+ elif name == 'Withdrawal':
+ return loadParticleFile('withdrawal.ptf')
+ else:
+ notify.warning('createParticleEffect() - no name: %s' % name)
+ return None
+
+
+def setEffectTexture(effect, name, color = None):
+ particles = effect.getParticlesNamed('particles-1')
+ np = getParticle(name)
+ if color:
+ particles.renderer.setColor(color)
+ particles.renderer.setFromNode(np)
+
+
+def __makeGearExplosion(numParticles = None, style = 'Normal'):
+ if style == 'Normal':
+ effect = loadParticleFile('gearExplosion.ptf')
+ elif style == 'Big':
+ effect = loadParticleFile('gearExplosionBig.ptf')
+ elif style == 'Wide':
+ effect = loadParticleFile('gearExplosionWide.ptf')
+ if numParticles:
+ particles = effect.getParticlesNamed('particles-1')
+ particles.setPoolSize(numParticles)
+ return effect
+
+
+def __makeRubOut(color = None):
+ effect = loadParticleFile('demotionUnFreeze.ptf')
+ loadParticles()
+ setEffectTexture(effect, 'snow-particle')
+ particles = effect.getParticlesNamed('particles-1')
+ particles.renderer.setInitialXScale(0.03)
+ particles.renderer.setFinalXScale(0.0)
+ particles.renderer.setInitialYScale(0.02)
+ particles.renderer.setFinalYScale(0.0)
+ if color:
+ particles.renderer.setColor(color)
+ else:
+ particles.renderer.setColor(Vec4(0.54, 0.92, 0.32, 0.7))
+ return effect
+
+
+def __makeShiftLift():
+ effect = loadParticleFile('pixieDrop.ptf')
+ particles = effect.getParticlesNamed('particles-1')
+ particles.renderer.setCenterColor(Vec4(1, 1, 0, 0.9))
+ particles.renderer.setEdgeColor(Vec4(1, 1, 0, 0.6))
+ particles.emitter.setRadius(0.01)
+ effect.setHpr(0, 180, 0)
+ effect.setPos(0, 0, 0)
+ return effect
diff --git a/toontown/battle/BattlePlace.py b/toontown/battle/BattlePlace.py
new file mode 100644
index 0000000..57f11aa
--- /dev/null
+++ b/toontown/battle/BattlePlace.py
@@ -0,0 +1,100 @@
+from pandac.PandaModules import *
+from toontown.toon import Toon
+from toontown.hood import Place
+
+class BattlePlace(Place.Place):
+
+ def __init__(self, loader, doneEvent):
+ Place.Place.__init__(self, loader, doneEvent)
+
+ def load(self):
+ Place.Place.load(self)
+ Toon.loadBattleAnims()
+
+ def setState(self, state, battleEvent = None):
+ if battleEvent:
+ if not self.fsm.request(state, [battleEvent]):
+ self.notify.warning("fsm.request('%s') returned 0 (zone id %s, avatar pos %s)." % (state, self.zoneId, base.localAvatar.getPos(render)))
+ elif not self.fsm.request(state):
+ self.notify.warning("fsm.request('%s') returned 0 (zone id %s, avatar pos %s)." % (state, self.zoneId, base.localAvatar.getPos(render)))
+
+ def enterWalk(self, flag = 0):
+ Place.Place.enterWalk(self, flag)
+ self.accept('enterBattle', self.handleBattleEntry)
+
+ def exitWalk(self):
+ Place.Place.exitWalk(self)
+ self.ignore('enterBattle')
+
+ def enterWaitForBattle(self):
+ base.localAvatar.b_setAnimState('neutral', 1)
+
+ def exitWaitForBattle(self):
+ pass
+
+ def enterBattle(self, event):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: COGBATTLE: Enter Battle')
+ self.loader.music.stop()
+ base.playMusic(self.loader.battleMusic, looping=1, volume=0.9)
+ self.enterTownBattle(event)
+ base.localAvatar.b_setAnimState('off', 1)
+ self.accept('teleportQuery', self.handleTeleportQuery)
+ base.localAvatar.setTeleportAvailable(1)
+ base.localAvatar.cantLeaveGame = 1
+
+ def enterTownBattle(self, event):
+ self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle'))
+
+ def exitBattle(self):
+ self.loader.townBattle.exit()
+ self.loader.battleMusic.stop()
+ base.playMusic(self.loader.music, looping=1, volume=0.8)
+ base.localAvatar.cantLeaveGame = 0
+ base.localAvatar.setTeleportAvailable(0)
+ self.ignore('teleportQuery')
+
+ def handleBattleEntry(self):
+ self.fsm.request('battle')
+
+ def enterFallDown(self, extraArgs = []):
+ base.localAvatar.laffMeter.start()
+ base.localAvatar.b_setAnimState('FallDown', callback=self.handleFallDownDone, extraArgs=extraArgs)
+
+ def handleFallDownDone(self):
+ base.cr.playGame.getPlace().setState('walk')
+
+ def exitFallDown(self):
+ base.localAvatar.laffMeter.stop()
+
+ def enterSquished(self):
+ base.localAvatar.laffMeter.start()
+ base.localAvatar.b_setAnimState('Squish')
+ taskMgr.doMethodLater(2.0, self.handleSquishDone, base.localAvatar.uniqueName('finishSquishTask'))
+
+ def handleSquishDone(self, extraArgs = []):
+ base.cr.playGame.getPlace().setState('walk')
+
+ def exitSquished(self):
+ taskMgr.remove(base.localAvatar.uniqueName('finishSquishTask'))
+ base.localAvatar.laffMeter.stop()
+
+ def enterZone(self, newZone):
+ if isinstance(newZone, CollisionEntry):
+ try:
+ newZoneId = int(newZone.getIntoNode().getName())
+ except:
+ self.notify.warning('Invalid floor collision node in street: %s' % newZone.getIntoNode().getName())
+ return
+
+ else:
+ newZoneId = newZone
+ self.doEnterZone(newZoneId)
+
+ def doEnterZone(self, newZoneId):
+ if newZoneId != self.zoneId:
+ if newZoneId != None:
+ base.cr.sendSetZoneMsg(newZoneId)
+ self.notify.debug('Entering Zone %d' % newZoneId)
+ self.zoneId = newZoneId
+ return
diff --git a/toontown/battle/BattleProps.py b/toontown/battle/BattleProps.py
new file mode 100644
index 0000000..2879c35
--- /dev/null
+++ b/toontown/battle/BattleProps.py
@@ -0,0 +1,435 @@
+from pandac.PandaModules import *
+from direct.actor import Actor
+from direct.directnotify import DirectNotifyGlobal
+from otp.otpbase import OTPGlobals
+import random
+Props = ((5, 'partyBall', 'partyBall'),
+ (5,
+ 'feather',
+ 'feather-mod',
+ 'feather-chan'),
+ (5, 'lips', 'lips'),
+ (5, 'lipstick', 'lipstick'),
+ (5, 'hat', 'hat'),
+ (5, 'cane', 'cane'),
+ (5,
+ 'cubes',
+ 'cubes-mod',
+ 'cubes-chan'),
+ (5, 'ladder', 'ladder2'),
+ (4,
+ 'fishing-pole',
+ 'fishing-pole-mod',
+ 'fishing-pole-chan'),
+ (5,
+ '1dollar',
+ '1dollar-bill-mod',
+ '1dollar-bill-chan'),
+ (5, 'big-magnet', 'magnet'),
+ (5,
+ 'hypno-goggles',
+ 'hypnotize-mod',
+ 'hypnotize-chan'),
+ (5, 'slideshow', 'av_screen'),
+ (5,
+ 'banana',
+ 'banana-peel-mod',
+ 'banana-peel-chan'),
+ (5,
+ 'rake',
+ 'rake-mod',
+ 'rake-chan'),
+ (5,
+ 'marbles',
+ 'marbles-mod',
+ 'marbles-chan'),
+ (5,
+ 'tnt',
+ 'tnt-mod',
+ 'tnt-chan'),
+ (5, 'trapdoor', 'trapdoor'),
+ (5, 'quicksand', 'quicksand'),
+ (5, 'traintrack', 'traintrack2'),
+ (5, 'train', 'train'),
+ (5, 'megaphone', 'megaphone'),
+ (5, 'aoogah', 'aoogah'),
+ (5, 'bikehorn', 'bikehorn'),
+ (5, 'bugle', 'bugle'),
+ (5, 'elephant', 'elephant'),
+ (5, 'fog_horn', 'fog_horn'),
+ (5, 'whistle', 'whistle'),
+ (5, 'singing', 'singing'),
+ (3.5, 'creampie', 'tart'),
+ (5, 'fruitpie-slice', 'fruit-pie-slice'),
+ (5, 'creampie-slice', 'cream-pie-slice'),
+ (5,
+ 'birthday-cake',
+ 'birthday-cake-mod',
+ 'birthday-cake-chan'),
+ (5, 'wedding-cake', 'wedding_cake'),
+ (3.5, 'squirting-flower', 'squirting-flower'),
+ (5,
+ 'glass',
+ 'glass-mod',
+ 'glass-chan'),
+ (4, 'water-gun', 'water-gun'),
+ (3.5, 'bottle', 'bottle'),
+ (5,
+ 'firehose',
+ 'firehose-mod',
+ 'firehose-chan'),
+ (5, 'hydrant', 'battle_hydrant'),
+ (4,
+ 'stormcloud',
+ 'stormcloud-mod',
+ 'stormcloud-chan'),
+ (5, 'geyser', 'geyser'),
+ (3.5, 'button', 'button'),
+ (5,
+ 'flowerpot',
+ 'flowerpot-mod',
+ 'flowerpot-chan'),
+ (5,
+ 'sandbag',
+ 'sandbag-mod',
+ 'sandbag-chan'),
+ (4,
+ 'anvil',
+ 'anvil-mod',
+ 'anvil-chan'),
+ (5,
+ 'weight',
+ 'weight-mod',
+ 'weight-chan'),
+ (5,
+ 'safe',
+ 'safe-mod',
+ 'safe-chan'),
+ (5,
+ 'piano',
+ 'piano-mod',
+ 'piano-chan'),
+ (5,
+ 'rake-react',
+ 'rake-step-mod',
+ 'rake-step-chan'),
+ (5, 'pad', 'pad'),
+ (4,
+ 'propeller',
+ 'propeller-mod',
+ 'propeller-chan'),
+ (5,
+ 'calculator',
+ 'calculator-mod',
+ 'calculator-chan'),
+ (5, 'rollodex', 'roll-o-dex'),
+ (5, 'rubber-stamp', 'rubber-stamp'),
+ (5,
+ 'rubber-stamp-pad',
+ 'rubber-stamp-pad-mod',
+ 'rubber-stamp-pad-chan'),
+ (5,
+ 'smile',
+ 'smile-mod',
+ 'smile-chan'),
+ (5, 'golf-club', 'golf-club'),
+ (5, 'golf-ball', 'golf-ball'),
+ (5, 'redtape', 'redtape'),
+ (5, 'redtape-tube', 'redtape-tube'),
+ (5, 'bounced-check', 'bounced-check'),
+ (5,
+ 'calculator',
+ 'calculator-mod',
+ 'calculator-chan'),
+ (3.5,
+ 'clip-on-tie',
+ 'clip-on-tie-mod',
+ 'clip-on-tie-chan'),
+ (5, 'pen', 'pen'),
+ (5, 'pencil', 'pencil'),
+ (3.5, 'phone', 'phone'),
+ (3.5, 'receiver', 'receiver'),
+ (5, 'sharpener', 'sharpener'),
+ (3.5, 'shredder', 'shredder'),
+ (3.5,
+ 'shredder-paper',
+ 'shredder-paper-mod',
+ 'shredder-paper-chan'),
+ (5, 'watercooler', 'watercooler'),
+ (5, 'dagger', 'dagger'),
+ (5, 'card', 'card'),
+ (5, 'baseball', 'baseball'),
+ (5, 'bird', 'bird'),
+ (5, 'can', 'can'),
+ (5, 'cigar', 'cigar'),
+ (5, 'evil-eye', 'evil-eye'),
+ (5, 'gavel', 'gavel'),
+ (5, 'half-windsor', 'half-windsor'),
+ (5, 'lawbook', 'lawbook'),
+ (5, 'newspaper', 'newspaper'),
+ (5, 'pink-slip', 'pink-slip'),
+ (5,
+ 'teeth',
+ 'teeth-mod',
+ 'teeth-chan'),
+ (5, 'power-tie', 'power-tie'),
+ (3.5, 'spray', 'spray'),
+ (3.5, 'splash', 'splash'),
+ (3.5,
+ 'splat',
+ 'splat-mod',
+ 'splat-chan'),
+ (3.5,
+ 'stun',
+ 'stun-mod',
+ 'stun-chan'),
+ (3.5, 'glow', 'glow'),
+ (3.5,
+ 'suit_explosion',
+ 'suit_explosion-mod',
+ 'suit_explosion-chan'),
+ (3.5, 'suit_explosion_dust', 'dust_cloud'),
+ (4, 'ripples', 'ripples'),
+ (4, 'wake', 'wake'),
+ (4,
+ 'splashdown',
+ 'SZ_splashdown-mod',
+ 'SZ_splashdown-chan'))
+CreampieColor = VBase4(250.0 / 255.0, 241.0 / 255.0, 24.0 / 255.0, 1.0)
+FruitpieColor = VBase4(55.0 / 255.0, 40.0 / 255.0, 148.0 / 255.0, 1.0)
+BirthdayCakeColor = VBase4(253.0 / 255.0, 119.0 / 255.0, 220.0 / 255.0, 1.0)
+Splats = {'tart': (0.3, FruitpieColor),
+ 'fruitpie-slice': (0.5, FruitpieColor),
+ 'creampie-slice': (0.5, CreampieColor),
+ 'fruitpie': (0.7, FruitpieColor),
+ 'creampie': (0.7, CreampieColor),
+ 'birthday-cake': (0.9, BirthdayCakeColor)}
+Variants = ('tart',
+ 'fruitpie',
+ 'splat-tart',
+ 'dust',
+ 'kapow',
+ 'double-windsor',
+ 'splat-fruitpie-slice',
+ 'splat-creampie-slice',
+ 'splat-fruitpie',
+ 'splat-creampie',
+ 'splat-birthday-cake',
+ 'splash-from-splat',
+ 'clip-on-tie',
+ 'lips',
+ 'small-magnet',
+ '5dollar',
+ '10dollar',
+ 'suit_explosion',
+ 'quicksand',
+ 'trapdoor',
+ 'geyser',
+ 'ship',
+ 'trolley',
+ 'traintrack')
+
+class PropPool:
+ notify = DirectNotifyGlobal.directNotify.newCategory('PropPool')
+
+ def __init__(self):
+ self.props = {}
+ self.propCache = []
+ self.propStrings = {}
+ self.propTypes = {}
+ self.maxPoolSize = base.config.GetInt('prop-pool-size', 8)
+ for p in Props:
+ phase = p[0]
+ propName = p[1]
+ modelName = p[2]
+ if len(p) == 4:
+ animName = p[3]
+ propPath = self.getPath(phase, modelName)
+ animPath = self.getPath(phase, animName)
+ self.propTypes[propName] = 'actor'
+ self.propStrings[propName] = (propPath, animPath)
+ else:
+ propPath = self.getPath(phase, modelName)
+ self.propTypes[propName] = 'model'
+ self.propStrings[propName] = (propPath,)
+
+ propName = 'tart'
+ self.propStrings[propName] = (self.getPath(3.5, 'tart'),)
+ self.propTypes[propName] = 'model'
+ propName = 'fruitpie'
+ self.propStrings[propName] = (self.getPath(3.5, 'tart'),)
+ self.propTypes[propName] = 'model'
+ propName = 'double-windsor'
+ self.propStrings[propName] = (self.getPath(5, 'half-windsor'),)
+ self.propTypes[propName] = 'model'
+ splatAnimFileName = self.getPath(3.5, 'splat-chan')
+ for splat in Splats.keys():
+ propName = 'splat-' + splat
+ self.propStrings[propName] = (self.getPath(3.5, 'splat-mod'), splatAnimFileName)
+ self.propTypes[propName] = 'actor'
+
+ propName = 'splash-from-splat'
+ self.propStrings[propName] = (self.getPath(3.5, 'splat-mod'), splatAnimFileName)
+ self.propTypes[propName] = 'actor'
+ propName = 'small-magnet'
+ self.propStrings[propName] = (self.getPath(5, 'magnet'),)
+ self.propTypes[propName] = 'model'
+ propName = '5dollar'
+ self.propStrings[propName] = (self.getPath(5, '1dollar-bill-mod'), self.getPath(5, '1dollar-bill-chan'))
+ self.propTypes[propName] = 'actor'
+ propName = '10dollar'
+ self.propStrings[propName] = (self.getPath(5, '1dollar-bill-mod'), self.getPath(5, '1dollar-bill-chan'))
+ self.propTypes[propName] = 'actor'
+ propName = 'dust'
+ self.propStrings[propName] = (self.getPath(5, 'dust-mod'), self.getPath(5, 'dust-chan'))
+ self.propTypes[propName] = 'actor'
+ propName = 'kapow'
+ self.propStrings[propName] = (self.getPath(5, 'kapow-mod'), self.getPath(5, 'kapow-chan'))
+ self.propTypes[propName] = 'actor'
+ propName = 'ship'
+ self.propStrings[propName] = ('phase_5/models/props/ship.bam',)
+ self.propTypes[propName] = 'model'
+ propName = 'trolley'
+ self.propStrings[propName] = ('phase_4/models/modules/trolley_station_TT',)
+ self.propTypes[propName] = 'model'
+
+ def getPath(self, phase, model):
+ return 'phase_%s/models/props/%s' % (phase, model)
+
+ def makeVariant(self, name):
+ if name == 'tart':
+ self.props[name].setScale(0.5)
+ elif name == 'fruitpie':
+ self.props[name].setScale(0.75)
+ elif name == 'double-windsor':
+ self.props[name].setScale(1.5)
+ elif name[:6] == 'splat-':
+ prop = self.props[name]
+ scale = prop.getScale() * Splats[name[6:]][0]
+ prop.setScale(scale)
+ prop.setColor(Splats[name[6:]][1])
+ elif name == 'splash-from-splat':
+ self.props[name].setColor(0.75, 0.75, 1.0, 1.0)
+ elif name == 'clip-on-tie':
+ tie = self.props[name]
+ tie.getChild(0).setHpr(23.86, -16.03, 9.18)
+ elif name == 'small-magnet':
+ self.props[name].setScale(0.5)
+ elif name == 'shredder-paper':
+ paper = self.props[name]
+ paper.setPosHpr(2.22, -0.95, 1.16, -48.61, 26.57, -111.51)
+ paper.flattenMedium()
+ elif name == 'lips':
+ lips = self.props[name]
+ lips.setPos(0, 0, -3.04)
+ lips.flattenMedium()
+ elif name == '5dollar':
+ tex = loader.loadTexture('phase_5/maps/dollar_5.jpg')
+ tex.setMinfilter(Texture.FTLinearMipmapLinear)
+ tex.setMagfilter(Texture.FTLinear)
+ self.props[name].setTexture(tex, 1)
+ elif name == '10dollar':
+ tex = loader.loadTexture('phase_5/maps/dollar_10.jpg')
+ tex.setMinfilter(Texture.FTLinearMipmapLinear)
+ tex.setMagfilter(Texture.FTLinear)
+ self.props[name].setTexture(tex, 1)
+ elif name == 'dust':
+ bin = 110
+ for cloudNum in range(1, 12):
+ cloudName = '**/cloud' + str(cloudNum)
+ cloud = self.props[name].find(cloudName)
+ cloud.setBin('fixed', bin)
+ bin -= 10
+
+ elif name == 'kapow':
+ l = self.props[name].find('**/letters')
+ l.setBin('fixed', 20)
+ e = self.props[name].find('**/explosion')
+ e.setBin('fixed', 10)
+ elif name == 'suit_explosion':
+ joints = ['**/joint_scale_POW', '**/joint_scale_BLAM', '**/joint_scale_BOOM']
+ joint = random.choice(joints)
+ self.props[name].find(joint).hide()
+ joints.remove(joint)
+ joint = random.choice(joints)
+ self.props[name].find(joint).hide()
+ elif name == 'quicksand' or name == 'trapdoor':
+ p = self.props[name]
+ p.setBin('shadow', -5)
+ p.setDepthWrite(0)
+ p.getChild(0).setPos(0, 0, OTPGlobals.FloorOffset)
+ elif name == 'traintrack' or name == 'traintrack2':
+ prop = self.props[name]
+ prop.find('**/tunnel3').hide()
+ prop.find('**/tunnel2').hide()
+ prop.find('**/tracksA').setPos(0, 0, OTPGlobals.FloorOffset)
+ elif name == 'geyser':
+ p = self.props[name]
+ s = SequenceNode('geyser')
+ p.findAllMatches('**/Splash*').reparentTo(NodePath(s))
+ s.loop(0)
+ s.setFrameRate(12)
+ p.attachNewNode(s)
+ elif name == 'ship':
+ self.props[name] = self.props[name].find('**/ship_gag')
+ elif name == 'trolley':
+ self.props[name] = self.props[name].find('**/trolley_car')
+
+ def unloadProps(self):
+ for p in self.props.values():
+ if type(p) != type(()):
+ self.__delProp(p)
+
+ self.props = {}
+ self.propCache = []
+
+ def getProp(self, name):
+ return self.__getPropCopy(name)
+
+ def __getPropCopy(self, name):
+ if self.propTypes[name] == 'actor':
+ if not self.props.has_key(name):
+ prop = Actor.Actor()
+ prop.loadModel(self.propStrings[name][0])
+ animDict = {}
+ animDict[name] = self.propStrings[name][1]
+ prop.loadAnims(animDict)
+ prop.setName(name)
+ self.storeProp(name, prop)
+ if name in Variants:
+ self.makeVariant(name)
+ return Actor.Actor(other=self.props[name])
+ else:
+ if not self.props.has_key(name):
+ prop = loader.loadModel(self.propStrings[name][0])
+ prop.setName(name)
+ self.storeProp(name, prop)
+ if name in Variants:
+ self.makeVariant(name)
+ return self.props[name].copyTo(hidden)
+
+ def storeProp(self, name, prop):
+ self.props[name] = prop
+ self.propCache.append(prop)
+ if len(self.props) > self.maxPoolSize:
+ oldest = self.propCache.pop(0)
+ del self.props[oldest.getName()]
+ self.__delProp(oldest)
+ self.notify.debug('props = %s' % self.props)
+ self.notify.debug('propCache = %s' % self.propCache)
+
+ def getPropType(self, name):
+ return self.propTypes[name]
+
+ def __delProp(self, prop):
+ if prop == None:
+ self.notify.warning('tried to delete null prop!')
+ return
+ if isinstance(prop, Actor.Actor):
+ prop.cleanup()
+ else:
+ prop.removeNode()
+ return
+
+
+globalPropPool = PropPool()
diff --git a/toontown/battle/BattleSounds.py b/toontown/battle/BattleSounds.py
new file mode 100644
index 0000000..ba88dbc
--- /dev/null
+++ b/toontown/battle/BattleSounds.py
@@ -0,0 +1,56 @@
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.showbase import AppRunnerGlobal
+import os
+
+class BattleSounds:
+ notify = DirectNotifyGlobal.directNotify.newCategory('BattleSounds')
+
+ def __init__(self):
+ self.mgr = AudioManager.createAudioManager()
+ self.isValid = 0
+ if self.mgr != None and self.mgr.isValid():
+ self.isValid = 1
+ limit = base.config.GetInt('battle-sound-cache-size', 15)
+ self.mgr.setCacheLimit(limit)
+ base.addSfxManager(self.mgr)
+ self.setupSearchPath()
+ return
+
+ def setupSearchPath(self):
+ self.sfxSearchPath = DSearchPath()
+ if AppRunnerGlobal.appRunner:
+ self.sfxSearchPath.appendDirectory(Filename.expandFrom('$TT_3_ROOT/phase_3/audio/sfx'))
+ self.sfxSearchPath.appendDirectory(Filename.expandFrom('$TT_3_5_ROOT/phase_3.5/audio/sfx'))
+ self.sfxSearchPath.appendDirectory(Filename.expandFrom('$TT_4_ROOT/phase_4/audio/sfx'))
+ self.sfxSearchPath.appendDirectory(Filename.expandFrom('$TT_5_ROOT/phase_5/audio/sfx'))
+ else:
+ self.sfxSearchPath.appendDirectory(Filename('phase_3/audio/sfx'))
+ self.sfxSearchPath.appendDirectory(Filename('phase_3.5/audio/sfx'))
+ self.sfxSearchPath.appendDirectory(Filename('phase_4/audio/sfx'))
+ self.sfxSearchPath.appendDirectory(Filename('phase_5/audio/sfx'))
+ self.sfxSearchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('$TTMODELS/built/phase_3/audio/sfx')))
+ self.sfxSearchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('$TTMODELS/built/phase_3.5/audio/sfx')))
+ self.sfxSearchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('$TTMODELS/built/phase_4/audio/sfx')))
+ self.sfxSearchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('$TTMODELS/built/phase_5/audio/sfx')))
+
+ def clear(self):
+ if self.isValid:
+ self.mgr.clearCache()
+
+ def getSound(self, name):
+ if self.isValid:
+ filename = Filename(name)
+ found = vfs.resolveFilename(filename, self.sfxSearchPath)
+ if not found:
+ self.setupSearchPath()
+ found = vfs.resolveFilename(filename, self.sfxSearchPath)
+ if not found:
+ self.notify.warning('%s not found on:' % name)
+ print self.sfxSearchPath
+ else:
+ return self.mgr.getSound(filename.getFullpath())
+ return self.mgr.getNullSound()
+
+
+globalBattleSoundCache = BattleSounds()
diff --git a/toontown/battle/DistributedBattle.py b/toontown/battle/DistributedBattle.py
new file mode 100644
index 0000000..b0ae897
--- /dev/null
+++ b/toontown/battle/DistributedBattle.py
@@ -0,0 +1,234 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import ToontownBattleGlobals
+import DistributedBattleBase
+from direct.directnotify import DirectNotifyGlobal
+import MovieUtil
+from toontown.suit import Suit
+from direct.actor import Actor
+from toontown.toon import TTEmote
+from otp.avatar import Emote
+import SuitBattleGlobals
+from toontown.distributed import DelayDelete
+import random
+
+class DistributedBattle(DistributedBattleBase.DistributedBattleBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattle')
+ camFOFov = ToontownBattleGlobals.BattleCamFaceOffFov
+ camFOPos = ToontownBattleGlobals.BattleCamFaceOffPos
+ PlayGameSetPlaceEvent = 'playGameSetPlace'
+
+ def __init__(self, cr):
+ townBattle = cr.playGame.hood.loader.townBattle
+ DistributedBattleBase.DistributedBattleBase.__init__(self, cr, townBattle)
+ self.setupCollisions(self.uniqueBattleName('battle-collide'))
+
+ def generate(self):
+ DistributedBattleBase.DistributedBattleBase.generate(self)
+
+ def announceGenerate(self):
+ DistributedBattleBase.DistributedBattleBase.generate(self)
+
+ def disable(self):
+ DistributedBattleBase.DistributedBattleBase.disable(self)
+ self.ignore(self.PlayGameSetPlaceEvent)
+
+ def delete(self):
+ DistributedBattleBase.DistributedBattleBase.delete(self)
+ self.ignore(self.PlayGameSetPlaceEvent)
+ self.removeCollisionData()
+
+ def setInteractivePropTrackBonus(self, trackBonus):
+ DistributedBattleBase.DistributedBattleBase.setInteractivePropTrackBonus(self, trackBonus)
+ if self.interactivePropTrackBonus >= 0:
+ if base.cr.playGame.hood:
+ self.calcInteractiveProp()
+ else:
+ self.acceptOnce(self.PlayGameSetPlaceEvent, self.calcInteractiveProp)
+
+ def calcInteractiveProp(self):
+ if base.cr.playGame.hood:
+ loader = base.cr.playGame.hood.loader
+ if hasattr(loader, 'getInteractiveProp'):
+ self.interactiveProp = loader.getInteractiveProp(self.zoneId)
+ self.notify.debug('self.interactiveProp = %s' % self.interactiveProp)
+ else:
+ self.notify.warning('no loader.getInteractiveProp self.interactiveProp is None')
+ else:
+ self.notify.warning('no hood self.interactiveProp is None')
+
+ def setMembers(self, suits, suitsJoining, suitsPending, suitsActive, suitsLured, suitTraps, toons, toonsJoining, toonsPending, toonsActive, toonsRunning, timestamp):
+ if self.battleCleanedUp():
+ return
+ oldtoons = DistributedBattleBase.DistributedBattleBase.setMembers(self, suits, suitsJoining, suitsPending, suitsActive, suitsLured, suitTraps, toons, toonsJoining, toonsPending, toonsActive, toonsRunning, timestamp)
+ if len(self.toons) == 4 and len(oldtoons) < 4:
+ self.notify.debug('setMembers() - battle is now full of toons')
+ self.closeBattleCollision()
+ elif len(self.toons) < 4 and len(oldtoons) == 4:
+ self.openBattleCollision()
+
+ def __faceOff(self, ts, name, callback):
+ if len(self.suits) == 0:
+ self.notify.warning('__faceOff(): no suits.')
+ return
+ if len(self.toons) == 0:
+ self.notify.warning('__faceOff(): no toons.')
+ return
+ suit = self.suits[0]
+ point = self.suitPoints[0][0]
+ suitPos = point[0]
+ suitHpr = VBase3(point[1], 0.0, 0.0)
+ toon = self.toons[0]
+ point = self.toonPoints[0][0]
+ toonPos = point[0]
+ toonHpr = VBase3(point[1], 0.0, 0.0)
+ p = toon.getPos(self)
+ toon.setPos(self, p[0], p[1], 0.0)
+ toon.setShadowHeight(0)
+ suit.setState('Battle')
+ suitTrack = Sequence()
+ toonTrack = Sequence()
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ suitTrack.append(Func(suit.headsUp, toon))
+ taunt = SuitBattleGlobals.getFaceoffTaunt(suit.getStyleName(), suit.doId)
+ suitTrack.append(Func(suit.setChatAbsolute, taunt, CFSpeech | CFTimeout))
+ toonTrack.append(Func(toon.loop, 'neutral'))
+ toonTrack.append(Func(toon.headsUp, suit))
+ suitHeight = suit.getHeight()
+ suitOffsetPnt = Point3(0, 0, suitHeight)
+ faceoffTime = self.calcFaceoffTime(self.getPos(), self.initialSuitPos)
+ faceoffTime = max(faceoffTime, BATTLE_SMALL_VALUE)
+ delay = FACEOFF_TAUNT_T
+ if self.hasLocalToon():
+ MidTauntCamHeight = suitHeight * 0.66
+ MidTauntCamHeightLim = suitHeight - 1.8
+ if MidTauntCamHeight < MidTauntCamHeightLim:
+ MidTauntCamHeight = MidTauntCamHeightLim
+ TauntCamY = 16
+ TauntCamX = random.choice((-5, 5))
+ TauntCamHeight = random.choice((MidTauntCamHeight, 1, 11))
+ camTrack = Sequence()
+ camTrack.append(Func(camera.wrtReparentTo, suit))
+ camTrack.append(Func(base.camLens.setFov, self.camFOFov))
+ camTrack.append(Func(camera.setPos, TauntCamX, TauntCamY, TauntCamHeight))
+ camTrack.append(Func(camera.lookAt, suit, suitOffsetPnt))
+ camTrack.append(Wait(delay))
+ camTrack.append(Func(base.camLens.setFov, self.camFov))
+ camTrack.append(Func(camera.wrtReparentTo, self))
+ camTrack.append(Func(camera.setPos, self.camFOPos))
+ camTrack.append(Func(camera.lookAt, suit.getPos(self)))
+ camTrack.append(Wait(faceoffTime))
+ if self.interactiveProp:
+ camTrack.append(Func(camera.lookAt, self.interactiveProp.node.getPos(self)))
+ camTrack.append(Wait(FACEOFF_LOOK_AT_PROP_T))
+ suitTrack.append(Wait(delay))
+ toonTrack.append(Wait(delay))
+ suitTrack.append(Func(suit.headsUp, self, suitPos))
+ suitTrack.append(Func(suit.clearChat))
+ toonTrack.append(Func(toon.headsUp, self, toonPos))
+ suitTrack.append(Func(suit.loop, 'walk'))
+ suitTrack.append(LerpPosInterval(suit, faceoffTime, suitPos, other=self))
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ suitTrack.append(Func(suit.setHpr, self, suitHpr))
+ toonTrack.append(Func(toon.loop, 'run'))
+ toonTrack.append(LerpPosInterval(toon, faceoffTime, toonPos, other=self))
+ toonTrack.append(Func(toon.loop, 'neutral'))
+ toonTrack.append(Func(toon.setHpr, self, toonHpr))
+ if base.localAvatar == toon:
+ soundTrack = Sequence(Wait(delay), SoundInterval(base.localAvatar.soundRun, loop=1, duration=faceoffTime, node=base.localAvatar))
+ else:
+ soundTrack = Wait(delay + faceoffTime)
+ mtrack = Parallel(suitTrack, toonTrack, soundTrack)
+ if self.hasLocalToon():
+ NametagGlobals.setMasterArrowsOn(0)
+ mtrack = Parallel(mtrack, camTrack)
+ done = Func(callback)
+ track = Sequence(mtrack, done, name=name)
+ track.delayDeletes = [DelayDelete.DelayDelete(toon, '__faceOff'), DelayDelete.DelayDelete(suit, '__faceOff')]
+ track.start(ts)
+ self.storeInterval(track, name)
+
+ def enterFaceOff(self, ts):
+ self.notify.debug('enterFaceOff()')
+ self.delayDeleteMembers()
+ if len(self.toons) > 0 and base.localAvatar == self.toons[0]:
+ Emote.globalEmote.disableAll(self.toons[0], 'dbattle, enterFaceOff')
+ self.__faceOff(ts, self.faceOffName, self.__handleFaceOffDone)
+ if self.interactiveProp:
+ self.interactiveProp.gotoFaceoff()
+
+ def __handleFaceOffDone(self):
+ self.notify.debug('FaceOff done')
+ if len(self.toons) > 0 and base.localAvatar == self.toons[0]:
+ self.d_faceOffDone(base.localAvatar.doId)
+
+ def exitFaceOff(self):
+ self.notify.debug('exitFaceOff()')
+ if len(self.toons) > 0 and base.localAvatar == self.toons[0]:
+ Emote.globalEmote.releaseAll(self.toons[0], 'dbattle exitFaceOff')
+ self.finishInterval(self.faceOffName)
+ self.clearInterval(self.faceOffName)
+ self._removeMembersKeep()
+
+ def enterReward(self, ts):
+ self.notify.debug('enterReward()')
+ self.disableCollision()
+ self.delayDeleteMembers()
+ Emote.globalEmote.disableAll(base.localAvatar, 'dbattle, enterReward')
+ if self.hasLocalToon():
+ NametagGlobals.setMasterArrowsOn(0)
+ if self.localToonActive() == 0:
+ self.removeInactiveLocalToon(base.localAvatar)
+ for toon in self.toons:
+ toon.startSmooth()
+
+ self.accept('resumeAfterReward', self.handleResumeAfterReward)
+ if self.interactiveProp:
+ self.interactiveProp.gotoVictory()
+ self.playReward(ts)
+
+ def playReward(self, ts):
+ self.movie.playReward(ts, self.uniqueName('reward'), self.handleRewardDone)
+
+ def handleRewardDone(self):
+ self.notify.debug('Reward done')
+ if self.hasLocalToon():
+ self.d_rewardDone(base.localAvatar.doId)
+ self.movie.resetReward()
+ messenger.send('resumeAfterReward')
+
+ def handleResumeAfterReward(self):
+ self.fsm.request('Resume')
+
+ def exitReward(self):
+ self.notify.debug('exitReward()')
+ self.ignore('resumeAfterReward')
+ self.movie.resetReward(finish=1)
+ self._removeMembersKeep()
+ NametagGlobals.setMasterArrowsOn(1)
+ Emote.globalEmote.releaseAll(base.localAvatar, 'dbattle, exitReward')
+
+ def enterResume(self, ts = 0):
+ self.notify.debug('enterResume()')
+ if self.hasLocalToon():
+ self.removeLocalToon()
+ if self.interactiveProp:
+ self.interactiveProp.requestIdleOrSad()
+
+ def exitResume(self):
+ pass
+
+ def enterNoLocalToon(self):
+ self.notify.debug('enterNoLocalToon()')
+ self.enableCollision()
+
+ def exitNoLocalToon(self):
+ self.disableCollision()
+
+ def enterWaitForServer(self):
+ self.notify.debug('enterWaitForServer()')
+
+ def exitWaitForServer(self):
+ pass
diff --git a/toontown/battle/DistributedBattleAI.py b/toontown/battle/DistributedBattleAI.py
new file mode 100644
index 0000000..00b85a3
--- /dev/null
+++ b/toontown/battle/DistributedBattleAI.py
@@ -0,0 +1,132 @@
+from otp.ai.AIBase import *
+from BattleBase import *
+from BattleCalculatorAI import *
+from toontown.toonbase.ToontownBattleGlobals import *
+from SuitBattleGlobals import *
+import DistributedBattleBaseAI
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+import random
+
+class DistributedBattleAI(DistributedBattleBaseAI.DistributedBattleBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleAI')
+
+ def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, finishCallback=None, maxSuits=4, tutorialFlag=0, levelFlag=0, interactivePropTrackBonus=-1):
+ DistributedBattleBaseAI.DistributedBattleBaseAI.__init__(self, air, zoneId, finishCallback, maxSuits=maxSuits, tutorialFlag=tutorialFlag, interactivePropTrackBonus=interactivePropTrackBonus)
+ self.battleMgr = battleMgr
+ self.pos = pos
+ self.initialSuitPos = suit.getConfrontPosHpr()[0]
+ self.initialToonPos = suit.getConfrontPosHpr()[0]
+ self.addSuit(suit)
+ self.avId = toonId
+ if levelFlag == 0:
+ self.addToon(toonId)
+ self.faceOffToon = toonId
+ self.fsm.request('FaceOff')
+
+ def generate(self):
+ DistributedBattleBaseAI.DistributedBattleBaseAI.generate(self)
+ toon = simbase.air.doId2do.get(self.avId)
+ if toon:
+ if hasattr(self, 'doId'):
+ toon.b_setBattleId(self.doId)
+ else:
+ toon.b_setBattleId(-1)
+ self.avId = None
+ return
+
+ def faceOffDone(self):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreFaceOffDone == 1:
+ self.notify.debug('faceOffDone() - ignoring toon: %d' % toonId)
+ return
+ else:
+ if self.fsm.getCurrentState().getName() != 'FaceOff':
+ self.notify.warning('faceOffDone() - in state: %s' % self.fsm.getCurrentState().getName())
+ return
+ else:
+ if self.toons.count(toonId) == 0:
+ self.notify.warning('faceOffDone() - toon: %d not in toon list' % toonId)
+ return
+ self.notify.debug('toon: %d done facing off' % toonId)
+ self.handleFaceOffDone()
+
+ def enterFaceOff(self):
+ self.notify.debug('enterFaceOff()')
+ self.joinableFsm.request('Joinable')
+ self.runableFsm.request('Unrunable')
+ self.suits[0].releaseControl()
+ timeForFaceoff = self.calcFaceoffTime(self.pos, self.initialSuitPos) + FACEOFF_TAUNT_T + SERVER_BUFFER_TIME
+ if self.interactivePropTrackBonus >= 0:
+ timeForFaceoff += FACEOFF_LOOK_AT_PROP_T
+ self.timer.startCallback(timeForFaceoff, self.__serverFaceOffDone)
+ return None
+
+ def __serverFaceOffDone(self):
+ self.notify.debug('faceoff timed out on server')
+ self.ignoreFaceOffDone = 1
+ self.handleFaceOffDone()
+
+ def exitFaceOff(self):
+ self.timer.stop()
+ return None
+
+ def handleFaceOffDone(self):
+ self.timer.stop()
+ self.activeSuits.append(self.suits[0])
+ if len(self.toons) == 0:
+ self.b_setState('Resume')
+ else:
+ if self.faceOffToon == self.toons[0]:
+ self.activeToons.append(self.toons[0])
+ self.sendEarnedExperience(self.toons[0])
+ self.d_setMembers()
+ self.b_setState('WaitForInput')
+
+ def localMovieDone(self, needUpdate, deadToons, deadSuits, lastActiveSuitDied):
+ if len(self.toons) == 0:
+ self.d_setMembers()
+ self.b_setState('Resume')
+ else:
+ if len(self.suits) == 0:
+ for toonId in self.activeToons:
+ toon = self.getToon(toonId)
+ if toon:
+ self.toonItems[toonId] = self.air.questManager.recoverItems(toon, self.suitsKilled, self.zoneId)
+ if toonId in self.helpfulToons:
+ self.toonMerits[toonId] = self.air.promotionMgr.recoverMerits(toon, self.suitsKilled, self.zoneId)
+ else:
+ self.notify.debug('toon %d not helpful, skipping merits' % toonId)
+
+ self.d_setMembers()
+ self.d_setBattleExperience()
+ self.b_setState('Reward')
+ else:
+ if needUpdate == 1:
+ self.d_setMembers()
+ if len(deadSuits) > 0 and lastActiveSuitDied == 0 or len(deadToons) > 0:
+ self.needAdjust = 1
+ self.setState('WaitForJoin')
+
+ def enterReward(self):
+ self.notify.debug('enterReward()')
+ self.joinableFsm.request('Unjoinable')
+ self.runableFsm.request('Unrunable')
+ self.resetResponses()
+ self.assignRewards()
+ self.startRewardTimer()
+
+ def startRewardTimer(self):
+ self.timer.startCallback(REWARD_TIMEOUT, self.serverRewardDone)
+
+ def exitReward(self):
+ return None
+
+ def enterResume(self):
+ self.notify.debug('enterResume()')
+ self.joinableFsm.request('Unjoinable')
+ self.runableFsm.request('Unrunable')
+ DistributedBattleBaseAI.DistributedBattleBaseAI.enterResume(self)
+ if self.finishCallback:
+ self.finishCallback(self.zoneId)
+ self.battleMgr.destroy(self)
diff --git a/toontown/battle/DistributedBattleBase.py b/toontown/battle/DistributedBattleBase.py
new file mode 100644
index 0000000..01b856b
--- /dev/null
+++ b/toontown/battle/DistributedBattleBase.py
@@ -0,0 +1,1502 @@
+from pandac.PandaModules import *
+from toontown.toonbase.ToonBaseGlobal import *
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from direct.distributed.ClockDelta import *
+from toontown.toonbase import ToontownBattleGlobals
+from direct.distributed import DistributedNode
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task.Task import Task
+from direct.directnotify import DirectNotifyGlobal
+import Movie
+import MovieUtil
+from toontown.suit import Suit
+from direct.actor import Actor
+import BattleProps
+from direct.particles import ParticleEffect
+import BattleParticles
+from toontown.hood import ZoneUtil
+from toontown.distributed import DelayDelete
+from toontown.toon import TTEmote
+from otp.avatar import Emote
+
+class DistributedBattleBase(DistributedNode.DistributedNode, BattleBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleBase')
+ id = 0
+ camPos = ToontownBattleGlobals.BattleCamDefaultPos
+ camHpr = ToontownBattleGlobals.BattleCamDefaultHpr
+ camFov = ToontownBattleGlobals.BattleCamDefaultFov
+ camMenuFov = ToontownBattleGlobals.BattleCamMenuFov
+ camJoinPos = ToontownBattleGlobals.BattleCamJoinPos
+ camJoinHpr = ToontownBattleGlobals.BattleCamJoinHpr
+
+ def __init__(self, cr, townBattle):
+ DistributedNode.DistributedNode.__init__(self, cr)
+ NodePath.__init__(self)
+ self.assign(render.attachNewNode(self.uniqueBattleName('distributed-battle')))
+ BattleBase.__init__(self)
+ self.bossBattle = 0
+ self.townBattle = townBattle
+ self.__battleCleanedUp = 0
+ self.activeIntervals = {}
+ self.localToonJustJoined = 0
+ self.choseAttackAlready = 0
+ self.toons = []
+ self.exitedToons = []
+ self.suitTraps = ''
+ self.membersKeep = None
+ self.faceOffName = self.uniqueBattleName('faceoff')
+ self.localToonBattleEvent = self.uniqueBattleName('localtoon-battle-event')
+ self.adjustName = self.uniqueBattleName('adjust')
+ self.timerCountdownTaskName = self.uniqueBattleName('timer-countdown')
+ self.movie = Movie.Movie(self)
+ self.timer = Timer()
+ self.needAdjustTownBattle = 0
+ self.streetBattle = 1
+ self.levelBattle = 0
+ self.localToonFsm = ClassicFSM.ClassicFSM('LocalToon', [State.State('HasLocalToon', self.enterHasLocalToon, self.exitHasLocalToon, ['NoLocalToon', 'WaitForServer']), State.State('NoLocalToon', self.enterNoLocalToon, self.exitNoLocalToon, ['HasLocalToon', 'WaitForServer']), State.State('WaitForServer', self.enterWaitForServer, self.exitWaitForServer, ['HasLocalToon', 'NoLocalToon'])], 'WaitForServer', 'WaitForServer')
+ self.localToonFsm.enterInitialState()
+ self.fsm = ClassicFSM.ClassicFSM('DistributedBattle', [State.State('Off', self.enterOff, self.exitOff, ['FaceOff',
+ 'WaitForInput',
+ 'WaitForJoin',
+ 'MakeMovie',
+ 'PlayMovie',
+ 'Reward',
+ 'Resume']),
+ State.State('FaceOff', self.enterFaceOff, self.exitFaceOff, ['WaitForInput']),
+ State.State('WaitForJoin', self.enterWaitForJoin, self.exitWaitForJoin, ['WaitForInput', 'Resume']),
+ State.State('WaitForInput', self.enterWaitForInput, self.exitWaitForInput, ['WaitForInput', 'PlayMovie', 'Resume']),
+ State.State('MakeMovie', self.enterMakeMovie, self.exitMakeMovie, ['PlayMovie', 'Resume']),
+ State.State('PlayMovie', self.enterPlayMovie, self.exitPlayMovie, ['WaitForInput',
+ 'WaitForJoin',
+ 'Reward',
+ 'Resume']),
+ State.State('Reward', self.enterReward, self.exitReward, ['Resume']),
+ State.State('Resume', self.enterResume, self.exitResume, [])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.adjustFsm = ClassicFSM.ClassicFSM('Adjust', [State.State('Adjusting', self.enterAdjusting, self.exitAdjusting, ['NotAdjusting']), State.State('NotAdjusting', self.enterNotAdjusting, self.exitNotAdjusting, ['Adjusting'])], 'NotAdjusting', 'NotAdjusting')
+ self.adjustFsm.enterInitialState()
+ self.interactiveProp = None
+ return
+
+ def uniqueBattleName(self, name):
+ DistributedBattleBase.id += 1
+ return name + '-%d' % DistributedBattleBase.id
+
+ def generate(self):
+ self.notify.debug('generate(%s)' % self.doId)
+ DistributedNode.DistributedNode.generate(self)
+ self.__battleCleanedUp = 0
+ self.reparentTo(render)
+ self._skippingRewardMovie = False
+
+ def storeInterval(self, interval, name):
+ if name in self.activeIntervals:
+ ival = self.activeIntervals[name]
+ if hasattr(ival, 'delayDelete') or hasattr(ival, 'delayDeletes'):
+ self.clearInterval(name, finish=1)
+ self.activeIntervals[name] = interval
+
+ def __cleanupIntervals(self):
+ for interval in self.activeIntervals.values():
+ interval.finish()
+ DelayDelete.cleanupDelayDeletes(interval)
+
+ self.activeIntervals = {}
+
+ def clearInterval(self, name, finish = 0):
+ if self.activeIntervals.has_key(name):
+ ival = self.activeIntervals[name]
+ if finish:
+ ival.finish()
+ else:
+ ival.pause()
+ if self.activeIntervals.has_key(name):
+ DelayDelete.cleanupDelayDeletes(ival)
+ if self.activeIntervals.has_key(name):
+ del self.activeIntervals[name]
+ else:
+ self.notify.debug('interval: %s already cleared' % name)
+
+ def finishInterval(self, name):
+ if self.activeIntervals.has_key(name):
+ interval = self.activeIntervals[name]
+ interval.finish()
+
+ def disable(self):
+ self.notify.debug('disable(%s)' % self.doId)
+ self.cleanupBattle()
+ DistributedNode.DistributedNode.disable(self)
+
+ def battleCleanedUp(self):
+ return self.__battleCleanedUp
+
+ def cleanupBattle(self):
+ if self.__battleCleanedUp:
+ return
+ self.notify.debug('cleanupBattle(%s)' % self.doId)
+ self.__battleCleanedUp = 1
+ self.__cleanupIntervals()
+ self.fsm.requestFinalState()
+ if self.hasLocalToon():
+ self.removeLocalToon()
+ base.camLens.setFov(ToontownGlobals.DefaultCameraFov)
+ self.localToonFsm.request('WaitForServer')
+ self.ignoreAll()
+ for suit in self.suits:
+ if suit.battleTrap != NO_TRAP:
+ self.notify.debug('250 calling self.removeTrap, suit=%d' % suit.doId)
+ self.removeTrap(suit)
+ suit.battleTrap = NO_TRAP
+ suit.battleTrapProp = None
+ self.notify.debug('253 suit.battleTrapProp = None')
+ suit.battleTrapIsFresh = 0
+
+ self.suits = []
+ self.pendingSuits = []
+ self.joiningSuits = []
+ self.activeSuits = []
+ self.suitTraps = ''
+ self.toons = []
+ self.joiningToons = []
+ self.pendingToons = []
+ self.activeToons = []
+ self.runningToons = []
+ self.__stopTimer()
+ self.__cleanupIntervals()
+ self._removeMembersKeep()
+ return
+
+ def delete(self):
+ self.notify.debug('delete(%s)' % self.doId)
+ self.__cleanupIntervals()
+ self._removeMembersKeep()
+ self.movie.cleanup()
+ del self.townBattle
+ self.removeNode()
+ self.fsm = None
+ self.localToonFsm = None
+ self.adjustFsm = None
+ self.__stopTimer()
+ self.timer = None
+ DistributedNode.DistributedNode.delete(self)
+ return
+
+ def loadTrap(self, suit, trapid):
+ self.notify.debug('loadTrap() trap: %d suit: %d' % (trapid, suit.doId))
+ trapName = AvProps[TRAP][trapid]
+ trap = BattleProps.globalPropPool.getProp(trapName)
+ suit.battleTrap = trapid
+ suit.battleTrapIsFresh = 0
+ suit.battleTrapProp = trap
+ self.notify.debug('suit.battleTrapProp = trap %s' % trap)
+ if trap.getName() == 'traintrack':
+ pass
+ else:
+ trap.wrtReparentTo(suit)
+ distance = MovieUtil.SUIT_TRAP_DISTANCE
+ if trapName == 'rake':
+ distance = MovieUtil.SUIT_TRAP_RAKE_DISTANCE
+ distance += MovieUtil.getSuitRakeOffset(suit)
+ trap.setH(180)
+ trap.setScale(0.7)
+ elif trapName == 'trapdoor' or trapName == 'quicksand':
+ trap.setScale(1.7)
+ elif trapName == 'marbles':
+ distance = MovieUtil.SUIT_TRAP_MARBLES_DISTANCE
+ trap.setH(94)
+ elif trapName == 'tnt':
+ trap.setP(90)
+ tip = trap.find('**/joint_attachEmitter')
+ sparks = BattleParticles.createParticleEffect(file='tnt')
+ trap.sparksEffect = sparks
+ sparks.start(tip)
+ trap.setPos(0, distance, 0)
+ if isinstance(trap, Actor.Actor):
+ frame = trap.getNumFrames(trapName) - 1
+ trap.pose(trapName, frame)
+
+ def removeTrap(self, suit, removeTrainTrack = False):
+ self.notify.debug('removeTrap() from suit: %d, removeTrainTrack=%s' % (suit.doId, removeTrainTrack))
+ if suit.battleTrapProp == None:
+ self.notify.debug('suit.battleTrapProp == None, suit.battleTrap=%s setting to NO_TRAP, returning' % suit.battleTrap)
+ suit.battleTrap = NO_TRAP
+ return
+ if suit.battleTrap == UBER_GAG_LEVEL_INDEX:
+ if removeTrainTrack:
+ self.notify.debug('doing removeProp on traintrack')
+ MovieUtil.removeProp(suit.battleTrapProp)
+ for otherSuit in self.suits:
+ if not otherSuit == suit:
+ otherSuit.battleTrapProp = None
+ self.notify.debug('351 otherSuit=%d otherSuit.battleTrapProp = None' % otherSuit.doId)
+ otherSuit.battleTrap = NO_TRAP
+ otherSuit.battleTrapIsFresh = 0
+
+ else:
+ self.notify.debug('deliberately not doing removeProp on traintrack')
+ else:
+ self.notify.debug('suit.battleTrap != UBER_GAG_LEVEL_INDEX')
+ MovieUtil.removeProp(suit.battleTrapProp)
+ suit.battleTrapProp = None
+ self.notify.debug('360 suit.battleTrapProp = None')
+ suit.battleTrap = NO_TRAP
+ suit.battleTrapIsFresh = 0
+ return
+
+ def pause(self):
+ self.timer.stop()
+
+ def unpause(self):
+ self.timer.resume()
+
+ def findSuit(self, id):
+ for s in self.suits:
+ if s.doId == id:
+ return s
+
+ return None
+
+ def findToon(self, id):
+ toon = self.getToon(id)
+ if toon == None:
+ return
+ for t in self.toons:
+ if t == toon:
+ return t
+
+ return
+
+ def isSuitLured(self, suit):
+ if self.luredSuits.count(suit) != 0:
+ return 1
+ return 0
+
+ def unlureSuit(self, suit):
+ self.notify.debug('movie unluring suit %s' % suit.doId)
+ if self.luredSuits.count(suit) != 0:
+ self.luredSuits.remove(suit)
+ self.needAdjustTownBattle = 1
+ return None
+
+ def lureSuit(self, suit):
+ self.notify.debug('movie luring suit %s' % suit.doId)
+ if self.luredSuits.count(suit) == 0:
+ self.luredSuits.append(suit)
+ self.needAdjustTownBattle = 1
+ return None
+
+ def getActorPosHpr(self, actor, actorList = []):
+ if isinstance(actor, Suit.Suit):
+ if actorList == []:
+ actorList = self.activeSuits
+ if actorList.count(actor) != 0:
+ numSuits = len(actorList) - 1
+ index = actorList.index(actor)
+ point = self.suitPoints[numSuits][index]
+ return (Point3(point[0]), VBase3(point[1], 0.0, 0.0))
+ else:
+ self.notify.warning('getActorPosHpr() - suit not active')
+ else:
+ if actorList == []:
+ actorList = self.activeToons
+ if actorList.count(actor) != 0:
+ numToons = len(actorList) - 1
+ index = actorList.index(actor)
+ point = self.toonPoints[numToons][index]
+ return (Point3(point[0]), VBase3(point[1], 0.0, 0.0))
+ else:
+ self.notify.warning('getActorPosHpr() - toon not active')
+
+ def setLevelDoId(self, levelDoId):
+ pass
+
+ def setBattleCellId(self, battleCellId):
+ pass
+
+ def setInteractivePropTrackBonus(self, trackBonus):
+ self.interactivePropTrackBonus = trackBonus
+
+ def getInteractivePropTrackBonus(self):
+ return self.interactivePropTrackBonus
+
+ def setPosition(self, x, y, z):
+ self.notify.debug('setPosition() - %d %d %d' % (x, y, z))
+ pos = Point3(x, y, z)
+ self.setPos(pos)
+
+ def setInitialSuitPos(self, x, y, z):
+ self.initialSuitPos = Point3(x, y, z)
+ self.headsUp(self.initialSuitPos)
+
+ def setZoneId(self, zoneId):
+ self.zoneId = zoneId
+
+ def setBossBattle(self, value):
+ self.bossBattle = value
+
+ def setState(self, state, timestamp):
+ if self.__battleCleanedUp:
+ return
+ self.notify.debug('setState(%s)' % state)
+ self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])
+
+ def setMembers(self, suits, suitsJoining, suitsPending, suitsActive, suitsLured, suitTraps, toons, toonsJoining, toonsPending, toonsActive, toonsRunning, timestamp):
+ if self.__battleCleanedUp:
+ return
+ self.notify.debug('setMembers() - suits: %s suitsJoining: %s suitsPending: %s suitsActive: %s suitsLured: %s suitTraps: %s toons: %s toonsJoining: %s toonsPending: %s toonsActive: %s toonsRunning: %s' % (suits,
+ suitsJoining,
+ suitsPending,
+ suitsActive,
+ suitsLured,
+ suitTraps,
+ toons,
+ toonsJoining,
+ toonsPending,
+ toonsActive,
+ toonsRunning))
+ ts = globalClockDelta.localElapsedTime(timestamp)
+ oldsuits = self.suits
+ self.suits = []
+ suitGone = 0
+ for s in suits:
+ if self.cr.doId2do.has_key(s):
+ suit = self.cr.doId2do[s]
+ suit.setState('Battle')
+ self.suits.append(suit)
+ suit.interactivePropTrackBonus = self.interactivePropTrackBonus
+ try:
+ suit.battleTrap
+ except:
+ suit.battleTrap = NO_TRAP
+ suit.battleTrapProp = None
+ self.notify.debug('496 suit.battleTrapProp = None')
+ suit.battleTrapIsFresh = 0
+
+ else:
+ self.notify.warning('setMembers() - no suit in repository: %d' % s)
+ self.suits.append(None)
+ suitGone = 1
+
+ numSuitsThatDied = 0
+ for s in oldsuits:
+ if self.suits.count(s) == 0:
+ self.__removeSuit(s)
+ numSuitsThatDied += 1
+ self.notify.debug('suit %d dies, numSuitsThatDied=%d' % (s.doId, numSuitsThatDied))
+
+ if numSuitsThatDied == 4:
+ trainTrap = self.find('**/traintrack')
+ if not trainTrap.isEmpty():
+ self.notify.debug('removing old train trap when 4 suits died')
+ trainTrap.removeNode()
+ for s in suitsJoining:
+ suit = self.suits[int(s)]
+ if suit != None and self.joiningSuits.count(suit) == 0:
+ self.makeSuitJoin(suit, ts)
+
+ for s in suitsPending:
+ suit = self.suits[int(s)]
+ if suit != None and self.pendingSuits.count(suit) == 0:
+ self.__makeSuitPending(suit)
+
+ activeSuits = []
+ for s in suitsActive:
+ suit = self.suits[int(s)]
+ if suit != None and self.activeSuits.count(suit) == 0:
+ activeSuits.append(suit)
+
+ oldLuredSuits = self.luredSuits
+ self.luredSuits = []
+ for s in suitsLured:
+ suit = self.suits[int(s)]
+ if suit != None:
+ self.luredSuits.append(suit)
+ if oldLuredSuits.count(suit) == 0:
+ self.needAdjustTownBattle = 1
+
+ if self.needAdjustTownBattle == 0:
+ for s in oldLuredSuits:
+ if self.luredSuits.count(s) == 0:
+ self.needAdjustTownBattle = 1
+
+ index = 0
+ oldSuitTraps = self.suitTraps
+ self.suitTraps = suitTraps
+ for s in suitTraps:
+ trapid = int(s)
+ if trapid == 9:
+ trapid = -1
+ suit = self.suits[index]
+ index += 1
+ if suit != None:
+ if (trapid == NO_TRAP or trapid != suit.battleTrap) and suit.battleTrapProp != None:
+ self.notify.debug('569 calling self.removeTrap, suit=%d' % suit.doId)
+ self.removeTrap(suit)
+ if trapid != NO_TRAP and suit.battleTrapProp == None:
+ if self.fsm.getCurrentState().getName() != 'PlayMovie':
+ self.loadTrap(suit, trapid)
+
+ if len(oldSuitTraps) != len(self.suitTraps):
+ self.needAdjustTownBattle = 1
+ else:
+ for i in range(len(oldSuitTraps)):
+ if oldSuitTraps[i] == '9' and self.suitTraps[i] != '9' or oldSuitTraps[i] != '9' and self.suitTraps[i] == '9':
+ self.needAdjustTownBattle = 1
+ break
+
+ if suitGone:
+ validSuits = []
+ for s in self.suits:
+ if s != None:
+ validSuits.append(s)
+
+ self.suits = validSuits
+ self.needAdjustTownBattle = 1
+ oldtoons = self.toons
+ self.toons = []
+ toonGone = 0
+ for t in toons:
+ toon = self.getToon(t)
+ if toon == None:
+ self.notify.warning('setMembers() - toon not in cr!')
+ self.toons.append(None)
+ toonGone = 1
+ continue
+ self.toons.append(toon)
+ if oldtoons.count(toon) == 0:
+ self.notify.debug('setMembers() - add toon: %d' % toon.doId)
+ self.__listenForUnexpectedExit(toon)
+ toon.stopLookAround()
+ toon.stopSmooth()
+
+ for t in oldtoons:
+ if self.toons.count(t) == 0:
+ if self.__removeToon(t) == 1:
+ self.notify.debug('setMembers() - local toon left battle')
+ return []
+
+ for t in toonsJoining:
+ if int(t) < len(self.toons):
+ toon = self.toons[int(t)]
+ if toon != None and self.joiningToons.count(toon) == 0:
+ self.__makeToonJoin(toon, toonsPending, ts)
+ else:
+ self.notify.warning('setMembers toonsJoining t=%s not in self.toons %s' % (t, self.toons))
+
+ for t in toonsPending:
+ if int(t) < len(self.toons):
+ toon = self.toons[int(t)]
+ if toon != None and self.pendingToons.count(toon) == 0:
+ self.__makeToonPending(toon, ts)
+ else:
+ self.notify.warning('setMembers toonsPending t=%s not in self.toons %s' % (t, self.toons))
+
+ for t in toonsRunning:
+ toon = self.toons[int(t)]
+ if toon != None and self.runningToons.count(toon) == 0:
+ self.__makeToonRun(toon, ts)
+
+ activeToons = []
+ for t in toonsActive:
+ toon = self.toons[int(t)]
+ if toon != None and self.activeToons.count(toon) == 0:
+ activeToons.append(toon)
+
+ if len(activeSuits) > 0 or len(activeToons) > 0:
+ self.__makeAvsActive(activeSuits, activeToons)
+ if toonGone == 1:
+ validToons = []
+ for toon in self.toons:
+ if toon != None:
+ validToons.append(toon)
+
+ self.toons = validToons
+ if len(self.activeToons) > 0:
+ self.__requestAdjustTownBattle()
+ currStateName = self.localToonFsm.getCurrentState().getName()
+ if self.toons.count(base.localAvatar):
+ if oldtoons.count(base.localAvatar) == 0:
+ self.notify.debug('setMembers() - local toon just joined')
+ if self.streetBattle == 1:
+ base.cr.playGame.getPlace().enterZone(self.zoneId)
+ self.localToonJustJoined = 1
+ if currStateName != 'HasLocalToon':
+ self.localToonFsm.request('HasLocalToon')
+ else:
+ if oldtoons.count(base.localAvatar):
+ self.notify.debug('setMembers() - local toon just ran')
+ if self.levelBattle:
+ self.unlockLevelViz()
+ if currStateName != 'NoLocalToon':
+ self.localToonFsm.request('NoLocalToon')
+ return oldtoons
+
+ def adjust(self, timestamp):
+ if self.__battleCleanedUp:
+ return
+ self.notify.debug('adjust(%f) from server' % globalClockDelta.localElapsedTime(timestamp))
+ self.adjustFsm.request('Adjusting', [globalClockDelta.localElapsedTime(timestamp)])
+
+ def setMovie(self, active, toons, suits, id0, tr0, le0, tg0, hp0, ac0, hpb0, kbb0, died0, revive0, id1, tr1, le1, tg1, hp1, ac1, hpb1, kbb1, died1, revive1, id2, tr2, le2, tg2, hp2, ac2, hpb2, kbb2, died2, revive2, id3, tr3, le3, tg3, hp3, ac3, hpb3, kbb3, died3, revive3, sid0, at0, stg0, dm0, sd0, sb0, st0, sid1, at1, stg1, dm1, sd1, sb1, st1, sid2, at2, stg2, dm2, sd2, sb2, st2, sid3, at3, stg3, dm3, sd3, sb3, st3):
+ if self.__battleCleanedUp:
+ return
+ self.notify.debug('setMovie()')
+ if int(active) == 1:
+ self.notify.debug('setMovie() - movie is active')
+ self.movie.genAttackDicts(toons, suits, id0, tr0, le0, tg0, hp0, ac0, hpb0, kbb0, died0, revive0, id1, tr1, le1, tg1, hp1, ac1, hpb1, kbb1, died1, revive1, id2, tr2, le2, tg2, hp2, ac2, hpb2, kbb2, died2, revive2, id3, tr3, le3, tg3, hp3, ac3, hpb3, kbb3, died3, revive3, sid0, at0, stg0, dm0, sd0, sb0, st0, sid1, at1, stg1, dm1, sd1, sb1, st1, sid2, at2, stg2, dm2, sd2, sb2, st2, sid3, at3, stg3, dm3, sd3, sb3, st3)
+
+ def setChosenToonAttacks(self, ids, tracks, levels, targets):
+ if self.__battleCleanedUp:
+ return
+ self.notify.debug('setChosenToonAttacks() - (%s), (%s), (%s), (%s)' % (ids,
+ tracks,
+ levels,
+ targets))
+ toonIndices = []
+ targetIndices = []
+ unAttack = 0
+ localToonInList = 0
+ for i in range(len(ids)):
+ track = tracks[i]
+ level = levels[i]
+ toon = self.findToon(ids[i])
+ if toon == None or self.activeToons.count(toon) == 0:
+ self.notify.warning('setChosenToonAttacks() - toon gone or not in battle: %d!' % ids[i])
+ toonIndices.append(-1)
+ tracks.append(-1)
+ levels.append(-1)
+ targetIndices.append(-1)
+ continue
+ if toon == base.localAvatar:
+ localToonInList = 1
+ toonIndices.append(self.activeToons.index(toon))
+ if track == SOS:
+ targetIndex = -1
+ elif track == NPCSOS:
+ targetIndex = -1
+ elif track == PETSOS:
+ targetIndex = -1
+ elif track == PASS:
+ targetIndex = -1
+ tracks[i] = PASS_ATTACK
+ elif attackAffectsGroup(track, level):
+ targetIndex = -1
+ elif track == HEAL:
+ target = self.findToon(targets[i])
+ if target != None and self.activeToons.count(target) != 0:
+ targetIndex = self.activeToons.index(target)
+ else:
+ targetIndex = -1
+ elif track == UN_ATTACK:
+ targetIndex = -1
+ tracks[i] = NO_ATTACK
+ if toon == base.localAvatar:
+ unAttack = 1
+ self.choseAttackAlready = 0
+ elif track == NO_ATTACK:
+ targetIndex = -1
+ else:
+ target = self.findSuit(targets[i])
+ if target != None and self.activeSuits.count(target) != 0:
+ targetIndex = self.activeSuits.index(target)
+ else:
+ targetIndex = -1
+ targetIndices.append(targetIndex)
+
+ for i in range(4 - len(ids)):
+ toonIndices.append(-1)
+ tracks.append(-1)
+ levels.append(-1)
+ targetIndices.append(-1)
+
+ self.townBattleAttacks = (toonIndices,
+ tracks,
+ levels,
+ targetIndices)
+ if self.localToonActive() and localToonInList == 1:
+ if unAttack == 1 and self.fsm.getCurrentState().getName() == 'WaitForInput':
+ if self.townBattle.fsm.getCurrentState().getName() != 'Attack':
+ self.townBattle.setState('Attack')
+ self.townBattle.updateChosenAttacks(self.townBattleAttacks[0], self.townBattleAttacks[1], self.townBattleAttacks[2], self.townBattleAttacks[3])
+ return
+
+ def setBattleExperience(self, id0, origExp0, earnedExp0, origQuests0, items0, missedItems0, origMerits0, merits0, parts0, id1, origExp1, earnedExp1, origQuests1, items1, missedItems1, origMerits1, merits1, parts1, id2, origExp2, earnedExp2, origQuests2, items2, missedItems2, origMerits2, merits2, parts2, id3, origExp3, earnedExp3, origQuests3, items3, missedItems3, origMerits3, merits3, parts3, deathList, uberList, helpfulToonsList):
+ if self.__battleCleanedUp:
+ return
+ self.movie.genRewardDicts(id0, origExp0, earnedExp0, origQuests0, items0, missedItems0, origMerits0, merits0, parts0, id1, origExp1, earnedExp1, origQuests1, items1, missedItems1, origMerits1, merits1, parts1, id2, origExp2, earnedExp2, origQuests2, items2, missedItems2, origMerits2, merits2, parts2, id3, origExp3, earnedExp3, origQuests3, items3, missedItems3, origMerits3, merits3, parts3, deathList, uberList, helpfulToonsList)
+
+ def __listenForUnexpectedExit(self, toon):
+ self.accept(toon.uniqueName('disable'), self.__handleUnexpectedExit, extraArgs=[toon])
+ self.accept(toon.uniqueName('died'), self.__handleDied, extraArgs=[toon])
+
+ def __handleUnexpectedExit(self, toon):
+ self.notify.warning('handleUnexpectedExit() - toon: %d' % toon.doId)
+ self.__removeToon(toon, unexpected=1)
+
+ def __handleDied(self, toon):
+ self.notify.warning('handleDied() - toon: %d' % toon.doId)
+ if toon == base.localAvatar:
+ self.d_toonDied(toon.doId)
+ self.cleanupBattle()
+
+ def delayDeleteMembers(self):
+ membersKeep = []
+ for t in self.toons:
+ membersKeep.append(DelayDelete.DelayDelete(t, 'delayDeleteMembers'))
+
+ for s in self.suits:
+ membersKeep.append(DelayDelete.DelayDelete(s, 'delayDeleteMembers'))
+
+ self._removeMembersKeep()
+ self.membersKeep = membersKeep
+
+ def _removeMembersKeep(self):
+ if self.membersKeep:
+ for delayDelete in self.membersKeep:
+ delayDelete.destroy()
+
+ self.membersKeep = None
+ return
+
+ def __removeSuit(self, suit):
+ self.notify.debug('__removeSuit(%d)' % suit.doId)
+ if self.suits.count(suit) != 0:
+ self.suits.remove(suit)
+ if self.joiningSuits.count(suit) != 0:
+ self.joiningSuits.remove(suit)
+ if self.pendingSuits.count(suit) != 0:
+ self.pendingSuits.remove(suit)
+ if self.activeSuits.count(suit) != 0:
+ self.activeSuits.remove(suit)
+ self.suitGone = 1
+ if suit.battleTrap != NO_TRAP:
+ self.notify.debug('882 calling self.removeTrap, suit=%d' % suit.doId)
+ self.removeTrap(suit)
+ suit.battleTrap = NO_TRAP
+ suit.battleTrapProp = None
+ self.notify.debug('883 suit.battleTrapProp = None')
+ suit.battleTrapIsFresh = 0
+ return
+
+ def __removeToon(self, toon, unexpected = 0):
+ self.notify.debug('__removeToon(%d)' % toon.doId)
+ self.exitedToons.append(toon)
+ if self.toons.count(toon) != 0:
+ self.toons.remove(toon)
+ if self.joiningToons.count(toon) != 0:
+ self.clearInterval(self.taskName('to-pending-toon-%d' % toon.doId))
+ if toon in self.joiningToons:
+ self.joiningToons.remove(toon)
+ if self.pendingToons.count(toon) != 0:
+ self.pendingToons.remove(toon)
+ if self.activeToons.count(toon) != 0:
+ self.activeToons.remove(toon)
+ if self.runningToons.count(toon) != 0:
+ self.clearInterval(self.taskName('running-%d' % toon.doId), finish=1)
+ if toon in self.runningToons:
+ self.runningToons.remove(toon)
+ self.ignore(toon.uniqueName('disable'))
+ self.ignore(toon.uniqueName('died'))
+ self.toonGone = 1
+ if toon == base.localAvatar:
+ self.removeLocalToon()
+ self.__teleportToSafeZone(toon)
+ return 1
+ return 0
+
+ def removeLocalToon(self):
+ if self._skippingRewardMovie:
+ return
+ if base.cr.playGame.getPlace() != None:
+ base.cr.playGame.getPlace().setState('walk')
+ base.localAvatar.earnedExperience = None
+ self.localToonFsm.request('NoLocalToon')
+ return
+
+ def removeInactiveLocalToon(self, toon):
+ self.notify.debug('removeInactiveLocalToon(%d)' % toon.doId)
+ self.exitedToons.append(toon)
+ if self.toons.count(toon) != 0:
+ self.toons.remove(toon)
+ if self.joiningToons.count(toon) != 0:
+ self.clearInterval(self.taskName('to-pending-toon-%d' % toon.doId), finish=1)
+ if toon in self.joiningToons:
+ self.joiningToons.remove(toon)
+ if self.pendingToons.count(toon) != 0:
+ self.pendingToons.remove(toon)
+ self.ignore(toon.uniqueName('disable'))
+ self.ignore(toon.uniqueName('died'))
+ base.cr.playGame.getPlace().setState('walk')
+ self.localToonFsm.request('WaitForServer')
+
+ def __createJoinInterval(self, av, destPos, destHpr, name, ts, callback, toon = 0):
+ joinTrack = Sequence()
+ joinTrack.append(Func(Emote.globalEmote.disableAll, av, 'dbattlebase, createJoinInterval'))
+ avPos = av.getPos(self)
+ avPos = Point3(avPos[0], avPos[1], 0.0)
+ av.setShadowHeight(0)
+ plist = self.buildJoinPointList(avPos, destPos, toon)
+ if len(plist) == 0:
+ joinTrack.append(Func(av.headsUp, self, destPos))
+ if toon == 0:
+ timeToDest = self.calcSuitMoveTime(avPos, destPos)
+ joinTrack.append(Func(av.loop, 'walk'))
+ else:
+ timeToDest = self.calcToonMoveTime(avPos, destPos)
+ joinTrack.append(Func(av.loop, 'run'))
+ if timeToDest > BATTLE_SMALL_VALUE:
+ joinTrack.append(LerpPosInterval(av, timeToDest, destPos, other=self))
+ totalTime = timeToDest
+ else:
+ totalTime = 0
+ else:
+ timeToPerimeter = 0
+ if toon == 0:
+ timeToPerimeter = self.calcSuitMoveTime(plist[0], avPos)
+ timePerSegment = 10.0 / BattleBase.suitSpeed
+ timeToDest = self.calcSuitMoveTime(BattleBase.posA, destPos)
+ else:
+ timeToPerimeter = self.calcToonMoveTime(plist[0], avPos)
+ timePerSegment = 10.0 / BattleBase.toonSpeed
+ timeToDest = self.calcToonMoveTime(BattleBase.posE, destPos)
+ totalTime = timeToPerimeter + (len(plist) - 1) * timePerSegment + timeToDest
+ if totalTime > MAX_JOIN_T:
+ self.notify.warning('__createJoinInterval() - time: %f' % totalTime)
+ joinTrack.append(Func(av.headsUp, self, plist[0]))
+ if toon == 0:
+ joinTrack.append(Func(av.loop, 'walk'))
+ else:
+ joinTrack.append(Func(av.loop, 'run'))
+ joinTrack.append(LerpPosInterval(av, timeToPerimeter, plist[0], other=self))
+ for p in plist[1:]:
+ joinTrack.append(Func(av.headsUp, self, p))
+ joinTrack.append(LerpPosInterval(av, timePerSegment, p, other=self))
+
+ joinTrack.append(Func(av.headsUp, self, destPos))
+ joinTrack.append(LerpPosInterval(av, timeToDest, destPos, other=self))
+ joinTrack.append(Func(av.loop, 'neutral'))
+ joinTrack.append(Func(av.headsUp, self, Point3(0, 0, 0)))
+ tval = totalTime - ts
+ if tval < 0:
+ tval = totalTime
+ joinTrack.append(Func(Emote.globalEmote.releaseAll, av, 'dbattlebase, createJoinInterval'))
+ joinTrack.append(Func(callback, av, tval))
+ if av == base.localAvatar:
+ camTrack = Sequence()
+
+ def setCamFov(fov):
+ base.camLens.setFov(fov)
+
+ camTrack.append(Func(setCamFov, self.camFov))
+ camTrack.append(Func(camera.wrtReparentTo, self))
+ camTrack.append(Func(camera.setPos, self.camJoinPos))
+ camTrack.append(Func(camera.setHpr, self.camJoinHpr))
+ return Parallel(joinTrack, camTrack, name=name)
+ else:
+ return Sequence(joinTrack, name=name)
+
+ def makeSuitJoin(self, suit, ts):
+ self.notify.debug('makeSuitJoin(%d)' % suit.doId)
+ spotIndex = len(self.pendingSuits) + len(self.joiningSuits)
+ self.joiningSuits.append(suit)
+ suit.setState('Battle')
+ openSpot = self.suitPendingPoints[spotIndex]
+ pos = openSpot[0]
+ hpr = VBase3(openSpot[1], 0.0, 0.0)
+ trackName = self.taskName('to-pending-suit-%d' % suit.doId)
+ track = self.__createJoinInterval(suit, pos, hpr, trackName, ts, self.__handleSuitJoinDone)
+ track.start(ts)
+ track.delayDelete = DelayDelete.DelayDelete(suit, 'makeSuitJoin')
+ self.storeInterval(track, trackName)
+ if ToontownBattleGlobals.SkipMovie:
+ track.finish()
+
+ def __handleSuitJoinDone(self, suit, ts):
+ self.notify.debug('suit: %d is now pending' % suit.doId)
+ if self.hasLocalToon():
+ self.d_joinDone(base.localAvatar.doId, suit.doId)
+
+ def __makeSuitPending(self, suit):
+ self.notify.debug('__makeSuitPending(%d)' % suit.doId)
+ self.clearInterval(self.taskName('to-pending-suit-%d' % suit.doId), finish=1)
+ if self.joiningSuits.count(suit):
+ self.joiningSuits.remove(suit)
+ self.pendingSuits.append(suit)
+
+ def __teleportToSafeZone(self, toon):
+ self.notify.debug('teleportToSafeZone(%d)' % toon.doId)
+ hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId)
+ if hoodId in base.localAvatar.hoodsVisited:
+ target_sz = ZoneUtil.getSafeZoneId(self.zoneId)
+ else:
+ target_sz = ZoneUtil.getSafeZoneId(base.localAvatar.defaultZone)
+ base.cr.playGame.getPlace().fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(target_sz),
+ 'where': ZoneUtil.getWhereName(target_sz, 1),
+ 'how': 'teleportIn',
+ 'hoodId': target_sz,
+ 'zoneId': target_sz,
+ 'shardId': None,
+ 'avId': -1,
+ 'battle': 1}])
+ return
+
+ def __makeToonJoin(self, toon, pendingToons, ts):
+ self.notify.debug('__makeToonJoin(%d)' % toon.doId)
+ spotIndex = len(pendingToons) + len(self.joiningToons)
+ self.joiningToons.append(toon)
+ openSpot = self.toonPendingPoints[spotIndex]
+ pos = openSpot[0]
+ hpr = VBase3(openSpot[1], 0.0, 0.0)
+ trackName = self.taskName('to-pending-toon-%d' % toon.doId)
+ track = self.__createJoinInterval(toon, pos, hpr, trackName, ts, self.__handleToonJoinDone, toon=1)
+ if toon != base.localAvatar:
+ toon.animFSM.request('off')
+ track.start(ts)
+ track.delayDelete = DelayDelete.DelayDelete(toon, '__makeToonJoin')
+ self.storeInterval(track, trackName)
+
+ def __handleToonJoinDone(self, toon, ts):
+ self.notify.debug('__handleToonJoinDone() - pending: %d' % toon.doId)
+ if self.hasLocalToon():
+ self.d_joinDone(base.localAvatar.doId, toon.doId)
+
+ def __makeToonPending(self, toon, ts):
+ self.notify.debug('__makeToonPending(%d)' % toon.doId)
+ self.clearInterval(self.taskName('to-pending-toon-%d' % toon.doId), finish=1)
+ if self.joiningToons.count(toon):
+ self.joiningToons.remove(toon)
+ spotIndex = len(self.pendingToons)
+ self.pendingToons.append(toon)
+ openSpot = self.toonPendingPoints[spotIndex]
+ pos = openSpot[0]
+ hpr = VBase3(openSpot[1], 0.0, 0.0)
+ toon.loop('neutral')
+ toon.setPosHpr(self, pos, hpr)
+ if base.localAvatar == toon:
+ currStateName = self.fsm.getCurrentState().getName()
+
+ def __makeAvsActive(self, suits, toons):
+ self.notify.debug('__makeAvsActive()')
+ self.__stopAdjusting()
+ for s in suits:
+ if self.joiningSuits.count(s):
+ self.notify.warning('suit: %d was in joining list!' % s.doId)
+ self.joiningSuits.remove(s)
+ if self.pendingSuits.count(s):
+ self.pendingSuits.remove(s)
+ self.notify.debug('__makeAvsActive() - suit: %d' % s.doId)
+ self.activeSuits.append(s)
+
+ if len(self.activeSuits) >= 1:
+ for suit in self.activeSuits:
+ suitPos, suitHpr = self.getActorPosHpr(suit)
+ if self.isSuitLured(suit) == 0:
+ suit.setPosHpr(self, suitPos, suitHpr)
+ else:
+ spos = Point3(suitPos[0], suitPos[1] - MovieUtil.SUIT_LURE_DISTANCE, suitPos[2])
+ suit.setPosHpr(self, spos, suitHpr)
+ suit.loop('neutral')
+
+ for toon in toons:
+ if self.joiningToons.count(toon):
+ self.notify.warning('toon: %d was in joining list!' % toon.doId)
+ self.joiningToons.remove(toon)
+ if self.pendingToons.count(toon):
+ self.pendingToons.remove(toon)
+ self.notify.debug('__makeAvsActive() - toon: %d' % toon.doId)
+ if self.activeToons.count(toon) == 0:
+ self.activeToons.append(toon)
+ else:
+ self.notify.warning('makeAvsActive() - toon: %d is active!' % toon.doId)
+
+ if len(self.activeToons) >= 1:
+ for toon in self.activeToons:
+ toonPos, toonHpr = self.getActorPosHpr(toon)
+ toon.setPosHpr(self, toonPos, toonHpr)
+ toon.loop('neutral')
+
+ if self.fsm.getCurrentState().getName() == 'WaitForInput' and self.localToonActive() and self.localToonJustJoined == 1:
+ self.notify.debug('makeAvsActive() - local toon just joined')
+ self.__enterLocalToonWaitForInput()
+ self.localToonJustJoined = 0
+ self.startTimer()
+
+ def __makeToonRun(self, toon, ts):
+ self.notify.debug('__makeToonRun(%d)' % toon.doId)
+ if self.activeToons.count(toon):
+ self.activeToons.remove(toon)
+ self.runningToons.append(toon)
+ self.toonGone = 1
+ self.__stopTimer()
+ if self.localToonRunning():
+ self.townBattle.setState('Off')
+ runMTrack = MovieUtil.getToonTeleportOutInterval(toon)
+ runName = self.taskName('running-%d' % toon.doId)
+ self.notify.debug('duration: %f' % runMTrack.getDuration())
+ runMTrack.start(ts)
+ runMTrack.delayDelete = DelayDelete.DelayDelete(toon, '__makeToonRun')
+ self.storeInterval(runMTrack, runName)
+
+ def getToon(self, toonId):
+ if self.cr.doId2do.has_key(toonId):
+ return self.cr.doId2do[toonId]
+ else:
+ self.notify.warning('getToon() - toon: %d not in repository!' % toonId)
+ return None
+ return None
+
+ def d_toonRequestJoin(self, toonId, pos):
+ self.notify.debug('network:toonRequestJoin()')
+ self.sendUpdate('toonRequestJoin', [pos[0], pos[1], pos[2]])
+
+ def d_toonRequestRun(self, toonId):
+ self.notify.debug('network:toonRequestRun()')
+ self.sendUpdate('toonRequestRun', [])
+
+ def d_toonDied(self, toonId):
+ self.notify.debug('network:toonDied()')
+ self.sendUpdate('toonDied', [])
+
+ def d_faceOffDone(self, toonId):
+ self.notify.debug('network:faceOffDone()')
+ self.sendUpdate('faceOffDone', [])
+
+ def d_adjustDone(self, toonId):
+ self.notify.debug('network:adjustDone()')
+ self.sendUpdate('adjustDone', [])
+
+ def d_timeout(self, toonId):
+ self.notify.debug('network:timeout()')
+ self.sendUpdate('timeout', [])
+
+ def d_movieDone(self, toonId):
+ self.notify.debug('network:movieDone()')
+ self.sendUpdate('movieDone', [])
+
+ def d_rewardDone(self, toonId):
+ self.notify.debug('network:rewardDone()')
+ self.sendUpdate('rewardDone', [])
+
+ def d_joinDone(self, toonId, avId):
+ self.notify.debug('network:joinDone(%d)' % avId)
+ self.sendUpdate('joinDone', [avId])
+
+ def d_requestAttack(self, toonId, track, level, av):
+ self.notify.debug('network:requestAttack(%d, %d, %d)' % (track, level, av))
+ self.sendUpdate('requestAttack', [track, level, av])
+
+ def d_requestPetProxy(self, toonId, av):
+ self.notify.debug('network:requestPetProxy(%s)' % av)
+ self.sendUpdate('requestPetProxy', [av])
+
+ def enterOff(self, ts = 0):
+ self.localToonFsm.requestFinalState()
+ return None
+
+ def exitOff(self):
+ return None
+
+ def enterFaceOff(self, ts = 0):
+ return None
+
+ def exitFaceOff(self):
+ return None
+
+ def enterWaitForJoin(self, ts = 0):
+ self.notify.debug('enterWaitForJoin()')
+ return None
+
+ def exitWaitForJoin(self):
+ return None
+
+ def __enterLocalToonWaitForInput(self):
+ self.notify.debug('enterLocalToonWaitForInput()')
+ camera.setPosHpr(self.camPos, self.camHpr)
+ base.camLens.setFov(self.camMenuFov)
+ NametagGlobals.setMasterArrowsOn(0)
+ self.townBattle.setState('Attack')
+ self.accept(self.localToonBattleEvent, self.__handleLocalToonBattleEvent)
+
+ def startTimer(self, ts = 0):
+ self.notify.debug('startTimer()')
+ if ts >= CLIENT_INPUT_TIMEOUT:
+ self.notify.warning('startTimer() - ts: %f timeout: %f' % (ts, CLIENT_INPUT_TIMEOUT))
+ self.__timedOut()
+ return
+ self.timer.startCallback(CLIENT_INPUT_TIMEOUT - ts, self.__timedOut)
+ timeTask = Task.loop(Task(self.__countdown), Task.pause(0.2))
+ taskMgr.add(timeTask, self.timerCountdownTaskName)
+
+ def __stopTimer(self):
+ self.notify.debug('__stopTimer()')
+ self.timer.stop()
+ taskMgr.remove(self.timerCountdownTaskName)
+
+ def __countdown(self, task):
+ if hasattr(self.townBattle, 'timer'):
+ self.townBattle.updateTimer(int(self.timer.getT()))
+ else:
+ self.notify.warning('__countdown has tried to update a timer that has been deleted. Stopping timer')
+ self.__stopTimer()
+ return Task.done
+
+ def enterWaitForInput(self, ts = 0):
+ self.notify.debug('enterWaitForInput()')
+ if self.interactiveProp:
+ self.interactiveProp.gotoBattleCheer()
+ self.choseAttackAlready = 0
+ if self.localToonActive():
+ self.__enterLocalToonWaitForInput()
+ self.startTimer(ts)
+ if self.needAdjustTownBattle == 1:
+ self.__adjustTownBattle()
+ return None
+
+ def exitWaitForInput(self):
+ self.notify.debug('exitWaitForInput()')
+ if self.localToonActive():
+ self.townBattle.setState('Off')
+ base.camLens.setFov(self.camFov)
+ self.ignore(self.localToonBattleEvent)
+ self.__stopTimer()
+ return None
+
+ def __handleLocalToonBattleEvent(self, response):
+ mode = response['mode']
+ noAttack = 0
+ if mode == 'Attack':
+ self.notify.debug('got an attack')
+ track = response['track']
+ level = response['level']
+ target = response['target']
+ targetId = target
+ if track == HEAL and not levelAffectsGroup(HEAL, level):
+ if target >= 0 and target < len(self.activeToons):
+ targetId = self.activeToons[target].doId
+ else:
+ self.notify.warning('invalid toon target: %d' % target)
+ track = -1
+ level = -1
+ targetId = -1
+ elif track == HEAL and len(self.activeToons) == 1:
+ self.notify.warning('invalid group target for heal')
+ track = -1
+ level = -1
+ elif not attackAffectsGroup(track, level):
+ if target >= 0 and target < len(self.activeSuits):
+ targetId = self.activeSuits[target].doId
+ else:
+ target = -1
+ if len(self.luredSuits) > 0:
+ if track == TRAP or track == LURE and not levelAffectsGroup(LURE, level):
+ if target != -1:
+ suit = self.findSuit(targetId)
+ if self.luredSuits.count(suit) != 0:
+ self.notify.warning('Suit: %d was lured!' % targetId)
+ track = -1
+ level = -1
+ targetId = -1
+ elif track == LURE:
+ if levelAffectsGroup(LURE, level) and len(self.activeSuits) == len(self.luredSuits):
+ self.notify.warning('All suits are lured!')
+ track = -1
+ level = -1
+ targetId = -1
+ if track == TRAP:
+ if target != -1:
+ if attackAffectsGroup(track, level):
+ pass
+ else:
+ suit = self.findSuit(targetId)
+ if suit.battleTrap != NO_TRAP:
+ self.notify.warning('Suit: %d was already trapped!' % targetId)
+ track = -1
+ level = -1
+ targetId = -1
+ self.d_requestAttack(base.localAvatar.doId, track, level, targetId)
+ elif mode == 'Run':
+ self.notify.debug('got a run')
+ self.d_toonRequestRun(base.localAvatar.doId)
+ elif mode == 'SOS':
+ targetId = response['id']
+ self.notify.debug('got an SOS for friend: %d' % targetId)
+ self.d_requestAttack(base.localAvatar.doId, SOS, -1, targetId)
+ elif mode == 'NPCSOS':
+ targetId = response['id']
+ self.notify.debug('got an NPCSOS for friend: %d' % targetId)
+ self.d_requestAttack(base.localAvatar.doId, NPCSOS, -1, targetId)
+ elif mode == 'PETSOS':
+ targetId = response['id']
+ trickId = response['trickId']
+ self.notify.debug('got an PETSOS for pet: %d' % targetId)
+ self.d_requestAttack(base.localAvatar.doId, PETSOS, trickId, targetId)
+ elif mode == 'PETSOSINFO':
+ petProxyId = response['id']
+ self.notify.debug('got a PETSOSINFO for pet: %d' % petProxyId)
+ if base.cr.doId2do.has_key(petProxyId):
+ self.notify.debug('pet: %d was already in the repository' % petProxyId)
+ proxyGenerateMessage = 'petProxy-%d-generated' % petProxyId
+ messenger.send(proxyGenerateMessage)
+ else:
+ self.d_requestPetProxy(base.localAvatar.doId, petProxyId)
+ noAttack = 1
+ elif mode == 'Pass':
+ targetId = response['id']
+ self.notify.debug('got a Pass')
+ self.d_requestAttack(base.localAvatar.doId, PASS, -1, -1)
+ elif mode == 'UnAttack':
+ self.d_requestAttack(base.localAvatar.doId, UN_ATTACK, -1, -1)
+ noAttack = 1
+ elif mode == 'Fire':
+ target = response['target']
+ targetId = self.activeSuits[target].doId
+ self.d_requestAttack(base.localAvatar.doId, FIRE, -1, targetId)
+ else:
+ self.notify.warning('unknown battle response')
+ return
+ if noAttack == 1:
+ self.choseAttackAlready = 0
+ else:
+ self.choseAttackAlready = 1
+
+ def __timedOut(self):
+ if self.choseAttackAlready == 1:
+ return
+ self.notify.debug('WaitForInput timed out')
+ if self.localToonActive():
+ self.notify.debug('battle timed out')
+ self.d_timeout(base.localAvatar.doId)
+
+ def enterMakeMovie(self, ts = 0):
+ self.notify.debug('enterMakeMovie()')
+ return None
+
+ def exitMakeMovie(self):
+ return None
+
+ def enterPlayMovie(self, ts):
+ self.notify.debug('enterPlayMovie()')
+ self.delayDeleteMembers()
+ if self.hasLocalToon():
+ NametagGlobals.setMasterArrowsOn(0)
+ if ToontownBattleGlobals.SkipMovie:
+ self.movie.play(ts, self.__handleMovieDone)
+ self.movie.finish()
+ else:
+ self.movie.play(ts, self.__handleMovieDone)
+ return None
+
+ def __handleMovieDone(self):
+ self.notify.debug('__handleMovieDone()')
+ if self.hasLocalToon():
+ self.d_movieDone(base.localAvatar.doId)
+ self.movie.reset()
+
+ def exitPlayMovie(self):
+ self.notify.debug('exitPlayMovie()')
+ self.movie.reset(finish=1)
+ self._removeMembersKeep()
+ self.townBattleAttacks = ([-1,
+ -1,
+ -1,
+ -1],
+ [-1,
+ -1,
+ -1,
+ -1],
+ [-1,
+ -1,
+ -1,
+ -1],
+ [0,
+ 0,
+ 0,
+ 0])
+ return None
+
+ def hasLocalToon(self):
+ return self.toons.count(base.localAvatar) > 0
+
+ def localToonPendingOrActive(self):
+ return self.pendingToons.count(base.localAvatar) > 0 or self.activeToons.count(base.localAvatar) > 0
+
+ def localToonActive(self):
+ return self.activeToons.count(base.localAvatar) > 0
+
+ def localToonActiveOrRunning(self):
+ return self.activeToons.count(base.localAvatar) > 0 or self.runningToons.count(base.localAvatar) > 0
+
+ def localToonRunning(self):
+ return self.runningToons.count(base.localAvatar) > 0
+
+ def enterHasLocalToon(self):
+ self.notify.debug('enterHasLocalToon()')
+ if base.cr.playGame.getPlace() != None:
+ base.cr.playGame.getPlace().setState('battle', self.localToonBattleEvent)
+ if localAvatar and hasattr(localAvatar, 'inventory') and localAvatar.inventory:
+ localAvatar.inventory.setInteractivePropTrackBonus(self.interactivePropTrackBonus)
+ camera.wrtReparentTo(self)
+ base.camLens.setFov(self.camFov)
+ return
+
+ def exitHasLocalToon(self):
+ self.ignore(self.localToonBattleEvent)
+ self.__stopTimer()
+ if localAvatar and hasattr(localAvatar, 'inventory') and localAvatar.inventory:
+ localAvatar.inventory.setInteractivePropTrackBonus(-1)
+ stateName = None
+ place = base.cr.playGame.getPlace()
+ if place:
+ stateName = place.fsm.getCurrentState().getName()
+ if stateName == 'died':
+ self.movie.reset()
+ camera.reparentTo(render)
+ camera.setPosHpr(localAvatar, 5.2, 5.45, localAvatar.getHeight() * 0.66, 131.5, 3.6, 0)
+ else:
+ camera.wrtReparentTo(base.localAvatar)
+ messenger.send('localToonLeftBattle')
+ base.camLens.setFov(ToontownGlobals.DefaultCameraFov)
+ return
+
+ def enterNoLocalToon(self):
+ self.notify.debug('enterNoLocalToon()')
+ return None
+
+ def exitNoLocalToon(self):
+ return None
+
+ def setSkippingRewardMovie(self):
+ self._skippingRewardMovie = True
+
+ def enterWaitForServer(self):
+ self.notify.debug('enterWaitForServer()')
+ return None
+
+ def exitWaitForServer(self):
+ return None
+
+ def createAdjustInterval(self, av, destPos, destHpr, toon = 0, run = 0):
+ if run == 1:
+ adjustTime = self.calcToonMoveTime(destPos, av.getPos(self))
+ else:
+ adjustTime = self.calcSuitMoveTime(destPos, av.getPos(self))
+ self.notify.debug('creating adjust interval for: %d' % av.doId)
+ adjustTrack = Sequence()
+ if run == 1:
+ adjustTrack.append(Func(av.loop, 'run'))
+ else:
+ adjustTrack.append(Func(av.loop, 'walk'))
+ adjustTrack.append(Func(av.headsUp, self, destPos))
+ adjustTrack.append(LerpPosInterval(av, adjustTime, destPos, other=self))
+ adjustTrack.append(Func(av.setHpr, self, destHpr))
+ adjustTrack.append(Func(av.loop, 'neutral'))
+ return adjustTrack
+
+ def __adjust(self, ts, callback):
+ self.notify.debug('__adjust(%f)' % ts)
+ adjustTrack = Parallel()
+ if len(self.pendingSuits) > 0 or self.suitGone == 1:
+ self.suitGone = 0
+ numSuits = len(self.pendingSuits) + len(self.activeSuits) - 1
+ index = 0
+ for suit in self.activeSuits:
+ point = self.suitPoints[numSuits][index]
+ pos = suit.getPos(self)
+ destPos = point[0]
+ if self.isSuitLured(suit) == 1:
+ destPos = Point3(destPos[0], destPos[1] - MovieUtil.SUIT_LURE_DISTANCE, destPos[2])
+ if pos != destPos:
+ destHpr = VBase3(point[1], 0.0, 0.0)
+ adjustTrack.append(self.createAdjustInterval(suit, destPos, destHpr))
+ index += 1
+
+ for suit in self.pendingSuits:
+ point = self.suitPoints[numSuits][index]
+ destPos = point[0]
+ destHpr = VBase3(point[1], 0.0, 0.0)
+ adjustTrack.append(self.createAdjustInterval(suit, destPos, destHpr))
+ index += 1
+
+ if len(self.pendingToons) > 0 or self.toonGone == 1:
+ self.toonGone = 0
+ numToons = len(self.pendingToons) + len(self.activeToons) - 1
+ index = 0
+ for toon in self.activeToons:
+ point = self.toonPoints[numToons][index]
+ pos = toon.getPos(self)
+ destPos = point[0]
+ if pos != destPos:
+ destHpr = VBase3(point[1], 0.0, 0.0)
+ adjustTrack.append(self.createAdjustInterval(toon, destPos, destHpr))
+ index += 1
+
+ for toon in self.pendingToons:
+ point = self.toonPoints[numToons][index]
+ destPos = point[0]
+ destHpr = VBase3(point[1], 0.0, 0.0)
+ adjustTrack.append(self.createAdjustInterval(toon, destPos, destHpr))
+ index += 1
+
+ if len(adjustTrack) > 0:
+ self.notify.debug('creating adjust multitrack')
+ e = Func(self.__handleAdjustDone)
+ track = Sequence(adjustTrack, e, name=self.adjustName)
+ self.storeInterval(track, self.adjustName)
+ track.start(ts)
+ if ToontownBattleGlobals.SkipMovie:
+ track.finish()
+ else:
+ self.notify.warning('adjust() - nobody needed adjusting')
+ self.__adjustDone()
+
+ def __handleAdjustDone(self):
+ self.notify.debug('__handleAdjustDone() - client adjust finished')
+ self.clearInterval(self.adjustName)
+ self.__adjustDone()
+
+ def __stopAdjusting(self):
+ self.notify.debug('__stopAdjusting()')
+ self.clearInterval(self.adjustName)
+ if self.adjustFsm.getCurrentState().getName() == 'Adjusting':
+ self.adjustFsm.request('NotAdjusting')
+
+ def __requestAdjustTownBattle(self):
+ self.notify.debug('__requestAdjustTownBattle() curstate = %s' % self.fsm.getCurrentState().getName())
+ if self.fsm.getCurrentState().getName() == 'WaitForInput':
+ self.__adjustTownBattle()
+ else:
+ self.needAdjustTownBattle = 1
+
+ def __adjustTownBattle(self):
+ self.notify.debug('__adjustTownBattle()')
+ if self.localToonActive() and len(self.activeSuits) > 0:
+ self.notify.debug('__adjustTownBattle() - adjusting town battle')
+ luredSuits = []
+ for suit in self.luredSuits:
+ if suit not in self.activeSuits:
+ self.notify.error('lured suit not in self.activeSuits')
+ luredSuits.append(self.activeSuits.index(suit))
+
+ trappedSuits = []
+ for suit in self.activeSuits:
+ if suit.battleTrap != NO_TRAP:
+ trappedSuits.append(self.activeSuits.index(suit))
+
+ self.townBattle.adjustCogsAndToons(self.activeSuits, luredSuits, trappedSuits, self.activeToons)
+ if hasattr(self, 'townBattleAttacks'):
+ self.townBattle.updateChosenAttacks(self.townBattleAttacks[0], self.townBattleAttacks[1], self.townBattleAttacks[2], self.townBattleAttacks[3])
+ self.needAdjustTownBattle = 0
+
+ def __adjustDone(self):
+ self.notify.debug('__adjustDone()')
+ if self.hasLocalToon():
+ self.d_adjustDone(base.localAvatar.doId)
+ self.adjustFsm.request('NotAdjusting')
+
+ def enterAdjusting(self, ts):
+ self.notify.debug('enterAdjusting()')
+ if self.localToonActive():
+ self.__stopTimer()
+ self.delayDeleteMembers()
+ self.__adjust(ts, self.__handleAdjustDone)
+ return None
+
+ def exitAdjusting(self):
+ self.notify.debug('exitAdjusting()')
+ self.finishInterval(self.adjustName)
+ self._removeMembersKeep()
+ currStateName = self.fsm.getCurrentState().getName()
+ if currStateName == 'WaitForInput' and self.localToonActive():
+ self.startTimer()
+ return None
+
+ def enterNotAdjusting(self):
+ self.notify.debug('enterNotAdjusting()')
+ return None
+
+ def exitNotAdjusting(self):
+ return None
+
+ def visualize(self):
+ try:
+ self.isVisualized
+ except:
+ self.isVisualized = 0
+
+ if self.isVisualized:
+ self.vis.removeNode()
+ del self.vis
+ self.detachNode()
+ self.isVisualized = 0
+ else:
+ lsegs = LineSegs()
+ lsegs.setColor(0.5, 0.5, 1, 1)
+ lsegs.moveTo(0, 0, 0)
+ for p in BattleBase.allPoints:
+ lsegs.drawTo(p[0], p[1], p[2])
+
+ p = BattleBase.allPoints[0]
+ lsegs.drawTo(p[0], p[1], p[2])
+ self.vis = self.attachNewNode(lsegs.create())
+ self.reparentTo(render)
+ self.isVisualized = 1
+
+ def setupCollisions(self, name):
+ self.lockout = CollisionTube(0, 0, 0, 0, 0, 9, 9)
+ lockoutNode = CollisionNode(name)
+ lockoutNode.addSolid(self.lockout)
+ lockoutNode.setCollideMask(ToontownGlobals.WallBitmask)
+ self.lockoutNodePath = self.attachNewNode(lockoutNode)
+ self.lockoutNodePath.detachNode()
+
+ def removeCollisionData(self):
+ del self.lockout
+ self.lockoutNodePath.removeNode()
+ del self.lockoutNodePath
+
+ def enableCollision(self):
+ self.lockoutNodePath.reparentTo(self)
+ if len(self.toons) < 4:
+ self.accept(self.getCollisionName(), self.__handleLocalToonCollision)
+
+ def __handleLocalToonCollision(self, collEntry):
+ self.notify.debug('localToonCollision')
+ if self.fsm.getCurrentState().getName() == 'Off':
+ self.notify.debug('ignoring collision in Off state')
+ return
+ if not base.localAvatar.wantBattles:
+ return
+ if self._skippingRewardMovie:
+ return
+ base.cr.playGame.getPlace().setState('WaitForBattle')
+ toon = base.localAvatar
+ self.d_toonRequestJoin(toon.doId, toon.getPos(self))
+ base.localAvatar.preBattleHpr = base.localAvatar.getHpr(render)
+ self.localToonFsm.request('WaitForServer')
+ self.onWaitingForJoin()
+
+ def onWaitingForJoin(self):
+ pass
+
+ def denyLocalToonJoin(self):
+ self.notify.debug('denyLocalToonJoin()')
+ place = self.cr.playGame.getPlace()
+ if place.fsm.getCurrentState().getName() == 'WaitForBattle':
+ place.setState('walk')
+ self.localToonFsm.request('NoLocalToon')
+
+ def disableCollision(self):
+ self.ignore(self.getCollisionName())
+ self.lockoutNodePath.detachNode()
+
+ def openBattleCollision(self):
+ if not self.hasLocalToon():
+ self.enableCollision()
+
+ def closeBattleCollision(self):
+ self.ignore(self.getCollisionName())
+
+ def getCollisionName(self):
+ return 'enter' + self.lockoutNodePath.getName()
diff --git a/toontown/battle/DistributedBattleBaseAI.py b/toontown/battle/DistributedBattleBaseAI.py
new file mode 100644
index 0000000..1089a54
--- /dev/null
+++ b/toontown/battle/DistributedBattleBaseAI.py
@@ -0,0 +1,1840 @@
+from otp.ai.AIBase import *
+from direct.distributed.ClockDelta import *
+from BattleBase import *
+from BattleCalculatorAI import *
+from toontown.toonbase.ToontownBattleGlobals import *
+from SuitBattleGlobals import *
+from pandac.PandaModules import *
+import BattleExperienceAI
+from direct.distributed import DistributedObjectAI
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+from toontown.ai import DatabaseObject
+from toontown.toon import DistributedToonAI
+from toontown.toon import InventoryBase
+from toontown.toonbase import ToontownGlobals
+import random
+from toontown.toon import NPCToons
+
+class DistributedBattleBaseAI(DistributedObjectAI.DistributedObjectAI, BattleBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleBaseAI')
+
+ def __init__(self, air, zoneId, finishCallback=None, maxSuits=4, bossBattle=0, tutorialFlag=0, interactivePropTrackBonus=-1):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ self.serialNum = 0
+ self.zoneId = zoneId
+ self.maxSuits = maxSuits
+ self.setBossBattle(bossBattle)
+ self.tutorialFlag = tutorialFlag
+ self.interactivePropTrackBonus = interactivePropTrackBonus
+ self.finishCallback = finishCallback
+ self.avatarExitEvents = []
+ self.responses = {}
+ self.adjustingResponses = {}
+ self.joinResponses = {}
+ self.adjustingSuits = []
+ self.adjustingToons = []
+ self.numSuitsEver = 0
+ BattleBase.__init__(self)
+ self.streetBattle = 1
+ self.pos = Point3(0, 0, 0)
+ self.initialSuitPos = Point3(0, 0, 0)
+ self.toonExp = {}
+ self.toonOrigQuests = {}
+ self.toonItems = {}
+ self.toonOrigMerits = {}
+ self.toonMerits = {}
+ self.toonParts = {}
+ self.battleCalc = BattleCalculatorAI(self, tutorialFlag)
+ if self.air.suitInvasionManager.getInvading():
+ mult = getInvasionMultiplier()
+ self.battleCalc.setSkillCreditMultiplier(mult)
+ if self.air.holidayManager.isMoreXpHolidayRunning():
+ mult = getMoreXpHolidayMultiplier()
+ self.battleCalc.setSkillCreditMultiplier(mult)
+ self.fsm = None
+ self.clearAttacks()
+ self.ignoreFaceOffDone = 0
+ self.needAdjust = 0
+ self.movieHasBeenMade = 0
+ self.movieHasPlayed = 0
+ self.rewardHasPlayed = 0
+ self.movieRequested = 0
+ self.ignoreResponses = 0
+ self.ignoreAdjustingResponses = 0
+ self.taskNames = []
+ self.exitedToons = []
+ self.suitsKilled = []
+ self.suitsKilledThisBattle = []
+ self.suitsKilledPerFloor = []
+ self.suitsEncountered = []
+ self.newToons = []
+ self.newSuits = []
+ self.numNPCAttacks = 0
+ self.npcAttacks = {}
+ self.pets = {}
+ self.fsm = ClassicFSM.ClassicFSM('DistributedBattleAI', [
+ State.State('FaceOff', self.enterFaceOff, self.exitFaceOff, [
+ 'WaitForInput', 'Resume']),
+ State.State('WaitForJoin', self.enterWaitForJoin, self.exitWaitForJoin, [
+ 'WaitForInput', 'Resume']),
+ State.State('WaitForInput', self.enterWaitForInput, self.exitWaitForInput, [
+ 'MakeMovie', 'Resume']),
+ State.State('MakeMovie', self.enterMakeMovie, self.exitMakeMovie, [
+ 'PlayMovie', 'Resume']),
+ State.State('PlayMovie', self.enterPlayMovie, self.exitPlayMovie, [
+ 'WaitForJoin', 'Reward', 'Resume']),
+ State.State('Reward', self.enterReward, self.exitReward, [
+ 'Resume']),
+ State.State('Resume', self.enterResume, self.exitResume, []),
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'FaceOff', 'WaitForJoin'])], 'Off', 'Off')
+ self.joinableFsm = ClassicFSM.ClassicFSM('Joinable', [
+ State.State('Joinable', self.enterJoinable, self.exitJoinable, [
+ 'Unjoinable']),
+ State.State('Unjoinable', self.enterUnjoinable, self.exitUnjoinable, [
+ 'Joinable'])], 'Unjoinable', 'Unjoinable')
+ self.joinableFsm.enterInitialState()
+ self.runableFsm = ClassicFSM.ClassicFSM('Runable', [
+ State.State('Runable', self.enterRunable, self.exitRunable, [
+ 'Unrunable']),
+ State.State('Unrunable', self.enterUnrunable, self.exitUnrunable, [
+ 'Runable'])], 'Unrunable', 'Unrunable')
+ self.runableFsm.enterInitialState()
+ self.adjustFsm = ClassicFSM.ClassicFSM('Adjust', [
+ State.State('Adjusting', self.enterAdjusting, self.exitAdjusting, [
+ 'NotAdjusting', 'Adjusting']),
+ State.State('NotAdjusting', self.enterNotAdjusting, self.exitNotAdjusting, [
+ 'Adjusting'])], 'NotAdjusting', 'NotAdjusting')
+ self.adjustFsm.enterInitialState()
+ self.fsm.enterInitialState()
+ self.startTime = globalClock.getRealTime()
+ self.adjustingTimer = Timer()
+ return
+
+ def clearAttacks(self):
+ self.toonAttacks = {}
+ self.suitAttacks = getDefaultSuitAttacks()
+
+ def requestDelete(self):
+ if hasattr(self, 'fsm'):
+ self.fsm.request('Off')
+ self.__removeTaskName(self.uniqueName('make-movie'))
+ DistributedObjectAI.DistributedObjectAI.requestDelete(self)
+
+ def delete(self):
+ self.notify.debug('deleting battle')
+ self.fsm.request('Off')
+ self.ignoreAll()
+ self.__removeAllTasks()
+ del self.fsm
+ del self.joinableFsm
+ del self.runableFsm
+ del self.adjustFsm
+ self.__cleanupJoinResponses()
+ self.timer.stop()
+ del self.timer
+ self.adjustingTimer.stop()
+ del self.adjustingTimer
+ self.battleCalc.cleanup()
+ del self.battleCalc
+ for suit in self.suits:
+ del suit.battleTrap
+
+ del self.finishCallback
+ for petProxy in self.pets.values():
+ petProxy.requestDelete()
+
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def pause(self):
+ self.timer.stop()
+ self.adjustingTimer.stop()
+
+ def unpause(self):
+ self.timer.resume()
+ self.adjustingTimer.resume()
+
+ def abortBattle(self):
+ self.notify.debug('%s.abortBattle() called.' % self.doId)
+ toonsCopy = self.toons[:]
+ for toonId in toonsCopy:
+ self.__removeToon(toonId)
+ if self.fsm.getCurrentState().getName() == 'PlayMovie' or self.fsm.getCurrentState().getName() == 'MakeMovie':
+ self.exitedToons.append(toonId)
+
+ self.d_setMembers()
+ self.b_setState('Resume')
+ self.__removeAllTasks()
+ self.timer.stop()
+ self.adjustingTimer.stop()
+
+ def findSuit(self, id):
+ for s in self.suits:
+ if s.doId == id:
+ return s
+
+ return None
+
+ def __removeTaskName(self, name):
+ if self.taskNames.count(name):
+ self.taskNames.remove(name)
+ self.notify.debug('removeTaskName() - %s' % name)
+ taskMgr.remove(name)
+
+ def __removeAllTasks(self):
+ for n in self.taskNames:
+ self.notify.debug('removeAllTasks() - %s' % n)
+ taskMgr.remove(n)
+
+ self.taskNames = []
+
+ def __removeToonTasks(self, toonId):
+ name = self.taskName('running-toon-%d' % toonId)
+ self.__removeTaskName(name)
+ name = self.taskName('to-pending-av-%d' % toonId)
+ self.__removeTaskName(name)
+
+ def getLevelDoId(self):
+ return 0
+
+ def getBattleCellId(self):
+ return 0
+
+ def getPosition(self):
+ self.notify.debug('getPosition() - %s' % self.pos)
+ return [
+ self.pos[0], self.pos[1], self.pos[2]]
+
+ def getInitialSuitPos(self):
+ p = []
+ p.append(self.initialSuitPos[0])
+ p.append(self.initialSuitPos[1])
+ p.append(self.initialSuitPos[2])
+ return p
+
+ def setBossBattle(self, bossBattle):
+ self.bossBattle = bossBattle
+
+ def getBossBattle(self):
+ return self.bossBattle
+
+ def b_setState(self, state):
+ self.notify.debug('network:setState(%s)' % state)
+ stime = globalClock.getRealTime() + SERVER_BUFFER_TIME
+ self.sendUpdate('setState', [state, globalClockDelta.localToNetworkTime(stime)])
+ self.setState(state)
+
+ def setState(self, state):
+ self.fsm.request(state)
+
+ def getState(self):
+ return [
+ self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]
+
+ def d_setMembers(self):
+ self.notify.debug('network:setMembers()')
+ self.sendUpdate('setMembers', self.getMembers())
+
+ def getMembers(self):
+ suits = []
+ for s in self.suits:
+ suits.append(s.doId)
+
+ joiningSuits = ''
+ for s in self.joiningSuits:
+ joiningSuits += str(suits.index(s.doId))
+
+ pendingSuits = ''
+ for s in self.pendingSuits:
+ pendingSuits += str(suits.index(s.doId))
+
+ activeSuits = ''
+ for s in self.activeSuits:
+ activeSuits += str(suits.index(s.doId))
+
+ luredSuits = ''
+ for s in self.luredSuits:
+ luredSuits += str(suits.index(s.doId))
+
+ suitTraps = ''
+ for s in self.suits:
+ if s.battleTrap == NO_TRAP:
+ suitTraps += '9'
+ elif s.battleTrap == BattleCalculatorAI.TRAP_CONFLICT:
+ suitTraps += '9'
+ else:
+ suitTraps += str(s.battleTrap)
+
+ toons = []
+ for t in self.toons:
+ toons.append(t)
+
+ joiningToons = ''
+ for t in self.joiningToons:
+ joiningToons += str(toons.index(t))
+
+ pendingToons = ''
+ for t in self.pendingToons:
+ pendingToons += str(toons.index(t))
+
+ activeToons = ''
+ for t in self.activeToons:
+ activeToons += str(toons.index(t))
+
+ runningToons = ''
+ for t in self.runningToons:
+ runningToons += str(toons.index(t))
+
+ self.notify.debug('getMembers() - suits: %s joiningSuits: %s pendingSuits: %s activeSuits: %s luredSuits: %s suitTraps: %s toons: %s joiningToons: %s pendingToons: %s activeToons: %s runningToons: %s' % (suits, joiningSuits, pendingSuits, activeSuits, luredSuits, suitTraps, toons, joiningToons, pendingToons, activeToons, runningToons))
+ return [
+ suits, joiningSuits, pendingSuits, activeSuits, luredSuits, suitTraps, toons, joiningToons, pendingToons, activeToons, runningToons, globalClockDelta.getRealNetworkTime()]
+
+ def d_adjust(self):
+ self.notify.debug('network:adjust()')
+ self.sendUpdate('adjust', [globalClockDelta.getRealNetworkTime()])
+
+ def getInteractivePropTrackBonus(self):
+ return self.interactivePropTrackBonus
+
+ def getZoneId(self):
+ return self.zoneId
+
+ def getTaskZoneId(self):
+ return self.zoneId
+
+ def d_setMovie(self):
+ self.notify.debug('network:setMovie()')
+ self.sendUpdate('setMovie', self.getMovie())
+ self.__updateEncounteredCogs()
+
+ def getMovie(self):
+ suitIds = []
+ for s in self.activeSuits:
+ suitIds.append(s.doId)
+
+ p = [self.movieHasBeenMade]
+ p.append(self.activeToons)
+ p.append(suitIds)
+ for t in self.activeToons:
+ if self.toonAttacks.has_key(t):
+ ta = self.toonAttacks[t]
+ index = -1
+ id = ta[TOON_ID_COL]
+ if id != -1:
+ index = self.activeToons.index(id)
+ track = ta[TOON_TRACK_COL]
+ if (track == NO_ATTACK or attackAffectsGroup(track, ta[TOON_LVL_COL])) and track != NPCSOS and track != PETSOS:
+ target = -1
+ if track == HEAL:
+ if ta[TOON_LVL_COL] == 1:
+ ta[TOON_HPBONUS_COL] = random.randint(0, 10000)
+ elif track == SOS or track == NPCSOS or track == PETSOS:
+ target = ta[TOON_TGT_COL]
+ elif track == HEAL:
+ if self.activeToons.count(ta[TOON_TGT_COL]) != 0:
+ target = self.activeToons.index(ta[TOON_TGT_COL])
+ else:
+ target = -1
+ elif suitIds.count(ta[TOON_TGT_COL]) != 0:
+ target = suitIds.index(ta[TOON_TGT_COL])
+ else:
+ target = -1
+ p = p + [index,
+ track,
+ ta[TOON_LVL_COL],
+ target]
+ p = p + ta[4:]
+ else:
+ index = self.activeToons.index(t)
+ attack = getToonAttack(index)
+ p = p + attack
+
+ for i in range(4 - len(self.activeToons)):
+ p = p + getToonAttack(-1)
+
+ for sa in self.suitAttacks:
+ index = -1
+ id = sa[SUIT_ID_COL]
+ if id != -1:
+ index = suitIds.index(id)
+ if sa[SUIT_ATK_COL] == -1:
+ targetIndex = -1
+ else:
+ targetIndex = sa[SUIT_TGT_COL]
+ if targetIndex == -1:
+ self.notify.debug('suit attack: %d must be group' % sa[SUIT_ATK_COL])
+ else:
+ toonId = self.activeToons[targetIndex]
+ p = p + [index, sa[SUIT_ATK_COL], targetIndex]
+ sa[SUIT_TAUNT_COL] = 0
+ if sa[SUIT_ATK_COL] != -1:
+ suit = self.findSuit(id)
+ sa[SUIT_TAUNT_COL] = getAttackTauntIndexFromIndex(suit, sa[SUIT_ATK_COL])
+ p = p + sa[3:]
+
+ return p
+
+ def d_setChosenToonAttacks(self):
+ self.notify.debug('network:setChosenToonAttacks()')
+ self.sendUpdate('setChosenToonAttacks', self.getChosenToonAttacks())
+
+ def getChosenToonAttacks(self):
+ ids = []
+ tracks = []
+ levels = []
+ targets = []
+ for t in self.activeToons:
+ if self.toonAttacks.has_key(t):
+ ta = self.toonAttacks[t]
+ else:
+ ta = getToonAttack(t)
+ ids.append(t)
+ tracks.append(ta[TOON_TRACK_COL])
+ levels.append(ta[TOON_LVL_COL])
+ targets.append(ta[TOON_TGT_COL])
+
+ return [ids, tracks, levels, targets]
+
+ def d_setBattleExperience(self):
+ self.notify.debug('network:setBattleExperience()')
+ self.sendUpdate('setBattleExperience', self.getBattleExperience())
+
+ def getBattleExperience(self):
+ returnValue = BattleExperienceAI.getBattleExperience(4, self.activeToons, self.toonExp, self.battleCalc.toonSkillPtsGained, self.toonOrigQuests, self.toonItems, self.toonOrigMerits, self.toonMerits, self.toonParts, self.suitsKilled, self.helpfulToons)
+ return returnValue
+
+ def getToonUberStatus(self):
+ fieldList = []
+ uberIndex = LAST_REGULAR_GAG_LEVEL + 1
+ for toon in self.activeToons:
+ toonList = []
+ for trackIndex in range(MAX_TRACK_INDEX):
+ toonList.append(toon.inventory.numItem(track, uberIndex))
+
+ fieldList.append(encodeUber(toonList))
+
+ return fieldList
+
+ def addSuit(self, suit):
+ self.notify.debug('addSuit(%d)' % suit.doId)
+ self.newSuits.append(suit)
+ self.suits.append(suit)
+ suit.battleTrap = NO_TRAP
+ self.numSuitsEver += 1
+
+ def __joinSuit(self, suit):
+ self.joiningSuits.append(suit)
+ toPendingTime = MAX_JOIN_T + SERVER_BUFFER_TIME
+ taskName = self.taskName('to-pending-av-%d' % suit.doId)
+ self.__addJoinResponse(suit.doId, taskName)
+ self.taskNames.append(taskName)
+ taskMgr.doMethodLater(toPendingTime, self.__serverJoinDone, taskName, extraArgs=(suit.doId, taskName))
+
+ def __serverJoinDone(self, avId, taskName):
+ self.notify.debug('join for av: %d timed out on server' % avId)
+ self.__removeTaskName(taskName)
+ self.__makeAvPending(avId)
+ return Task.done
+
+ def __makeAvPending(self, avId):
+ self.notify.debug('__makeAvPending(%d)' % avId)
+ self.__removeJoinResponse(avId)
+ self.__removeTaskName(self.taskName('to-pending-av-%d' % avId))
+ if self.toons.count(avId) > 0:
+ self.joiningToons.remove(avId)
+ self.pendingToons.append(avId)
+ else:
+ suit = self.findSuit(avId)
+ if suit != None:
+ if not suit.isEmpty():
+ if not self.joiningSuits.count(suit) == 1:
+ self.notify.warning('__makeAvPending(%d) in zone: %d' % (avId, self.zoneId))
+ self.notify.warning('toons: %s' % self.toons)
+ self.notify.warning('joining toons: %s' % self.joiningToons)
+ self.notify.warning('pending toons: %s' % self.pendingToons)
+ self.notify.warning('suits: %s' % self.suits)
+ self.notify.warning('joining suits: %s' % self.joiningSuits)
+ self.notify.warning('pending suits: %s' % self.pendingSuits)
+ self.joiningSuits.remove(suit)
+ self.pendingSuits.append(suit)
+ else:
+ self.notify.warning('makeAvPending() %d not in toons or suits' % avId)
+ return
+ self.d_setMembers()
+ self.needAdjust = 1
+ self.__requestAdjust()
+ return
+
+ def suitRequestJoin(self, suit):
+ self.notify.debug('suitRequestJoin(%d)' % suit.getDoId())
+ if self.suitCanJoin():
+ self.addSuit(suit)
+ self.__joinSuit(suit)
+ self.d_setMembers()
+ suit.prepareToJoinBattle()
+ return 1
+ else:
+ self.notify.warning('suitRequestJoin() - not joinable - joinable state: %s max suits: %d' % (self.joinableFsm.getCurrentState().getName(), self.maxSuits))
+ return 0
+
+ def addToon(self, avId):
+ print 'DBB-addToon %s' % avId
+ self.notify.debug('addToon(%d)' % avId)
+ toon = self.getToon(avId)
+ if toon == None:
+ return 0
+ toon.stopToonUp()
+ event = simbase.air.getAvatarExitEvent(avId)
+ self.avatarExitEvents.append(event)
+ self.accept(event, self.__handleUnexpectedExit, extraArgs=[avId])
+ event = 'inSafezone-%s' % avId
+ self.avatarExitEvents.append(event)
+ self.accept(event, self.__handleSuddenExit, extraArgs=[avId, 0])
+ self.newToons.append(avId)
+ self.toons.append(avId)
+ toon = simbase.air.doId2do.get(avId)
+ if toon:
+ if hasattr(self, 'doId'):
+ toon.b_setBattleId(self.doId)
+ else:
+ toon.b_setBattleId(-1)
+ messageToonAdded = 'Battle adding toon %s' % avId
+ messenger.send(messageToonAdded, [avId])
+ if self.fsm != None and self.fsm.getCurrentState().getName() == 'PlayMovie':
+ self.responses[avId] = 1
+ else:
+ self.responses[avId] = 0
+ self.adjustingResponses[avId] = 0
+ if avId not in self.toonExp:
+ p = []
+ for t in Tracks:
+ p.append(toon.experience.getExp(t))
+
+ self.toonExp[avId] = p
+ if avId not in self.toonOrigMerits:
+ self.toonOrigMerits[avId] = toon.cogMerits[:]
+ if avId not in self.toonMerits:
+ self.toonMerits[avId] = [0,
+ 0,
+ 0,
+ 0]
+ if avId not in self.toonOrigQuests:
+ flattenedQuests = []
+ for quest in toon.quests:
+ flattenedQuests.extend(quest)
+
+ self.toonOrigQuests[avId] = flattenedQuests
+ if avId not in self.toonItems:
+ self.toonItems[avId] = ([], [])
+ return 1
+
+ def __joinToon(self, avId, pos):
+ self.joiningToons.append(avId)
+ toPendingTime = MAX_JOIN_T + SERVER_BUFFER_TIME
+ taskName = self.taskName('to-pending-av-%d' % avId)
+ self.__addJoinResponse(avId, taskName, toon=1)
+ taskMgr.doMethodLater(toPendingTime, self.__serverJoinDone, taskName, extraArgs=(avId, taskName))
+ self.taskNames.append(taskName)
+
+ def __updateEncounteredCogs(self):
+ for toon in self.activeToons:
+ if toon in self.newToons:
+ for suit in self.activeSuits:
+ if hasattr(suit, 'dna'):
+ self.suitsEncountered.append({'type': suit.dna.name, 'activeToons': self.activeToons[:]})
+ else:
+ self.notify.warning('Suit has no DNA in zone %s: toons involved = %s' % (self.zoneId, self.activeToons))
+ return
+
+ self.newToons.remove(toon)
+
+ for suit in self.activeSuits:
+ if suit in self.newSuits:
+ if hasattr(suit, 'dna'):
+ self.suitsEncountered.append({'type': suit.dna.name, 'activeToons': self.activeToons[:]})
+ else:
+ self.notify.warning('Suit has no DNA in zone %s: toons involved = %s' % (self.zoneId, self.activeToons))
+ return
+ self.newSuits.remove(suit)
+
+ def __makeToonRun(self, toonId, updateAttacks):
+ self.activeToons.remove(toonId)
+ self.toonGone = 1
+ self.runningToons.append(toonId)
+ taskName = self.taskName('running-toon-%d' % toonId)
+ taskMgr.doMethodLater(TOON_RUN_T, self.__serverRunDone, taskName, extraArgs=(toonId, updateAttacks, taskName))
+ self.taskNames.append(taskName)
+
+ def __serverRunDone(self, toonId, updateAttacks, taskName):
+ self.notify.debug('run for toon: %d timed out on server' % toonId)
+ self.__removeTaskName(taskName)
+ self.__removeToon(toonId)
+ self.d_setMembers()
+ if len(self.toons) == 0:
+ self.notify.debug('last toon is gone - battle is finished')
+ self.b_setState('Resume')
+ else:
+ if updateAttacks == 1:
+ self.d_setChosenToonAttacks()
+ self.needAdjust = 1
+ self.__requestAdjust()
+ return Task.done
+
+ def __requestAdjust(self):
+ if not self.fsm:
+ return
+ cstate = self.fsm.getCurrentState().getName()
+ if cstate == 'WaitForInput' or cstate == 'WaitForJoin':
+ if self.adjustFsm.getCurrentState().getName() == 'NotAdjusting':
+ if self.needAdjust == 1:
+ self.d_adjust()
+ self.adjustingSuits = []
+ for s in self.pendingSuits:
+ self.adjustingSuits.append(s)
+
+ self.adjustingToons = []
+ for t in self.pendingToons:
+ self.adjustingToons.append(t)
+
+ self.adjustFsm.request('Adjusting')
+ else:
+ self.notify.debug('requestAdjust() - dont need to')
+ else:
+ self.notify.debug('requestAdjust() - already adjusting')
+ else:
+ self.notify.debug('requestAdjust() - in state: %s' % cstate)
+
+ def __handleUnexpectedExit(self, avId):
+ disconnectCode = self.air.getAvatarDisconnectReason(avId)
+ self.notify.warning('toon: %d exited unexpectedly, reason %d' % (avId, disconnectCode))
+ userAborted = disconnectCode == ToontownGlobals.DisconnectCloseWindow
+ self.__handleSuddenExit(avId, userAborted)
+
+ def __handleSuddenExit(self, avId, userAborted):
+ self.__removeToon(avId, userAborted=userAborted)
+ if self.fsm.getCurrentState().getName() == 'PlayMovie' or self.fsm.getCurrentState().getName() == 'MakeMovie':
+ self.exitedToons.append(avId)
+ self.d_setMembers()
+ if len(self.toons) == 0:
+ self.notify.debug('last toon is gone - battle is finished')
+ self.__removeAllTasks()
+ self.timer.stop()
+ self.adjustingTimer.stop()
+ self.b_setState('Resume')
+ else:
+ self.needAdjust = 1
+ self.__requestAdjust()
+
+ def __removeSuit(self, suit):
+ self.notify.debug('__removeSuit(%d)' % suit.doId)
+ if self.suits.count(suit) != 0:
+ self.suits.remove(suit)
+ else:
+ self.air.writeServerEvent('suspicious', self.activeToons, 'Trying to remove a suit, with suit count at zero. Probably hacker related.')
+ if self.joiningSuits.count(suit) != 0:
+ self.air.writeServerEvent('suspicious', self.activeToons, 'Trying to remove a suit, but joiningSuits is not zero. Probably hacker related.')
+ if self.pendingSuits.count(suit) != 0:
+ self.air.writeServerEvent('suspicious', self.activeToons, 'Trying to remove a suit, but pendingSuits is not zero. Probably hacker related.')
+ if self.adjustingSuits.count(suit) != 0:
+ self.air.writeServerEvent('suspicious', self.activeToons, 'Trying to remove a suit, but adjustingSuits is not zero. Probably hacker related.')
+ if self.activeSuits.count(suit) != 0:
+ self.activeSuits.remove(suit)
+ else:
+ self.air.writeServerEvent('suspicious', self.activeToons, 'Trying to remove a suit, but has no active suits. Probably hacker related.')
+ if self.luredSuits.count(suit) == 1:
+ self.luredSuits.remove(suit)
+ self.suitGone = 1
+ del suit.battleTrap
+
+ def __removeToon(self, toonId, userAborted=0):
+ self.notify.debug('__removeToon(%d)' % toonId)
+ if self.toons.count(toonId) == 0:
+ return
+ self.battleCalc.toonLeftBattle(toonId)
+ self.__removeToonTasks(toonId)
+ self.toons.remove(toonId)
+ if self.joiningToons.count(toonId) == 1:
+ self.joiningToons.remove(toonId)
+ if self.pendingToons.count(toonId) == 1:
+ self.pendingToons.remove(toonId)
+ if self.activeToons.count(toonId) == 1:
+ self.notify.debug('__removeToon(%d) - suitAttacks : %s' % (toonId, self.suitAttacks))
+ activeToonIdx = self.activeToons.index(toonId)
+ self.notify.debug('removing activeToons[%d], updating suitAttacks SUIT_HP_COL to match' % activeToonIdx)
+ for i in range(len(self.suitAttacks)):
+ if activeToonIdx < len(self.suitAttacks[i][SUIT_HP_COL]):
+ del self.suitAttacks[i][SUIT_HP_COL][activeToonIdx]
+ targetIndex = self.suitAttacks[i][SUIT_TGT_COL]
+ if targetIndex == activeToonIdx:
+ self.suitAttacks[i][SUIT_TGT_COL] = -1
+ elif targetIndex > activeToonIdx:
+ self.suitAttacks[i][SUIT_TGT_COL] = targetIndex - 1
+ else:
+ self.notify.warning("suitAttacks %d doesn't have an HP column for active toon index %d" % (i, activeToonIdx))
+
+ self.activeToons.remove(toonId)
+ if self.runningToons.count(toonId) == 1:
+ self.runningToons.remove(toonId)
+ if self.adjustingToons.count(toonId) == 1:
+ self.notify.warning('removeToon() - toon: %d was adjusting!' % toonId)
+ self.adjustingToons.remove(toonId)
+ self.toonGone = 1
+ if self.pets.has_key(toonId):
+ self.pets[toonId].requestDelete()
+ del self.pets[toonId]
+ self.__removeResponse(toonId)
+ self.__removeAdjustingResponse(toonId)
+ self.__removeJoinResponses(toonId)
+ event = simbase.air.getAvatarExitEvent(toonId)
+ self.avatarExitEvents.remove(event)
+ self.ignore(event)
+ event = 'inSafezone-%s' % toonId
+ self.avatarExitEvents.remove(event)
+ self.ignore(event)
+ toon = simbase.air.doId2do.get(toonId)
+ if toon:
+ toon.b_setBattleId(0)
+ messageToonReleased = 'Battle releasing toon %s' % toon.doId
+ messenger.send(messageToonReleased, [toon.doId])
+ if not userAborted:
+ toon = self.getToon(toonId)
+ if toon != None:
+ toon.hpOwnedByBattle = 0
+ toon.d_setHp(toon.hp)
+ toon.d_setInventory(toon.inventory.makeNetString())
+ self.air.cogPageManager.toonEncounteredCogs(toon, self.suitsEncountered, self.getTaskZoneId())
+ else:
+ if len(self.suits) > 0 and not self.streetBattle:
+ self.notify.info('toon %d aborted non-street battle; clearing inventory and hp.' % toonId)
+ toon = DistributedToonAI.DistributedToonAI(self.air)
+ toon.doId = toonId
+ empty = InventoryBase.InventoryBase(toon)
+ toon.b_setInventory(empty.makeNetString())
+ toon.b_setHp(0)
+ db = DatabaseObject.DatabaseObject(self.air, toonId)
+ db.storeObject(toon, ['setInventory', 'setHp'])
+ self.notify.info('killing mem leak from temporary DistributedToonAI %d' % toonId)
+ toon.deleteDummy()
+ return
+
+ def getToon(self, toonId):
+ if self.air.doId2do.has_key(toonId):
+ return self.air.doId2do[toonId]
+ else:
+ self.notify.warning('getToon() - toon: %d not in repository!' % toonId)
+ return None
+
+ def toonRequestRun(self):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreResponses == 1:
+ self.notify.debug('ignoring response from toon: %d' % toonId)
+ return
+ self.notify.debug('toonRequestRun(%d)' % toonId)
+ if not self.isRunable():
+ self.notify.warning('toonRequestRun() - not runable')
+ return
+ updateAttacks = 0
+ if self.activeToons.count(toonId) == 0:
+ self.notify.warning('toon tried to run, but not found in activeToons: %d' % toonId)
+ return
+ for toon in self.activeToons:
+ if self.toonAttacks.has_key(toon):
+ ta = self.toonAttacks[toon]
+ track = ta[TOON_TRACK_COL]
+ level = ta[TOON_LVL_COL]
+ if ta[TOON_TGT_COL] == toonId or track == HEAL and attackAffectsGroup(track, level) and len(self.activeToons) <= 2:
+ healerId = ta[TOON_ID_COL]
+ self.notify.debug('resetting toon: %ds attack' % healerId)
+ self.toonAttacks[toon] = getToonAttack(toon, track=UN_ATTACK)
+ self.responses[healerId] = 0
+ updateAttacks = 1
+
+ self.__makeToonRun(toonId, updateAttacks)
+ self.d_setMembers()
+ self.needAdjust = 1
+ self.__requestAdjust()
+
+ def toonRequestJoin(self, x, y, z):
+ toonId = self.air.getAvatarIdFromSender()
+ self.notify.debug('toonRequestJoin(%d)' % toonId)
+ self.signupToon(toonId, x, y, z)
+
+ def toonDied(self):
+ toonId = self.air.getAvatarIdFromSender()
+ self.notify.debug('toonDied(%d)' % toonId)
+ if toonId in self.toons:
+ toon = self.getToon(toonId)
+ if toon:
+ toon.hp = -1
+ toon.inventory.zeroInv(1)
+ self.__handleSuddenExit(toonId, 0)
+
+ def signupToon(self, toonId, x, y, z):
+ if self.toons.count(toonId):
+ return
+ if self.toonCanJoin():
+ if self.addToon(toonId):
+ self.__joinToon(toonId, Point3(x, y, z))
+ self.d_setMembers()
+ else:
+ self.notify.warning('toonRequestJoin() - not joinable')
+ self.d_denyLocalToonJoin(toonId)
+
+ def d_denyLocalToonJoin(self, toonId):
+ self.notify.debug('network: denyLocalToonJoin(%d)' % toonId)
+ self.sendUpdateToAvatarId(toonId, 'denyLocalToonJoin', [])
+
+ def resetResponses(self):
+ self.responses = {}
+ for t in self.toons:
+ self.responses[t] = 0
+
+ self.ignoreResponses = 0
+
+ def allToonsResponded(self):
+ for t in self.toons:
+ if self.responses[t] == 0:
+ return 0
+
+ self.ignoreResponses = 1
+ return 1
+
+ def __allPendingActiveToonsResponded(self):
+ for t in self.pendingToons + self.activeToons:
+ if self.responses[t] == 0:
+ return 0
+
+ self.ignoreResponses = 1
+ return 1
+
+ def __allActiveToonsResponded(self):
+ for t in self.activeToons:
+ if self.responses[t] == 0:
+ return 0
+
+ self.ignoreResponses = 1
+ return 1
+
+ def __removeResponse(self, toonId):
+ del self.responses[toonId]
+ if self.ignoreResponses == 0 and len(self.toons) > 0:
+ currStateName = self.fsm.getCurrentState().getName()
+ if currStateName == 'WaitForInput':
+ if self.__allActiveToonsResponded():
+ self.notify.debug('removeResponse() - dont wait for movie')
+ self.__requestMovie()
+ elif currStateName == 'PlayMovie':
+ if self.__allPendingActiveToonsResponded():
+ self.notify.debug('removeResponse() - surprise movie done')
+ self.__movieDone()
+ elif currStateName == 'Reward' or currStateName == 'BuildingReward':
+ if self.__allActiveToonsResponded():
+ self.notify.debug('removeResponse() - surprise reward done')
+ self.handleRewardDone()
+
+ def __resetAdjustingResponses(self):
+ self.adjustingResponses = {}
+ for t in self.toons:
+ self.adjustingResponses[t] = 0
+
+ self.ignoreAdjustingResponses = 0
+
+ def __allAdjustingToonsResponded(self):
+ for t in self.toons:
+ if self.adjustingResponses[t] == 0:
+ return 0
+
+ self.ignoreAdjustingResponses = 1
+ return 1
+
+ def __removeAdjustingResponse(self, toonId):
+ if self.adjustingResponses.has_key(toonId):
+ del self.adjustingResponses[toonId]
+ if self.ignoreAdjustingResponses == 0 and len(self.toons) > 0:
+ if self.__allAdjustingToonsResponded():
+ self.__adjustDone()
+
+ def __addJoinResponse(self, avId, taskName, toon=0):
+ if toon == 1:
+ for jr in self.joinResponses.values():
+ jr[avId] = 0
+
+ self.joinResponses[avId] = {}
+ for t in self.toons:
+ self.joinResponses[avId][t] = 0
+
+ self.joinResponses[avId]['taskName'] = taskName
+
+ def __removeJoinResponses(self, avId):
+ self.__removeJoinResponse(avId)
+ removedOne = 0
+ for j in self.joinResponses.values():
+ if j.has_key(avId):
+ del j[avId]
+ removedOne = 1
+
+ if removedOne == 1:
+ for t in self.joiningToons:
+ if self.__allToonsRespondedJoin(t):
+ self.__makeAvPending(t)
+
+ def __removeJoinResponse(self, avId):
+ if self.joinResponses.has_key(avId):
+ taskMgr.remove(self.joinResponses[avId]['taskName'])
+ del self.joinResponses[avId]
+
+ def __allToonsRespondedJoin(self, avId):
+ jr = self.joinResponses[avId]
+ for t in self.toons:
+ if jr[t] == 0:
+ return 0
+
+ return 1
+
+ def __cleanupJoinResponses(self):
+ for jr in self.joinResponses.values():
+ taskMgr.remove(jr['taskName'])
+ del jr
+
+ def adjustDone(self):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreAdjustingResponses == 1:
+ self.notify.debug('adjustDone() - ignoring toon: %d' % toonId)
+ return
+ else:
+ if self.adjustFsm.getCurrentState().getName() != 'Adjusting':
+ self.notify.warning('adjustDone() - in state %s' % self.fsm.getCurrentState().getName())
+ return
+ else:
+ if self.toons.count(toonId) == 0:
+ self.notify.warning('adjustDone() - toon: %d not in toon list' % toonId)
+ return
+ self.adjustingResponses[toonId] += 1
+ self.notify.debug('toon: %d done adjusting' % toonId)
+ if self.__allAdjustingToonsResponded():
+ self.__adjustDone()
+
+ def timeout(self):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreResponses == 1:
+ self.notify.debug('timeout() - ignoring toon: %d' % toonId)
+ return
+ else:
+ if self.fsm.getCurrentState().getName() != 'WaitForInput':
+ self.notify.warning('timeout() - in state: %s' % self.fsm.getCurrentState().getName())
+ return
+ else:
+ if self.toons.count(toonId) == 0:
+ self.notify.warning('timeout() - toon: %d not in toon list' % toonId)
+ return
+ self.toonAttacks[toonId] = getToonAttack(toonId)
+ self.d_setChosenToonAttacks()
+ self.responses[toonId] += 1
+ self.notify.debug('toon: %d timed out' % toonId)
+ if self.__allActiveToonsResponded():
+ self.__requestMovie(timeout=1)
+
+ def movieDone(self):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreResponses == 1:
+ self.notify.debug('movieDone() - ignoring toon: %d' % toonId)
+ return
+ else:
+ if self.fsm.getCurrentState().getName() != 'PlayMovie':
+ self.notify.warning('movieDone() - in state %s' % self.fsm.getCurrentState().getName())
+ return
+ else:
+ if self.toons.count(toonId) == 0:
+ self.notify.warning('movieDone() - toon: %d not in toon list' % toonId)
+ return
+ self.responses[toonId] += 1
+ self.notify.debug('toon: %d done with movie' % toonId)
+ if self.__allPendingActiveToonsResponded():
+ self.__movieDone()
+ else:
+ self.timer.stop()
+ self.timer.startCallback(TIMEOUT_PER_USER, self.__serverMovieDone)
+
+ def rewardDone(self):
+ toonId = self.air.getAvatarIdFromSender()
+ stateName = self.fsm.getCurrentState().getName()
+ if self.ignoreResponses == 1:
+ self.notify.debug('rewardDone() - ignoring toon: %d' % toonId)
+ return
+ else:
+ if stateName not in ('Reward', 'BuildingReward', 'FactoryReward', 'MintReward',
+ 'StageReward', 'CountryClubReward'):
+ self.notify.warning('rewardDone() - in state %s' % stateName)
+ return
+ else:
+ if self.toons.count(toonId) == 0:
+ self.notify.warning('rewardDone() - toon: %d not in toon list' % toonId)
+ return
+ self.responses[toonId] += 1
+ self.notify.debug('toon: %d done with reward' % toonId)
+ if self.__allActiveToonsResponded():
+ self.handleRewardDone()
+ else:
+ self.timer.stop()
+ self.timer.startCallback(TIMEOUT_PER_USER, self.serverRewardDone)
+
+ def assignRewards(self):
+ if self.rewardHasPlayed == 1:
+ self.notify.debug('handleRewardDone() - reward has already played')
+ return
+ self.rewardHasPlayed = 1
+ BattleExperienceAI.assignRewards(self.activeToons, self.battleCalc.toonSkillPtsGained, self.suitsKilled, self.getTaskZoneId(), self.helpfulToons)
+
+ def joinDone(self, avId):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.toons.count(toonId) == 0:
+ self.notify.warning('joinDone() - toon: %d not in toon list' % toonId)
+ return
+ if not self.joinResponses.has_key(avId):
+ self.notify.debug('joinDone() - no entry for: %d - ignoring: %d' % (avId, toonId))
+ return
+ jr = self.joinResponses[avId]
+ if jr.has_key(toonId):
+ jr[toonId] += 1
+ self.notify.debug('client with localToon: %d done joining av: %d' % (toonId, avId))
+ if self.__allToonsRespondedJoin(avId):
+ self.__makeAvPending(avId)
+
+ def requestAttack(self, track, level, av):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreResponses == 1:
+ self.notify.debug('requestAttack() - ignoring toon: %d' % toonId)
+ return
+ else:
+ if self.fsm.getCurrentState().getName() != 'WaitForInput':
+ self.notify.warning('requestAttack() - in state: %s' % self.fsm.getCurrentState().getName())
+ return
+ else:
+ if self.activeToons.count(toonId) == 0:
+ self.notify.warning('requestAttack() - toon: %d not in toon list' % toonId)
+ return
+ self.notify.debug('requestAttack(%d, %d, %d, %d)' % (toonId, track, level, av))
+ toon = self.getToon(toonId)
+ if toon == None:
+ self.notify.warning('requestAttack() - no toon: %d' % toonId)
+ return
+ validResponse = 1
+ if toon.getGameAccess() != ToontownGlobals.AccessFull:
+ if track in [HEAL, TRAP, LURE, SOUND, THROW, SQUIRT, DROP] and gagIsPaidOnly(track, level):
+ self.air.writeServerEvent('suspicious', toonId, 'requestAttack: non-paid player requesting attack with paid gag')
+ return
+
+ if track == SOS:
+ self.notify.debug('toon: %d calls for help' % toonId)
+ self.air.writeServerEvent('friendSOS', toonId, '%s' % av)
+ self.toonAttacks[toonId] = getToonAttack(toonId, track=SOS, target=av)
+ elif track == NPCSOS:
+ self.notify.debug('toon: %d calls for help' % toonId)
+ self.air.writeServerEvent('NPCSOS', toonId, '%s' % av)
+ toon = self.getToon(toonId)
+ if toon == None:
+ return
+ if toon.NPCFriendsDict.has_key(av):
+ npcCollision = 0
+ if self.npcAttacks.has_key(av):
+ callingToon = self.npcAttacks[av]
+ if self.activeToons.count(callingToon) == 1:
+ self.toonAttacks[toonId] = getToonAttack(toonId, track=PASS)
+ npcCollision = 1
+ if npcCollision == 0:
+ self.toonAttacks[toonId] = getToonAttack(toonId, track=NPCSOS, level=5, target=av)
+ self.numNPCAttacks += 1
+ self.npcAttacks[av] = toonId
+ elif track == PETSOS:
+ self.notify.debug('toon: %d calls for pet: %d' % (toonId, av))
+ self.air.writeServerEvent('PETSOS', toonId, '%s' % av)
+ toon = self.getToon(toonId)
+ if toon == None:
+ return
+ if not self.validate(toonId, level in toon.petTrickPhrases, 'requestAttack: invalid pet trickId: %s' % level):
+ return
+ self.toonAttacks[toonId] = getToonAttack(toonId, track=PETSOS, level=level, target=av)
+ elif track == UN_ATTACK:
+ self.notify.debug('toon: %d changed its mind' % toonId)
+ self.toonAttacks[toonId] = getToonAttack(toonId, track=UN_ATTACK)
+ if self.responses.has_key(toonId):
+ self.responses[toonId] = 0
+ validResponse = 0
+ elif track == PASS:
+ self.toonAttacks[toonId] = getToonAttack(toonId, track=PASS)
+ elif track == FIRE:
+ self.toonAttacks[toonId] = getToonAttack(toonId, track=FIRE, target=av)
+ else:
+ if not self.validate(toonId, track >= 0 and track <= MAX_TRACK_INDEX, 'requestAttack: invalid track %s' % track):
+ return
+ if not self.validate(toonId, level >= 0 and level <= MAX_LEVEL_INDEX, 'requestAttack: invalid level %s' % level):
+ return
+ if toon.inventory.numItem(track, level) == 0:
+ self.notify.warning('requestAttack() - toon has no item track: %d level: %d' % (track, level))
+ self.toonAttacks[toonId] = getToonAttack(toonId)
+ return
+ if track == HEAL:
+ if self.runningToons.count(av) == 1 or attackAffectsGroup(track, level) and len(self.activeToons) < 2:
+ self.toonAttacks[toonId] = getToonAttack(toonId, track=UN_ATTACK)
+ validResponse = 0
+ else:
+ self.toonAttacks[toonId] = getToonAttack(toonId, track=track, level=level, target=av)
+ else:
+ self.toonAttacks[toonId] = getToonAttack(toonId, track=track, level=level, target=av)
+ if av == -1 and not attackAffectsGroup(track, level):
+ validResponse = 0
+ self.d_setChosenToonAttacks()
+ if validResponse == 1:
+ self.responses[toonId] += 1
+ self.notify.debug('toon: %d chose an attack' % toonId)
+ if self.__allActiveToonsResponded():
+ self.__requestMovie()
+ return
+
+ def requestPetProxy(self, av):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreResponses == 1:
+ self.notify.debug('requestPetProxy() - ignoring toon: %d' % toonId)
+ return
+ else:
+ if self.fsm.getCurrentState().getName() != 'WaitForInput':
+ self.notify.warning('requestPetProxy() - in state: %s' % self.fsm.getCurrentState().getName())
+ return
+ else:
+ if self.activeToons.count(toonId) == 0:
+ self.notify.warning('requestPetProxy() - toon: %d not in toon list' % toonId)
+ return
+ self.notify.debug('requestPetProxy(%s, %s)' % (toonId, av))
+ toon = self.getToon(toonId)
+ if toon == None:
+ self.notify.warning('requestPetProxy() - no toon: %d' % toonId)
+ return
+ petId = toon.getPetId()
+ zoneId = self.zoneId
+ if petId == av:
+ if not self.pets.has_key(toonId):
+
+ def handleGetPetProxy(success, petProxy, petId=petId, zoneId=zoneId, toonId=toonId):
+ if success:
+ if petId not in simbase.air.doId2do:
+ simbase.air.requestDeleteDoId(petId)
+ else:
+ petDO = simbase.air.doId2do[petId]
+ petDO.requestDelete()
+ simbase.air.deleteDistObject(petDO)
+ petProxy.dbObject = 1
+ petProxy.generateWithRequiredAndId(petId, self.air.districtId, zoneId)
+ petProxy.broadcastDominantMood()
+ self.pets[toonId] = petProxy
+ else:
+ self.notify.warning('error generating petProxy: %s' % petId)
+
+ self.getPetProxyObject(petId, handleGetPetProxy)
+ return
+
+ def suitCanJoin(self):
+ return len(self.suits) < self.maxSuits and self.isJoinable()
+
+ def toonCanJoin(self):
+ return len(self.toons) < 4 and self.isJoinable()
+
+ def __requestMovie(self, timeout=0):
+ if self.adjustFsm.getCurrentState().getName() == 'Adjusting':
+ self.notify.debug('__requestMovie() - in Adjusting')
+ self.movieRequested = 1
+ else:
+ movieDelay = 0
+ if len(self.activeToons) == 0:
+ self.notify.warning('only pending toons left in battle %s, toons = %s' % (self.doId, self.toons))
+ else:
+ if len(self.activeSuits) == 0:
+ self.notify.warning('only pending suits left in battle %s, suits = %s' % (self.doId, self.suits))
+ else:
+ if len(self.activeToons) > 1 and not timeout:
+ movieDelay = 1
+ self.fsm.request('MakeMovie')
+ if movieDelay:
+ taskMgr.doMethodLater(0.8, self.__makeMovie, self.uniqueName('make-movie'))
+ self.taskNames.append(self.uniqueName('make-movie'))
+ else:
+ self.__makeMovie()
+
+ def __makeMovie(self, task=None):
+ self.notify.debug('makeMovie()')
+ if self._DOAI_requestedDelete:
+ self.notify.warning('battle %s requested delete, then __makeMovie was called!' % self.doId)
+ if hasattr(self, 'levelDoId'):
+ self.notify.warning('battle %s in level %s' % (self.doId, self.levelDoId))
+ return
+ self.__removeTaskName(self.uniqueName('make-movie'))
+ if self.movieHasBeenMade == 1:
+ self.notify.debug('__makeMovie() - movie has already been made')
+ return
+ self.movieRequested = 0
+ self.movieHasBeenMade = 1
+ self.movieHasPlayed = 0
+ self.rewardHasPlayed = 0
+ for t in self.activeToons:
+ if not self.toonAttacks.has_key(t):
+ self.toonAttacks[t] = getToonAttack(t)
+ attack = self.toonAttacks[t]
+ if attack[TOON_TRACK_COL] == PASS or attack[TOON_TRACK_COL] == UN_ATTACK:
+ self.toonAttacks[t] = getToonAttack(t)
+ if self.toonAttacks[t][TOON_TRACK_COL] != NO_ATTACK:
+ self.addHelpfulToon(t)
+
+ self.battleCalc.calculateRound()
+ for t in self.activeToons:
+ self.sendEarnedExperience(t)
+ toon = self.getToon(t)
+ if toon != None:
+ toon.hpOwnedByBattle = 1
+ if toon.immortalMode:
+ toon.toonUp(toon.maxHp)
+
+ self.d_setMovie()
+ self.b_setState('PlayMovie')
+ return Task.done
+
+ def sendEarnedExperience(self, toonId):
+ toon = self.getToon(toonId)
+ if toon != None:
+ expList = self.battleCalc.toonSkillPtsGained.get(toonId, None)
+ if expList == None:
+ toon.d_setEarnedExperience([])
+ else:
+ roundList = []
+ for exp in expList:
+ roundList.append(int(exp + 0.5))
+
+ toon.d_setEarnedExperience(roundList)
+ return
+
+ def enterOff(self):
+ return None
+
+ def exitOff(self):
+ return None
+
+ def enterFaceOff(self):
+ return None
+
+ def exitFaceOff(self):
+ return None
+
+ def enterWaitForJoin(self):
+ self.notify.debug('enterWaitForJoin()')
+ if len(self.activeSuits) > 0:
+ self.b_setState('WaitForInput')
+ else:
+ self.notify.debug('enterWaitForJoin() - no active suits')
+ self.runableFsm.request('Runable')
+ self.resetResponses()
+ self.__requestAdjust()
+ return None
+
+ def exitWaitForJoin(self):
+ return None
+
+ def enterWaitForInput(self):
+ self.notify.debug('enterWaitForInput()')
+ self.joinableFsm.request('Joinable')
+ self.runableFsm.request('Runable')
+ self.resetResponses()
+ self.__requestAdjust()
+ if not self.tutorialFlag:
+ self.timer.startCallback(SERVER_INPUT_TIMEOUT, self.__serverTimedOut)
+ self.npcAttacks = {}
+ for toonId in self.toons:
+ if bboard.get('autoRestock-%s' % toonId, False):
+ toon = self.air.doId2do.get(toonId)
+ if toon is not None:
+ toon.doRestock(0)
+
+ return None
+
+ def exitWaitForInput(self):
+ self.npcAttacks = {}
+ self.timer.stop()
+ return None
+
+ def __serverTimedOut(self):
+ self.notify.debug('wait for input timed out on server')
+ self.ignoreResponses = 1
+ self.__requestMovie(timeout=1)
+
+ def enterMakeMovie(self):
+ self.notify.debug('enterMakeMovie()')
+ self.runableFsm.request('Unrunable')
+ self.resetResponses()
+ return None
+
+ def exitMakeMovie(self):
+ return None
+
+ def enterPlayMovie(self):
+ self.notify.debug('enterPlayMovie()')
+ self.joinableFsm.request('Joinable')
+ self.runableFsm.request('Unrunable')
+ self.resetResponses()
+ movieTime = TOON_ATTACK_TIME * (len(self.activeToons) + self.numNPCAttacks) + SUIT_ATTACK_TIME * len(self.activeSuits) + SERVER_BUFFER_TIME
+ self.numNPCAttacks = 0
+ self.notify.debug('estimated upper bound of movie time: %f' % movieTime)
+ self.timer.startCallback(movieTime, self.__serverMovieDone)
+
+ def __serverMovieDone(self):
+ self.notify.debug('movie timed out on server')
+ self.ignoreResponses = 1
+ self.__movieDone()
+
+ def serverRewardDone(self):
+ self.notify.debug('reward timed out on server')
+ self.ignoreResponses = 1
+ self.handleRewardDone()
+
+ def handleRewardDone(self):
+ self.b_setState('Resume')
+
+ def exitPlayMovie(self):
+ self.timer.stop()
+ return None
+
+ def __movieDone(self):
+ self.notify.debug('__movieDone() - movie is finished')
+ if self.movieHasPlayed == 1:
+ self.notify.debug('__movieDone() - movie had already finished')
+ return
+ self.movieHasBeenMade = 0
+ self.movieHasPlayed = 1
+ self.ignoreResponses = 1
+ needUpdate = 0
+ toonHpDict = {}
+ for toon in self.activeToons:
+ toonHpDict[toon] = [0, 0, 0]
+ actualToon = self.getToon(toon)
+ self.notify.debug('BEFORE ROUND: toon: %d hp: %d' % (toon, actualToon.hp))
+
+ deadSuits = []
+ trapDict = {}
+ suitsLuredOntoTraps = []
+ npcTrapAttacks = []
+ for activeToon in self.activeToons + self.exitedToons:
+ if self.toonAttacks.has_key(activeToon):
+ attack = self.toonAttacks[activeToon]
+ track = attack[TOON_TRACK_COL]
+ npc_level = None
+ if track == NPCSOS:
+ track, npc_level, npc_hp = NPCToons.getNPCTrackLevelHp(attack[TOON_TGT_COL])
+ if track == None:
+ track = NPCSOS
+ elif track == TRAP:
+ npcTrapAttacks.append(attack)
+ toon = self.getToon(attack[TOON_ID_COL])
+ av = attack[TOON_TGT_COL]
+ if toon:
+ toon.attemptSubtractNPCFriend(av)
+ continue
+ if track != NO_ATTACK:
+ toonId = attack[TOON_ID_COL]
+ level = attack[TOON_LVL_COL]
+ if npc_level != None:
+ level = npc_level
+ if attack[TOON_TRACK_COL] == NPCSOS:
+ toon = self.getToon(toonId)
+ av = attack[TOON_TGT_COL]
+ if toon:
+ toon.attemptSubtractNPCFriend(av)
+ elif track == PETSOS:
+ pass
+ elif track == FIRE:
+ pass
+ elif track != SOS:
+ toon = self.getToon(toonId)
+ if toon != None:
+ check = toon.inventory.useItem(track, level)
+ if check == -1:
+ self.air.writeServerEvent('suspicious', toonId, 'Toon generating movie for non-existant gag track %s level %s' % (track, level))
+ self.notify.warning('generating movie for non-existant gag track %s level %s! avId: %s' % (track, level, toonId))
+ if not toon.hasTrackAccess(track):
+ self.air.writeServerEvent('suspicious', toonId, 'Toon trying to throw gag on track they do not have access to (gag track %s level %s)' % (track, level))
+ toon.d_setInventory(toon.inventory.makeNetString())
+ hps = attack[TOON_HP_COL]
+ if track == SOS:
+ self.notify.debug('toon: %d called for help' % toonId)
+ elif track == NPCSOS:
+ self.notify.debug('toon: %d called for help' % toonId)
+ elif track == PETSOS:
+ self.notify.debug('toon: %d called for pet' % toonId)
+ for i in range(len(self.activeToons)):
+ toon = self.getToon(self.activeToons[i])
+ if toon != None:
+ if i < len(hps):
+ hp = hps[i]
+ if hp > 0:
+ toonHpDict[toon.doId][0] += hp
+ self.notify.debug('pet heal: toon: %d healed for hp: %d' % (toon.doId, hp))
+ else:
+ self.notify.warning('Invalid targetIndex %s in hps %s.' % (i, hps))
+
+ elif track == NPC_RESTOCK_GAGS:
+ for at in self.activeToons:
+ toon = self.getToon(at)
+ if toon != None:
+ toon.inventory.NPCMaxOutInv(npc_level)
+ toon.d_setInventory(toon.inventory.makeNetString())
+
+ elif track == HEAL:
+ if levelAffectsGroup(HEAL, level):
+ for i in range(len(self.activeToons)):
+ at = self.activeToons[i]
+ if at != toonId or attack[TOON_TRACK_COL] == NPCSOS:
+ toon = self.getToon(at)
+ if toon != None:
+ if i < len(hps):
+ hp = hps[i]
+ else:
+ self.notify.warning('Invalid targetIndex %s in hps %s.' % (i, hps))
+ hp = 0
+ toonHpDict[toon.doId][0] += hp
+ self.notify.debug('HEAL: toon: %d healed for hp: %d' % (toon.doId, hp))
+
+ else:
+ targetId = attack[TOON_TGT_COL]
+ toon = self.getToon(targetId)
+ if toon != None and targetId in self.activeToons:
+ targetIndex = self.activeToons.index(targetId)
+ if targetIndex < len(hps):
+ hp = hps[targetIndex]
+ else:
+ self.notify.warning('Invalid targetIndex %s in hps %s.' % (targetIndex, hps))
+ hp = 0
+ toonHpDict[toon.doId][0] += hp
+ elif attackAffectsGroup(track, level, attack[TOON_TRACK_COL]):
+ for suit in self.activeSuits:
+ targetIndex = self.activeSuits.index(suit)
+ if targetIndex < 0 or targetIndex >= len(hps):
+ self.notify.warning('Got attack (%s, %s) on target suit %s, but hps has only %s entries: %s' % (track,
+ level,
+ targetIndex,
+ len(hps),
+ hps))
+ else:
+ hp = hps[targetIndex]
+ if hp > 0 and track == LURE:
+ if suit.battleTrap == UBER_GAG_LEVEL_INDEX:
+ pass
+ suit.battleTrap = NO_TRAP
+ needUpdate = 1
+ if trapDict.has_key(suit.doId):
+ del trapDict[suit.doId]
+ if suitsLuredOntoTraps.count(suit) == 0:
+ suitsLuredOntoTraps.append(suit)
+ if track == TRAP:
+ targetId = suit.doId
+ if trapDict.has_key(targetId):
+ trapDict[targetId].append(attack)
+ else:
+ trapDict[targetId] = [attack]
+ needUpdate = 1
+ died = attack[SUIT_DIED_COL] & 1 << targetIndex
+ if died != 0:
+ if deadSuits.count(suit) == 0:
+ deadSuits.append(suit)
+
+ else:
+ targetId = attack[TOON_TGT_COL]
+ target = self.findSuit(targetId)
+ if target != None:
+ targetIndex = self.activeSuits.index(target)
+ if targetIndex < 0 or targetIndex >= len(hps):
+ self.notify.warning('Got attack (%s, %s) on target suit %s, but hps has only %s entries: %s' % (track,
+ level,
+ targetIndex,
+ len(hps),
+ hps))
+ else:
+ hp = hps[targetIndex]
+ if track == TRAP:
+ if trapDict.has_key(targetId):
+ trapDict[targetId].append(attack)
+ else:
+ trapDict[targetId] = [attack]
+ if hp > 0 and track == LURE:
+ oldBattleTrap = target.battleTrap
+ if oldBattleTrap == UBER_GAG_LEVEL_INDEX:
+ pass
+ target.battleTrap = NO_TRAP
+ needUpdate = 1
+ if trapDict.has_key(target.doId):
+ del trapDict[target.doId]
+ if suitsLuredOntoTraps.count(target) == 0:
+ suitsLuredOntoTraps.append(target)
+ if oldBattleTrap == UBER_GAG_LEVEL_INDEX:
+ for otherSuit in self.activeSuits:
+ if not otherSuit == target:
+ otherSuit.battleTrap = NO_TRAP
+ if trapDict.has_key(otherSuit.doId):
+ del trapDict[otherSuit.doId]
+
+ died = attack[SUIT_DIED_COL] & 1 << targetIndex
+ if died != 0:
+ if deadSuits.count(target) == 0:
+ deadSuits.append(target)
+
+ self.exitedToons = []
+ for suitKey in trapDict.keys():
+ attackList = trapDict[suitKey]
+ attack = attackList[0]
+ target = self.findSuit(attack[TOON_TGT_COL])
+ if attack[TOON_LVL_COL] == UBER_GAG_LEVEL_INDEX:
+ targetId = suitKey
+ target = self.findSuit(targetId)
+ if len(attackList) == 1:
+ if suitsLuredOntoTraps.count(target) == 0:
+ self.notify.debug('movieDone() - trap set')
+ target.battleTrap = attack[TOON_LVL_COL]
+ needUpdate = 1
+ else:
+ target.battleTrap = NO_TRAP
+ else:
+ self.notify.debug('movieDone() - traps collided')
+ if target != None:
+ target.battleTrap = NO_TRAP
+
+ if self.battleCalc.trainTrapTriggered:
+ self.notify.debug('Train trap triggered, clearing all traps')
+ for otherSuit in self.activeSuits:
+ self.notify.debug('suit =%d, oldBattleTrap=%d' % (otherSuit.doId, otherSuit.battleTrap))
+ otherSuit.battleTrap = NO_TRAP
+
+ currLuredSuits = self.battleCalc.getLuredSuits()
+ if len(self.luredSuits) == len(currLuredSuits):
+ for suit in self.luredSuits:
+ if currLuredSuits.count(suit.doId) == 0:
+ needUpdate = 1
+ break
+
+ else:
+ needUpdate = 1
+ self.luredSuits = []
+ for i in currLuredSuits:
+ suit = self.air.doId2do[i]
+ self.luredSuits.append(suit)
+ self.notify.debug('movieDone() - suit: %d is lured' % i)
+
+ for attack in npcTrapAttacks:
+ track, level, hp = NPCToons.getNPCTrackLevelHp(attack[TOON_TGT_COL])
+ for suit in self.activeSuits:
+ if self.luredSuits.count(suit) == 0 and suit.battleTrap == NO_TRAP:
+ suit.battleTrap = level
+
+ needUpdate = 1
+
+ for suit in deadSuits:
+ self.notify.debug('removing dead suit: %d' % suit.doId)
+ if suit.isDeleted():
+ self.notify.debug('whoops, suit %d is deleted.' % suit.doId)
+ else:
+ self.notify.debug('suit had revives? %d' % suit.getMaxSkeleRevives())
+ encounter = {'type': suit.dna.name,
+ 'level': suit.getActualLevel(),
+ 'track': suit.dna.dept,
+ 'isSkelecog': suit.getSkelecog(),
+ 'isForeman': suit.isForeman(),
+ 'isVP': 0,
+ 'isCFO': 0,
+ 'isSupervisor': suit.isSupervisor(),
+ 'isVirtual': suit.isVirtual(),
+ 'hasRevives': suit.getMaxSkeleRevives(),
+ 'activeToons': self.activeToons[:]}
+ self.suitsKilled.append(encounter)
+ self.suitsKilledThisBattle.append(encounter)
+ self.__removeSuit(suit)
+ needUpdate = 1
+ suit.resume()
+
+ lastActiveSuitDied = 0
+ if len(self.activeSuits) == 0 and len(self.pendingSuits) == 0:
+ lastActiveSuitDied = 1
+ self.notify.debug('calculate hit points, %s' % self.suitAttacks)
+ for i in range(4):
+ attack = self.suitAttacks[i][SUIT_ATK_COL]
+ if attack != NO_ATTACK:
+ suitId = self.suitAttacks[i][SUIT_ID_COL]
+ suit = self.findSuit(suitId)
+ if suit == None:
+ self.notify.warning('movieDone() - suit: %d is gone!' % suitId)
+ continue
+ if not (hasattr(suit, 'dna') and suit.dna):
+ toonId = self.air.getAvatarIdFromSender()
+ self.notify.warning('_movieDone avoiding crash, sender=%s but suit has no dna' % toonId)
+ self.air.writeServerEvent('suspicious', toonId, '_movieDone avoiding crash, suit has no dna')
+ continue
+ adict = getSuitAttack(suit.getStyleName(), suit.getLevel(), attack)
+ hps = self.suitAttacks[i][SUIT_HP_COL]
+ if adict['group'] == ATK_TGT_GROUP:
+ for activeToon in self.activeToons:
+ toon = self.getToon(activeToon)
+ if toon != None:
+ targetIndex = self.activeToons.index(activeToon)
+ toonDied = self.suitAttacks[i][TOON_DIED_COL] & 1 << targetIndex
+ if targetIndex >= len(hps):
+ self.notify.warning('DAMAGE GRP: toon %s is no longer in battle!' % activeToon)
+ else:
+ hp = hps[targetIndex]
+ if hp > 0:
+ self.notify.debug('DAMAGE GRP: toon: %d hit for dmg: %d' % (activeToon, hp))
+ if toonDied != 0:
+ toonHpDict[toon.doId][2] = 1
+ toonHpDict[toon.doId][1] += hp
+
+ elif adict['group'] == ATK_TGT_SINGLE:
+ targetIndex = self.suitAttacks[i][SUIT_TGT_COL]
+ if targetIndex >= len(self.activeToons):
+ self.notify.warning('movieDone() - toon: %d gone!' % targetIndex)
+ break
+ if targetIndex < 0:
+ self.notify.warning('movieDone() - target index is for an already gone toon!')
+ continue
+ toonId = self.activeToons[targetIndex]
+ toon = self.getToon(toonId)
+ toonDied = self.suitAttacks[i][TOON_DIED_COL] & 1 << targetIndex
+ if targetIndex >= len(hps):
+ self.notify.warning('DAMAGE SGL: toon %s is no longer in battle!' % toonId)
+ else:
+ hp = hps[targetIndex]
+ if hp > 0:
+ self.notify.debug('DAMAGE SGL: toon: %d hit for dmg: %d' % (toonId, hp))
+ if toonDied != 0:
+ toonHpDict[toon.doId][2] = 1
+ toonHpDict[toon.doId][1] += hp
+
+ deadToons = []
+ for activeToon in self.activeToons:
+ hp = toonHpDict[activeToon]
+ toon = self.getToon(activeToon)
+ if toon != None:
+ self.notify.debug('AFTER ROUND: currtoonHP: %d toonMAX: %d hheal: %d damage: %d' % (toon.hp,
+ toon.maxHp,
+ hp[0],
+ hp[1]))
+ toon.hpOwnedByBattle = 0
+ hpDelta = hp[0] - hp[1]
+ if hpDelta >= 0:
+ toon.toonUp(hpDelta, quietly=1)
+ else:
+ toon.takeDamage(-hpDelta, quietly=1)
+ if toon.hp <= 0:
+ self.notify.debug('movieDone() - toon: %d was killed' % activeToon)
+ toon.inventory.zeroInv(1)
+ deadToons.append(activeToon)
+ self.notify.debug('AFTER ROUND: toon: %d setHp: %d' % (toon.doId, toon.hp))
+
+ for deadToon in deadToons:
+ self.__removeToon(deadToon)
+ needUpdate = 1
+
+ self.clearAttacks()
+ self.d_setMovie()
+ self.d_setChosenToonAttacks()
+ self.localMovieDone(needUpdate, deadToons, deadSuits, lastActiveSuitDied)
+ return
+
+ def enterResume(self):
+ for suit in self.suits:
+ self.notify.info('battle done, resuming suit: %d' % suit.doId)
+ if suit.isDeleted():
+ self.notify.info('whoops, suit %d is deleted.' % suit.doId)
+ else:
+ suit.resume()
+
+ self.suits = []
+ self.joiningSuits = []
+ self.pendingSuits = []
+ self.adjustingSuits = []
+ self.activeSuits = []
+ self.luredSuits = []
+ for toonId in self.toons:
+ toon = simbase.air.doId2do.get(toonId)
+ if toon:
+ toon.b_setBattleId(0)
+ messageToonReleased = 'Battle releasing toon %s' % toon.doId
+ messenger.send(messageToonReleased, [toon.doId])
+
+ for exitEvent in self.avatarExitEvents:
+ self.ignore(exitEvent)
+
+ eventMsg = {}
+ for encounter in self.suitsKilledThisBattle:
+ cog = encounter['type']
+ level = encounter['level']
+ msgName = '%s%s' % (cog, level)
+ if encounter['isSkelecog']:
+ msgName += '+'
+ if eventMsg.has_key(msgName):
+ eventMsg[msgName] += 1
+ else:
+ eventMsg[msgName] = 1
+
+ msgText = ''
+ for msgName, count in eventMsg.items():
+ if msgText != '':
+ msgText += ','
+ msgText += '%s%s' % (count, msgName)
+
+ self.air.writeServerEvent('battleCogsDefeated', self.doId, '%s|%s' % (msgText, self.getTaskZoneId()))
+
+ def exitResume(self):
+ pass
+
+ def isJoinable(self):
+ return self.joinableFsm.getCurrentState().getName() == 'Joinable'
+
+ def enterJoinable(self):
+ self.notify.debug('enterJoinable()')
+ return None
+
+ def exitJoinable(self):
+ return None
+
+ def enterUnjoinable(self):
+ self.notify.debug('enterUnjoinable()')
+ return None
+
+ def exitUnjoinable(self):
+ return None
+
+ def isRunable(self):
+ return self.runableFsm.getCurrentState().getName() == 'Runable'
+
+ def enterRunable(self):
+ self.notify.debug('enterRunable()')
+ return None
+
+ def exitRunable(self):
+ return None
+
+ def enterUnrunable(self):
+ self.notify.debug('enterUnrunable()')
+ return None
+
+ def exitUnrunable(self):
+ return None
+
+ def __estimateAdjustTime(self):
+ self.needAdjust = 0
+ adjustTime = 0
+ if len(self.pendingSuits) > 0 or self.suitGone == 1:
+ self.suitGone = 0
+ pos0 = self.suitPendingPoints[0][0]
+ pos1 = self.suitPoints[0][0][0]
+ adjustTime = self.calcSuitMoveTime(pos0, pos1)
+ if len(self.pendingToons) > 0 or self.toonGone == 1:
+ self.toonGone = 0
+ if adjustTime == 0:
+ pos0 = self.toonPendingPoints[0][0]
+ pos1 = self.toonPoints[0][0][0]
+ adjustTime = self.calcToonMoveTime(pos0, pos1)
+ return adjustTime
+
+ def enterAdjusting(self):
+ self.notify.debug('enterAdjusting()')
+ self.timer.stop()
+ self.__resetAdjustingResponses()
+ self.adjustingTimer.startCallback(self.__estimateAdjustTime() + SERVER_BUFFER_TIME, self.__serverAdjustingDone)
+ return None
+
+ def __serverAdjustingDone(self):
+ if self.needAdjust == 1:
+ self.adjustFsm.request('NotAdjusting')
+ self.__requestAdjust()
+ else:
+ self.notify.debug('adjusting timed out on the server')
+ self.ignoreAdjustingResponses = 1
+ self.__adjustDone()
+
+ def exitAdjusting(self):
+ currStateName = self.fsm.getCurrentState().getName()
+ if currStateName == 'WaitForInput':
+ self.timer.restart()
+ else:
+ if currStateName == 'WaitForJoin':
+ self.b_setState('WaitForInput')
+ self.adjustingTimer.stop()
+ return None
+
+ def __addTrainTrapForNewSuits(self):
+ hasTrainTrap = False
+ trapInfo = None
+ for otherSuit in self.activeSuits:
+ if otherSuit.battleTrap == UBER_GAG_LEVEL_INDEX:
+ hasTrainTrap = True
+
+ if hasTrainTrap:
+ for curSuit in self.activeSuits:
+ if not curSuit.battleTrap == UBER_GAG_LEVEL_INDEX:
+ oldBattleTrap = curSuit.battleTrap
+ curSuit.battleTrap = UBER_GAG_LEVEL_INDEX
+ self.battleCalc.addTrainTrapForJoiningSuit(curSuit.doId)
+ self.notify.debug('setting traintrack trap for joining suit %d oldTrap=%s' % (curSuit.doId, oldBattleTrap))
+
+ return
+
+ def __adjustDone(self):
+ for s in self.adjustingSuits:
+ self.pendingSuits.remove(s)
+ self.activeSuits.append(s)
+
+ self.adjustingSuits = []
+ for toon in self.adjustingToons:
+ if self.pendingToons.count(toon) == 1:
+ self.pendingToons.remove(toon)
+ else:
+ self.notify.warning('adjustDone() - toon: %d not pending!' % toon.doId)
+ if self.activeToons.count(toon) == 0:
+ self.activeToons.append(toon)
+ self.ignoreResponses = 0
+ self.sendEarnedExperience(toon)
+ else:
+ self.notify.warning('adjustDone() - toon: %d already active!' % toon.doId)
+
+ self.adjustingToons = []
+ self.__addTrainTrapForNewSuits()
+ self.d_setMembers()
+ self.adjustFsm.request('NotAdjusting')
+ if self.needAdjust == 1:
+ self.notify.debug('__adjustDone() - need to adjust again')
+ self.__requestAdjust()
+
+ def enterNotAdjusting(self):
+ self.notify.debug('enterNotAdjusting()')
+ if self.movieRequested == 1:
+ if len(self.activeToons) > 0 and self.__allActiveToonsResponded():
+ self.__requestMovie()
+ return None
+
+ def exitNotAdjusting(self):
+ return None
+
+ def getPetProxyObject(self, petId, callback):
+ doneEvent = 'readPet-%s' % self._getNextSerialNum()
+ dbo = DatabaseObject.DatabaseObject(self.air, petId, doneEvent=doneEvent)
+ pet = dbo.readPetProxy()
+
+ def handlePetProxyRead(dbo, retCode, callback=callback, pet=pet):
+ success = retCode == 0
+ if not success:
+ self.notify.warning('pet DB read failed')
+ pet = None
+ callback(success, pet)
+ return
+
+ self.acceptOnce(doneEvent, handlePetProxyRead)
+
+ def _getNextSerialNum(self):
+ num = self.serialNum
+ self.serialNum += 1
+ return num
diff --git a/toontown/battle/DistributedBattleBldg.py b/toontown/battle/DistributedBattleBldg.py
new file mode 100644
index 0000000..3053a3a
--- /dev/null
+++ b/toontown/battle/DistributedBattleBldg.py
@@ -0,0 +1,218 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from direct.actor import Actor
+from toontown.suit import SuitDNA
+from direct.directnotify import DirectNotifyGlobal
+import DistributedBattleBase
+from toontown.toon import TTEmote
+from otp.avatar import Emote
+from toontown.toonbase import TTLocalizer
+import MovieUtil
+from direct.fsm import State
+from toontown.suit import Suit
+import SuitBattleGlobals
+import random
+from toontown.toonbase import ToontownGlobals
+
+class DistributedBattleBldg(DistributedBattleBase.DistributedBattleBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleBldg')
+ camFOFov = 30.0
+ camFOPos = Point3(0, -10, 4)
+
+ def __init__(self, cr):
+ townBattle = cr.playGame.getPlace().townBattle
+ DistributedBattleBase.DistributedBattleBase.__init__(self, cr, townBattle)
+ self.streetBattle = 0
+ self.fsm.addState(State.State('BuildingReward', self.enterBuildingReward, self.exitBuildingReward, ['Resume']))
+ offState = self.fsm.getStateNamed('Off')
+ offState.addTransition('BuildingReward')
+ playMovieState = self.fsm.getStateNamed('PlayMovie')
+ playMovieState.addTransition('BuildingReward')
+
+ def generate(self):
+ DistributedBattleBase.DistributedBattleBase.generate(self)
+
+ def setBossBattle(self, value):
+ self.bossBattle = value
+ if self.bossBattle:
+ self.battleMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.mid')
+ else:
+ self.battleMusic = base.loadMusic('phase_7/audio/bgm/encntr_general_bg_indoor.mid')
+ base.playMusic(self.battleMusic, looping=1, volume=0.9)
+
+ def getBossBattleTaunt(self):
+ return TTLocalizer.BattleBldgBossTaunt
+
+ def disable(self):
+ DistributedBattleBase.DistributedBattleBase.disable(self)
+ self.battleMusic.stop()
+
+ def delete(self):
+ DistributedBattleBase.DistributedBattleBase.delete(self)
+ del self.battleMusic
+
+ def buildJoinPointList(self, avPos, destPos, toon = 0):
+ return []
+
+ def __faceOff(self, ts, name, callback):
+ if len(self.suits) == 0:
+ self.notify.warning('__faceOff(): no suits.')
+ return
+ if len(self.toons) == 0:
+ self.notify.warning('__faceOff(): no toons.')
+ return
+ elevatorPos = self.toons[0].getPos()
+ if len(self.suits) == 1:
+ leaderIndex = 0
+ elif self.bossBattle == 1:
+ leaderIndex = 1
+ else:
+ maxTypeNum = -1
+ for suit in self.suits:
+ suitTypeNum = SuitDNA.getSuitType(suit.dna.name)
+ if maxTypeNum < suitTypeNum:
+ maxTypeNum = suitTypeNum
+ leaderIndex = self.suits.index(suit)
+
+ delay = FACEOFF_TAUNT_T
+ suitTrack = Parallel()
+ suitLeader = None
+ for suit in self.suits:
+ suit.setState('Battle')
+ suitIsLeader = 0
+ oneSuitTrack = Sequence()
+ oneSuitTrack.append(Func(suit.loop, 'neutral'))
+ oneSuitTrack.append(Func(suit.headsUp, elevatorPos))
+ if self.suits.index(suit) == leaderIndex:
+ suitLeader = suit
+ suitIsLeader = 1
+ if self.bossBattle == 1:
+ taunt = self.getBossBattleTaunt()
+ else:
+ taunt = SuitBattleGlobals.getFaceoffTaunt(suit.getStyleName(), suit.doId)
+ oneSuitTrack.append(Func(suit.setChatAbsolute, taunt, CFSpeech | CFTimeout))
+ destPos, destHpr = self.getActorPosHpr(suit, self.suits)
+ oneSuitTrack.append(Wait(delay))
+ if suitIsLeader == 1:
+ oneSuitTrack.append(Func(suit.clearChat))
+ oneSuitTrack.append(self.createAdjustInterval(suit, destPos, destHpr))
+ suitTrack.append(oneSuitTrack)
+
+ toonTrack = Parallel()
+ for toon in self.toons:
+ oneToonTrack = Sequence()
+ destPos, destHpr = self.getActorPosHpr(toon, self.toons)
+ oneToonTrack.append(Wait(delay))
+ oneToonTrack.append(self.createAdjustInterval(toon, destPos, destHpr, toon=1, run=1))
+ toonTrack.append(oneToonTrack)
+
+ camTrack = Sequence()
+
+ def setCamFov(fov):
+ base.camLens.setFov(fov)
+
+ camTrack.append(Func(camera.wrtReparentTo, suitLeader))
+ camTrack.append(Func(setCamFov, self.camFOFov))
+ suitHeight = suitLeader.getHeight()
+ suitOffsetPnt = Point3(0, 0, suitHeight)
+ MidTauntCamHeight = suitHeight * 0.66
+ MidTauntCamHeightLim = suitHeight - 1.8
+ if MidTauntCamHeight < MidTauntCamHeightLim:
+ MidTauntCamHeight = MidTauntCamHeightLim
+ TauntCamY = 18
+ TauntCamX = 0
+ TauntCamHeight = random.choice((MidTauntCamHeight, 1, 11))
+ camTrack.append(Func(camera.setPos, TauntCamX, TauntCamY, TauntCamHeight))
+ camTrack.append(Func(camera.lookAt, suitLeader, suitOffsetPnt))
+ camTrack.append(Wait(delay))
+ camPos = Point3(0, -6, 4)
+ camHpr = Vec3(0, 0, 0)
+ camTrack.append(Func(camera.reparentTo, base.localAvatar))
+ camTrack.append(Func(setCamFov, ToontownGlobals.DefaultCameraFov))
+ camTrack.append(Func(camera.setPosHpr, camPos, camHpr))
+ mtrack = Parallel(suitTrack, toonTrack, camTrack)
+ done = Func(callback)
+ track = Sequence(mtrack, done, name=name)
+ track.start(ts)
+ self.storeInterval(track, name)
+ return
+
+ def enterFaceOff(self, ts):
+ if len(self.toons) > 0 and base.localAvatar == self.toons[0]:
+ Emote.globalEmote.disableAll(self.toons[0], 'dbattlebldg, enterFaceOff')
+ self.delayDeleteMembers()
+ self.__faceOff(ts, self.faceOffName, self.__handleFaceOffDone)
+ return None
+
+ def __handleFaceOffDone(self):
+ self.notify.debug('FaceOff done')
+ self.d_faceOffDone(base.localAvatar.doId)
+
+ def exitFaceOff(self):
+ self.notify.debug('exitFaceOff()')
+ if len(self.toons) > 0 and base.localAvatar == self.toons[0]:
+ Emote.globalEmote.releaseAll(self.toons[0], 'dbattlebldg exitFaceOff')
+ self.clearInterval(self.faceOffName)
+ self._removeMembersKeep()
+ camera.wrtReparentTo(self)
+ base.camLens.setFov(self.camFov)
+ return None
+
+ def __playReward(self, ts, callback):
+ toonTracks = Parallel()
+ for toon in self.toons:
+ toonTracks.append(Sequence(Func(toon.loop, 'victory'), Wait(FLOOR_REWARD_TIMEOUT), Func(toon.loop, 'neutral')))
+
+ name = self.uniqueName('floorReward')
+ track = Sequence(toonTracks, Func(callback), name=name)
+ camera.setPos(0, 0, 1)
+ camera.setHpr(180, 10, 0)
+ self.storeInterval(track, name)
+ track.start(ts)
+
+ def enterReward(self, ts):
+ self.notify.debug('enterReward()')
+ self.delayDeleteMembers()
+ self.__playReward(ts, self.__handleFloorRewardDone)
+ return None
+
+ def __handleFloorRewardDone(self):
+ return None
+
+ def exitReward(self):
+ self.notify.debug('exitReward()')
+ self.clearInterval(self.uniqueName('floorReward'))
+ self._removeMembersKeep()
+ NametagGlobals.setMasterArrowsOn(1)
+ for toon in self.toons:
+ toon.startSmooth()
+
+ return None
+
+ def enterBuildingReward(self, ts):
+ self.delayDeleteMembers()
+ if self.hasLocalToon():
+ NametagGlobals.setMasterArrowsOn(0)
+ self.movie.playReward(ts, self.uniqueName('building-reward'), self.__handleBuildingRewardDone, noSkip=True)
+ return None
+
+ def __handleBuildingRewardDone(self):
+ if self.hasLocalToon():
+ self.d_rewardDone(base.localAvatar.doId)
+ self.movie.resetReward()
+ self.fsm.request('Resume')
+
+ def exitBuildingReward(self):
+ self.movie.resetReward(finish=1)
+ self._removeMembersKeep()
+ NametagGlobals.setMasterArrowsOn(1)
+ return None
+
+ def enterResume(self, ts = 0):
+ if self.hasLocalToon():
+ self.removeLocalToon()
+ return None
+
+ def exitResume(self):
+ return None
diff --git a/toontown/battle/DistributedBattleBldgAI.py b/toontown/battle/DistributedBattleBldgAI.py
new file mode 100644
index 0000000..73a6416
--- /dev/null
+++ b/toontown/battle/DistributedBattleBldgAI.py
@@ -0,0 +1,176 @@
+from otp.ai.AIBase import *
+from direct.distributed.ClockDelta import *
+from BattleBase import *
+from BattleCalculatorAI import *
+from toontown.toonbase.ToontownBattleGlobals import *
+from SuitBattleGlobals import *
+from direct.showbase.PythonUtil import addListsByValue
+import DistributedBattleBaseAI
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+import random
+from direct.fsm import State
+from direct.fsm import ClassicFSM, State
+from direct.showbase import PythonUtil
+
+class DistributedBattleBldgAI(DistributedBattleBaseAI.DistributedBattleBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleBldgAI')
+
+ def __init__(self, air, zoneId, roundCallback=None, finishCallback=None, maxSuits=4, bossBattle=0):
+ DistributedBattleBaseAI.DistributedBattleBaseAI.__init__(self, air, zoneId, finishCallback, maxSuits, bossBattle)
+ self.streetBattle = 0
+ self.roundCallback = roundCallback
+ self.fsm.addState(State.State('BuildingReward', self.enterBuildingReward, self.exitBuildingReward, [
+ 'Resume']))
+ playMovieState = self.fsm.getStateNamed('PlayMovie')
+ playMovieState.addTransition('BuildingReward')
+ self.elevatorPos = Point3(0, -30, 0)
+ self.resumeNeedUpdate = 0
+
+ def setInitialMembers(self, toonIds, suits):
+ for suit in suits:
+ self.addSuit(suit)
+
+ for toonId in toonIds:
+ self.addToon(toonId)
+
+ self.fsm.request('FaceOff')
+
+ def delete(self):
+ del self.roundCallback
+ DistributedBattleBaseAI.DistributedBattleBaseAI.delete(self)
+
+ def faceOffDone(self):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreResponses == 1:
+ self.notify.debug('faceOffDone() - ignoring toon: %d' % toonId)
+ return
+ else:
+ if self.fsm.getCurrentState().getName() != 'FaceOff':
+ self.notify.warning('faceOffDone() - in state: %s' % self.fsm.getCurrentState().getName())
+ return
+ else:
+ if self.toons.count(toonId) == 0:
+ self.notify.warning('faceOffDone() - toon: %d not in toon list' % toonId)
+ return
+ self.responses[toonId] += 1
+ self.notify.debug('toon: %d done facing off' % toonId)
+ if not self.ignoreFaceOffDone:
+ if self.allToonsResponded():
+ self.handleFaceOffDone()
+ else:
+ self.timer.stop()
+ self.timer.startCallback(TIMEOUT_PER_USER, self.__serverFaceOffDone)
+
+ def enterFaceOff(self):
+ self.notify.debug('enterFaceOff()')
+ self.joinableFsm.request('Joinable')
+ self.runableFsm.request('Unrunable')
+ self.timer.startCallback(self.calcToonMoveTime(self.pos, self.elevatorPos) + FACEOFF_TAUNT_T + SERVER_BUFFER_TIME, self.__serverFaceOffDone)
+ return None
+
+ def __serverFaceOffDone(self):
+ self.notify.debug('faceoff timed out on server')
+ self.ignoreFaceOffDone = 1
+ self.handleFaceOffDone()
+
+ def exitFaceOff(self):
+ self.timer.stop()
+ self.resetResponses()
+ return None
+
+ def handleFaceOffDone(self):
+ for suit in self.suits:
+ self.activeSuits.append(suit)
+
+ for toon in self.toons:
+ self.activeToons.append(toon)
+ self.sendEarnedExperience(toon)
+
+ self.d_setMembers()
+ self.b_setState('WaitForInput')
+
+ def localMovieDone(self, needUpdate, deadToons, deadSuits, lastActiveSuitDied):
+ self.timer.stop()
+ self.resumeNeedUpdate = needUpdate
+ self.resumeDeadToons = deadToons
+ self.resumeDeadSuits = deadSuits
+ self.resumeLastActiveSuitDied = lastActiveSuitDied
+ if len(self.toons) == 0:
+ self.d_setMembers()
+ self.b_setState('Resume')
+ else:
+ totalHp = 0
+ for suit in self.suits:
+ if suit.currHP > 0:
+ totalHp += suit.currHP
+
+ self.roundCallback(self.activeToons, totalHp, deadSuits)
+
+ def __goToResumeState(self, task):
+ self.b_setState('Resume')
+
+ def resume(self, currentFloor=0, topFloor=0):
+ if len(self.suits) == 0:
+ self.d_setMembers()
+ self.suitsKilledPerFloor.append(self.suitsKilledThisBattle)
+ if topFloor == 0:
+ self.b_setState('Reward')
+ else:
+ for floorNum, cogsThisFloor in PythonUtil.enumerate(self.suitsKilledPerFloor):
+ for toonId in self.activeToons:
+ toon = self.getToon(toonId)
+ if toon:
+ recovered, notRecovered = self.air.questManager.recoverItems(toon, cogsThisFloor, self.zoneId)
+ self.toonItems[toonId][0].extend(recovered)
+ self.toonItems[toonId][1].extend(notRecovered)
+ meritArray = self.air.promotionMgr.recoverMerits(toon, cogsThisFloor, self.zoneId, getCreditMultiplier(floorNum))
+ if toonId in self.helpfulToons:
+ self.toonMerits[toonId] = addListsByValue(self.toonMerits[toonId], meritArray)
+ else:
+ self.notify.debug('toon %d not helpful, skipping merits' % toonId)
+
+ self.d_setBattleExperience()
+ self.b_setState('BuildingReward')
+ else:
+ if self.resumeNeedUpdate == 1:
+ self.d_setMembers()
+ if len(self.resumeDeadSuits) > 0 and self.resumeLastActiveSuitDied == 0 or len(self.resumeDeadToons) > 0:
+ self.needAdjust = 1
+ self.setState('WaitForJoin')
+ self.resumeNeedUpdate = 0
+ self.resumeDeadToons = []
+ self.resumeDeadSuits = []
+ self.resumeLastActiveSuitDied = 0
+
+ def enterReservesJoining(self, ts=0):
+ return None
+
+ def exitReservesJoining(self, ts=0):
+ return None
+
+ def enterReward(self):
+ self.timer.startCallback(FLOOR_REWARD_TIMEOUT, self.serverRewardDone)
+ return None
+
+ def exitReward(self):
+ self.timer.stop()
+ return None
+
+ def enterBuildingReward(self):
+ self.resetResponses()
+ self.assignRewards()
+ self.timer.startCallback(BUILDING_REWARD_TIMEOUT, self.serverRewardDone)
+ return None
+
+ def exitBuildingReward(self):
+ return None
+
+ def enterResume(self):
+ DistributedBattleBaseAI.DistributedBattleBaseAI.enterResume(self)
+ self.finishCallback(self.zoneId, self.activeToons)
+
+ def exitResume(self):
+ DistributedBattleBaseAI.DistributedBattleBaseAI.exitResume(self)
+ taskName = self.taskName('finish')
+ taskMgr.remove(taskName)
diff --git a/toontown/battle/DistributedBattleDiners.py b/toontown/battle/DistributedBattleDiners.py
new file mode 100644
index 0000000..a16efd0
--- /dev/null
+++ b/toontown/battle/DistributedBattleDiners.py
@@ -0,0 +1,123 @@
+import random
+from pandac.PandaModules import VBase3, Point3
+from direct.interval.IntervalGlobal import Sequence, Wait, Func, Parallel, Track, LerpPosInterval, ProjectileInterval, SoundInterval, ActorInterval
+from direct.directnotify import DirectNotifyGlobal
+from toontown.battle import DistributedBattleFinal
+from toontown.suit import SuitTimings
+from toontown.toonbase import ToontownGlobals
+from toontown.battle import BattleProps
+
+class DistributedBattleDiners(DistributedBattleFinal.DistributedBattleFinal):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleDiners')
+
+ def __init__(self, cr):
+ DistributedBattleFinal.DistributedBattleFinal.__init__(self, cr)
+ self.initialReservesJoiningDone = False
+ base.dbw = self
+
+ def announceGenerate(self):
+ DistributedBattleFinal.DistributedBattleFinal.announceGenerate(self)
+ self.moveSuitsToInitialPos()
+
+ def showSuitsJoining(self, suits, ts, name, callback):
+ if len(suits) == 0 and not self.initialReservesJoiningDone:
+ self.initialReservesJoiningDone = True
+ self.doInitialSuitsJoining(ts, name, callback)
+ return
+ self.showSuitsFalling(suits, ts, name, callback)
+
+ def doInitialSuitsJoining(self, ts, name, callback):
+ done = Func(callback)
+ if self.hasLocalToon():
+ camera.reparentTo(self)
+ if random.choice([0, 1]):
+ camera.setPosHpr(20, -4, 7, 60, 0, 0)
+ else:
+ camera.setPosHpr(-20, -4, 7, -60, 0, 0)
+ track = Sequence(Wait(0.5), done, name=name)
+ track.start(ts)
+ self.storeInterval(track, name)
+
+ def moveSuitsToInitialPos(self):
+ battlePts = self.suitPoints[len(self.suitPendingPoints) - 1]
+ for i in xrange(len(self.suits)):
+ suit = self.suits[i]
+ suit.reparentTo(self)
+ destPos, destHpr = self.getActorPosHpr(suit, self.suits)
+ suit.setPos(destPos)
+ suit.setHpr(destHpr)
+
+ def showSuitsFalling(self, suits, ts, name, callback):
+ if self.bossCog == None:
+ return
+ suitTrack = Parallel()
+ delay = 0
+ for suit in suits:
+ suit.setState('Battle')
+ if suit.dna.dept == 'l':
+ suit.reparentTo(self.bossCog)
+ suit.setPos(0, 0, 0)
+ if suit in self.joiningSuits:
+ i = len(self.pendingSuits) + self.joiningSuits.index(suit)
+ destPos, h = self.suitPendingPoints[i]
+ destHpr = VBase3(h, 0, 0)
+ else:
+ destPos, destHpr = self.getActorPosHpr(suit, self.suits)
+ startPos = destPos + Point3(0, 0, SuitTimings.fromSky * ToontownGlobals.SuitWalkSpeed)
+ self.notify.debug('startPos for %s = %s' % (suit, startPos))
+ suit.reparentTo(self)
+ suit.setPos(startPos)
+ suit.headsUp(self)
+ moveIval = Sequence()
+ chairInfo = self.bossCog.claimOneChair()
+ if chairInfo:
+ moveIval = self.createDinerMoveIval(suit, destPos, chairInfo)
+ suitTrack.append(Track((delay, Sequence(moveIval, Func(suit.loop, 'neutral')))))
+ delay += 1
+
+ if self.hasLocalToon():
+ camera.reparentTo(self)
+ self.notify.debug('self.battleSide =%s' % self.battleSide)
+ camHeading = -20
+ camX = -4
+ if self.battleSide == 0:
+ camHeading = 20
+ camX = 4
+ camera.setPosHpr(camX, -15, 7, camHeading, 0, 0)
+ done = Func(callback)
+ track = Sequence(suitTrack, done, name=name)
+ track.start(ts)
+ self.storeInterval(track, name)
+ return
+
+ def createDinerMoveIval(self, suit, destPos, chairInfo):
+ dur = suit.getDuration('landing')
+ fr = suit.getFrameRate('landing')
+ landingDur = dur
+ totalDur = 7.3
+ animTimeInAir = totalDur - dur
+ flyingDur = animTimeInAir
+ impactLength = dur - animTimeInAir
+ tableIndex = chairInfo[0]
+ chairIndex = chairInfo[1]
+ table = self.bossCog.tables[tableIndex]
+ chairLocator = table.chairLocators[chairIndex]
+ chairPos = chairLocator.getPos(self)
+ chairHpr = chairLocator.getHpr(self)
+ suit.setPos(chairPos)
+ table.setDinerStatus(chairIndex, table.HIDDEN)
+ suit.setHpr(chairHpr)
+ wayPoint = (chairPos + destPos) / 2.0
+ wayPoint.setZ(wayPoint.getZ() + 20)
+ moveIval = Sequence(Func(suit.headsUp, self), Func(suit.pose, 'landing', 0), ProjectileInterval(suit, duration=flyingDur, startPos=chairPos, endPos=destPos, gravityMult=0.25), ActorInterval(suit, 'landing'))
+ if suit.prop == None:
+ suit.prop = BattleProps.globalPropPool.getProp('propeller')
+ propDur = suit.prop.getDuration('propeller')
+ lastSpinFrame = 8
+ fr = suit.prop.getFrameRate('propeller')
+ spinTime = lastSpinFrame / fr
+ openTime = (lastSpinFrame + 1) / fr
+ suit.attachPropeller()
+ propTrack = Parallel(SoundInterval(suit.propInSound, duration=flyingDur, node=suit), Sequence(ActorInterval(suit.prop, 'propeller', constrainedLoop=1, duration=flyingDur + 1, startTime=0.0, endTime=spinTime), ActorInterval(suit.prop, 'propeller', duration=landingDur, startTime=openTime), Func(suit.detachPropeller)))
+ result = Parallel(moveIval, propTrack)
+ return result
diff --git a/toontown/battle/DistributedBattleDinersAI.py b/toontown/battle/DistributedBattleDinersAI.py
new file mode 100644
index 0000000..5b689d4
--- /dev/null
+++ b/toontown/battle/DistributedBattleDinersAI.py
@@ -0,0 +1,22 @@
+from direct.directnotify import DirectNotifyGlobal
+from toontown.battle import DistributedBattleFinalAI
+
+class DistributedBattleDinersAI(DistributedBattleFinalAI.DistributedBattleFinalAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleDinersAI')
+
+ def __init__(self, air, bossCog, roundCallback, finishCallback, battleSide):
+ DistributedBattleFinalAI.DistributedBattleFinalAI.__init__(self, air, bossCog, roundCallback, finishCallback, battleSide)
+
+ def startBattle(self, toonIds, suits):
+ self.joinableFsm.request('Joinable')
+ for toonId in toonIds:
+ if self.addToon(toonId):
+ self.activeToons.append(toonId)
+
+ self.d_setMembers()
+ for suit in suits:
+ self.pendingSuits.append(suit)
+
+ self.d_setMembers()
+ self.needAdjust = 1
+ self.b_setState('ReservesJoining')
diff --git a/toontown/battle/DistributedBattleFinal.py b/toontown/battle/DistributedBattleFinal.py
new file mode 100644
index 0000000..6e4a0c8
--- /dev/null
+++ b/toontown/battle/DistributedBattleFinal.py
@@ -0,0 +1,199 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from direct.actor import Actor
+from toontown.distributed import DelayDelete
+from direct.directnotify import DirectNotifyGlobal
+import DistributedBattleBase
+import MovieUtil
+from toontown.suit import Suit
+import SuitBattleGlobals
+from toontown.toonbase import ToontownBattleGlobals
+from toontown.toonbase import ToontownGlobals
+from direct.fsm import State
+import random
+
+class DistributedBattleFinal(DistributedBattleBase.DistributedBattleBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleFinal')
+
+ def __init__(self, cr):
+ townBattle = cr.playGame.hood.loader.townBattle
+ DistributedBattleBase.DistributedBattleBase.__init__(self, cr, townBattle)
+ self.setupCollisions(self.uniqueBattleName('battle-collide'))
+ self.bossCog = None
+ self.bossCogRequest = None
+ self.streetBattle = 0
+ self.joiningSuitsName = self.uniqueBattleName('joiningSuits')
+ self.fsm.addState(State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, ['WaitForJoin']))
+ offState = self.fsm.getStateNamed('Off')
+ offState.addTransition('ReservesJoining')
+ waitForJoinState = self.fsm.getStateNamed('WaitForJoin')
+ waitForJoinState.addTransition('ReservesJoining')
+ playMovieState = self.fsm.getStateNamed('PlayMovie')
+ playMovieState.addTransition('ReservesJoining')
+ return
+
+ def generate(self):
+ DistributedBattleBase.DistributedBattleBase.generate(self)
+
+ def disable(self):
+ DistributedBattleBase.DistributedBattleBase.disable(self)
+ base.cr.relatedObjectMgr.abortRequest(self.bossCogRequest)
+ self.bossCogRequest = None
+ self.bossCog = None
+ return
+
+ def delete(self):
+ DistributedBattleBase.DistributedBattleBase.delete(self)
+ self.removeCollisionData()
+
+ def setBossCogId(self, bossCogId):
+ self.bossCogId = bossCogId
+ if base.cr.doId2do.has_key(bossCogId):
+ tempBossCog = base.cr.doId2do[bossCogId]
+ self.__gotBossCog([tempBossCog])
+ else:
+ self.notify.debug('doing relatedObjectMgr.request for bossCog')
+ self.bossCogRequest = base.cr.relatedObjectMgr.requestObjects([bossCogId], allCallback=self.__gotBossCog)
+
+ def __gotBossCog(self, bossCogList):
+ self.bossCogRequest = None
+ self.bossCog = bossCogList[0]
+ currStateName = self.localToonFsm.getCurrentState().getName()
+ if currStateName == 'NoLocalToon' and self.bossCog.hasLocalToon():
+ self.enableCollision()
+ return
+
+ def setBattleNumber(self, battleNumber):
+ self.battleNumber = battleNumber
+
+ def setBattleSide(self, battleSide):
+ self.battleSide = battleSide
+
+ def setMembers(self, suits, suitsJoining, suitsPending, suitsActive, suitsLured, suitTraps, toons, toonsJoining, toonsPending, toonsActive, toonsRunning, timestamp):
+ if self.battleCleanedUp():
+ return
+ oldtoons = DistributedBattleBase.DistributedBattleBase.setMembers(self, suits, suitsJoining, suitsPending, suitsActive, suitsLured, suitTraps, toons, toonsJoining, toonsPending, toonsActive, toonsRunning, timestamp)
+ if len(self.toons) == 4 and len(oldtoons) < 4:
+ self.notify.debug('setMembers() - battle is now full of toons')
+ self.closeBattleCollision()
+ elif len(self.toons) < 4 and len(oldtoons) == 4:
+ self.openBattleCollision()
+
+ def makeSuitJoin(self, suit, ts):
+ self.notify.debug('makeSuitJoin(%d)' % suit.doId)
+ self.joiningSuits.append(suit)
+ if self.hasLocalToon():
+ self.d_joinDone(base.localAvatar.doId, suit.doId)
+
+ def showSuitsJoining(self, suits, ts, name, callback):
+ if self.bossCog == None:
+ return
+ if self.battleSide:
+ openDoor = Func(self.bossCog.doorB.request, 'open')
+ closeDoor = Func(self.bossCog.doorB.request, 'close')
+ else:
+ openDoor = Func(self.bossCog.doorA.request, 'open')
+ closeDoor = Func(self.bossCog.doorA.request, 'close')
+ suitTrack = Parallel()
+ delay = 0
+ for suit in suits:
+ suit.setState('Battle')
+ if suit.dna.dept == 'l':
+ suit.reparentTo(self.bossCog)
+ suit.setPos(0, 0, 0)
+ suit.setPos(self.bossCog, 0, 0, 0)
+ suit.headsUp(self)
+ suit.setScale(3.8 / suit.height)
+ if suit in self.joiningSuits:
+ i = len(self.pendingSuits) + self.joiningSuits.index(suit)
+ destPos, h = self.suitPendingPoints[i]
+ destHpr = VBase3(h, 0, 0)
+ else:
+ destPos, destHpr = self.getActorPosHpr(suit, self.suits)
+ suitTrack.append(Track((delay, self.createAdjustInterval(suit, destPos, destHpr)), (delay + 1.5, suit.scaleInterval(1.5, 1))))
+ delay += 1
+
+ if self.hasLocalToon():
+ camera.reparentTo(self)
+ if random.choice([0, 1]):
+ camera.setPosHpr(20, -4, 7, 60, 0, 0)
+ else:
+ camera.setPosHpr(-20, -4, 7, -60, 0, 0)
+ done = Func(callback)
+ track = Sequence(openDoor, suitTrack, closeDoor, done, name=name)
+ track.start(ts)
+ self.storeInterval(track, name)
+ return
+
+ def __playReward(self, ts, callback):
+ toonTracks = Parallel()
+ for toon in self.toons:
+ toonTracks.append(Sequence(Func(toon.loop, 'victory'), Wait(FLOOR_REWARD_TIMEOUT), Func(toon.loop, 'neutral')))
+
+ name = self.uniqueName('floorReward')
+ track = Sequence(toonTracks, name=name)
+ if self.hasLocalToon():
+ camera.setPos(0, 0, 1)
+ camera.setHpr(180, 10, 0)
+ track += [self.bossCog.makeEndOfBattleMovie(self.hasLocalToon()), Func(callback)]
+ self.storeInterval(track, name)
+ track.start(ts)
+
+ def enterReward(self, ts):
+ self.notify.debug('enterReward()')
+ self.disableCollision()
+ self.delayDeleteMembers()
+ self.__playReward(ts, self.__handleFloorRewardDone)
+ return None
+
+ def __handleFloorRewardDone(self):
+ return None
+
+ def exitReward(self):
+ self.notify.debug('exitReward()')
+ self.clearInterval(self.uniqueName('floorReward'), finish=1)
+ self._removeMembersKeep()
+ NametagGlobals.setMasterArrowsOn(1)
+ for toon in self.toons:
+ toon.startSmooth()
+
+ return None
+
+ def enterResume(self, ts = 0):
+ if self.hasLocalToon():
+ self.removeLocalToon()
+ self.fsm.requestFinalState()
+
+ def exitResume(self):
+ return None
+
+ def enterReservesJoining(self, ts = 0):
+ self.delayDeleteMembers()
+ self.showSuitsJoining(self.joiningSuits, ts, self.joiningSuitsName, self.__reservesJoiningDone)
+
+ def __reservesJoiningDone(self):
+ self._removeMembersKeep()
+ self.doneBarrier()
+
+ def exitReservesJoining(self):
+ self.clearInterval(self.joiningSuitsName)
+
+ def enterNoLocalToon(self):
+ self.notify.debug('enterNoLocalToon()')
+ if self.bossCog != None and self.bossCog.hasLocalToon():
+ self.enableCollision()
+ else:
+ self.disableCollision()
+ return
+
+ def exitNoLocalToon(self):
+ self.disableCollision()
+ return None
+
+ def enterWaitForServer(self):
+ self.notify.debug('enterWaitForServer()')
+ return None
+
+ def exitWaitForServer(self):
+ return None
diff --git a/toontown/battle/DistributedBattleFinalAI.py b/toontown/battle/DistributedBattleFinalAI.py
new file mode 100644
index 0000000..8aabc32
--- /dev/null
+++ b/toontown/battle/DistributedBattleFinalAI.py
@@ -0,0 +1,126 @@
+from otp.ai.AIBase import *
+from BattleBase import *
+from BattleCalculatorAI import *
+from toontown.toonbase.ToontownBattleGlobals import *
+from SuitBattleGlobals import *
+import DistributedBattleBaseAI
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import State
+from direct.showbase.PythonUtil import addListsByValue
+import random, types
+
+class DistributedBattleFinalAI(DistributedBattleBaseAI.DistributedBattleBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleFinalAI')
+
+ def __init__(self, air, bossCog, roundCallback, finishCallback, battleSide):
+ DistributedBattleBaseAI.DistributedBattleBaseAI.__init__(self, air, bossCog.zoneId, finishCallback)
+ self.bossCogId = bossCog.doId
+ self.battleNumber = bossCog.battleNumber
+ self.battleSide = battleSide
+ self.streetBattle = 0
+ self.roundCallback = roundCallback
+ self.elevatorPos = Point3(0, 0, 0)
+ self.pos = Point3(0, 30, 0)
+ self.resumeNeedUpdate = 0
+ self.fsm.addState(State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, [
+ 'WaitForJoin']))
+ offState = self.fsm.getStateNamed('Off')
+ offState.addTransition('ReservesJoining')
+ waitForJoinState = self.fsm.getStateNamed('WaitForJoin')
+ waitForJoinState.addTransition('ReservesJoining')
+ playMovieState = self.fsm.getStateNamed('PlayMovie')
+ playMovieState.addTransition('ReservesJoining')
+
+ def getBossCogId(self):
+ return self.bossCogId
+
+ def getBattleNumber(self):
+ return self.battleNumber
+
+ def getBattleSide(self):
+ return self.battleSide
+
+ def startBattle(self, toonIds, suits):
+ self.joinableFsm.request('Joinable')
+ for toonId in toonIds:
+ if self.addToon(toonId):
+ self.activeToons.append(toonId)
+
+ self.d_setMembers()
+ for suit in suits:
+ joined = self.suitRequestJoin(suit)
+
+ self.d_setMembers()
+ self.b_setState('ReservesJoining')
+
+ def localMovieDone(self, needUpdate, deadToons, deadSuits, lastActiveSuitDied):
+ self.timer.stop()
+ self.resumeNeedUpdate = needUpdate
+ self.resumeDeadToons = deadToons
+ self.resumeDeadSuits = deadSuits
+ self.resumeLastActiveSuitDied = lastActiveSuitDied
+ if len(self.toons) == 0:
+ self.d_setMembers()
+ self.b_setState('Resume')
+ else:
+ totalHp = 0
+ for suit in self.suits:
+ if suit.currHP > 0:
+ totalHp += suit.currHP
+
+ self.roundCallback(self.activeToons, totalHp, deadSuits)
+
+ def resume(self, joinedReserves):
+ if len(joinedReserves) != 0:
+ for info in joinedReserves:
+ joined = self.suitRequestJoin(info[0])
+
+ self.d_setMembers()
+ self.b_setState('ReservesJoining')
+ else:
+ if len(self.suits) == 0:
+ battleMultiplier = getBossBattleCreditMultiplier(self.battleNumber)
+ for toonId in self.activeToons:
+ toon = self.getToon(toonId)
+ if toon:
+ recovered, notRecovered = self.air.questManager.recoverItems(toon, self.suitsKilledThisBattle, self.zoneId)
+ self.toonItems[toonId][0].extend(recovered)
+ self.toonItems[toonId][1].extend(notRecovered)
+
+ self.d_setMembers()
+ self.d_setBattleExperience()
+ self.b_setState('Reward')
+ else:
+ if self.resumeNeedUpdate == 1:
+ self.d_setMembers()
+ if len(self.resumeDeadSuits) > 0 and self.resumeLastActiveSuitDied == 0 or len(self.resumeDeadToons) > 0:
+ self.needAdjust = 1
+ self.setState('WaitForJoin')
+ self.resumeNeedUpdate = 0
+ self.resumeDeadToons = []
+ self.resumeDeadSuits = []
+ self.resumeLastActiveSuitDied = 0
+
+ def enterReservesJoining(self, ts=0):
+ self.beginBarrier('ReservesJoining', self.toons, 15, self.__doneReservesJoining)
+
+ def __doneReservesJoining(self, avIds):
+ self.b_setState('WaitForJoin')
+
+ def exitReservesJoining(self, ts=0):
+ return None
+
+ def enterReward(self):
+ self.timer.startCallback(FLOOR_REWARD_TIMEOUT + 5, self.serverRewardDone)
+ return None
+
+ def exitReward(self):
+ self.timer.stop()
+ return None
+
+ def enterResume(self):
+ self.joinableFsm.request('Unjoinable')
+ self.runableFsm.request('Unrunable')
+ DistributedBattleBaseAI.DistributedBattleBaseAI.enterResume(self)
+ self.finishCallback(self.zoneId, self.activeToons)
diff --git a/toontown/battle/DistributedBattleWaiters.py b/toontown/battle/DistributedBattleWaiters.py
new file mode 100644
index 0000000..400675e
--- /dev/null
+++ b/toontown/battle/DistributedBattleWaiters.py
@@ -0,0 +1,94 @@
+import random
+from pandac.PandaModules import VBase3, Point3
+from direct.interval.IntervalGlobal import Sequence, Wait, Func, Parallel, Track
+from direct.directnotify import DirectNotifyGlobal
+from toontown.battle import DistributedBattleFinal
+from toontown.suit import SuitTimings
+from toontown.toonbase import ToontownGlobals
+
+class DistributedBattleWaiters(DistributedBattleFinal.DistributedBattleFinal):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleWaiters')
+
+ def __init__(self, cr):
+ DistributedBattleFinal.DistributedBattleFinal.__init__(self, cr)
+ self.initialReservesJoiningDone = False
+ base.dbw = self
+
+ def announceGenerate(self):
+ DistributedBattleFinal.DistributedBattleFinal.announceGenerate(self)
+ for suit in self.suits:
+ suit.makeWaiter()
+
+ self.moveSuitsToInitialPos()
+
+ def showSuitsJoining(self, suits, ts, name, callback):
+ if len(suits) == 0 and not self.initialReservesJoiningDone:
+ self.initialReservesJoiningDone = True
+ self.doInitialSuitsJoining(ts, name, callback)
+ return
+ self.showSuitsFalling(suits, ts, name, callback)
+
+ def doInitialSuitsJoining(self, ts, name, callback):
+ done = Func(callback)
+ if self.hasLocalToon():
+ self.notify.debug('parenting camera to distributed battle waiters')
+ camera.reparentTo(self)
+ if random.choice([0, 1]):
+ camera.setPosHpr(20, -4, 7, 60, 0, 0)
+ else:
+ camera.setPosHpr(-20, -4, 7, -60, 0, 0)
+ track = Sequence(Wait(0.5), done, name=name)
+ track.start(ts)
+ self.storeInterval(track, name)
+
+ def moveSuitsToInitialPos(self):
+ battlePts = self.suitPoints[len(self.suitPendingPoints) - 1]
+ for i in xrange(len(self.suits)):
+ suit = self.suits[i]
+ suit.reparentTo(self)
+ destPos, destHpr = self.getActorPosHpr(suit, self.suits)
+ suit.setPos(destPos)
+ suit.setHpr(destHpr)
+
+ def showSuitsFalling(self, suits, ts, name, callback):
+ if self.bossCog == None:
+ return
+ suitTrack = Parallel()
+ delay = 0
+ for suit in suits:
+ suit.makeWaiter()
+ suit.setState('Battle')
+ if suit.dna.dept == 'l':
+ suit.reparentTo(self.bossCog)
+ suit.setPos(0, 0, 0)
+ if suit in self.joiningSuits:
+ i = len(self.pendingSuits) + self.joiningSuits.index(suit)
+ destPos, h = self.suitPendingPoints[i]
+ destHpr = VBase3(h, 0, 0)
+ else:
+ destPos, destHpr = self.getActorPosHpr(suit, self.suits)
+ startPos = destPos + Point3(0, 0, SuitTimings.fromSky * ToontownGlobals.SuitWalkSpeed)
+ self.notify.debug('startPos for %s = %s' % (suit, startPos))
+ suit.reparentTo(self)
+ suit.setPos(startPos)
+ suit.headsUp(self)
+ flyIval = suit.beginSupaFlyMove(destPos, True, 'flyIn')
+ suitTrack.append(Track((delay, Sequence(flyIval, Func(suit.loop, 'neutral')))))
+ delay += 1
+
+ if self.hasLocalToon():
+ camera.reparentTo(self)
+ if random.choice([0, 1]):
+ camera.setPosHpr(20, -4, 7, 60, 0, 0)
+ else:
+ camera.setPosHpr(-20, -4, 7, -60, 0, 0)
+ done = Func(callback)
+ track = Sequence(suitTrack, done, name=name)
+ track.start(ts)
+ self.storeInterval(track, name)
+ return
+
+ def enterWaitForInput(self, ts = 0):
+ DistributedBattleFinal.DistributedBattleFinal.enterWaitForInput(self, ts)
+ if self.hasLocalToon():
+ camera.reparentTo(self)
diff --git a/toontown/battle/DistributedBattleWaitersAI.py b/toontown/battle/DistributedBattleWaitersAI.py
new file mode 100644
index 0000000..66f312e
--- /dev/null
+++ b/toontown/battle/DistributedBattleWaitersAI.py
@@ -0,0 +1,22 @@
+from direct.directnotify import DirectNotifyGlobal
+from toontown.battle import DistributedBattleFinalAI
+
+class DistributedBattleWaitersAI(DistributedBattleFinalAI.DistributedBattleFinalAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleWaitersAI')
+
+ def __init__(self, air, bossCog, roundCallback, finishCallback, battleSide):
+ DistributedBattleFinalAI.DistributedBattleFinalAI.__init__(self, air, bossCog, roundCallback, finishCallback, battleSide)
+
+ def startBattle(self, toonIds, suits):
+ self.joinableFsm.request('Joinable')
+ for toonId in toonIds:
+ if self.addToon(toonId):
+ self.activeToons.append(toonId)
+
+ self.d_setMembers()
+ for suit in suits:
+ self.pendingSuits.append(suit)
+
+ self.d_setMembers()
+ self.needAdjust = 1
+ self.b_setState('ReservesJoining')
diff --git a/toontown/battle/Fanfare.py b/toontown/battle/Fanfare.py
new file mode 100644
index 0000000..25c0352
--- /dev/null
+++ b/toontown/battle/Fanfare.py
@@ -0,0 +1,155 @@
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from BattleProps import *
+from BattleSounds import *
+from toontown.toon.ToonDNA import *
+from toontown.suit.SuitDNA import *
+from direct.particles.ParticleEffect import *
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+import MovieUtil
+import MovieCamera
+from direct.directnotify import DirectNotifyGlobal
+import BattleParticles
+from toontown.toonbase import ToontownGlobals
+import RewardPanel
+notify = DirectNotifyGlobal.directNotify.newCategory('Fanfare')
+
+def makePanel(toon, showToonName):
+ panel = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.75, 1, 0.75), pos=(0, 0, 0.587))
+ panel.initialiseoptions(RewardPanel)
+ panel.setTransparency(1)
+ panel.hide()
+ if showToonName is 1:
+ panel.avNameLabel = DirectLabel(parent=panel, relief=None, pos=Vec3(0, 0, 0.3), text=toon.getName(), text_scale=0.08)
+ return panel
+
+
+def makeMessageBox(panel, message, messagePos, messageScale, wordwrap = 100):
+ panel.itemFrame = DirectFrame(parent=panel, relief=None, text=message, text_pos=messagePos, text_scale=messageScale, text_wordwrap=wordwrap)
+ return
+
+
+def makeImageBox(frame, image, imagePos, imageScale):
+ frame.imageIcon = image.copyTo(frame)
+ frame.imageIcon.setPos(imagePos)
+ frame.imageIcon.setScale(imageScale)
+
+
+def makeFanfare(delay, toon):
+ return doFanfare(delay, toon, None)
+
+
+def makeFanfareWithMessage(delay, toon, showToonName, message, messagePos, messageScale, wordwrap = 100):
+ panel = makePanel(toon, showToonName)
+ makeMessageBox(panel, message, messagePos, messageScale, wordwrap)
+ return doFanfare(delay, toon, panel)
+
+
+def makeFanfareWithImage(delay, toon, showToonName, image, imagePos, imageScale, wordwrap = 100):
+ panel = makePanel(toon, showToonName)
+ makeMessageBox(panel, '', Vec3(0, 0, 0), 1, wordwrap)
+ makeImageBox(panel.itemFrame, image, imagePos, imageScale)
+ return doFanfare(delay, toon, panel)
+
+
+def makeFanfareWithMessageImage(delay, toon, showToonName, message, messagePos, messageScale, image, imagePos, imageScale, wordwrap = 100):
+ panel = makePanel(toon, showToonName)
+ makeMessageBox(panel, message, messagePos, messageScale, wordwrap)
+ makeImageBox(panel.itemFrame, image, imagePos, imageScale)
+ return doFanfare(delay, toon, panel)
+
+
+def doFanfare(delay, toon, panel):
+ fanfareNode = toon.attachNewNode('fanfareNode')
+ partyBall = fanfareNode.attachNewNode('partyBall')
+ headparts = toon.getHeadParts()
+ pos = headparts[2].getPos(fanfareNode)
+ partyBallLeft = globalPropPool.getProp('partyBall')
+ partyBallLeft.reparentTo(partyBall)
+ partyBallLeft.setScale(0.8)
+ partyBallLeft.setH(90)
+ partyBallLeft.setColorScale(1, 0, 0, 0)
+ partyBallRight = globalPropPool.getProp('partyBall')
+ partyBallRight.reparentTo(partyBall)
+ partyBallRight.setScale(0.8)
+ partyBallRight.setH(-90)
+ partyBallRight.setColorScale(1, 1, 0, 0)
+ partyBall.setZ(pos.getZ() + 3.2)
+ ballShake1 = Sequence(Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, 0, 0), hpr=Vec3(90, 10, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, 0, 0), hpr=Vec3(-90, -10, 0), blendType='easeInOut')), Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, 10, 0), hpr=Vec3(90, -10, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, -10, 0), hpr=Vec3(-90, 10, 0), blendType='easeInOut')), Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, -10, 0), hpr=Vec3(90, 0, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, 10, 0), hpr=Vec3(-90, 0, 0), blendType='easeInOut')))
+ ballShake2 = Sequence(Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, 0, 0), hpr=Vec3(90, -10, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, 0, 0), hpr=Vec3(-90, 10, 0), blendType='easeInOut')), Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, -10, 0), hpr=Vec3(90, 10, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, 10, 0), hpr=Vec3(-90, -10, 0), blendType='easeInOut')), Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, 10, 0), hpr=Vec3(90, 0, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, -10, 0), hpr=Vec3(-90, 0, 0), blendType='easeInOut')))
+ openBall = Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, 0, 0), hpr=Vec3(90, 30, 0)), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, 0, 0), hpr=Vec3(-90, 30, 0)))
+ confettiNode = fanfareNode.attachNewNode('confetti')
+ confettiNode.setScale(3)
+ confettiNode.setZ(pos.getZ() + 2.5)
+
+ def longshake(models, num, duration):
+ inShake = getScaleBlendIntervals(models, duration=duration, startScale=0.23, endScale=0.2, blendType='easeInOut')
+ outShake = getScaleBlendIntervals(models, duration=duration, startScale=0.2, endScale=0.23, blendType='easeInOut')
+ i = 1
+ seq = Sequence()
+ while i < num:
+ if i % 2 == 0:
+ seq.append(inShake)
+ else:
+ seq.append(outShake)
+ i += 1
+
+ return seq
+
+ def getScaleBlendIntervals(props, duration, startScale, endScale, blendType):
+ tracks = Parallel()
+ for prop in props:
+ tracks.append(LerpScaleInterval(prop, duration, endScale, startScale=startScale, blendType=blendType))
+
+ return tracks
+
+ trumpetNode = fanfareNode.attachNewNode('trumpetNode')
+ trumpet1 = globalPropPool.getProp('bugle')
+ trumpet2 = MovieUtil.copyProp(trumpet1)
+ trumpet1.reparentTo(trumpetNode)
+ trumpet1.setScale(0.2)
+ trumpet1.setPos(2, 2, 1)
+ trumpet1.setHpr(120, 65, 0)
+ trumpet2.reparentTo(trumpetNode)
+ trumpet2.setScale(0.2)
+ trumpet2.setPos(-2, 2, 1)
+ trumpet2.setHpr(-120, 65, 0)
+ trumpetNode.setTransparency(1)
+ trumpetNode.setColor(1, 1, 1, 0)
+ trumpturn1 = LerpHprInterval(trumpet1, duration=4, startHpr=Vec3(80, 15, 0), hpr=Vec3(150, 40, 0))
+ trumpturn2 = LerpHprInterval(trumpet2, duration=4, startHpr=Vec3(-80, 15, 0), hpr=Vec3(-150, 40, 0))
+ trumpetTurn = Parallel(trumpturn1, trumpturn2)
+ BattleParticles.loadParticles()
+ confettiBlue = BattleParticles.createParticleEffect('Confetti')
+ confettiBlue.reparentTo(confettiNode)
+ blue_p0 = confettiBlue.getParticlesNamed('particles-1')
+ blue_p0.renderer.getColorInterpolationManager().addConstant(0.0, 1.0, Vec4(0.0, 0.0, 1.0, 1.0), 1)
+ confettiYellow = BattleParticles.createParticleEffect('Confetti')
+ confettiYellow.reparentTo(confettiNode)
+ yellow_p0 = confettiYellow.getParticlesNamed('particles-1')
+ yellow_p0.renderer.getColorInterpolationManager().addConstant(0.0, 1.0, Vec4(1.0, 1.0, 0.0, 1.0), 1)
+ confettiRed = BattleParticles.createParticleEffect('Confetti')
+ confettiRed.reparentTo(confettiNode)
+ red_p0 = confettiRed.getParticlesNamed('particles-1')
+ red_p0.renderer.getColorInterpolationManager().addConstant(0.0, 1.0, Vec4(1.0, 0.0, 0.0, 1.0), 1)
+ trumpetsAppear = LerpColorInterval(trumpetNode, 0.3, startColor=Vec4(1, 1, 0, 0), color=Vec4(1, 1, 0, 1))
+ trumpetsVanish = LerpColorInterval(trumpetNode, 0.3, startColor=Vec4(1, 1, 0, 1), color=Vec4(1, 1, 0, 0))
+ crabHorn = globalBattleSoundCache.getSound('King_Crab.mp3')
+ drumroll = globalBattleSoundCache.getSound('SZ_MM_drumroll.mp3')
+ fanfare = globalBattleSoundCache.getSound('SZ_MM_fanfare.mp3')
+ crabHorn.setTime(1.5)
+ partyBall.setTransparency(1)
+ partyBall.setColorScale(1, 1, 1, 1)
+ ballAppear = Parallel(LerpColorScaleInterval(partyBallLeft, 0.3, startColorScale=Vec4(1, 0, 0, 0), colorScale=Vec4(1, 0, 0, 1)), LerpColorScaleInterval(partyBallRight, 0.3, startColorScale=Vec4(1, 1, 0, 0), colorScale=Vec4(1, 1, 0, 1)))
+ ballVanish = Parallel(LerpColorScaleInterval(partyBallLeft, 0.3, startColorScale=Vec4(1, 0, 0, 1), colorScale=Vec4(1, 0, 0, 0)), LerpColorScaleInterval(partyBallRight, 0.3, startColorScale=Vec4(1, 1, 0, 1), colorScale=Vec4(1, 1, 0, 0)))
+ play = Parallel(SoundInterval(crabHorn, startTime=1.5, duration=4.0, node=toon), Sequence(Wait(0.25), longshake([trumpet1, trumpet2], 3, 0.2), Wait(0.5), longshake([trumpet1, trumpet2], 3, 0.2), Wait(0.5), longshake([trumpet1, trumpet2], 9, 0.1), longshake([trumpet1, trumpet2], 3, 0.2)))
+ killParticles = Parallel(Func(blue_p0.setLitterSize, 0), Func(red_p0.setLitterSize, 0), Func(yellow_p0.setLitterSize, 0))
+ p = Parallel(ParticleInterval(confettiBlue, confettiNode, worldRelative=0, duration=3, cleanup=True), ParticleInterval(confettiRed, confettiNode, worldRelative=0, duration=3, cleanup=True), ParticleInterval(confettiYellow, confettiNode, worldRelative=0, duration=3, cleanup=True))
+ pOff = Parallel(Func(confettiBlue.remove), Func(confettiRed.remove), Func(confettiYellow.remove))
+ partInterval = Parallel(p, Sequence(Wait(1.7), killParticles, Wait(1.3), pOff, Func(p.finish)), Sequence(Wait(3), Parallel(ballVanish)))
+ seq1 = Parallel(Sequence(Wait(delay + 4.1), SoundInterval(drumroll, node=toon), Wait(0.25), SoundInterval(fanfare, node=toon)), Sequence(Wait(delay), trumpetsAppear, Wait(3), ballAppear, Wait(0.5), ballShake1, Wait(0.1), ballShake2, Wait(0.2), Wait(0.1), Parallel(openBall, partInterval), Func(fanfareNode.remove)))
+ seq = Parallel(seq1, Sequence(Wait(delay), Parallel(trumpetTurn, Sequence(Wait(0.5), play)), Wait(0.5), trumpetsVanish))
+ if panel != None:
+ return (seq, panel)
+ return (seq, None)
diff --git a/toontown/battle/FireCogPanel.py b/toontown/battle/FireCogPanel.py
new file mode 100644
index 0000000..6966376
--- /dev/null
+++ b/toontown/battle/FireCogPanel.py
@@ -0,0 +1,126 @@
+from toontown.toonbase.ToontownBattleGlobals import *
+from toontown.toonbase import ToontownGlobals
+from direct.fsm import StateData
+from direct.directnotify import DirectNotifyGlobal
+from toontown.battle import BattleBase
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from toontown.toonbase import TTLocalizer
+
+class FireCogPanel(StateData.StateData):
+ notify = DirectNotifyGlobal.directNotify.newCategory('ChooseAvatarPanel')
+
+ def __init__(self, doneEvent):
+ self.notify.debug('Init choose panel...')
+ StateData.StateData.__init__(self, doneEvent)
+ self.numAvatars = 0
+ self.chosenAvatar = 0
+ self.toon = 0
+ self.loaded = 0
+
+ def load(self):
+ gui = loader.loadModel('phase_3.5/models/gui/battle_gui')
+ self.frame = DirectFrame(relief=None, image=gui.find('**/BtlPick_TAB'), image_color=Vec4(1, 0.2, 0.2, 1))
+ self.frame.hide()
+ self.statusFrame = DirectFrame(parent=self.frame, relief=None, image=gui.find('**/ToonBtl_Status_BG'), image_color=Vec4(0.5, 0.9, 0.5, 1), pos=(0.611, 0, 0))
+ self.textFrame = DirectFrame(parent=self.frame, relief=None, image=gui.find('**/PckMn_Select_Tab'), image_color=Vec4(1, 1, 0, 1), image_scale=(1.0, 1.0, 2.0), text='', text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0.02, 0), text_scale=TTLocalizer.FCPtextFrame, pos=(-0.013, 0, 0.013))
+ self.textFrame['text'] = TTLocalizer.FireCogTitle % localAvatar.getPinkSlips()
+ self.avatarButtons = []
+ for i in range(4):
+ button = DirectButton(parent=self.frame, relief=None, text='', text_fg=Vec4(0, 0, 0, 1), text_scale=0.067, text_pos=(0, -0.015, 0), textMayChange=1, image_scale=(1.0, 1.0, 1.0), image=(gui.find('**/PckMn_Arrow_Up'), gui.find('**/PckMn_Arrow_Dn'), gui.find('**/PckMn_Arrow_Rlvr')), command=self.__handleAvatar, extraArgs=[i])
+ button.setScale(1, 1, 1)
+ button.setPos(0, 0, 0.2)
+ self.avatarButtons.append(button)
+
+ self.backButton = DirectButton(parent=self.frame, relief=None, image=(gui.find('**/PckMn_BackBtn'), gui.find('**/PckMn_BackBtn_Dn'), gui.find('**/PckMn_BackBtn_Rlvr')), pos=(-0.647, 0, 0.006), scale=1.05, text=TTLocalizer.TownBattleChooseAvatarBack, text_scale=0.05, text_pos=(0.01, -0.012), text_fg=Vec4(0, 0, 0.8, 1), command=self.__handleBack)
+ gui.removeNode()
+ self.loaded = 1
+ return
+
+ def unload(self):
+ if self.loaded:
+ self.frame.destroy()
+ del self.frame
+ del self.statusFrame
+ del self.textFrame
+ del self.avatarButtons
+ del self.backButton
+ self.loaded = 0
+
+ def enter(self, numAvatars, localNum = None, luredIndices = None, trappedIndices = None, track = None, fireCosts = None):
+ if not self.loaded:
+ self.load()
+ self.frame.show()
+ invalidTargets = []
+ if not self.toon:
+ if len(luredIndices) > 0:
+ if track == BattleBase.TRAP or track == BattleBase.LURE:
+ invalidTargets += luredIndices
+ if len(trappedIndices) > 0:
+ if track == BattleBase.TRAP:
+ invalidTargets += trappedIndices
+ self.__placeButtons(numAvatars, invalidTargets, localNum, fireCosts)
+
+ def exit(self):
+ self.frame.hide()
+
+ def __handleBack(self):
+ doneStatus = {'mode': 'Back'}
+ messenger.send(self.doneEvent, [doneStatus])
+
+ def __handleAvatar(self, avatar):
+ doneStatus = {'mode': 'Avatar',
+ 'avatar': avatar}
+ messenger.send(self.doneEvent, [doneStatus])
+
+ def adjustCogs(self, numAvatars, luredIndices, trappedIndices, track):
+ invalidTargets = []
+ if len(luredIndices) > 0:
+ if track == BattleBase.TRAP or track == BattleBase.LURE:
+ invalidTargets += luredIndices
+ if len(trappedIndices) > 0:
+ if track == BattleBase.TRAP:
+ invalidTargets += trappedIndices
+ self.__placeButtons(numAvatars, invalidTargets, None)
+ return
+
+ def adjustToons(self, numToons, localNum):
+ self.__placeButtons(numToons, [], localNum)
+
+ def __placeButtons(self, numAvatars, invalidTargets, localNum, fireCosts):
+ canfire = 0
+ for i in range(4):
+ if numAvatars > i and i not in invalidTargets and i != localNum:
+ self.avatarButtons[i].show()
+ self.avatarButtons[i]['text'] = ''
+ if fireCosts[i] <= localAvatar.getPinkSlips():
+ self.avatarButtons[i]['state'] = DGG.NORMAL
+ self.avatarButtons[i]['text_fg'] = (0, 0, 0, 1)
+ canfire = 1
+ else:
+ self.avatarButtons[i]['state'] = DGG.DISABLED
+ self.avatarButtons[i]['text_fg'] = (1.0, 0, 0, 1)
+ else:
+ self.avatarButtons[i].hide()
+
+ if canfire:
+ self.textFrame['text'] = TTLocalizer.FireCogTitle % localAvatar.getPinkSlips()
+ else:
+ self.textFrame['text'] = TTLocalizer.FireCogLowTitle % localAvatar.getPinkSlips()
+ if numAvatars == 1:
+ self.avatarButtons[0].setX(0)
+ elif numAvatars == 2:
+ self.avatarButtons[0].setX(0.2)
+ self.avatarButtons[1].setX(-0.2)
+ elif numAvatars == 3:
+ self.avatarButtons[0].setX(0.4)
+ self.avatarButtons[1].setX(0.0)
+ self.avatarButtons[2].setX(-0.4)
+ elif numAvatars == 4:
+ self.avatarButtons[0].setX(0.6)
+ self.avatarButtons[1].setX(0.2)
+ self.avatarButtons[2].setX(-0.2)
+ self.avatarButtons[3].setX(-0.6)
+ else:
+ self.notify.error('Invalid number of avatars: %s' % numAvatars)
+ return None
diff --git a/toontown/battle/HealJokes.py b/toontown/battle/HealJokes.py
new file mode 100644
index 0000000..3c081bb
--- /dev/null
+++ b/toontown/battle/HealJokes.py
@@ -0,0 +1,2 @@
+from toontown.toonbase import TTLocalizer
+toonHealJokes = TTLocalizer.ToonHealJokes
diff --git a/toontown/battle/Movie.py b/toontown/battle/Movie.py
new file mode 100644
index 0000000..f663dc1
--- /dev/null
+++ b/toontown/battle/Movie.py
@@ -0,0 +1,909 @@
+from toontown.toonbase.ToontownBattleGlobals import *
+from BattleBase import *
+from direct.interval.IntervalGlobal import *
+from direct.showbase import DirectObject
+import MovieFire
+import MovieSOS
+import MovieNPCSOS
+import MoviePetSOS
+import MovieHeal
+import MovieTrap
+import MovieLure
+import MovieSound
+import MovieThrow
+import MovieSquirt
+import MovieDrop
+import MovieSuitAttacks
+import MovieToonVictory
+import PlayByPlayText
+import BattleParticles
+from toontown.distributed import DelayDelete
+import BattleExperience
+from SuitBattleGlobals import *
+from direct.directnotify import DirectNotifyGlobal
+import RewardPanel
+import random
+import MovieUtil
+from toontown.toon import Toon
+from toontown.toonbase import ToontownGlobals
+from toontown.toontowngui import TTDialog
+import copy
+from toontown.toonbase import TTLocalizer
+from toontown.toon import NPCToons
+camPos = Point3(14, 0, 10)
+camHpr = Vec3(89, -30, 0)
+randomBattleTimestamp = base.config.GetBool('random-battle-timestamp', 0)
+
+class Movie(DirectObject.DirectObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('Movie')
+
+ def __init__(self, battle):
+ self.battle = battle
+ self.track = None
+ self.rewardPanel = None
+ self.rewardCallback = None
+ self.playByPlayText = PlayByPlayText.PlayByPlayText()
+ self.playByPlayText.hide()
+ self.renderProps = []
+ self.hasBeenReset = 0
+ self.reset()
+ self.rewardHasBeenReset = 0
+ self.resetReward()
+ return
+
+ def cleanup(self):
+ self.reset()
+ self.resetReward()
+ self.battle = None
+ if self.playByPlayText != None:
+ self.playByPlayText.cleanup()
+ self.playByPlayText = None
+ if self.rewardPanel != None:
+ self.rewardPanel.cleanup()
+ self.rewardPanel = None
+ self.rewardCallback = None
+ return
+
+ def needRestoreColor(self):
+ self.restoreColor = 1
+
+ def clearRestoreColor(self):
+ self.restoreColor = 0
+
+ def needRestoreHips(self):
+ self.restoreHips = 1
+
+ def clearRestoreHips(self):
+ self.restoreHips = 0
+
+ def needRestoreHeadScale(self):
+ self.restoreHeadScale = 1
+
+ def clearRestoreHeadScale(self):
+ self.restoreHeadScale = 0
+
+ def needRestoreToonScale(self):
+ self.restoreToonScale = 1
+
+ def clearRestoreToonScale(self):
+ self.restoreToonScale = 0
+
+ def needRestoreParticleEffect(self, effect):
+ self.specialParticleEffects.append(effect)
+
+ def clearRestoreParticleEffect(self, effect):
+ if self.specialParticleEffects.count(effect) > 0:
+ self.specialParticleEffects.remove(effect)
+
+ def needRestoreRenderProp(self, prop):
+ self.renderProps.append(prop)
+
+ def clearRenderProp(self, prop):
+ if self.renderProps.count(prop) > 0:
+ self.renderProps.remove(prop)
+
+ def restore(self):
+ return
+ for toon in self.battle.activeToons:
+ toon.loop('neutral')
+ origPos, origHpr = self.battle.getActorPosHpr(toon)
+ toon.setPosHpr(self.battle, origPos, origHpr)
+ hands = toon.getRightHands()[:]
+ hands += toon.getLeftHands()
+ for hand in hands:
+ props = hand.getChildren()
+ for prop in props:
+ if prop.getName() != 'book':
+ MovieUtil.removeProp(prop)
+
+ if self.restoreColor == 1:
+ headParts = toon.getHeadParts()
+ torsoParts = toon.getTorsoParts()
+ legsParts = toon.getLegsParts()
+ partsList = [headParts, torsoParts, legsParts]
+ for parts in partsList:
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ nextPart.clearColorScale()
+ nextPart.clearTransparency()
+
+ if self.restoreHips == 1:
+ parts = toon.getHipsParts()
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ props = nextPart.getChildren()
+ for prop in props:
+ if prop.getName() == 'redtape-tube.egg':
+ MovieUtil.removeProp(prop)
+
+ if self.restoreHeadScale == 1:
+ headScale = ToontownGlobals.toonHeadScales[toon.style.getAnimal()]
+ for lod in toon.getLODNames():
+ toon.getPart('head', lod).setScale(headScale)
+
+ if self.restoreToonScale == 1:
+ toon.setScale(1)
+ headParts = toon.getHeadParts()
+ for partNum in range(0, headParts.getNumPaths()):
+ part = headParts.getPath(partNum)
+ part.setHpr(0, 0, 0)
+ part.setPos(0, 0, 0)
+
+ arms = toon.findAllMatches('**/arms')
+ sleeves = toon.findAllMatches('**/sleeves')
+ hands = toon.findAllMatches('**/hands')
+ for partNum in range(0, arms.getNumPaths()):
+ armPart = arms.getPath(partNum)
+ sleevePart = sleeves.getPath(partNum)
+ handsPart = hands.getPath(partNum)
+ armPart.setHpr(0, 0, 0)
+ sleevePart.setHpr(0, 0, 0)
+ handsPart.setHpr(0, 0, 0)
+
+ for suit in self.battle.activeSuits:
+ if suit._Actor__animControlDict != None:
+ suit.loop('neutral')
+ suit.battleTrapIsFresh = 0
+ origPos, origHpr = self.battle.getActorPosHpr(suit)
+ suit.setPosHpr(self.battle, origPos, origHpr)
+ hands = [suit.getRightHand(), suit.getLeftHand()]
+ for hand in hands:
+ props = hand.getChildren()
+ for prop in props:
+ MovieUtil.removeProp(prop)
+
+ for effect in self.specialParticleEffects:
+ if effect != None:
+ effect.cleanup()
+
+ self.specialParticleEffects = []
+ for prop in self.renderProps:
+ MovieUtil.removeProp(prop)
+
+ self.renderProps = []
+ return
+
+ def _deleteTrack(self):
+ if self.track:
+ DelayDelete.cleanupDelayDeletes(self.track)
+ self.track = None
+ return
+
+ def reset(self, finish = 0):
+ if self.hasBeenReset == 1:
+ return
+ self.hasBeenReset = 1
+ self.stop()
+ self._deleteTrack()
+ if finish == 1:
+ self.restore()
+ self.toonAttackDicts = []
+ self.suitAttackDicts = []
+ self.restoreColor = 0
+ self.restoreHips = 0
+ self.restoreHeadScale = 0
+ self.restoreToonScale = 0
+ self.specialParticleEffects = []
+ for prop in self.renderProps:
+ MovieUtil.removeProp(prop)
+
+ self.renderProps = []
+
+ def resetReward(self, finish = 0):
+ if self.rewardHasBeenReset == 1:
+ return
+ self.rewardHasBeenReset = 1
+ self.stop()
+ self._deleteTrack()
+ if finish == 1:
+ self.restore()
+ self.toonRewardDicts = []
+ if self.rewardPanel != None:
+ self.rewardPanel.destroy()
+ self.rewardPanel = None
+ return
+
+ def play(self, ts, callback):
+ self.hasBeenReset = 0
+ ptrack = Sequence()
+ camtrack = Sequence()
+ if random.random() > 0.5:
+ MovieUtil.shotDirection = 'left'
+ else:
+ MovieUtil.shotDirection = 'right'
+ for s in self.battle.activeSuits:
+ s.battleTrapIsFresh = 0
+
+ tattacks, tcam = self.__doToonAttacks()
+ if tattacks:
+ ptrack.append(tattacks)
+ camtrack.append(tcam)
+ sattacks, scam = self.__doSuitAttacks()
+ if sattacks:
+ ptrack.append(sattacks)
+ camtrack.append(scam)
+ ptrack.append(Func(callback))
+ self._deleteTrack()
+ self.track = Sequence(ptrack, name='movie-track-%d' % self.battle.doId)
+ if self.battle.localToonPendingOrActive():
+ self.track = Parallel(self.track, Sequence(camtrack), name='movie-track-with-cam-%d' % self.battle.doId)
+ if randomBattleTimestamp == 1:
+ randNum = random.randint(0, 99)
+ dur = self.track.getDuration()
+ ts = float(randNum) / 100.0 * dur
+ self.track.delayDeletes = []
+ for suit in self.battle.suits:
+ self.track.delayDeletes.append(DelayDelete.DelayDelete(suit, 'Movie.play'))
+
+ for toon in self.battle.toons:
+ self.track.delayDeletes.append(DelayDelete.DelayDelete(toon, 'Movie.play'))
+
+ self.track.start(ts)
+ return None
+
+ def finish(self):
+ self.track.finish()
+ return None
+
+ def playReward(self, ts, name, callback, noSkip = False):
+ self.rewardHasBeenReset = 0
+ ptrack = Sequence()
+ camtrack = Sequence()
+ self.rewardPanel = RewardPanel.RewardPanel(name)
+ self.rewardPanel.hide()
+ victory, camVictory, skipper = MovieToonVictory.doToonVictory(self.battle.localToonActive(), self.battle.activeToons, self.toonRewardIds, self.toonRewardDicts, self.deathList, self.rewardPanel, 1, self.uberList, self.helpfulToonsList, noSkip=noSkip)
+ if victory:
+ skipper.setIvals((ptrack, camtrack), ptrack.getDuration())
+ ptrack.append(victory)
+ camtrack.append(camVictory)
+ ptrack.append(Func(callback))
+ self._deleteTrack()
+ self.track = Sequence(ptrack, name='movie-reward-track-%d' % self.battle.doId)
+ if self.battle.localToonActive():
+ self.track = Parallel(self.track, camtrack, name='movie-reward-track-with-cam-%d' % self.battle.doId)
+ self.track.delayDeletes = []
+ for t in self.battle.activeToons:
+ self.track.delayDeletes.append(DelayDelete.DelayDelete(t, 'Movie.playReward'))
+
+ skipper.setIvals((self.track,), 0.0)
+ skipper.setBattle(self.battle)
+ self.track.start(ts)
+ return None
+
+ def playTutorialReward(self, ts, name, callback):
+ self.rewardHasBeenReset = 0
+ self.rewardPanel = RewardPanel.RewardPanel(name)
+ self.rewardCallback = callback
+ self.questList = self.rewardPanel.getQuestIntervalList(base.localAvatar, [0,
+ 1,
+ 1,
+ 0], [base.localAvatar], base.localAvatar.quests[0], [], [base.localAvatar.getDoId()])
+ camera.setPosHpr(0, 8, base.localAvatar.getHeight() * 0.66, 179, 15, 0)
+ self.playTutorialReward_1()
+
+ def playTutorialReward_1(self):
+ self.tutRewardDialog_1 = TTDialog.TTDialog(text=TTLocalizer.MovieTutorialReward1, command=self.playTutorialReward_2, style=TTDialog.Acknowledge, fadeScreen=None, pos=(0.65, 0, 0.5), scale=0.8)
+ self.tutRewardDialog_1.hide()
+ self._deleteTrack()
+ self.track = Sequence(name='tutorial-reward-1')
+ self.track.append(Func(self.rewardPanel.initGagFrame, base.localAvatar, [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0], [0,
+ 0,
+ 0,
+ 0], noSkip=True))
+ self.track += self.rewardPanel.getTrackIntervalList(base.localAvatar, THROW_TRACK, 0, 1, 0)
+ self.track.append(Func(self.tutRewardDialog_1.show))
+ self.track.start()
+ return
+
+ def playTutorialReward_2(self, value):
+ self.tutRewardDialog_1.cleanup()
+ self.tutRewardDialog_2 = TTDialog.TTDialog(text=TTLocalizer.MovieTutorialReward2, command=self.playTutorialReward_3, style=TTDialog.Acknowledge, fadeScreen=None, pos=(0.65, 0, 0.5), scale=0.8)
+ self.tutRewardDialog_2.hide()
+ self._deleteTrack()
+ self.track = Sequence(name='tutorial-reward-2')
+ self.track.append(Wait(1.0))
+ self.track += self.rewardPanel.getTrackIntervalList(base.localAvatar, SQUIRT_TRACK, 0, 1, 0)
+ self.track.append(Func(self.tutRewardDialog_2.show))
+ self.track.start()
+ return
+
+ def playTutorialReward_3(self, value):
+ self.tutRewardDialog_2.cleanup()
+ from toontown.toon import Toon
+ from toontown.toon import ToonDNA
+
+ def doneChat1(page, elapsed = 0):
+ self.track2.start()
+
+ def doneChat2(elapsed):
+ self.track2.pause()
+ self.track3.start()
+
+ def uniqueName(hook):
+ return 'TutorialTom-' + hook
+
+ self.tutorialTom = Toon.Toon()
+ dna = ToonDNA.ToonDNA()
+ dnaList = ('dll', 'ms', 'm', 'm', 7, 0, 7, 7, 2, 6, 2, 6, 2, 16)
+ dna.newToonFromProperties(*dnaList)
+ self.tutorialTom.setDNA(dna)
+ self.tutorialTom.setName(TTLocalizer.NPCToonNames[20000])
+ self.tutorialTom.uniqueName = uniqueName
+ if base.config.GetString('language', 'english') == 'japanese':
+ self.tomDialogue03 = base.loadSfx('phase_3.5/audio/dial/CC_tom_movie_tutorial_reward01.mp3')
+ self.tomDialogue04 = base.loadSfx('phase_3.5/audio/dial/CC_tom_movie_tutorial_reward02.mp3')
+ self.tomDialogue05 = base.loadSfx('phase_3.5/audio/dial/CC_tom_movie_tutorial_reward03.mp3')
+ self.musicVolume = base.config.GetFloat('tutorial-music-volume', 0.5)
+ else:
+ self.tomDialogue03 = None
+ self.tomDialogue04 = None
+ self.tomDialogue05 = None
+ self.musicVolume = 0.9
+ music = base.cr.playGame.place.loader.battleMusic
+ if self.questList:
+ self.track1 = Sequence(Wait(1.0), Func(self.rewardPanel.initQuestFrame, base.localAvatar, copy.deepcopy(base.localAvatar.quests)), Wait(1.0), Sequence(*self.questList), Wait(1.0), Func(self.rewardPanel.hide), Func(camera.setPosHpr, render, 34, 19.88, 3.48, -90, -2.36, 0), Func(base.localAvatar.animFSM.request, 'neutral'), Func(base.localAvatar.setPosHpr, 40.31, 22.0, -0.47, 150.0, 360.0, 0.0), Wait(0.5), Func(self.tutorialTom.reparentTo, render), Func(self.tutorialTom.show), Func(self.tutorialTom.setPosHpr, 40.29, 17.9, -0.47, 11.31, 0.0, 0.07), Func(self.tutorialTom.animFSM.request, 'TeleportIn'), Wait(1.517), Func(self.tutorialTom.animFSM.request, 'neutral'), Func(self.acceptOnce, self.tutorialTom.uniqueName('doneChatPage'), doneChat1), Func(self.tutorialTom.addActive), Func(music.setVolume, self.musicVolume), Func(self.tutorialTom.setLocalPageChat, TTLocalizer.MovieTutorialReward3, 0, None, [self.tomDialogue03]), name='tutorial-reward-3a')
+ self.track2 = Sequence(Func(self.acceptOnce, self.tutorialTom.uniqueName('doneChatPage'), doneChat2), Func(self.tutorialTom.setLocalPageChat, TTLocalizer.MovieTutorialReward4, 1, None, [self.tomDialogue04]), Func(self.tutorialTom.setPlayRate, 1.5, 'right-hand-start'), Func(self.tutorialTom.play, 'right-hand-start'), Wait(self.tutorialTom.getDuration('right-hand-start') / 1.5), Func(self.tutorialTom.loop, 'right-hand'), name='tutorial-reward-3b')
+ self.track3 = Parallel(Sequence(Func(self.tutorialTom.setPlayRate, -1.8, 'right-hand-start'), Func(self.tutorialTom.play, 'right-hand-start'), Wait(self.tutorialTom.getDuration('right-hand-start') / 1.8), Func(self.tutorialTom.animFSM.request, 'neutral'), name='tutorial-reward-3ca'), Sequence(Wait(0.5), Func(self.tutorialTom.setChatAbsolute, TTLocalizer.MovieTutorialReward5, CFSpeech | CFTimeout, self.tomDialogue05), Wait(1.0), Func(self.tutorialTom.animFSM.request, 'TeleportOut'), Wait(self.tutorialTom.getDuration('teleport')), Wait(1.0), Func(self.playTutorialReward_4, 0), name='tutorial-reward-3cb'), name='tutorial-reward-3c')
+ self.track1.start()
+ else:
+ self.playTutorialReward_4(0)
+ return
+
+ def playTutorialReward_4(self, value):
+ base.localAvatar.setH(270)
+ self.tutorialTom.removeActive()
+ self.tutorialTom.delete()
+ self.questList = None
+ self.rewardCallback()
+ return
+
+ def stop(self):
+ if self.track:
+ self.track.finish()
+ self._deleteTrack()
+ if hasattr(self, 'track1'):
+ self.track1.finish()
+ self.track1 = None
+ if hasattr(self, 'track2'):
+ self.track2.finish()
+ self.track2 = None
+ if hasattr(self, 'track3'):
+ self.track3.finish()
+ self.track3 = None
+ if self.rewardPanel:
+ self.rewardPanel.hide()
+ if self.playByPlayText:
+ self.playByPlayText.hide()
+ return
+
+ def __doToonAttacks(self):
+ if base.config.GetBool('want-toon-attack-anims', 1):
+ track = Sequence(name='toon-attacks')
+ camTrack = Sequence(name='toon-attacks-cam')
+ ival, camIval = MovieFire.doFires(self.__findToonAttack(FIRE))
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ ival, camIval = MovieSOS.doSOSs(self.__findToonAttack(SOS))
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ ival, camIval = MovieNPCSOS.doNPCSOSs(self.__findToonAttack(NPCSOS))
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ ival, camIval = MoviePetSOS.doPetSOSs(self.__findToonAttack(PETSOS))
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ hasHealBonus = self.battle.getInteractivePropTrackBonus() == HEAL
+ ival, camIval = MovieHeal.doHeals(self.__findToonAttack(HEAL), hasHealBonus)
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ ival, camIval = MovieTrap.doTraps(self.__findToonAttack(TRAP))
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ ival, camIval = MovieLure.doLures(self.__findToonAttack(LURE))
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ ival, camIval = MovieSound.doSounds(self.__findToonAttack(SOUND))
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ ival, camIval = MovieThrow.doThrows(self.__findToonAttack(THROW))
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ ival, camIval = MovieSquirt.doSquirts(self.__findToonAttack(SQUIRT))
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ ival, camIval = MovieDrop.doDrops(self.__findToonAttack(DROP))
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ if len(track) == 0:
+ return (None, None)
+ else:
+ return (track, camTrack)
+ else:
+ return (None, None)
+ return None
+
+ def genRewardDicts(self, id0, origExp0, earnedExp0, origQuests0, items0, missedItems0, origMerits0, merits0, parts0, id1, origExp1, earnedExp1, origQuests1, items1, missedItems1, origMerits1, merits1, parts1, id2, origExp2, earnedExp2, origQuests2, items2, missedItems2, origMerits2, merits2, parts2, id3, origExp3, earnedExp3, origQuests3, items3, missedItems3, origMerits3, merits3, parts3, deathList, uberList, helpfulToonsList):
+ self.deathList = deathList
+ self.helpfulToonsList = helpfulToonsList
+ entries = ((id0,
+ origExp0,
+ earnedExp0,
+ origQuests0,
+ items0,
+ missedItems0,
+ origMerits0,
+ merits0,
+ parts0),
+ (id1,
+ origExp1,
+ earnedExp1,
+ origQuests1,
+ items1,
+ missedItems1,
+ origMerits1,
+ merits1,
+ parts1),
+ (id2,
+ origExp2,
+ earnedExp2,
+ origQuests2,
+ items2,
+ missedItems2,
+ origMerits2,
+ merits2,
+ parts2),
+ (id3,
+ origExp3,
+ earnedExp3,
+ origQuests3,
+ items3,
+ missedItems3,
+ origMerits3,
+ merits3,
+ parts3))
+ self.toonRewardDicts = BattleExperience.genRewardDicts(entries)
+ self.toonRewardIds = [id0,
+ id1,
+ id2,
+ id3]
+ self.uberList = uberList
+
+ def genAttackDicts(self, toons, suits, id0, tr0, le0, tg0, hp0, ac0, hpb0, kbb0, died0, revive0, id1, tr1, le1, tg1, hp1, ac1, hpb1, kbb1, died1, revive1, id2, tr2, le2, tg2, hp2, ac2, hpb2, kbb2, died2, revive2, id3, tr3, le3, tg3, hp3, ac3, hpb3, kbb3, died3, revive3, sid0, at0, stg0, dm0, sd0, sb0, st0, sid1, at1, stg1, dm1, sd1, sb1, st1, sid2, at2, stg2, dm2, sd2, sb2, st2, sid3, at3, stg3, dm3, sd3, sb3, st3):
+ if self.track and self.track.isPlaying():
+ self.notify.warning('genAttackDicts() - track is playing!')
+ toonAttacks = ((id0,
+ tr0,
+ le0,
+ tg0,
+ hp0,
+ ac0,
+ hpb0,
+ kbb0,
+ died0,
+ revive0),
+ (id1,
+ tr1,
+ le1,
+ tg1,
+ hp1,
+ ac1,
+ hpb1,
+ kbb1,
+ died1,
+ revive1),
+ (id2,
+ tr2,
+ le2,
+ tg2,
+ hp2,
+ ac2,
+ hpb2,
+ kbb2,
+ died2,
+ revive2),
+ (id3,
+ tr3,
+ le3,
+ tg3,
+ hp3,
+ ac3,
+ hpb3,
+ kbb3,
+ died3,
+ revive3))
+ self.__genToonAttackDicts(toons, suits, toonAttacks)
+ suitAttacks = ((sid0,
+ at0,
+ stg0,
+ dm0,
+ sd0,
+ sb0,
+ st0),
+ (sid1,
+ at1,
+ stg1,
+ dm1,
+ sd1,
+ sb1,
+ st1),
+ (sid2,
+ at2,
+ stg2,
+ dm2,
+ sd2,
+ sb2,
+ st2),
+ (sid3,
+ at3,
+ stg3,
+ dm3,
+ sd3,
+ sb3,
+ st3))
+ self.__genSuitAttackDicts(toons, suits, suitAttacks)
+
+ def __genToonAttackDicts(self, toons, suits, toonAttacks):
+ for ta in toonAttacks:
+ targetGone = 0
+ track = ta[TOON_TRACK_COL]
+ if track != NO_ATTACK:
+ adict = {}
+ toonIndex = ta[TOON_ID_COL]
+ toonId = toons[toonIndex]
+ toon = self.battle.findToon(toonId)
+ if toon == None:
+ continue
+ level = ta[TOON_LVL_COL]
+ adict['toon'] = toon
+ adict['track'] = track
+ adict['level'] = level
+ hps = ta[TOON_HP_COL]
+ kbbonuses = ta[TOON_KBBONUS_COL]
+ if track == NPCSOS:
+ adict['npcId'] = ta[TOON_TGT_COL]
+ toonId = ta[TOON_TGT_COL]
+ track, npc_level, npc_hp = NPCToons.getNPCTrackLevelHp(adict['npcId'])
+ if track == None:
+ track = NPCSOS
+ adict['track'] = track
+ adict['level'] = npc_level
+ elif track == PETSOS:
+ petId = ta[TOON_TGT_COL]
+ adict['toonId'] = toonId
+ adict['petId'] = petId
+ if track == SOS:
+ targetId = ta[TOON_TGT_COL]
+ if targetId == base.localAvatar.doId:
+ target = base.localAvatar
+ adict['targetType'] = 'callee'
+ elif toon == base.localAvatar:
+ target = base.cr.identifyAvatar(targetId)
+ adict['targetType'] = 'caller'
+ else:
+ target = None
+ adict['targetType'] = 'observer'
+ adict['target'] = target
+ elif track == NPCSOS or track == NPC_COGS_MISS or track == NPC_TOONS_HIT or track == NPC_RESTOCK_GAGS or track == PETSOS:
+ adict['special'] = 1
+ toonHandles = []
+ for t in toons:
+ if t != -1:
+ target = self.battle.findToon(t)
+ if target == None:
+ continue
+ if track == NPC_TOONS_HIT and t == toonId:
+ continue
+ toonHandles.append(target)
+
+ adict['toons'] = toonHandles
+ suitHandles = []
+ for s in suits:
+ if s != -1:
+ target = self.battle.findSuit(s)
+ if target == None:
+ continue
+ suitHandles.append(target)
+
+ adict['suits'] = suitHandles
+ if track == PETSOS:
+ del adict['special']
+ targets = []
+ for t in toons:
+ if t != -1:
+ target = self.battle.findToon(t)
+ if target == None:
+ continue
+ tdict = {}
+ tdict['toon'] = target
+ tdict['hp'] = hps[toons.index(t)]
+ self.notify.debug('PETSOS: toon: %d healed for hp: %d' % (target.doId, hps[toons.index(t)]))
+ targets.append(tdict)
+
+ if len(targets) > 0:
+ adict['target'] = targets
+ elif track == HEAL:
+ if levelAffectsGroup(HEAL, level):
+ targets = []
+ for t in toons:
+ if t != toonId and t != -1:
+ target = self.battle.findToon(t)
+ if target == None:
+ continue
+ tdict = {}
+ tdict['toon'] = target
+ tdict['hp'] = hps[toons.index(t)]
+ self.notify.debug('HEAL: toon: %d healed for hp: %d' % (target.doId, hps[toons.index(t)]))
+ targets.append(tdict)
+
+ if len(targets) > 0:
+ adict['target'] = targets
+ else:
+ targetGone = 1
+ else:
+ targetIndex = ta[TOON_TGT_COL]
+ if targetIndex < 0:
+ targetGone = 1
+ else:
+ targetId = toons[targetIndex]
+ target = self.battle.findToon(targetId)
+ if target != None:
+ tdict = {}
+ tdict['toon'] = target
+ tdict['hp'] = hps[targetIndex]
+ adict['target'] = tdict
+ else:
+ targetGone = 1
+ elif attackAffectsGroup(track, level, ta[TOON_TRACK_COL]):
+ targets = []
+ for s in suits:
+ if s != -1:
+ target = self.battle.findSuit(s)
+ if ta[TOON_TRACK_COL] == NPCSOS:
+ if track == LURE and self.battle.isSuitLured(target) == 1:
+ continue
+ elif track == TRAP and (self.battle.isSuitLured(target) == 1 or target.battleTrap != NO_TRAP):
+ continue
+ targetIndex = suits.index(s)
+ sdict = {}
+ sdict['suit'] = target
+ sdict['hp'] = hps[targetIndex]
+ if ta[TOON_TRACK_COL] == NPCSOS and track == DROP and hps[targetIndex] == 0:
+ continue
+ sdict['kbbonus'] = kbbonuses[targetIndex]
+ sdict['died'] = ta[SUIT_DIED_COL] & 1 << targetIndex
+ sdict['revived'] = ta[SUIT_REVIVE_COL] & 1 << targetIndex
+ if sdict['died'] != 0:
+ pass
+ sdict['leftSuits'] = []
+ sdict['rightSuits'] = []
+ targets.append(sdict)
+
+ adict['target'] = targets
+ else:
+ targetIndex = ta[TOON_TGT_COL]
+ if targetIndex < 0:
+ targetGone = 1
+ else:
+ targetId = suits[targetIndex]
+ target = self.battle.findSuit(targetId)
+ sdict = {}
+ sdict['suit'] = target
+ if self.battle.activeSuits.count(target) == 0:
+ targetGone = 1
+ suitIndex = 0
+ else:
+ suitIndex = self.battle.activeSuits.index(target)
+ leftSuits = []
+ for si in range(0, suitIndex):
+ asuit = self.battle.activeSuits[si]
+ if self.battle.isSuitLured(asuit) == 0:
+ leftSuits.append(asuit)
+
+ lenSuits = len(self.battle.activeSuits)
+ rightSuits = []
+ if lenSuits > suitIndex + 1:
+ for si in range(suitIndex + 1, lenSuits):
+ asuit = self.battle.activeSuits[si]
+ if self.battle.isSuitLured(asuit) == 0:
+ rightSuits.append(asuit)
+
+ sdict['leftSuits'] = leftSuits
+ sdict['rightSuits'] = rightSuits
+ sdict['hp'] = hps[targetIndex]
+ sdict['kbbonus'] = kbbonuses[targetIndex]
+ sdict['died'] = ta[SUIT_DIED_COL] & 1 << targetIndex
+ sdict['revived'] = ta[SUIT_REVIVE_COL] & 1 << targetIndex
+ if sdict['revived'] != 0:
+ pass
+ if sdict['died'] != 0:
+ pass
+ if track == DROP or track == TRAP:
+ adict['target'] = [sdict]
+ else:
+ adict['target'] = sdict
+ adict['hpbonus'] = ta[TOON_HPBONUS_COL]
+ adict['sidestep'] = ta[TOON_ACCBONUS_COL]
+ if adict.has_key('npcId'):
+ adict['sidestep'] = 0
+ adict['battle'] = self.battle
+ adict['playByPlayText'] = self.playByPlayText
+ if targetGone == 0:
+ self.toonAttackDicts.append(adict)
+ else:
+ self.notify.warning('genToonAttackDicts() - target gone!')
+
+ def compFunc(a, b):
+ alevel = a['level']
+ blevel = b['level']
+ if alevel > blevel:
+ return 1
+ elif alevel < blevel:
+ return -1
+ return 0
+
+ self.toonAttackDicts.sort(compFunc)
+ return
+
+ def __findToonAttack(self, track):
+ setCapture = 0
+ tp = []
+ for ta in self.toonAttackDicts:
+ if ta['track'] == track or track == NPCSOS and ta.has_key('special'):
+ tp.append(ta)
+ if track == SQUIRT:
+ setCapture = 1
+
+ if track == TRAP:
+ sortedTraps = []
+ for attack in tp:
+ if not attack.has_key('npcId'):
+ sortedTraps.append(attack)
+
+ for attack in tp:
+ if attack.has_key('npcId'):
+ sortedTraps.append(attack)
+
+ tp = sortedTraps
+ if setCapture:
+ pass
+ return tp
+
+ def __genSuitAttackDicts(self, toons, suits, suitAttacks):
+ for sa in suitAttacks:
+ targetGone = 0
+ attack = sa[SUIT_ATK_COL]
+ if attack != NO_ATTACK:
+ suitIndex = sa[SUIT_ID_COL]
+ suitId = suits[suitIndex]
+ suit = self.battle.findSuit(suitId)
+ if suit == None:
+ self.notify.error('suit: %d not in battle!' % suitId)
+ adict = getSuitAttack(suit.getStyleName(), suit.getLevel(), attack)
+ adict['suit'] = suit
+ adict['battle'] = self.battle
+ adict['playByPlayText'] = self.playByPlayText
+ adict['taunt'] = sa[SUIT_TAUNT_COL]
+ hps = sa[SUIT_HP_COL]
+ if adict['group'] == ATK_TGT_GROUP:
+ targets = []
+ for t in toons:
+ if t != -1:
+ target = self.battle.findToon(t)
+ if target == None:
+ continue
+ targetIndex = toons.index(t)
+ tdict = {}
+ tdict['toon'] = target
+ tdict['hp'] = hps[targetIndex]
+ self.notify.debug('DAMAGE: toon: %d hit for hp: %d' % (target.doId, hps[targetIndex]))
+ toonDied = sa[TOON_DIED_COL] & 1 << targetIndex
+ tdict['died'] = toonDied
+ targets.append(tdict)
+
+ if len(targets) > 0:
+ adict['target'] = targets
+ else:
+ targetGone = 1
+ elif adict['group'] == ATK_TGT_SINGLE:
+ targetIndex = sa[SUIT_TGT_COL]
+ targetId = toons[targetIndex]
+ target = self.battle.findToon(targetId)
+ if target == None:
+ targetGone = 1
+ break
+ tdict = {}
+ tdict['toon'] = target
+ tdict['hp'] = hps[targetIndex]
+ self.notify.debug('DAMAGE: toon: %d hit for hp: %d' % (target.doId, hps[targetIndex]))
+ toonDied = sa[TOON_DIED_COL] & 1 << targetIndex
+ tdict['died'] = toonDied
+ toonIndex = self.battle.activeToons.index(target)
+ rightToons = []
+ for ti in range(0, toonIndex):
+ rightToons.append(self.battle.activeToons[ti])
+
+ lenToons = len(self.battle.activeToons)
+ leftToons = []
+ if lenToons > toonIndex + 1:
+ for ti in range(toonIndex + 1, lenToons):
+ leftToons.append(self.battle.activeToons[ti])
+
+ tdict['leftToons'] = leftToons
+ tdict['rightToons'] = rightToons
+ adict['target'] = tdict
+ else:
+ self.notify.warning('got suit attack not group or single!')
+ if targetGone == 0:
+ self.suitAttackDicts.append(adict)
+ else:
+ self.notify.warning('genSuitAttackDicts() - target gone!')
+
+ return
+
+ def __doSuitAttacks(self):
+ if base.config.GetBool('want-suit-anims', 1):
+ track = Sequence(name='suit-attacks')
+ camTrack = Sequence(name='suit-attacks-cam')
+ isLocalToonSad = False
+ for a in self.suitAttackDicts:
+ ival, camIval = MovieSuitAttacks.doSuitAttack(a)
+ if ival:
+ track.append(ival)
+ camTrack.append(camIval)
+ targetField = a.get('target')
+ if targetField is None:
+ continue
+ if a['group'] == ATK_TGT_GROUP:
+ for target in targetField:
+ if target['died'] and target['toon'].doId == base.localAvatar.doId:
+ isLocalToonSad = True
+
+ elif a['group'] == ATK_TGT_SINGLE:
+ if targetField['died'] and targetField['toon'].doId == base.localAvatar.doId:
+ isLocalToonSad = True
+ if isLocalToonSad:
+ break
+
+ if len(track) == 0:
+ return (None, None)
+ return (track, camTrack)
+ else:
+ return (None, None)
+ return
diff --git a/toontown/battle/MovieCamera.py b/toontown/battle/MovieCamera.py
new file mode 100644
index 0000000..234cddb
--- /dev/null
+++ b/toontown/battle/MovieCamera.py
@@ -0,0 +1,979 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from BattleProps import *
+from toontown.toonbase.ToontownBattleGlobals import *
+from SuitBattleGlobals import *
+from direct.directnotify import DirectNotifyGlobal
+import random
+import MovieUtil
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieCamera')
+
+def chooseHealShot(heals, attackDuration):
+ isUber = 0
+ for heal in heals:
+ if heal['level'] == 6 and not heal.get('petId'):
+ isUber = 1
+
+ if isUber:
+ print 'is uber'
+ openShot = chooseHealOpenShot(heals, attackDuration, isUber)
+ openDuration = openShot.getDuration()
+ openName = openShot.getName()
+ closeShot = chooseHealCloseShot(heals, openDuration, openName, attackDuration * 3, isUber)
+ track = Sequence(closeShot)
+ else:
+ openShot = chooseHealOpenShot(heals, attackDuration, isUber)
+ openDuration = openShot.getDuration()
+ openName = openShot.getName()
+ closeShot = chooseHealCloseShot(heals, openDuration, openName, attackDuration, isUber)
+ track = Sequence(openShot, closeShot)
+ return track
+
+
+def chooseHealOpenShot(heals, attackDuration, isUber = 0):
+ numHeals = len(heals)
+ av = None
+ duration = 2.8
+ if isUber:
+ duration = 5.0
+ shotChoices = [toonGroupShot]
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseHealMidShot(heals, attackDuration, isUber = 0):
+ numHeals = len(heals)
+ av = None
+ duration = 2.1
+ if isUber:
+ duration = 2.1
+ shotChoices = [toonGroupHighShot]
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseHealCloseShot(heals, openDuration, openName, attackDuration, isUber = 0):
+ av = None
+ duration = attackDuration - openDuration
+ shotChoices = [toonGroupShot]
+ if isUber:
+ shotChoices = [allGroupLowShot]
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseTrapShot(traps, attackDuration, enterDuration = 0, exitDuration = 0):
+ enterShot = chooseNPCEnterShot(traps, enterDuration)
+ openShot = chooseTrapOpenShot(traps, attackDuration)
+ openDuration = openShot.getDuration()
+ openName = openShot.getName()
+ closeShot = chooseTrapCloseShot(traps, openDuration, openName, attackDuration)
+ exitShot = chooseNPCExitShot(traps, exitDuration)
+ track = Sequence(enterShot, openShot, closeShot, exitShot)
+ return track
+
+
+def chooseTrapOpenShot(traps, attackDuration):
+ numTraps = len(traps)
+ av = None
+ duration = 3.0
+ shotChoices = [allGroupLowShot]
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseTrapCloseShot(traps, openDuration, openName, attackDuration):
+ av = None
+ duration = attackDuration - openDuration
+ shotChoices = [allGroupLowShot]
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseLureShot(lures, attackDuration, enterDuration = 0.0, exitDuration = 0.0):
+ enterShot = chooseNPCEnterShot(lures, enterDuration)
+ openShot = chooseLureOpenShot(lures, attackDuration)
+ openDuration = openShot.getDuration()
+ openName = openShot.getName()
+ closeShot = chooseLureCloseShot(lures, openDuration, openName, attackDuration)
+ exitShot = chooseNPCExitShot(lures, exitDuration)
+ track = Sequence(enterShot, openShot, closeShot, exitShot)
+ return track
+
+
+def chooseLureOpenShot(lures, attackDuration):
+ numLures = len(lures)
+ av = None
+ duration = 3.0
+ shotChoices = [allGroupLowShot]
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseLureCloseShot(lures, openDuration, openName, attackDuration):
+ av = None
+ duration = attackDuration - openDuration
+ hasTrainTrackTrap = False
+ battle = lures[0]['battle']
+ for suit in battle.suits:
+ if hasattr(suit, 'battleTrap') and suit.battleTrap == UBER_GAG_LEVEL_INDEX:
+ hasTrainTrackTrap = True
+
+ if hasTrainTrackTrap:
+ shotChoices = [avatarBehindHighRightShot]
+ av = lures[0]['toon']
+ else:
+ shotChoices = [allGroupLowShot]
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseSoundShot(sounds, targets, attackDuration, enterDuration = 0.0, exitDuration = 0.0):
+ enterShot = chooseNPCEnterShot(sounds, enterDuration)
+ openShot = chooseSoundOpenShot(sounds, targets, attackDuration)
+ openDuration = openShot.getDuration()
+ openName = openShot.getName()
+ closeShot = chooseSoundCloseShot(sounds, targets, openDuration, openName, attackDuration)
+ exitShot = chooseNPCExitShot(sounds, exitDuration)
+ track = Sequence(enterShot, openShot, closeShot, exitShot)
+ return track
+
+
+def chooseSoundOpenShot(sounds, targets, attackDuration):
+ duration = 3.1
+ isUber = 0
+ for sound in sounds:
+ if sound['level'] == 6:
+ isUber = 1
+ duration = 5.0
+
+ numSounds = len(sounds)
+ av = None
+ if numSounds == 1:
+ av = sounds[0]['toon']
+ if isUber:
+ shotChoices = [avatarCloseUpThreeQuarterRightShotWide, allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ else:
+ shotChoices = [avatarCloseUpThreeQuarterRightShot, allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ elif numSounds >= 2 and numSounds <= 4:
+ shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ else:
+ notify.error('Bad number of sounds: %s' % numSounds)
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseSoundCloseShot(sounds, targets, openDuration, openName, attackDuration):
+ numSuits = len(targets)
+ av = None
+ duration = attackDuration - openDuration
+ if numSuits == 1:
+ av = targets[0]['suit']
+ shotChoices = [avatarCloseUpThrowShot,
+ avatarCloseUpThreeQuarterLeftShot,
+ allGroupLowShot,
+ suitGroupThreeQuarterLeftBehindShot]
+ elif numSuits >= 2 and numSuits <= 4:
+ shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ else:
+ notify.error('Bad number of suits: %s' % numSuits)
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseThrowShot(throws, suitThrowsDict, attackDuration):
+ openShot = chooseThrowOpenShot(throws, suitThrowsDict, attackDuration)
+ openDuration = openShot.getDuration()
+ openName = openShot.getName()
+ closeShot = chooseThrowCloseShot(throws, suitThrowsDict, openDuration, openName, attackDuration)
+ track = Sequence(openShot, closeShot)
+ return track
+
+
+def chooseThrowOpenShot(throws, suitThrowsDict, attackDuration):
+ numThrows = len(throws)
+ av = None
+ duration = 3.0
+ if numThrows == 1:
+ av = throws[0]['toon']
+ shotChoices = [avatarCloseUpThrowShot,
+ avatarCloseUpThreeQuarterRightShot,
+ avatarBehindShot,
+ allGroupLowShot,
+ suitGroupThreeQuarterLeftBehindShot]
+ elif numThrows >= 2 and numThrows <= 4:
+ shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ else:
+ notify.error('Bad number of throws: %s' % numThrows)
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseThrowCloseShot(throws, suitThrowsDict, openDuration, openName, attackDuration):
+ numSuits = len(suitThrowsDict)
+ av = None
+ duration = attackDuration - openDuration
+ if numSuits == 1:
+ av = base.cr.doId2do[suitThrowsDict.keys()[0]]
+ shotChoices = [avatarCloseUpThrowShot,
+ avatarCloseUpThreeQuarterLeftShot,
+ allGroupLowShot,
+ suitGroupThreeQuarterLeftBehindShot]
+ elif numSuits >= 2 and numSuits <= 4 or numSuits == 0:
+ shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ else:
+ notify.error('Bad number of suits: %s' % numSuits)
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseSquirtShot(squirts, suitSquirtsDict, attackDuration):
+ openShot = chooseSquirtOpenShot(squirts, suitSquirtsDict, attackDuration)
+ openDuration = openShot.getDuration()
+ openName = openShot.getName()
+ closeShot = chooseSquirtCloseShot(squirts, suitSquirtsDict, openDuration, openName, attackDuration)
+ track = Sequence(openShot, closeShot)
+ return track
+
+
+def chooseSquirtOpenShot(squirts, suitSquirtsDict, attackDuration):
+ numSquirts = len(squirts)
+ av = None
+ duration = 3.0
+ if numSquirts == 1:
+ av = squirts[0]['toon']
+ shotChoices = [avatarCloseUpThrowShot,
+ avatarCloseUpThreeQuarterRightShot,
+ avatarBehindShot,
+ allGroupLowShot,
+ suitGroupThreeQuarterLeftBehindShot]
+ elif numSquirts >= 2 and numSquirts <= 4:
+ shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ else:
+ notify.error('Bad number of squirts: %s' % numSquirts)
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseSquirtCloseShot(squirts, suitSquirtsDict, openDuration, openName, attackDuration):
+ numSuits = len(suitSquirtsDict)
+ av = None
+ duration = attackDuration - openDuration
+ if numSuits == 1:
+ av = base.cr.doId2do[suitSquirtsDict.keys()[0]]
+ shotChoices = [avatarCloseUpThrowShot,
+ avatarCloseUpThreeQuarterLeftShot,
+ allGroupLowShot,
+ suitGroupThreeQuarterLeftBehindShot]
+ elif numSuits >= 2 and numSuits <= 4:
+ shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ else:
+ notify.error('Bad number of suits: %s' % numSuits)
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseDropShot(drops, suitDropsDict, attackDuration, enterDuration = 0.0, exitDuration = 0.0):
+ enterShot = chooseNPCEnterShot(drops, enterDuration)
+ openShot = chooseDropOpenShot(drops, suitDropsDict, attackDuration)
+ openDuration = openShot.getDuration()
+ openName = openShot.getName()
+ closeShot = chooseDropCloseShot(drops, suitDropsDict, openDuration, openName, attackDuration)
+ exitShot = chooseNPCExitShot(drops, exitDuration)
+ track = Sequence(enterShot, openShot, closeShot, exitShot)
+ return track
+
+
+def chooseDropOpenShot(drops, suitDropsDict, attackDuration):
+ numDrops = len(drops)
+ av = None
+ duration = 3.0
+ if numDrops == 1:
+ av = drops[0]['toon']
+ shotChoices = [avatarCloseUpThrowShot,
+ avatarCloseUpThreeQuarterRightShot,
+ avatarBehindShot,
+ allGroupLowShot,
+ suitGroupThreeQuarterLeftBehindShot]
+ elif numDrops >= 2 and numDrops <= 4 or numDrops == 0:
+ shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ else:
+ notify.error('Bad number of drops: %s' % numDrops)
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseDropCloseShot(drops, suitDropsDict, openDuration, openName, attackDuration):
+ numSuits = len(suitDropsDict)
+ av = None
+ duration = attackDuration - openDuration
+ if numSuits == 1:
+ av = base.cr.doId2do[suitDropsDict.keys()[0]]
+ shotChoices = [avatarCloseUpThrowShot,
+ avatarCloseUpThreeQuarterLeftShot,
+ allGroupLowShot,
+ suitGroupThreeQuarterLeftBehindShot]
+ elif numSuits >= 2 and numSuits <= 4 or numSuits == 0:
+ shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ else:
+ notify.error('Bad number of suits: %s' % numSuits)
+ choice = random.choice(shotChoices)
+ track = choice(av, duration)
+ return track
+
+
+def chooseNPCEnterShot(enters, entersDuration):
+ av = None
+ duration = entersDuration
+ shotChoices = [toonGroupShot]
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseNPCExitShot(exits, exitsDuration):
+ av = None
+ duration = exitsDuration
+ shotChoices = [toonGroupShot]
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseSuitShot(attack, attackDuration):
+ groupStatus = attack['group']
+ target = attack['target']
+ if groupStatus == ATK_TGT_SINGLE:
+ toon = target['toon']
+ suit = attack['suit']
+ name = attack['id']
+ battle = attack['battle']
+ camTrack = Sequence()
+
+ def defaultCamera(attack = attack, attackDuration = attackDuration, openShotDuration = 3.5, target = target):
+ if attack['group'] == ATK_TGT_GROUP:
+ return randomGroupAttackCam(attack['suit'], target, attack['battle'], attackDuration, openShotDuration)
+ else:
+ return randomAttackCam(attack['suit'], target['toon'], attack['battle'], attackDuration, openShotDuration, 'suit')
+
+ if name == AUDIT:
+ camTrack.append(defaultCamera())
+ elif name == BITE:
+ camTrack.append(defaultCamera(openShotDuration=2.8))
+ elif name == BOUNCE_CHECK:
+ camTrack.append(defaultCamera())
+ elif name == BRAIN_STORM:
+ camTrack.append(defaultCamera(openShotDuration=2.4))
+ elif name == BUZZ_WORD:
+ camTrack.append(defaultCamera(openShotDuration=4.7))
+ elif name == CALCULATE:
+ camTrack.append(defaultCamera())
+ elif name == CANNED:
+ camTrack.append(defaultCamera(openShotDuration=2.9))
+ elif name == CHOMP:
+ camTrack.append(defaultCamera(openShotDuration=2.8))
+ elif name == CLIPON_TIE:
+ camTrack.append(defaultCamera(openShotDuration=3.3))
+ elif name == CRUNCH:
+ camTrack.append(defaultCamera(openShotDuration=3.4))
+ elif name == DEMOTION:
+ camTrack.append(defaultCamera(openShotDuration=1.7))
+ elif name == DOUBLE_TALK:
+ camTrack.append(defaultCamera(openShotDuration=3.9))
+ elif name == EVICTION_NOTICE:
+ camTrack.append(defaultCamera(openShotDuration=3.2))
+ elif name == EVIL_EYE:
+ camTrack.append(defaultCamera(openShotDuration=2.7))
+ elif name == FILIBUSTER:
+ camTrack.append(defaultCamera(openShotDuration=2.7))
+ elif name == FILL_WITH_LEAD:
+ camTrack.append(defaultCamera(openShotDuration=3.2))
+ elif name == FINGER_WAG:
+ camTrack.append(defaultCamera(openShotDuration=2.3))
+ elif name == FIRED:
+ camTrack.append(defaultCamera(openShotDuration=1.7))
+ elif name == FOUNTAIN_PEN:
+ camTrack.append(defaultCamera(openShotDuration=2.6))
+ elif name == FREEZE_ASSETS:
+ camTrack.append(defaultCamera(openShotDuration=2.5))
+ elif name == HALF_WINDSOR:
+ camTrack.append(defaultCamera(openShotDuration=2.8))
+ elif name == HEAD_SHRINK:
+ camTrack.append(defaultCamera(openShotDuration=1.3))
+ elif name == GLOWER_POWER:
+ camTrack.append(defaultCamera(openShotDuration=1.4))
+ elif name == GUILT_TRIP:
+ camTrack.append(defaultCamera(openShotDuration=0.9))
+ elif name == HANG_UP:
+ camTrack.append(defaultCamera(openShotDuration=5.1))
+ elif name == HOT_AIR:
+ camTrack.append(defaultCamera(openShotDuration=2.5))
+ elif name == JARGON:
+ camTrack.append(defaultCamera())
+ elif name == LEGALESE:
+ camTrack.append(defaultCamera(openShotDuration=1.5))
+ elif name == LIQUIDATE:
+ camTrack.append(defaultCamera(openShotDuration=2.5))
+ elif name == MARKET_CRASH:
+ camTrack.append(defaultCamera(openShotDuration=2.9))
+ elif name == MUMBO_JUMBO:
+ camTrack.append(defaultCamera(openShotDuration=2.8))
+ elif name == PARADIGM_SHIFT:
+ camTrack.append(defaultCamera(openShotDuration=1.6))
+ elif name == PECKING_ORDER:
+ camTrack.append(defaultCamera(openShotDuration=2.8))
+ elif name == PLAY_HARDBALL:
+ camTrack.append(defaultCamera(openShotDuration=2.3))
+ elif name == PICK_POCKET:
+ camTrack.append(allGroupLowShot(suit, 2.7))
+ elif name == PINK_SLIP:
+ camTrack.append(defaultCamera(openShotDuration=2.8))
+ elif name == POUND_KEY:
+ camTrack.append(defaultCamera(openShotDuration=2.8))
+ elif name == POWER_TIE:
+ camTrack.append(defaultCamera(openShotDuration=2.4))
+ elif name == POWER_TRIP:
+ camTrack.append(defaultCamera(openShotDuration=1.1))
+ elif name == QUAKE:
+ shakeIntensity = 5.15
+ quake = 1
+ camTrack.append(suitCameraShakeShot(suit, attackDuration, shakeIntensity, quake))
+ elif name == RAZZLE_DAZZLE:
+ camTrack.append(defaultCamera(openShotDuration=2.2))
+ elif name == RED_TAPE:
+ camTrack.append(defaultCamera(openShotDuration=3.5))
+ elif name == RE_ORG:
+ camTrack.append(defaultCamera(openShotDuration=1.1))
+ elif name == RESTRAINING_ORDER:
+ camTrack.append(defaultCamera(openShotDuration=2.8))
+ elif name == ROLODEX:
+ camTrack.append(defaultCamera())
+ elif name == RUBBER_STAMP:
+ camTrack.append(defaultCamera(openShotDuration=3.2))
+ elif name == RUB_OUT:
+ camTrack.append(defaultCamera(openShotDuration=2.2))
+ elif name == SACKED:
+ camTrack.append(defaultCamera(openShotDuration=2.9))
+ elif name == SCHMOOZE:
+ camTrack.append(defaultCamera(openShotDuration=2.8))
+ elif name == SHAKE:
+ shakeIntensity = 1.75
+ camTrack.append(suitCameraShakeShot(suit, attackDuration, shakeIntensity))
+ elif name == SHRED:
+ camTrack.append(defaultCamera(openShotDuration=4.1))
+ elif name == SPIN:
+ camTrack.append(defaultCamera(openShotDuration=1.7))
+ elif name == SYNERGY:
+ camTrack.append(defaultCamera(openShotDuration=1.7))
+ elif name == TABULATE:
+ camTrack.append(defaultCamera())
+ elif name == TEE_OFF:
+ camTrack.append(defaultCamera(openShotDuration=4.5))
+ elif name == TREMOR:
+ shakeIntensity = 0.25
+ camTrack.append(suitCameraShakeShot(suit, attackDuration, shakeIntensity))
+ elif name == WATERCOOLER:
+ camTrack.append(defaultCamera())
+ elif name == WITHDRAWAL:
+ camTrack.append(defaultCamera(openShotDuration=1.2))
+ elif name == WRITE_OFF:
+ camTrack.append(defaultCamera())
+ else:
+ notify.warning('unknown attack id in chooseSuitShot: %d using default cam' % name)
+ camTrack.append(defaultCamera())
+ pbpText = attack['playByPlayText']
+ displayName = TTLocalizer.SuitAttackNames[attack['name']]
+ pbpTrack = pbpText.getShowInterval(displayName, 3.5)
+ return Parallel(camTrack, pbpTrack)
+
+
+def chooseSuitCloseShot(attack, openDuration, openName, attackDuration):
+ av = None
+ duration = attackDuration - openDuration
+ if duration < 0:
+ duration = 1e-06
+ groupStatus = attack['group']
+ diedTrack = None
+ if groupStatus == ATK_TGT_SINGLE:
+ av = attack['target']['toon']
+ shotChoices = [avatarCloseUpThreeQuarterRightShot, suitGroupThreeQuarterLeftBehindShot]
+ died = attack['target']['died']
+ if died != 0:
+ pbpText = attack['playByPlayText']
+ diedText = av.getName() + ' was defeated!'
+ diedTextList = [diedText]
+ diedTrack = pbpText.getToonsDiedInterval(diedTextList, duration)
+ elif groupStatus == ATK_TGT_GROUP:
+ av = None
+ shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ deadToons = []
+ targetDicts = attack['target']
+ for targetDict in targetDicts:
+ died = targetDict['died']
+ if died != 0:
+ deadToons.append(targetDict['toon'])
+
+ if len(deadToons) > 0:
+ pbpText = attack['playByPlayText']
+ diedTextList = []
+ for toon in deadToons:
+ pbpText = attack['playByPlayText']
+ diedTextList.append(toon.getName() + ' was defeated!')
+
+ diedTrack = pbpText.getToonsDiedInterval(diedTextList, duration)
+ else:
+ notify.error('Bad groupStatus: %s' % groupStatus)
+ track = apply(random.choice(shotChoices), [av, duration])
+ if diedTrack == None:
+ return track
+ else:
+ mtrack = Parallel(track, diedTrack)
+ return mtrack
+ return
+
+
+def makeShot(x, y, z, h, p, r, duration, other = None, name = 'makeShot'):
+ if other:
+ return heldRelativeShot(other, x, y, z, h, p, r, duration, name)
+ else:
+ return heldShot(x, y, z, h, p, r, duration, name)
+
+
+def focusShot(x, y, z, duration, target, other = None, splitFocusPoint = None, name = 'focusShot'):
+ track = Sequence()
+ if other:
+ track.append(Func(camera.setPos, other, Point3(x, y, z)))
+ else:
+ track.append(Func(camera.setPos, Point3(x, y, z)))
+ if splitFocusPoint:
+ track.append(Func(focusCameraBetweenPoints, target, splitFocusPoint))
+ else:
+ track.append(Func(camera.lookAt, target))
+ track.append(Wait(duration))
+ return track
+
+
+def moveShot(x, y, z, h, p, r, duration, other = None, name = 'moveShot'):
+ return motionShot(x, y, z, h, p, r, duration, other, name)
+
+
+def focusMoveShot(x, y, z, duration, target, other = None, name = 'focusMoveShot'):
+ camera.setPos(Point3(x, y, z))
+ camera.lookAt(target)
+ hpr = camera.getHpr()
+ return motionShot(x, y, z, hpr[0], hpr[1], hpr[2], duration, other, name)
+
+
+def chooseSOSShot(av, duration):
+ shotChoices = [avatarCloseUpThreeQuarterRightShot,
+ avatarBehindShot,
+ avatarBehindHighShot,
+ suitGroupThreeQuarterLeftBehindShot]
+ track = apply(random.choice(shotChoices), [av, duration])
+ return track
+
+
+def chooseRewardShot(av, duration, allowGroupShot = 1):
+
+ def chooseRewardShotNow(av):
+ if av.playingAnim == 'victory' or not allowGroupShot:
+ shotChoices = [(0,
+ 8,
+ av.getHeight() * 0.66,
+ 179,
+ 15,
+ 0), (5.2,
+ 5.45,
+ av.getHeight() * 0.66,
+ 131.5,
+ 3.6,
+ 0)]
+ shot = random.choice(shotChoices)
+ camera.setPosHpr(av, *shot)
+ else:
+ camera.setPosHpr(10, 0, 10, 115, -30, 0)
+
+ return Sequence(Func(chooseRewardShotNow, av), Wait(duration))
+
+
+def heldShot(x, y, z, h, p, r, duration, name = 'heldShot'):
+ track = Sequence(name=name)
+ track.append(Func(camera.setPosHpr, x, y, z, h, p, r))
+ track.append(Wait(duration))
+ return track
+
+
+def heldRelativeShot(other, x, y, z, h, p, r, duration, name = 'heldRelativeShot'):
+ track = Sequence(name=name)
+ track.append(Func(camera.setPosHpr, other, x, y, z, h, p, r))
+ track.append(Wait(duration))
+ return track
+
+
+def motionShot(x, y, z, h, p, r, duration, other = None, name = 'motionShot'):
+ if other:
+ posTrack = LerpPosInterval(camera, duration, pos=Point3(x, y, z), other=other)
+ hprTrack = LerpHprInterval(camera, duration, hpr=Point3(h, p, r), other=other)
+ else:
+ posTrack = LerpPosInterval(camera, duration, pos=Point3(x, y, z))
+ hprTrack = LerpHprInterval(camera, duration, hpr=Point3(h, p, r))
+ return Parallel(posTrack, hprTrack)
+
+
+def allGroupShot(avatar, duration):
+ return heldShot(10, 0, 10, 89, -30, 0, duration, 'allGroupShot')
+
+
+def allGroupLowShot(avatar, duration):
+ return heldShot(15, 0, 3, 89, 0, 0, duration, 'allGroupLowShot')
+
+
+def allGroupLowDiagonalShot(avatar, duration):
+ return heldShot(7, 5, 6, 119, -30, 0, duration, 'allGroupLowShot')
+
+
+def toonGroupShot(avatar, duration):
+ return heldShot(10, 0, 10, 115, -30, 0, duration, 'toonGroupShot')
+
+
+def toonGroupHighShot(avatar, duration):
+ return heldShot(5, 0, 1, 115, 45, 0, duration, 'toonGroupHighShot')
+
+
+def suitGroupShot(avatar, duration):
+ return heldShot(10, 0, 10, 65, -30, 0, duration, 'suitGroupShot')
+
+
+def suitGroupLowLeftShot(avatar, duration):
+ return heldShot(8.4, -3.85, 2.75, 36.3, 3.25, 0, duration, 'suitGroupLowLeftShot')
+
+
+def suitGroupThreeQuarterLeftBehindShot(avatar, duration):
+ if random.random() > 0.5:
+ x = 12.37
+ h = 134.61
+ else:
+ x = -12.37
+ h = -134.61
+ return heldShot(x, 11.5, 8.16, h, -22.7, 0, duration, 'suitGroupThreeQuarterLeftBehindShot')
+
+
+def suitWakeUpShot(avatar, duration):
+ return heldShot(10, -5, 10, 65, -30, 0, duration, 'suitWakeUpShot')
+
+
+def suitCameraShakeShot(avatar, duration, shakeIntensity, quake = 0):
+ track = Sequence(name='suitShakeCameraShot')
+ if quake == 1:
+ shakeDelay = 1.1
+ numShakes = 4
+ else:
+ shakeDelay = 0.3
+ numShakes = 5
+ postShakeDelay = 0.5
+ shakeTime = (duration - shakeDelay - postShakeDelay) / numShakes
+ shakeDuration = shakeTime * (1.0 / numShakes)
+ shakeWaitInterval = shakeTime * ((numShakes - 1.0) / numShakes)
+
+ def shakeCameraTrack(intensity, shakeWaitInterval = shakeWaitInterval, quake = quake, shakeDuration = shakeDuration, numShakes = numShakes):
+ vertShakeTrack = Sequence(Wait(shakeWaitInterval), Func(camera.setZ, camera.getZ() + intensity / 2), Wait(shakeDuration / 2), Func(camera.setZ, camera.getZ() - intensity), Wait(shakeDuration / 2), Func(camera.setZ, camera.getZ() + intensity / 2))
+ horizShakeTrack = Sequence(Wait(shakeWaitInterval - shakeDuration / 2), Func(camera.setY, camera.getY() + intensity / 4), Wait(shakeDuration / 2), Func(camera.setY, camera.getY() - intensity / 2), Wait(shakeDuration / 2), Func(camera.setY, camera.getY() + intensity / 4), Wait(shakeDuration / 2), Func(camera.lookAt, Point3(0, 0, 0)))
+ shakeTrack = Sequence()
+ for i in range(0, numShakes):
+ if quake == 0:
+ shakeTrack.append(vertShakeTrack)
+ else:
+ shakeTrack.append(Parallel(vertShakeTrack, horizShakeTrack))
+
+ return shakeTrack
+
+ x = 10 + random.random() * 3
+ if random.random() > 0.5:
+ x = -x
+ z = 7 + random.random() * 3
+ track.append(Func(camera.setPos, x, -5, z))
+ track.append(Func(camera.lookAt, Point3(0, 0, 0)))
+ track.append(Wait(shakeDelay))
+ track.append(shakeCameraTrack(shakeIntensity))
+ track.append(Wait(postShakeDelay))
+ return track
+
+
+def avatarCloseUpShot(avatar, duration):
+ return heldRelativeShot(avatar, 0, 8, avatar.getHeight() * 0.66, 179, 15, 0, duration, 'avatarCloseUpShot')
+
+
+def avatarCloseUpThrowShot(avatar, duration):
+ return heldRelativeShot(avatar, 3, 8, avatar.getHeight() * 0.66, 159, 3.6, 0, duration, 'avatarCloseUpThrowShot')
+
+
+def avatarCloseUpThreeQuarterRightShot(avatar, duration):
+ return heldRelativeShot(avatar, 5.2, 5.45, avatar.getHeight() * 0.66, 131.5, 3.6, 0, duration, 'avatarCloseUpThreeQuarterRightShot')
+
+
+def avatarCloseUpThreeQuarterRightShotWide(avatar, duration):
+ return heldRelativeShot(avatar, 7.2, 8.45, avatar.getHeight() * 0.66, 131.5, 3.6, 0, duration, 'avatarCloseUpThreeQuarterRightShot')
+
+
+def avatarCloseUpThreeQuarterLeftShot(avatar, duration):
+ return heldRelativeShot(avatar, -5.2, 5.45, avatar.getHeight() * 0.66, -131.5, 3.6, 0, duration, 'avatarCloseUpThreeQuarterLeftShot')
+
+
+def avatarCloseUpThreeQuarterRightFollowShot(avatar, duration):
+ track = Sequence(name='avatarCloseUpThreeQuarterRightFollowShot')
+ track.append(heldRelativeShot(avatar, 5.2, 5.45, avatar.getHeight() * 0.66, 131.5, 3.6, 0, duration * 0.65))
+ track.append(LerpHprInterval(nodePath=camera, other=avatar, duration=duration * 0.2, hpr=Point3(110, 3.6, 0), blendType='easeInOut'))
+ track.append(Wait(duration * 0.25))
+ return track
+
+
+def avatarCloseUpZoomShot(avatar, duration):
+ track = Sequence('avatarCloseUpZoomShot')
+ track.append(LerpPosHprInterval(nodePath=camera, other=avatar, duration=duration / 2, startPos=Point3(0, 10, avatar.getHeight()), startHpr=Point3(179, -10, 0), pos=Point3(0, 6, avatar.getHeight()), hpr=Point3(179, -10, 0), blendType='easeInOut'))
+ track.append(Wait(duration / 2))
+ return track
+
+
+def avatarBehindShot(avatar, duration):
+ return heldRelativeShot(avatar, 5, -7, avatar.getHeight(), 40, -12, 0, duration, 'avatarBehindShot')
+
+
+def avatarBehindHighShot(avatar, duration):
+ return heldRelativeShot(avatar, -4, -7, 5 + avatar.getHeight(), -30, -35, 0, duration, 'avatarBehindHighShot')
+
+
+def avatarBehindHighRightShot(avatar, duration):
+ return heldRelativeShot(avatar, 4, -7, 5 + avatar.getHeight(), 30, -35, 0, duration, 'avatarBehindHighShot')
+
+
+def avatarBehindThreeQuarterRightShot(avatar, duration):
+ return heldRelativeShot(avatar, 7.67, -8.52, avatar.getHeight() * 0.66, 25, 7.5, 0, duration, 'avatarBehindThreeQuarterRightShot')
+
+
+def avatarSideFollowAttack(suit, toon, duration, battle):
+ windupDuration = duration * (0.1 + random.random() * 0.1)
+ projectDuration = duration * 0.75
+ impactDuration = duration - windupDuration - projectDuration
+ suitHeight = suit.getHeight()
+ toonHeight = toon.getHeight()
+ suitCentralPoint = suit.getPos(battle)
+ suitCentralPoint.setZ(suitCentralPoint.getZ() + suitHeight * 0.75)
+ toonCentralPoint = toon.getPos(battle)
+ toonCentralPoint.setZ(toonCentralPoint.getZ() + toonHeight * 0.75)
+ initialX = random.randint(12, 14)
+ finalX = random.randint(7, 8)
+ initialY = finalY = random.randint(-3, 0)
+ initialZ = suitHeight * 0.5 + random.random() * suitHeight
+ finalZ = toonHeight * 0.5 + random.random() * toonHeight
+ if random.random() > 0.5:
+ initialX = -initialX
+ finalX = -finalX
+ return Sequence(focusShot(initialX, initialY, initialZ, windupDuration, suitCentralPoint), focusMoveShot(finalX, finalY, finalZ, projectDuration, toonCentralPoint), Wait(impactDuration))
+
+
+def focusCameraBetweenPoints(point1, point2):
+ if point1[0] > point2[0]:
+ x = point2[0] + (point1[0] - point2[0]) * 0.5
+ else:
+ x = point1[0] + (point2[0] - point1[0]) * 0.5
+ if point1[1] > point2[1]:
+ y = point2[1] + (point1[1] - point2[1]) * 0.5
+ else:
+ y = point1[1] + (point2[1] - point1[1]) * 0.5
+ if point1[2] > point2[2]:
+ z = point2[2] + (point1[2] - point2[2]) * 0.5
+ else:
+ z = point1[2] + (point2[2] - point1[2]) * 0.5
+ camera.lookAt(Point3(x, y, z))
+
+
+def randomCamera(suit, toon, battle, attackDuration, openShotDuration):
+ return randomAttackCam(suit, toon, battle, attackDuration, openShotDuration, 'suit')
+
+
+def randomAttackCam(suit, toon, battle, attackDuration, openShotDuration, attackerString = 'suit'):
+ if openShotDuration > attackDuration:
+ openShotDuration = attackDuration
+ closeShotDuration = attackDuration - openShotDuration
+ if attackerString == 'suit':
+ attacker = suit
+ defender = toon
+ defenderString = 'toon'
+ else:
+ attacker = toon
+ defender = suit
+ defenderString = 'suit'
+ randomDouble = random.random()
+ if randomDouble > 0.6:
+ openShot = randomActorShot(attacker, battle, openShotDuration, attackerString)
+ elif randomDouble > 0.2:
+ openShot = randomOverShoulderShot(suit, toon, battle, openShotDuration, focus=attackerString)
+ else:
+ openShot = randomSplitShot(attacker, defender, battle, openShotDuration)
+ randomDouble = random.random()
+ if randomDouble > 0.6:
+ closeShot = randomActorShot(defender, battle, closeShotDuration, defenderString)
+ elif randomDouble > 0.2:
+ closeShot = randomOverShoulderShot(suit, toon, battle, closeShotDuration, focus=defenderString)
+ else:
+ closeShot = randomSplitShot(attacker, defender, battle, closeShotDuration)
+ return Sequence(openShot, closeShot)
+
+
+def randomGroupAttackCam(suit, targets, battle, attackDuration, openShotDuration):
+ if openShotDuration > attackDuration:
+ openShotDuration = attackDuration
+ closeShotDuration = attackDuration - openShotDuration
+ openShot = randomActorShot(suit, battle, openShotDuration, 'suit', groupShot=0)
+ closeShot = randomToonGroupShot(targets, suit, closeShotDuration, battle)
+ return Sequence(openShot, closeShot)
+
+
+def randomActorShot(actor, battle, duration, actorType, groupShot = 0):
+ height = actor.getHeight()
+ centralPoint = actor.getPos(battle)
+ centralPoint.setZ(centralPoint.getZ() + height * 0.75)
+ if actorType == 'suit':
+ x = 4 + random.random() * 8
+ y = -2 - random.random() * 4
+ z = height * 0.5 + random.random() * height * 1.5
+ if groupShot == 1:
+ y = -4
+ z = height * 0.5
+ else:
+ x = 2 + random.random() * 8
+ y = -2 + random.random() * 3
+ z = height + random.random() * height * 1.5
+ if groupShot == 1:
+ y = y + 3
+ z = height * 0.5
+ if MovieUtil.shotDirection == 'left':
+ x = -x
+ return focusShot(x, y, z, duration, centralPoint)
+
+
+def randomSplitShot(suit, toon, battle, duration):
+ suitHeight = suit.getHeight()
+ toonHeight = toon.getHeight()
+ suitCentralPoint = suit.getPos(battle)
+ suitCentralPoint.setZ(suitCentralPoint.getZ() + suitHeight * 0.75)
+ toonCentralPoint = toon.getPos(battle)
+ toonCentralPoint.setZ(toonCentralPoint.getZ() + toonHeight * 0.75)
+ x = 9 + random.random() * 2
+ y = -2 - random.random() * 2
+ z = suitHeight * 0.5 + random.random() * suitHeight
+ if MovieUtil.shotDirection == 'left':
+ x = -x
+ return focusShot(x, y, z, duration, toonCentralPoint, splitFocusPoint=suitCentralPoint)
+
+
+def randomOverShoulderShot(suit, toon, battle, duration, focus):
+ suitHeight = suit.getHeight()
+ toonHeight = toon.getHeight()
+ suitCentralPoint = suit.getPos(battle)
+ suitCentralPoint.setZ(suitCentralPoint.getZ() + suitHeight * 0.75)
+ toonCentralPoint = toon.getPos(battle)
+ toonCentralPoint.setZ(toonCentralPoint.getZ() + toonHeight * 0.75)
+ x = 2 + random.random() * 10
+ if focus == 'toon':
+ y = 8 + random.random() * 6
+ z = suitHeight * 1.2 + random.random() * suitHeight
+ else:
+ y = -10 - random.random() * 6
+ z = toonHeight * 1.5
+ if MovieUtil.shotDirection == 'left':
+ x = -x
+ return focusShot(x, y, z, duration, toonCentralPoint, splitFocusPoint=suitCentralPoint)
+
+
+def randomCameraSelection(suit, attack, attackDuration, openShotDuration):
+ shotChoices = [avatarCloseUpThrowShot,
+ avatarCloseUpThreeQuarterLeftShot,
+ allGroupLowShot,
+ suitGroupLowLeftShot,
+ avatarBehindHighShot]
+ if openShotDuration > attackDuration:
+ openShotDuration = attackDuration
+ closeShotDuration = attackDuration - openShotDuration
+ openShot = apply(random.choice(shotChoices), [suit, openShotDuration])
+ closeShot = chooseSuitCloseShot(attack, closeShotDuration, openShot.getName(), attackDuration)
+ return Sequence(openShot, closeShot)
+
+
+def randomToonGroupShot(toons, suit, duration, battle):
+ sum = 0
+ for t in toons:
+ toon = t['toon']
+ height = toon.getHeight()
+ sum = sum + height
+
+ avgHeight = sum / len(toons) * 0.75
+ suitPos = suit.getPos(battle)
+ x = 1 + random.random() * 6
+ if suitPos.getX() > 0:
+ x = -x
+ if random.random() > 0.5:
+ y = 4 + random.random() * 1
+ z = avgHeight + random.random() * 6
+ else:
+ y = 11 + random.random() * 2
+ z = 13 + random.random() * 2
+ focalPoint = Point3(0, -4, avgHeight)
+ return focusShot(x, y, z, duration, focalPoint)
+
+
+def chooseFireShot(throws, suitThrowsDict, attackDuration):
+ openShot = chooseFireOpenShot(throws, suitThrowsDict, attackDuration)
+ openDuration = openShot.getDuration()
+ openName = openShot.getName()
+ closeShot = chooseFireCloseShot(throws, suitThrowsDict, openDuration, openName, attackDuration)
+ track = Sequence(openShot, closeShot)
+ return track
+
+
+def chooseFireOpenShot(throws, suitThrowsDict, attackDuration):
+ numThrows = len(throws)
+ av = None
+ duration = 3.0
+ if numThrows == 1:
+ av = throws[0]['toon']
+ shotChoices = [avatarCloseUpThrowShot,
+ avatarCloseUpThreeQuarterRightShot,
+ avatarBehindShot,
+ allGroupLowShot,
+ suitGroupThreeQuarterLeftBehindShot]
+ elif numThrows >= 2 and numThrows <= 4:
+ shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ else:
+ notify.error('Bad number of throws: %s' % numThrows)
+ shotChoice = random.choice(shotChoices)
+ track = apply(shotChoice, [av, duration])
+ print 'chooseFireOpenShot %s' % shotChoice
+ return track
+
+
+def chooseFireCloseShot(throws, suitThrowsDict, openDuration, openName, attackDuration):
+ numSuits = len(suitThrowsDict)
+ av = None
+ duration = attackDuration - openDuration
+ if numSuits == 1:
+ av = base.cr.doId2do[suitThrowsDict.keys()[0]]
+ shotChoices = [avatarCloseUpFireShot,
+ avatarCloseUpThreeQuarterLeftFireShot,
+ allGroupLowShot,
+ suitGroupThreeQuarterLeftBehindShot]
+ elif numSuits >= 2 and numSuits <= 4 or numSuits == 0:
+ shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot]
+ else:
+ notify.error('Bad number of suits: %s' % numSuits)
+ shotChoice = random.choice(shotChoices)
+ track = apply(shotChoice, [av, duration])
+ print 'chooseFireOpenShot %s' % shotChoice
+ return track
+
+
+def avatarCloseUpFireShot(avatar, duration):
+ return heldRelativeShot(avatar, 7, 17, avatar.getHeight() * 0.66, 159, 3.6, 0, duration, 'avatarCloseUpFireShot')
+
+
+def avatarCloseUpThreeQuarterLeftFireShot(avatar, duration):
+ return heldRelativeShot(avatar, -8.2, 8.45, avatar.getHeight() * 0.66, -131.5, 3.6, 0, duration, 'avatarCloseUpThreeQuarterLeftShot')
diff --git a/toontown/battle/MovieDrop.py b/toontown/battle/MovieDrop.py
new file mode 100644
index 0000000..05e9c0b
--- /dev/null
+++ b/toontown/battle/MovieDrop.py
@@ -0,0 +1,435 @@
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from BattleProps import *
+from BattleSounds import *
+import MovieCamera
+from direct.directnotify import DirectNotifyGlobal
+import MovieUtil
+import MovieNPCSOS
+from MovieUtil import calcAvgSuitPos
+from direct.showutil import Effects
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieDrop')
+hitSoundFiles = ('AA_drop_flowerpot.mp3', 'AA_drop_sandbag.mp3', 'AA_drop_anvil.mp3', 'AA_drop_bigweight.mp3', 'AA_drop_safe.mp3', 'AA_drop_piano.mp3', 'AA_drop_boat.mp3')
+missSoundFiles = ('AA_drop_flowerpot_miss.mp3', 'AA_drop_sandbag_miss.mp3', 'AA_drop_anvil_miss.mp3', 'AA_drop_bigweight_miss.mp3', 'AA_drop_safe_miss.mp3', 'AA_drop_piano_miss.mp3', 'AA_drop_boat_miss.mp3')
+tDropShadow = 1.3
+tSuitDodges = 2.45 + tDropShadow
+tObjectAppears = 3.0 + tDropShadow
+tButtonPressed = 2.44
+dShrink = 0.3
+dShrinkOnMiss = 0.1
+dPropFall = 0.6
+objects = ('flowerpot', 'sandbag', 'anvil', 'weight', 'safe', 'piano', 'ship')
+objZOffsets = (0.75, 0.75, 0.0, 0.0, 0.0, 0.0, 0.0)
+objStartingScales = (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
+landFrames = (12, 4, 1, 11, 11, 11, 2)
+shoulderHeights = {'a': 13.28 / 4.0,
+ 'b': 13.74 / 4.0,
+ 'c': 10.02 / 4.0}
+
+def doDrops(drops):
+ if len(drops) == 0:
+ return (None, None)
+ npcArrivals, npcDepartures, npcs = MovieNPCSOS.doNPCTeleports(drops)
+ suitDropsDict = {}
+ groupDrops = []
+ for drop in drops:
+ track = drop['track']
+ level = drop['level']
+ targets = drop['target']
+ if len(targets) == 1:
+ suitId = targets[0]['suit'].doId
+ if suitDropsDict.has_key(suitId):
+ suitDropsDict[suitId].append((drop, targets[0]))
+ else:
+ suitDropsDict[suitId] = [(drop, targets[0])]
+ elif level <= MAX_LEVEL_INDEX and attackAffectsGroup(track, level):
+ groupDrops.append(drop)
+ else:
+ for target in targets:
+ suitId = target['suit'].doId
+ if suitDropsDict.has_key(suitId):
+ otherDrops = suitDropsDict[suitId]
+ alreadyInList = 0
+ for oDrop in otherDrops:
+ if oDrop[0]['toon'] == drop['toon']:
+ alreadyInList = 1
+
+ if alreadyInList == 0:
+ suitDropsDict[suitId].append((drop, target))
+ else:
+ suitDropsDict[suitId] = [(drop, target)]
+
+ suitDrops = suitDropsDict.values()
+
+ def compFunc(a, b):
+ if len(a) > len(b):
+ return 1
+ elif len(a) < len(b):
+ return -1
+ return 0
+
+ suitDrops.sort(compFunc)
+ delay = 0.0
+ mtrack = Parallel(name='toplevel-drop')
+ npcDrops = {}
+ for st in suitDrops:
+ if len(st) > 0:
+ ival = __doSuitDrops(st, npcs, npcDrops)
+ if ival:
+ mtrack.append(Sequence(Wait(delay), ival))
+ delay = delay + TOON_DROP_SUIT_DELAY
+
+ dropTrack = Sequence(npcArrivals, mtrack, npcDepartures)
+ camDuration = mtrack.getDuration()
+ if groupDrops:
+ ival = __doGroupDrops(groupDrops)
+ dropTrack.append(ival)
+ camDuration += ival.getDuration()
+ enterDuration = npcArrivals.getDuration()
+ exitDuration = npcDepartures.getDuration()
+ camTrack = MovieCamera.chooseDropShot(drops, suitDropsDict, camDuration, enterDuration, exitDuration)
+ return (dropTrack, camTrack)
+
+
+def __getSoundTrack(level, hitSuit, node = None):
+ if hitSuit:
+ soundEffect = globalBattleSoundCache.getSound(hitSoundFiles[level])
+ else:
+ soundEffect = globalBattleSoundCache.getSound(missSoundFiles[level])
+ soundTrack = Sequence()
+ if soundEffect:
+ buttonSound = globalBattleSoundCache.getSound('AA_drop_trigger_box.mp3')
+ fallingSound = None
+ buttonDelay = tButtonPressed - 0.3
+ fallingDuration = 1.5
+ if not level == UBER_GAG_LEVEL_INDEX:
+ fallingSound = globalBattleSoundCache.getSound('incoming_whistleALT.mp3')
+ soundTrack.append(Wait(buttonDelay))
+ soundTrack.append(SoundInterval(buttonSound, duration=0.67, node=node))
+ if fallingSound:
+ soundTrack.append(SoundInterval(fallingSound, duration=fallingDuration, node=node))
+ if not level == UBER_GAG_LEVEL_INDEX:
+ soundTrack.append(SoundInterval(soundEffect, node=node))
+ if level == UBER_GAG_LEVEL_INDEX:
+ if hitSuit:
+ uberDelay = tButtonPressed
+ else:
+ uberDelay = tButtonPressed - 0.1
+ oldSoundTrack = soundTrack
+ soundTrack = Parallel()
+ soundTrack.append(oldSoundTrack)
+ uberTrack = Sequence()
+ uberTrack.append(Wait(uberDelay))
+ uberTrack.append(SoundInterval(soundEffect, node=node))
+ soundTrack.append(uberTrack)
+ else:
+ soundTrack.append(Wait(0.1))
+ return soundTrack
+
+
+def __doSuitDrops(dropTargetPairs, npcs, npcDrops):
+ toonTracks = Parallel()
+ delay = 0.0
+ alreadyDodged = 0
+ alreadyTeased = 0
+ for dropTargetPair in dropTargetPairs:
+ drop = dropTargetPair[0]
+ level = drop['level']
+ objName = objects[level]
+ target = dropTargetPair[1]
+ track = __dropObjectForSingle(drop, delay, objName, level, alreadyDodged, alreadyTeased, npcs, target, npcDrops)
+ if track:
+ toonTracks.append(track)
+ delay += TOON_DROP_DELAY
+ hp = target['hp']
+ if hp <= 0:
+ if level >= 3:
+ alreadyTeased = 1
+ else:
+ alreadyDodged = 1
+
+ return toonTracks
+
+
+def __doGroupDrops(groupDrops):
+ toonTracks = Parallel()
+ delay = 0.0
+ alreadyDodged = 0
+ alreadyTeased = 0
+ for drop in groupDrops:
+ battle = drop['battle']
+ level = drop['level']
+ centerPos = calcAvgSuitPos(drop)
+ targets = drop['target']
+ numTargets = len(targets)
+ closestTarget = -1
+ nearestDistance = 100000.0
+ for i in range(numTargets):
+ suit = drop['target'][i]['suit']
+ suitPos = suit.getPos(battle)
+ displacement = Vec3(centerPos)
+ displacement -= suitPos
+ distance = displacement.lengthSquared()
+ if distance < nearestDistance:
+ closestTarget = i
+ nearestDistance = distance
+
+ track = __dropGroupObject(drop, delay, closestTarget, alreadyDodged, alreadyTeased)
+ if track:
+ toonTracks.append(track)
+ delay = delay + TOON_DROP_SUIT_DELAY
+ hp = drop['target'][closestTarget]['hp']
+ if hp <= 0:
+ if level >= 3:
+ alreadyTeased = 1
+ else:
+ alreadyDodged = 1
+
+ return toonTracks
+
+
+def __dropGroupObject(drop, delay, closestTarget, alreadyDodged, alreadyTeased):
+ level = drop['level']
+ objName = objects[level]
+ target = drop['target'][closestTarget]
+ suit = drop['target'][closestTarget]['suit']
+ npcDrops = {}
+ npcs = []
+ returnedParallel = __dropObject(drop, delay, objName, level, alreadyDodged, alreadyTeased, npcs, target, npcDrops)
+ for i in range(len(drop['target'])):
+ target = drop['target'][i]
+ suitTrack = __createSuitTrack(drop, delay, level, alreadyDodged, alreadyTeased, target, npcs)
+ if suitTrack:
+ returnedParallel.append(suitTrack)
+
+ return returnedParallel
+
+
+def __dropObjectForSingle(drop, delay, objName, level, alreadyDodged, alreadyTeased, npcs, target, npcDrops):
+ singleDropParallel = __dropObject(drop, delay, objName, level, alreadyDodged, alreadyTeased, npcs, target, npcDrops)
+ suitTrack = __createSuitTrack(drop, delay, level, alreadyDodged, alreadyTeased, target, npcs)
+ if suitTrack:
+ singleDropParallel.append(suitTrack)
+ return singleDropParallel
+
+
+def __dropObject(drop, delay, objName, level, alreadyDodged, alreadyTeased, npcs, target, npcDrops):
+ toon = drop['toon']
+ repeatNPC = 0
+ battle = drop['battle']
+ if drop.has_key('npc'):
+ toon = drop['npc']
+ if npcDrops.has_key(toon):
+ repeatNPC = 1
+ else:
+ npcDrops[toon] = 1
+ origHpr = Vec3(0, 0, 0)
+ else:
+ origHpr = toon.getHpr(battle)
+ hpbonus = drop['hpbonus']
+ suit = target['suit']
+ hp = target['hp']
+ hitSuit = hp > 0
+ died = target['died']
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ kbbonus = target['kbbonus']
+ suitPos = suit.getPos(battle)
+ majorObject = level >= 3
+ if repeatNPC == 0:
+ button = globalPropPool.getProp('button')
+ buttonType = globalPropPool.getPropType('button')
+ button2 = MovieUtil.copyProp(button)
+ buttons = [button, button2]
+ hands = toon.getLeftHands()
+ object = globalPropPool.getProp(objName)
+ objectType = globalPropPool.getPropType(objName)
+ if objName == 'weight':
+ object.setScale(object.getScale() * 0.75)
+ elif objName == 'safe':
+ object.setScale(object.getScale() * 0.85)
+ node = object.node()
+ node.setBounds(OmniBoundingVolume())
+ node.setFinal(1)
+ soundTrack = __getSoundTrack(level, hitSuit, toon)
+ toonTrack = Sequence()
+ if repeatNPC == 0:
+ toonFace = Func(toon.headsUp, battle, suitPos)
+ toonTrack.append(Wait(delay))
+ toonTrack.append(toonFace)
+ toonTrack.append(ActorInterval(toon, 'pushbutton'))
+ toonTrack.append(Func(toon.loop, 'neutral'))
+ toonTrack.append(Func(toon.setHpr, battle, origHpr))
+ buttonTrack = Sequence()
+ if repeatNPC == 0:
+ buttonShow = Func(MovieUtil.showProps, buttons, hands)
+ buttonScaleUp = LerpScaleInterval(button, 1.0, button.getScale(), startScale=Point3(0.01, 0.01, 0.01))
+ buttonScaleDown = LerpScaleInterval(button, 1.0, Point3(0.01, 0.01, 0.01), startScale=button.getScale())
+ buttonHide = Func(MovieUtil.removeProps, buttons)
+ buttonTrack.append(Wait(delay))
+ buttonTrack.append(buttonShow)
+ buttonTrack.append(buttonScaleUp)
+ buttonTrack.append(Wait(2.5))
+ buttonTrack.append(buttonScaleDown)
+ buttonTrack.append(buttonHide)
+ objectTrack = Sequence()
+
+ def posObject(object, suit, level, majorObject, miss, battle = battle):
+ object.reparentTo(battle)
+ if battle.isSuitLured(suit):
+ suitPos, suitHpr = battle.getActorPosHpr(suit)
+ object.setPos(suitPos)
+ object.setHpr(suitHpr)
+ if level >= 3:
+ object.setY(object.getY() + 2)
+ else:
+ object.setPos(suit.getPos(battle))
+ object.setHpr(suit.getHpr(battle))
+ if miss and level >= 3:
+ object.setY(object.getY(battle) + 5)
+ if not majorObject:
+ if not miss:
+ shoulderHeight = shoulderHeights[suit.style.body] * suit.scale
+ object.setZ(object.getPos(battle)[2] + shoulderHeight)
+ object.setZ(object.getPos(battle)[2] + objZOffsets[level])
+
+ objectTrack.append(Func(battle.movie.needRestoreRenderProp, object))
+ objInit = Func(posObject, object, suit, level, majorObject, hp <= 0)
+ objectTrack.append(Wait(delay + tObjectAppears))
+ objectTrack.append(objInit)
+ if hp > 0 or level == 1 or level == 2:
+ if hasattr(object, 'getAnimControls'):
+ animProp = ActorInterval(object, objName)
+ shrinkProp = LerpScaleInterval(object, dShrink, Point3(0.01, 0.01, 0.01), startScale=object.getScale())
+ objAnimShrink = ParallelEndTogether(animProp, shrinkProp)
+ objectTrack.append(objAnimShrink)
+ else:
+ startingScale = objStartingScales[level]
+ object2 = MovieUtil.copyProp(object)
+ posObject(object2, suit, level, majorObject, hp <= 0)
+ endingPos = object2.getPos()
+ startPos = Point3(endingPos[0], endingPos[1], endingPos[2] + 5)
+ startHpr = object2.getHpr()
+ endHpr = Point3(startHpr[0] + 90, startHpr[1], startHpr[2])
+ animProp = LerpPosInterval(object, landFrames[level] / 24.0, endingPos, startPos=startPos)
+ shrinkProp = LerpScaleInterval(object, dShrink, Point3(0.01, 0.01, 0.01), startScale=startingScale)
+ bounceProp = Effects.createZBounce(object, 2, endingPos, 0.5, 1.5)
+ objAnimShrink = Sequence(Func(object.setScale, startingScale), Func(object.setH, endHpr[0]), animProp, bounceProp, Wait(1.5), shrinkProp)
+ objectTrack.append(objAnimShrink)
+ MovieUtil.removeProp(object2)
+ elif hasattr(object, 'getAnimControls'):
+ animProp = ActorInterval(object, objName, duration=landFrames[level] / 24.0)
+
+ def poseProp(prop, animName, level):
+ prop.pose(animName, landFrames[level])
+
+ poseProp = Func(poseProp, object, objName, level)
+ wait = Wait(1.0)
+ shrinkProp = LerpScaleInterval(object, dShrinkOnMiss, Point3(0.01, 0.01, 0.01), startScale=object.getScale())
+ objectTrack.append(animProp)
+ objectTrack.append(poseProp)
+ objectTrack.append(wait)
+ objectTrack.append(shrinkProp)
+ else:
+ startingScale = objStartingScales[level]
+ object2 = MovieUtil.copyProp(object)
+ posObject(object2, suit, level, majorObject, hp <= 0)
+ endingPos = object2.getPos()
+ startPos = Point3(endingPos[0], endingPos[1], endingPos[2] + 5)
+ startHpr = object2.getHpr()
+ endHpr = Point3(startHpr[0] + 90, startHpr[1], startHpr[2])
+ animProp = LerpPosInterval(object, landFrames[level] / 24.0, endingPos, startPos=startPos)
+ shrinkProp = LerpScaleInterval(object, dShrinkOnMiss, Point3(0.01, 0.01, 0.01), startScale=startingScale)
+ bounceProp = Effects.createZBounce(object, 2, endingPos, 0.5, 1.5)
+ objAnimShrink = Sequence(Func(object.setScale, startingScale), Func(object.setH, endHpr[0]), animProp, bounceProp, Wait(1.5), shrinkProp)
+ objectTrack.append(objAnimShrink)
+ MovieUtil.removeProp(object2)
+ objectTrack.append(Func(MovieUtil.removeProp, object))
+ objectTrack.append(Func(battle.movie.clearRenderProp, object))
+ dropShadow = MovieUtil.copyProp(suit.getShadowJoint())
+ if level == 0:
+ dropShadow.setScale(0.5)
+ elif level <= 2:
+ dropShadow.setScale(0.8)
+ elif level == 3:
+ dropShadow.setScale(2.0)
+ elif level == 4:
+ dropShadow.setScale(2.3)
+ else:
+ dropShadow.setScale(3.6)
+
+ def posShadow(dropShadow = dropShadow, suit = suit, battle = battle, hp = hp, level = level):
+ dropShadow.reparentTo(battle)
+ if battle.isSuitLured(suit):
+ suitPos, suitHpr = battle.getActorPosHpr(suit)
+ dropShadow.setPos(suitPos)
+ dropShadow.setHpr(suitHpr)
+ if level >= 3:
+ dropShadow.setY(dropShadow.getY() + 2)
+ else:
+ dropShadow.setPos(suit.getPos(battle))
+ dropShadow.setHpr(suit.getHpr(battle))
+ if hp <= 0 and level >= 3:
+ dropShadow.setY(dropShadow.getY(battle) + 5)
+ dropShadow.setZ(dropShadow.getZ() + 0.5)
+
+ shadowTrack = Sequence(Wait(delay + tButtonPressed), Func(battle.movie.needRestoreRenderProp, dropShadow), Func(posShadow), LerpScaleInterval(dropShadow, tObjectAppears - tButtonPressed, dropShadow.getScale(), startScale=Point3(0.01, 0.01, 0.01)), Wait(0.3), Func(MovieUtil.removeProp, dropShadow), Func(battle.movie.clearRenderProp, dropShadow))
+ return Parallel(toonTrack, soundTrack, buttonTrack, objectTrack, shadowTrack)
+
+
+def __createSuitTrack(drop, delay, level, alreadyDodged, alreadyTeased, target, npcs):
+ toon = drop['toon']
+ if drop.has_key('npc'):
+ toon = drop['npc']
+ battle = drop['battle']
+ majorObject = level >= 3
+ suit = target['suit']
+ hp = target['hp']
+ hitSuit = hp > 0
+ died = target['died']
+ revived = target['revived']
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ kbbonus = target['kbbonus']
+ hpbonus = drop['hpbonus']
+ if hp > 0:
+ suitTrack = Sequence()
+ showDamage = Func(suit.showHpText, -hp, openEnded=0)
+ updateHealthBar = Func(suit.updateHealthBar, hp)
+ if majorObject:
+ anim = 'flatten'
+ else:
+ anim = 'drop-react'
+ suitReact = ActorInterval(suit, anim)
+ suitTrack.append(Wait(delay + tObjectAppears))
+ suitTrack.append(showDamage)
+ suitTrack.append(updateHealthBar)
+ suitGettingHit = Parallel(suitReact)
+ if level == UBER_GAG_LEVEL_INDEX:
+ gotHitSound = globalBattleSoundCache.getSound('AA_drop_boat_cog.mp3')
+ suitGettingHit.append(SoundInterval(gotHitSound, node=toon))
+ suitTrack.append(suitGettingHit)
+ bonusTrack = None
+ if hpbonus > 0:
+ bonusTrack = Sequence(Wait(delay + tObjectAppears + 0.75), Func(suit.showHpText, -hpbonus, 1, openEnded=0))
+ if revived != 0:
+ suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle, npcs))
+ elif died != 0:
+ suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle, npcs))
+ else:
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ if bonusTrack != None:
+ suitTrack = Parallel(suitTrack, bonusTrack)
+ elif kbbonus == 0:
+ suitTrack = Sequence(Wait(delay + tObjectAppears), Func(MovieUtil.indicateMissed, suit, 0.6), Func(suit.loop, 'neutral'))
+ else:
+ if alreadyDodged > 0:
+ return
+ if level >= 3:
+ if alreadyTeased > 0:
+ return
+ else:
+ suitTrack = MovieUtil.createSuitTeaseMultiTrack(suit, delay=delay + tObjectAppears)
+ else:
+ suitTrack = MovieUtil.createSuitDodgeMultitrack(delay + tSuitDodges, suit, leftSuits, rightSuits)
+ return suitTrack
diff --git a/toontown/battle/MovieFire.py b/toontown/battle/MovieFire.py
new file mode 100644
index 0000000..45ed781
--- /dev/null
+++ b/toontown/battle/MovieFire.py
@@ -0,0 +1,342 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from BattleProps import *
+from BattleSounds import *
+from toontown.toon.ToonDNA import *
+from toontown.suit.SuitDNA import *
+from direct.directnotify import DirectNotifyGlobal
+import random
+import MovieCamera
+import MovieUtil
+from MovieUtil import calcAvgSuitPos
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieThrow')
+hitSoundFiles = ('AA_tart_only.mp3', 'AA_slice_only.mp3', 'AA_slice_only.mp3', 'AA_slice_only.mp3', 'AA_slice_only.mp3', 'AA_wholepie_only.mp3', 'AA_wholepie_only.mp3')
+tPieLeavesHand = 2.7
+tPieHitsSuit = 3.0
+tSuitDodges = 2.45
+ratioMissToHit = 1.5
+tPieShrink = 0.7
+pieFlyTaskName = 'MovieThrow-pieFly'
+
+def addHit(dict, suitId, hitCount):
+ if dict.has_key(suitId):
+ dict[suitId] += hitCount
+ else:
+ dict[suitId] = hitCount
+
+
+def doFires(fires):
+ if len(fires) == 0:
+ return (None, None)
+
+ suitFiresDict = {}
+ for fire in fires:
+ suitId = fire['target']['suit'].doId
+ if suitFiresDict.has_key(suitId):
+ suitFiresDict[suitId].append(fire)
+ else:
+ suitFiresDict[suitId] = [fire]
+
+ suitFires = suitFiresDict.values()
+ def compFunc(a, b):
+ if len(a) > len(b):
+ return 1
+ elif len(a) < len(b):
+ return -1
+ return 0
+ suitFires.sort(compFunc)
+
+ totalHitDict = {}
+ singleHitDict = {}
+ groupHitDict = {}
+
+ for fire in fires:
+ suitId = fire['target']['suit'].doId
+ if 1:
+ if fire['target']['hp'] > 0:
+ addHit(singleHitDict, suitId, 1)
+ addHit(totalHitDict, suitId, 1)
+ else:
+ addHit(singleHitDict, suitId, 0)
+ addHit(totalHitDict, suitId, 0)
+
+ notify.debug('singleHitDict = %s' % singleHitDict)
+ notify.debug('groupHitDict = %s' % groupHitDict)
+ notify.debug('totalHitDict = %s' % totalHitDict)
+
+ delay = 0.0
+ mtrack = Parallel()
+ firedTargets = []
+ for sf in suitFires:
+ if len(sf) > 0:
+ ival = __doSuitFires(sf)
+ if ival:
+ mtrack.append(Sequence(Wait(delay), ival))
+ delay = delay + TOON_FIRE_SUIT_DELAY
+
+ retTrack = Sequence()
+ retTrack.append(mtrack)
+ camDuration = retTrack.getDuration()
+ camTrack = MovieCamera.chooseFireShot(fires, suitFiresDict, camDuration)
+ return (retTrack, camTrack)
+
+def __doSuitFires(fires):
+ toonTracks = Parallel()
+ delay = 0.0
+ hitCount = 0
+ for fire in fires:
+ if fire['target']['hp'] > 0:
+ hitCount += 1
+ else:
+ break
+
+ suitList = []
+ for fire in fires:
+ if fire['target']['suit'] not in suitList:
+ suitList.append(fire['target']['suit'])
+
+ for fire in fires:
+ showSuitCannon = 1
+ if fire['target']['suit'] not in suitList:
+ showSuitCannon = 0
+ else:
+ suitList.remove(fire['target']['suit'])
+ tracks = __throwPie(fire, delay, hitCount, showSuitCannon)
+ if tracks:
+ for track in tracks:
+ toonTracks.append(track)
+
+ delay = delay + TOON_THROW_DELAY
+
+ return toonTracks
+
+
+def __showProp(prop, parent, pos):
+ prop.reparentTo(parent)
+ prop.setPos(pos)
+
+
+def __animProp(props, propName, propType):
+ if 'actor' == propType:
+ for prop in props:
+ prop.play(propName)
+
+ elif 'model' == propType:
+ pass
+ else:
+ notify.error('No such propType as: %s' % propType)
+
+
+def __billboardProp(prop):
+ scale = prop.getScale()
+ prop.setBillboardPointWorld()
+ prop.setScale(scale)
+
+
+def __suitMissPoint(suit, other = render):
+ pnt = suit.getPos(other)
+ pnt.setZ(pnt[2] + suit.getHeight() * 1.3)
+ return pnt
+
+
+def __propPreflight(props, suit, toon, battle):
+ prop = props[0]
+ toon.update(0)
+ prop.wrtReparentTo(battle)
+ props[1].reparentTo(hidden)
+ for ci in range(prop.getNumChildren()):
+ prop.getChild(ci).setHpr(0, -90, 0)
+
+ targetPnt = MovieUtil.avatarFacePoint(suit, other=battle)
+ prop.lookAt(targetPnt)
+
+
+def __propPreflightGroup(props, suits, toon, battle):
+ prop = props[0]
+ toon.update(0)
+ prop.wrtReparentTo(battle)
+ props[1].reparentTo(hidden)
+ for ci in range(prop.getNumChildren()):
+ prop.getChild(ci).setHpr(0, -90, 0)
+
+ avgTargetPt = Point3(0, 0, 0)
+ for suit in suits:
+ avgTargetPt += MovieUtil.avatarFacePoint(suit, other=battle)
+
+ avgTargetPt /= len(suits)
+ prop.lookAt(avgTargetPt)
+
+
+def __piePreMiss(missDict, pie, suitPoint, other = render):
+ missDict['pie'] = pie
+ missDict['startScale'] = pie.getScale()
+ missDict['startPos'] = pie.getPos(other)
+ v = Vec3(suitPoint - missDict['startPos'])
+ endPos = missDict['startPos'] + v * ratioMissToHit
+ missDict['endPos'] = endPos
+
+
+def __pieMissLerpCallback(t, missDict):
+ pie = missDict['pie']
+ newPos = missDict['startPos'] * (1.0 - t) + missDict['endPos'] * t
+ if t < tPieShrink:
+ tScale = 0.0001
+ else:
+ tScale = (t - tPieShrink) / (1.0 - tPieShrink)
+ newScale = missDict['startScale'] * max(1.0 - tScale, 0.01)
+ pie.setPos(newPos)
+ pie.setScale(newScale)
+
+
+def __piePreMissGroup(missDict, pies, suitPoint, other = render):
+ missDict['pies'] = pies
+ missDict['startScale'] = pies[0].getScale()
+ missDict['startPos'] = pies[0].getPos(other)
+ v = Vec3(suitPoint - missDict['startPos'])
+ endPos = missDict['startPos'] + v * ratioMissToHit
+ missDict['endPos'] = endPos
+ notify.debug('startPos=%s' % missDict['startPos'])
+ notify.debug('v=%s' % v)
+ notify.debug('endPos=%s' % missDict['endPos'])
+
+
+def __pieMissGroupLerpCallback(t, missDict):
+ pies = missDict['pies']
+ newPos = missDict['startPos'] * (1.0 - t) + missDict['endPos'] * t
+ if t < tPieShrink:
+ tScale = 0.0001
+ else:
+ tScale = (t - tPieShrink) / (1.0 - tPieShrink)
+ newScale = missDict['startScale'] * max(1.0 - tScale, 0.01)
+ for pie in pies:
+ pie.setPos(newPos)
+ pie.setScale(newScale)
+
+
+def __getSoundTrack(level, hitSuit, node = None):
+ throwSound = globalBattleSoundCache.getSound('AA_drop_trigger_box.mp3')
+ throwTrack = Sequence(Wait(2.15), SoundInterval(throwSound, node=node))
+ return throwTrack
+
+
+def __throwPie(throw, delay, hitCount, showCannon = 1):
+ toon = throw['toon']
+ hpbonus = throw['hpbonus']
+ target = throw['target']
+ suit = target['suit']
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ sidestep = throw['sidestep']
+ died = target['died']
+ revived = target['revived']
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ level = throw['level']
+ battle = throw['battle']
+ suitPos = suit.getPos(battle)
+ origHpr = toon.getHpr(battle)
+ notify.debug('toon: %s throws tart at suit: %d for hp: %d died: %d' % (toon.getName(),
+ suit.doId,
+ hp,
+ died))
+ pieName = pieNames[0]
+ hitSuit = hp > 0
+ button = globalPropPool.getProp('button')
+ buttonType = globalPropPool.getPropType('button')
+ button2 = MovieUtil.copyProp(button)
+ buttons = [button, button2]
+ hands = toon.getLeftHands()
+ toonTrack = Sequence()
+ toonFace = Func(toon.headsUp, battle, suitPos)
+ toonTrack.append(Wait(delay))
+ toonTrack.append(toonFace)
+ toonTrack.append(ActorInterval(toon, 'pushbutton'))
+ toonTrack.append(ActorInterval(toon, 'wave', duration=2.0))
+ toonTrack.append(ActorInterval(toon, 'duck'))
+ toonTrack.append(Func(toon.loop, 'neutral'))
+ toonTrack.append(Func(toon.setHpr, battle, origHpr))
+ buttonTrack = Sequence()
+ buttonShow = Func(MovieUtil.showProps, buttons, hands)
+ buttonScaleUp = LerpScaleInterval(button, 1.0, button.getScale(), startScale=Point3(0.01, 0.01, 0.01))
+ buttonScaleDown = LerpScaleInterval(button, 1.0, Point3(0.01, 0.01, 0.01), startScale=button.getScale())
+ buttonHide = Func(MovieUtil.removeProps, buttons)
+ buttonTrack.append(Wait(delay))
+ buttonTrack.append(buttonShow)
+ buttonTrack.append(buttonScaleUp)
+ buttonTrack.append(Wait(2.5))
+ buttonTrack.append(buttonScaleDown)
+ buttonTrack.append(buttonHide)
+ soundTrack = __getSoundTrack(level, hitSuit, toon)
+ suitResponseTrack = Sequence()
+ reactIval = Sequence()
+ if showCannon:
+ showDamage = Func(suit.showHpText, -hp, openEnded=0)
+ updateHealthBar = Func(suit.updateHealthBar, hp)
+ cannon = loader.loadModel('phase_4/models/minigames/toon_cannon')
+ barrel = cannon.find('**/cannon')
+ barrel.setHpr(0, 90, 0)
+ cannonHolder = render.attachNewNode('CannonHolder')
+ cannon.reparentTo(cannonHolder)
+ cannon.setPos(0, 0, -8.6)
+ cannonHolder.setPos(suit.getPos(render))
+ cannonHolder.setHpr(suit.getHpr(render))
+ cannonAttachPoint = barrel.attachNewNode('CannonAttach')
+ kapowAttachPoint = barrel.attachNewNode('kapowAttach')
+ scaleFactor = 1.6
+ iScale = 1 / scaleFactor
+ barrel.setScale(scaleFactor, 1, scaleFactor)
+ cannonAttachPoint.setScale(iScale, 1, iScale)
+ cannonAttachPoint.setPos(0, 6.7, 0)
+ kapowAttachPoint.setPos(0, -0.5, 1.9)
+ suit.reparentTo(cannonAttachPoint)
+ suit.setPos(0, 0, 0)
+ suit.setHpr(0, -90, 0)
+ suitLevel = suit.getActualLevel()
+ deep = 2.5 + suitLevel * 0.2
+ suitScale = 0.9
+ import math
+ suitScale = 0.9 - math.sqrt(suitLevel) * 0.1
+ sival = []
+ posInit = cannonHolder.getPos()
+ posFinal = Point3(posInit[0] + 0.0, posInit[1] + 0.0, posInit[2] + 7.0)
+ kapow = globalPropPool.getProp('kapow')
+ kapow.reparentTo(kapowAttachPoint)
+ kapow.hide()
+ kapow.setScale(0.25)
+ kapow.setBillboardPointEye()
+ smoke = loader.loadModel('phase_4/models/props/test_clouds')
+ smoke.reparentTo(cannonAttachPoint)
+ smoke.setScale(0.5)
+ smoke.hide()
+ smoke.setBillboardPointEye()
+ soundBomb = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.mp3')
+ playSoundBomb = SoundInterval(soundBomb, node=cannonHolder)
+ soundFly = base.loadSfx('phase_4/audio/sfx/firework_whistle_01.mp3')
+ playSoundFly = SoundInterval(soundFly, node=cannonHolder)
+ soundCannonAdjust = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.mp3')
+ playSoundCannonAdjust = SoundInterval(soundCannonAdjust, duration=0.6, node=cannonHolder)
+ soundCogPanic = base.loadSfx('phase_5/audio/sfx/ENC_cogafssm.mp3')
+ playSoundCogPanic = SoundInterval(soundCogPanic, node=cannonHolder)
+ reactIval = Parallel(ActorInterval(suit, 'pie-small-react'), Sequence(Wait(0.0), LerpPosInterval(cannonHolder, 2.0, posFinal, startPos=posInit, blendType='easeInOut'), Parallel(LerpHprInterval(barrel, 0.6, Point3(0, 45, 0), startHpr=Point3(0, 90, 0), blendType='easeIn'), playSoundCannonAdjust), Wait(2.0), Parallel(LerpHprInterval(barrel, 0.6, Point3(0, 90, 0), startHpr=Point3(0, 45, 0), blendType='easeIn'), playSoundCannonAdjust), LerpPosInterval(cannonHolder, 1.0, posInit, startPos=posFinal, blendType='easeInOut')), Sequence(Wait(0.0), Parallel(ActorInterval(suit, 'flail'), suit.scaleInterval(1.0, suitScale), LerpPosInterval(suit, 0.25, Point3(0, -1.0, 0.0)), Sequence(Wait(0.25), Parallel(playSoundCogPanic, LerpPosInterval(suit, 1.5, Point3(0, -deep, 0.0), blendType='easeIn')))), Wait(2.5), Parallel(playSoundBomb, playSoundFly, Sequence(Func(smoke.show), Parallel(LerpScaleInterval(smoke, 0.5, 3), LerpColorScaleInterval(smoke, 0.5, Vec4(2, 2, 2, 0))), Func(smoke.hide)), Sequence(Func(kapow.show),
+ActorInterval(kapow, 'kapow'), Func(kapow.hide)), LerpPosInterval(suit, 3.0, Point3(0, 150.0, 0.0)), suit.scaleInterval(3.0, 0.01)), Func(suit.hide)))
+ if hitCount == 1:
+ sival = Sequence(Parallel(reactIval, MovieUtil.createSuitStunInterval(suit, 0.3, 1.3)), Wait(0.0), Func(cannonHolder.remove))
+ else:
+ sival = reactIval
+ suitResponseTrack.append(Wait(delay + tPieHitsSuit))
+ suitResponseTrack.append(showDamage)
+ suitResponseTrack.append(updateHealthBar)
+ suitResponseTrack.append(sival)
+ bonusTrack = Sequence(Wait(delay + tPieHitsSuit))
+ if kbbonus > 0:
+ bonusTrack.append(Wait(0.75))
+ bonusTrack.append(Func(suit.showHpText, -kbbonus, 2, openEnded=0))
+ if hpbonus > 0:
+ bonusTrack.append(Wait(0.75))
+ bonusTrack.append(Func(suit.showHpText, -hpbonus, 1, openEnded=0))
+ suitResponseTrack = Parallel(suitResponseTrack, bonusTrack)
+ return [toonTrack,
+ soundTrack,
+ buttonTrack,
+ suitResponseTrack]
\ No newline at end of file
diff --git a/toontown/battle/MovieHeal.py b/toontown/battle/MovieHeal.py
new file mode 100644
index 0000000..f02d7a6
--- /dev/null
+++ b/toontown/battle/MovieHeal.py
@@ -0,0 +1,484 @@
+from direct.interval.IntervalGlobal import *
+from BattleProps import *
+from BattleSounds import *
+from BattleBase import *
+from direct.directnotify import DirectNotifyGlobal
+import MovieCamera
+import random
+import MovieUtil
+import BattleParticles
+import HealJokes
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase.ToontownBattleGlobals import AvPropDamage
+from toontown.toon import NPCToons
+import MovieNPCSOS
+from toontown.effects import Splash
+from direct.task import Task
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieHeal')
+soundFiles = ('AA_heal_tickle.mp3', 'AA_heal_telljoke.mp3', 'AA_heal_smooch.mp3', 'AA_heal_happydance.mp3', 'AA_heal_pixiedust.mp3', 'AA_heal_juggle.mp3', 'AA_heal_High_Dive.mp3')
+healPos = Point3(0, 0, 0)
+healHpr = Vec3(180.0, 0, 0)
+runHealTime = 1.0
+
+def doHeals(heals, hasInteractivePropHealBonus):
+ if len(heals) == 0:
+ return (None, None)
+ track = Sequence()
+ for h in heals:
+ ival = __doHealLevel(h, hasInteractivePropHealBonus)
+ if ival:
+ track.append(ival)
+
+ camDuration = track.getDuration()
+ camTrack = MovieCamera.chooseHealShot(heals, camDuration)
+ return (track, camTrack)
+
+
+def __doHealLevel(heal, hasInteractivePropHealBonus):
+ level = heal['level']
+ if level == 0:
+ return __healTickle(heal, hasInteractivePropHealBonus)
+ elif level == 1:
+ return __healJoke(heal, hasInteractivePropHealBonus)
+ elif level == 2:
+ return __healSmooch(heal, hasInteractivePropHealBonus)
+ elif level == 3:
+ return __healDance(heal, hasInteractivePropHealBonus)
+ elif level == 4:
+ return __healSprinkle(heal, hasInteractivePropHealBonus)
+ elif level == 5:
+ return __healJuggle(heal, hasInteractivePropHealBonus)
+ elif level == 6:
+ return __healDive(heal, hasInteractivePropHealBonus)
+ return None
+
+
+def __runToHealSpot(heal):
+ toon = heal['toon']
+ battle = heal['battle']
+ level = heal['level']
+ origPos, origHpr = battle.getActorPosHpr(toon)
+ runAnimI = ActorInterval(toon, 'run', duration=runHealTime)
+ a = Func(toon.headsUp, battle, healPos)
+ b = Parallel(runAnimI, LerpPosInterval(toon, runHealTime, healPos, other=battle))
+ if levelAffectsGroup(HEAL, level):
+ c = Func(toon.setHpr, battle, healHpr)
+ else:
+ target = heal['target']['toon']
+ targetPos = target.getPos(battle)
+ c = Func(toon.headsUp, battle, targetPos)
+ return Sequence(a, b, c)
+
+
+def __returnToBase(heal):
+ toon = heal['toon']
+ battle = heal['battle']
+ origPos, origHpr = battle.getActorPosHpr(toon)
+ runAnimI = ActorInterval(toon, 'run', duration=runHealTime)
+ a = Func(toon.headsUp, battle, origPos)
+ b = Parallel(runAnimI, LerpPosInterval(toon, runHealTime, origPos, other=battle))
+ c = Func(toon.setHpr, battle, origHpr)
+ d = Func(toon.loop, 'neutral')
+ return Sequence(a, b, c, d)
+
+
+def __healToon(toon, hp, ineffective, hasInteractivePropHealBonus):
+ notify.debug('healToon() - toon: %d hp: %d ineffective: %d' % (toon.doId, hp, ineffective))
+ if ineffective == 1:
+ laughter = random.choice(TTLocalizer.MovieHealLaughterMisses)
+ else:
+ maxDam = AvPropDamage[0][1][0][1]
+ if hp >= maxDam - 1:
+ laughter = random.choice(TTLocalizer.MovieHealLaughterHits2)
+ else:
+ laughter = random.choice(TTLocalizer.MovieHealLaughterHits1)
+ toon.setChatAbsolute(laughter, CFSpeech | CFTimeout)
+ if hp > 0 and toon.hp != None:
+ toon.toonUp(hp, hasInteractivePropHealBonus)
+ else:
+ notify.debug('__healToon() - toon: %d hp: %d' % (toon.doId, hp))
+ return
+
+
+def __getPartTrack(particleEffect, startDelay, durationDelay, partExtraArgs):
+ pEffect = partExtraArgs[0]
+ parent = partExtraArgs[1]
+ if len(partExtraArgs) == 3:
+ worldRelative = partExtraArgs[2]
+ else:
+ worldRelative = 1
+ return Sequence(Wait(startDelay), ParticleInterval(pEffect, parent, worldRelative, duration=durationDelay, cleanup=True))
+
+
+def __getSoundTrack(level, delay, duration = None, node = None):
+ soundEffect = globalBattleSoundCache.getSound(soundFiles[level])
+ soundIntervals = Sequence()
+ if soundEffect:
+ if duration:
+ playSound = SoundInterval(soundEffect, duration=duration, node=node)
+ else:
+ playSound = SoundInterval(soundEffect, node=node)
+ soundIntervals.append(Wait(delay))
+ soundIntervals.append(playSound)
+ return soundIntervals
+
+
+def __healTickle(heal, hasInteractivePropHealBonus):
+ toon = heal['toon']
+ target = heal['target']['toon']
+ hp = heal['target']['hp']
+ ineffective = heal['sidestep']
+ level = heal['level']
+ track = Sequence(__runToHealSpot(heal))
+ feather = globalPropPool.getProp('feather')
+ feather2 = MovieUtil.copyProp(feather)
+ feathers = [feather, feather2]
+ hands = toon.getRightHands()
+
+ def scaleFeathers(feathers, toon = toon, target = target):
+ toon.pose('tickle', 63)
+ toon.update(0)
+ hand = toon.getRightHands()[0]
+ horizDistance = Vec3(hand.getPos(render) - target.getPos(render))
+ horizDistance.setZ(0)
+ distance = horizDistance.length()
+ if target.style.torso[0] == 's':
+ distance -= 0.5
+ else:
+ distance -= 0.3
+ featherLen = 2.4
+ scale = distance / (featherLen * hand.getScale(render)[0])
+ for feather in feathers:
+ feather.setScale(scale)
+
+ tFeatherScaleUp = 0.5
+ dFeatherScaleUp = 0.5
+ dFeatherScaleDown = 0.5
+ featherTrack = Parallel(MovieUtil.getActorIntervals(feathers, 'feather'), Sequence(Wait(tFeatherScaleUp), Func(MovieUtil.showProps, feathers, hands), Func(scaleFeathers, feathers), MovieUtil.getScaleIntervals(feathers, dFeatherScaleUp, MovieUtil.PNT3_NEARZERO, feathers[0].getScale)), Sequence(Wait(toon.getDuration('tickle') - dFeatherScaleDown), MovieUtil.getScaleIntervals(feathers, dFeatherScaleDown, None, MovieUtil.PNT3_NEARZERO)))
+ tHeal = 3.0
+ mtrack = Parallel(featherTrack, ActorInterval(toon, 'tickle'), __getSoundTrack(level, 1, node=toon), Sequence(Wait(tHeal), Func(__healToon, target, hp, ineffective, hasInteractivePropHealBonus), ActorInterval(target, 'cringe', startTime=20.0 / target.getFrameRate('cringe'))))
+ track.append(mtrack)
+ track.append(Func(MovieUtil.removeProps, feathers))
+ track.append(__returnToBase(heal))
+ track.append(Func(target.clearChat))
+ return track
+
+
+def __healJoke(heal, hasInteractivePropHealBonus):
+ npcId = 0
+ if heal.has_key('npcId'):
+ npcId = heal['npcId']
+ toon = NPCToons.createLocalNPC(npcId)
+ if toon == None:
+ return
+ else:
+ toon = heal['toon']
+ targets = heal['target']
+ ineffective = heal['sidestep']
+ level = heal['level']
+ jokeIndex = heal['hpbonus'] % len(HealJokes.toonHealJokes)
+ if npcId != 0:
+ track = Sequence(MovieNPCSOS.teleportIn(heal, toon))
+ else:
+ track = Sequence(__runToHealSpot(heal))
+ tracks = Parallel()
+ fSpeakPunchline = 58
+ tSpeakSetup = 0.0
+ tSpeakPunchline = 3.0
+ dPunchLine = 3.0
+ tTargetReact = tSpeakPunchline + 1.0
+ dTargetLaugh = 1.5
+ tRunBack = tSpeakPunchline + dPunchLine
+ tDoSoundAnimation = tSpeakPunchline - float(fSpeakPunchline) / toon.getFrameRate('sound')
+ megaphone = globalPropPool.getProp('megaphone')
+ megaphone2 = MovieUtil.copyProp(megaphone)
+ megaphones = [megaphone, megaphone2]
+ hands = toon.getRightHands()
+ dMegaphoneScale = 0.5
+ tracks.append(Sequence(Wait(tDoSoundAnimation), Func(MovieUtil.showProps, megaphones, hands), MovieUtil.getScaleIntervals(megaphones, dMegaphoneScale, MovieUtil.PNT3_NEARZERO, MovieUtil.PNT3_ONE), Wait(toon.getDuration('sound') - 2.0 * dMegaphoneScale), MovieUtil.getScaleIntervals(megaphones, dMegaphoneScale, MovieUtil.PNT3_ONE, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProps, megaphones)))
+ tracks.append(Sequence(Wait(tDoSoundAnimation), ActorInterval(toon, 'sound')))
+ soundTrack = __getSoundTrack(level, 2.0, node=toon)
+ tracks.append(soundTrack)
+ joke = HealJokes.toonHealJokes[jokeIndex]
+ tracks.append(Sequence(Wait(tSpeakSetup), Func(toon.setChatAbsolute, joke[0], CFSpeech | CFTimeout)))
+ tracks.append(Sequence(Wait(tSpeakPunchline), Func(toon.setChatAbsolute, joke[1], CFSpeech | CFTimeout)))
+ reactTrack = Sequence(Wait(tTargetReact))
+ for target in targets:
+ targetToon = target['toon']
+ hp = target['hp']
+ reactTrack.append(Func(__healToon, targetToon, hp, ineffective, hasInteractivePropHealBonus))
+
+ reactTrack.append(Wait(dTargetLaugh))
+ for target in targets:
+ targetToon = target['toon']
+ reactTrack.append(Func(targetToon.clearChat))
+
+ tracks.append(reactTrack)
+ if npcId != 0:
+ track.append(Sequence(Wait(tRunBack), Func(toon.clearChat), *MovieNPCSOS.teleportOut(heal, toon)))
+ else:
+ tracks.append(Sequence(Wait(tRunBack), Func(toon.clearChat), *__returnToBase(heal)))
+ track.append(tracks)
+ return track
+
+
+def __healSmooch(heal, hasInteractivePropHealBonus):
+ toon = heal['toon']
+ target = heal['target']['toon']
+ level = heal['level']
+ hp = heal['target']['hp']
+ ineffective = heal['sidestep']
+ track = Sequence(__runToHealSpot(heal))
+ lipstick = globalPropPool.getProp('lipstick')
+ lipstick2 = MovieUtil.copyProp(lipstick)
+ lipsticks = [lipstick, lipstick2]
+ rightHands = toon.getRightHands()
+ dScale = 0.5
+ lipstickTrack = Sequence(Func(MovieUtil.showProps, lipsticks, rightHands, Point3(-0.27, -0.24, -0.95), Point3(-118, -10.6, -25.9)), MovieUtil.getScaleIntervals(lipsticks, dScale, MovieUtil.PNT3_NEARZERO, MovieUtil.PNT3_ONE), Wait(toon.getDuration('smooch') - 2.0 * dScale), MovieUtil.getScaleIntervals(lipsticks, dScale, MovieUtil.PNT3_ONE, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProps, lipsticks))
+ lips = globalPropPool.getProp('lips')
+ dScale = 0.5
+ tLips = 2.5
+ tThrow = 115.0 / toon.getFrameRate('smooch')
+ dThrow = 0.5
+
+ def getLipPos(toon = toon):
+ toon.pose('smooch', 57)
+ toon.update(0)
+ hand = toon.getRightHands()[0]
+ return hand.getPos(render)
+
+ lipsTrack = Sequence(Wait(tLips), Func(MovieUtil.showProp, lips, render, getLipPos), Func(lips.setBillboardPointWorld), LerpScaleInterval(lips, dScale, Point3(3, 3, 3), startScale=MovieUtil.PNT3_NEARZERO), Wait(tThrow - tLips - dScale), LerpPosInterval(lips, dThrow, Point3(target.getPos() + Point3(0, 0, target.getHeight()))), Func(MovieUtil.removeProp, lips))
+ delay = tThrow + dThrow
+ mtrack = Parallel(lipstickTrack, lipsTrack, __getSoundTrack(level, 2, node=toon), Sequence(ActorInterval(toon, 'smooch'), *__returnToBase(heal)), Sequence(Wait(delay), ActorInterval(target, 'conked')), Sequence(Wait(delay), Func(__healToon, target, hp, ineffective, hasInteractivePropHealBonus)))
+ track.append(mtrack)
+ track.append(Func(target.clearChat))
+ return track
+
+
+def __healDance(heal, hasInteractivePropHealBonus):
+ npcId = 0
+ if heal.has_key('npcId'):
+ npcId = heal['npcId']
+ toon = NPCToons.createLocalNPC(npcId)
+ if toon == None:
+ return
+ else:
+ toon = heal['toon']
+ targets = heal['target']
+ ineffective = heal['sidestep']
+ level = heal['level']
+ if npcId != 0:
+ track = Sequence(MovieNPCSOS.teleportIn(heal, toon))
+ else:
+ track = Sequence(__runToHealSpot(heal))
+ delay = 3.0
+ first = 1
+ targetTrack = Sequence()
+ for target in targets:
+ targetToon = target['toon']
+ hp = target['hp']
+ reactIval = Func(__healToon, targetToon, hp, ineffective, hasInteractivePropHealBonus)
+ if first:
+ targetTrack.append(Wait(delay))
+ first = 0
+ targetTrack.append(reactIval)
+
+ hat = globalPropPool.getProp('hat')
+ hat2 = MovieUtil.copyProp(hat)
+ hats = [hat, hat2]
+ cane = globalPropPool.getProp('cane')
+ cane2 = MovieUtil.copyProp(cane)
+ canes = [cane, cane2]
+ leftHands = toon.getLeftHands()
+ rightHands = toon.getRightHands()
+ dScale = 0.5
+ propTrack = Sequence(Func(MovieUtil.showProps, hats, rightHands, Point3(0.23, 0.09, 0.69), Point3(180, 0, 0)), Func(MovieUtil.showProps, canes, leftHands, Point3(-0.28, 0.0, 0.14), Point3(0.0, 0.0, -150.0)), MovieUtil.getScaleIntervals(hats + canes, dScale, MovieUtil.PNT3_NEARZERO, MovieUtil.PNT3_ONE), Wait(toon.getDuration('happy-dance') - 2.0 * dScale), MovieUtil.getScaleIntervals(hats + canes, dScale, MovieUtil.PNT3_ONE, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProps, hats + canes))
+ mtrack = Parallel(propTrack, ActorInterval(toon, 'happy-dance'), __getSoundTrack(level, 0.2, duration=6.4, node=toon), targetTrack)
+ track.append(Func(toon.loop, 'neutral'))
+ track.append(Wait(0.1))
+ track.append(mtrack)
+ if npcId != 0:
+ track.append(MovieNPCSOS.teleportOut(heal, toon))
+ else:
+ track.append(__returnToBase(heal))
+ for target in targets:
+ targetToon = target['toon']
+ track.append(Func(targetToon.clearChat))
+
+ return track
+
+
+def __healSprinkle(heal, hasInteractivePropHealBonus):
+ toon = heal['toon']
+ target = heal['target']['toon']
+ hp = heal['target']['hp']
+ ineffective = heal['sidestep']
+ level = heal['level']
+ track = Sequence(__runToHealSpot(heal))
+ sprayEffect = BattleParticles.createParticleEffect(file='pixieSpray')
+ dropEffect = BattleParticles.createParticleEffect(file='pixieDrop')
+ explodeEffect = BattleParticles.createParticleEffect(file='pixieExplode')
+ poofEffect = BattleParticles.createParticleEffect(file='pixiePoof')
+ wallEffect = BattleParticles.createParticleEffect(file='pixieWall')
+
+ def face90(toon = toon, target = target):
+ vec = Point3(target.getPos() - toon.getPos())
+ vec.setZ(0)
+ temp = vec[0]
+ vec.setX(-vec[1])
+ vec.setY(temp)
+ targetPoint = Point3(toon.getPos() + vec)
+ toon.headsUp(render, targetPoint)
+
+ delay = 2.5
+ mtrack = Parallel(__getPartTrack(sprayEffect, 1.5, 0.5, [sprayEffect, toon, 0]), __getPartTrack(dropEffect, 1.9, 2.0, [dropEffect, target, 0]), __getPartTrack(explodeEffect, 2.7, 1.0, [explodeEffect, toon, 0]), __getPartTrack(poofEffect, 3.4, 1.0, [poofEffect, target, 0]), __getPartTrack(wallEffect, 4.05, 1.2, [wallEffect, toon, 0]), __getSoundTrack(level, 2, duration=4.1, node=toon), Sequence(Func(face90), ActorInterval(toon, 'sprinkle-dust')), Sequence(Wait(delay), Func(__healToon, target, hp, ineffective, hasInteractivePropHealBonus)))
+ track.append(mtrack)
+ track.append(__returnToBase(heal))
+ track.append(Func(target.clearChat))
+ return track
+
+
+def __healJuggle(heal, hasInteractivePropHealBonus):
+ npcId = 0
+ if heal.has_key('npcId'):
+ npcId = heal['npcId']
+ toon = NPCToons.createLocalNPC(npcId)
+ if toon == None:
+ return
+ else:
+ toon = heal['toon']
+ targets = heal['target']
+ ineffective = heal['sidestep']
+ level = heal['level']
+ if npcId != 0:
+ track = Sequence(MovieNPCSOS.teleportIn(heal, toon))
+ else:
+ track = Sequence(__runToHealSpot(heal))
+ delay = 4.0
+ first = 1
+ targetTrack = Sequence()
+ for target in targets:
+ targetToon = target['toon']
+ hp = target['hp']
+ reactIval = Func(__healToon, targetToon, hp, ineffective, hasInteractivePropHealBonus)
+ if first == 1:
+ targetTrack.append(Wait(delay))
+ first = 0
+ targetTrack.append(reactIval)
+
+ cube = globalPropPool.getProp('cubes')
+ cube2 = MovieUtil.copyProp(cube)
+ cubes = [cube, cube2]
+ hips = [toon.getLOD(toon.getLODNames()[0]).find('**/joint_hips'), toon.getLOD(toon.getLODNames()[1]).find('**/joint_hips')]
+ cubeTrack = Sequence(Func(MovieUtil.showProps, cubes, hips), MovieUtil.getActorIntervals(cubes, 'cubes'), Func(MovieUtil.removeProps, cubes))
+ mtrack = Parallel(cubeTrack, __getSoundTrack(level, 0.7, duration=7.7, node=toon), ActorInterval(toon, 'juggle'), targetTrack)
+ track.append(mtrack)
+ if npcId != 0:
+ track.append(MovieNPCSOS.teleportOut(heal, toon))
+ else:
+ track.append(__returnToBase(heal))
+ for target in targets:
+ targetToon = target['toon']
+ track.append(Func(targetToon.clearChat))
+
+ return track
+
+
+def __healDive(heal, hasInteractivePropHealBonus):
+ splash = Splash.Splash(render)
+ splash.reparentTo(render)
+ npcId = 0
+ if heal.has_key('npcId'):
+ npcId = heal['npcId']
+ toon = NPCToons.createLocalNPC(npcId)
+ if toon == None:
+ return
+ else:
+ toon = heal['toon']
+ targets = heal['target']
+ ineffective = heal['sidestep']
+ level = heal['level']
+ if npcId != 0:
+ track = Sequence(MovieNPCSOS.teleportIn(heal, toon))
+ else:
+ track = Sequence(__runToHealSpot(heal))
+ delay = 7.0
+ first = 1
+ targetTrack = Sequence()
+ for target in targets:
+ targetToon = target['toon']
+ hp = target['hp']
+ reactIval = Func(__healToon, targetToon, hp, ineffective, hasInteractivePropHealBonus)
+ if first == 1:
+ targetTrack.append(Wait(delay))
+ first = 0
+ targetTrack.append(reactIval)
+
+ thisBattle = heal['battle']
+ toonsInBattle = thisBattle.toons
+ glass = globalPropPool.getProp('glass')
+ glass.setScale(4.0)
+ glass.setHpr(0.0, 90.0, 0.0)
+ ladder = globalPropPool.getProp('ladder')
+ placeNode = NodePath('lookNode')
+ diveProps = [glass, ladder]
+ ladderScale = toon.getBodyScale() / 0.66
+ scaleUpPoint = Point3(0.5, 0.5, 0.45) * ladderScale
+ basePos = toon.getPos()
+ glassOffset = Point3(0, 1.1, 0.2)
+ glassToonOffset = Point3(0, 1.2, 0.2)
+ splashOffset = Point3(0, 1.0, 0.4)
+ ladderOffset = Point3(0, 4, 0)
+ ladderToonSep = Point3(0, 1, 0) * ladderScale
+ diveOffset = Point3(0, 0, 10)
+ divePos = add3(add3(ladderOffset, diveOffset), ladderToonSep)
+ ladder.setH(toon.getH())
+ glassPos = render.getRelativePoint(toon, glassOffset)
+ glassToonPos = render.getRelativePoint(toon, glassToonOffset)
+ ladderPos = render.getRelativePoint(toon, ladderOffset)
+ climbladderPos = render.getRelativePoint(toon, add3(ladderOffset, ladderToonSep))
+ divePos = render.getRelativePoint(toon, divePos)
+ topDivePos = render.getRelativePoint(toon, diveOffset)
+ lookBase = render.getRelativePoint(toon, ladderOffset)
+ lookTop = render.getRelativePoint(toon, add3(ladderOffset, diveOffset))
+ LookGlass = render.getRelativePoint(toon, glassOffset)
+ splash.setPos(splashOffset)
+ walkToLadderTime = 1.0
+ climbTime = 5.0
+ diveTime = 1.0
+ ladderGrowTime = 1.5
+ splash.setPos(glassPos)
+ toonNode = toon.getGeomNode()
+ placeNode.reparentTo(render)
+ placeNode.setScale(5.0)
+ placeNode.setPos(toon.getPos(render))
+ placeNode.setHpr(toon.getHpr(render))
+ toonscale = toonNode.getScale()
+ toonFacing = toon.getHpr()
+ propTrack = Sequence(Func(MovieUtil.showProp, glass, render, glassPos), Func(MovieUtil.showProp, ladder, render, ladderPos), Func(toonsLook, toonsInBattle, placeNode, Point3(0, 0, 0)), Func(placeNode.setPos, lookBase), LerpScaleInterval(ladder, ladderGrowTime, scaleUpPoint, startScale=MovieUtil.PNT3_NEARZERO), Func(placeNode.setPos, lookTop), Wait(2.1), MovieCamera.toonGroupHighShot(None, 0), Wait(2.1), Func(placeNode.setPos, LookGlass), Wait(0.4), MovieCamera.allGroupLowShot(None, 0), Wait(1.8), LerpScaleInterval(ladder, ladderGrowTime, MovieUtil.PNT3_NEARZERO, startScale=scaleUpPoint), Func(MovieUtil.removeProps, diveProps))
+ mtrack = Parallel(propTrack, __getSoundTrack(level, 0.6, duration=9.0, node=toon), Sequence(Parallel(Sequence(ActorInterval(toon, 'walk', loop=0, duration=walkToLadderTime), ActorInterval(toon, 'neutral', loop=0, duration=0.1)), LerpPosInterval(toon, walkToLadderTime, climbladderPos), Wait(ladderGrowTime)), Parallel(ActorInterval(toon, 'climb', loop=0, endFrame=116), Sequence(Wait(4.6), Func(toonNode.setTransparency, 1), LerpColorScaleInterval(toonNode, 0.25, VBase4(1, 1.0, 1, 0.0), blendType='easeInOut'), LerpScaleInterval(toonNode, 0.01, 0.1, startScale=toonscale), LerpHprInterval(toon, 0.01, toonFacing), LerpPosInterval(toon, 0.0, glassToonPos), Func(toonNode.clearTransparency), Func(toonNode.clearColorScale), Parallel(ActorInterval(toon, 'swim', loop=1, startTime=0.0, endTime=1.0), Wait(1.0))), Sequence(Wait(4.6), Func(splash.play), Wait(1.0), Func(splash.destroy))), Wait(0.5), Parallel(ActorInterval(toon, 'jump', loop=0, startTime=0.2), LerpScaleInterval(toonNode, 0.5, toonscale, startScale=0.1), Func(stopLook, toonsInBattle))), targetTrack)
+ track.append(mtrack)
+ if npcId != 0:
+ track.append(MovieNPCSOS.teleportOut(heal, toon))
+ else:
+ track.append(__returnToBase(heal))
+ for target in targets:
+ targetToon = target['toon']
+ track.append(Func(targetToon.clearChat))
+
+ return track
+
+
+def add3(t1, t2):
+ returnThree = Point3(t1[0] + t2[0], t1[1] + t2[1], t1[2] + t2[2])
+ return returnThree
+
+
+def stopLook(toonsInBattle):
+ for someToon in toonsInBattle:
+ someToon.stopStareAt()
+
+
+def toonsLook(toons, someNode, offset):
+ for someToon in toons:
+ someToon.startStareAt(someNode, offset)
diff --git a/toontown/battle/MovieLure.py b/toontown/battle/MovieLure.py
new file mode 100644
index 0000000..3ef8cda
--- /dev/null
+++ b/toontown/battle/MovieLure.py
@@ -0,0 +1,618 @@
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from BattleProps import *
+from toontown.suit.SuitBase import *
+from toontown.toon.ToonDNA import *
+from BattleSounds import *
+import MovieCamera
+from direct.directnotify import DirectNotifyGlobal
+import MovieUtil
+from toontown.toonbase import ToontownBattleGlobals
+import BattleParticles
+import BattleProps
+import MovieNPCSOS
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieLures')
+
+def safeWrtReparentTo(nodePath, parent):
+ if nodePath and not nodePath.isEmpty():
+ nodePath.wrtReparentTo(parent)
+
+
+def doLures(lures):
+ if len(lures) == 0:
+ return (None, None)
+ npcArrivals, npcDepartures, npcs = MovieNPCSOS.doNPCTeleports(lures)
+ mtrack = Parallel()
+ for l in lures:
+ ival = __doLureLevel(l, npcs)
+ if ival:
+ mtrack.append(ival)
+
+ lureTrack = Sequence(npcArrivals, mtrack, npcDepartures)
+ camDuration = mtrack.getDuration()
+ enterDuration = npcArrivals.getDuration()
+ exitDuration = npcDepartures.getDuration()
+ camTrack = MovieCamera.chooseLureShot(lures, camDuration, enterDuration, exitDuration)
+ return (lureTrack, camTrack)
+
+
+def __doLureLevel(lure, npcs):
+ level = lure['level']
+ if level == 0:
+ return __lureOneDollar(lure)
+ elif level == 1:
+ return __lureSmallMagnet(lure, npcs)
+ elif level == 2:
+ return __lureFiveDollar(lure)
+ elif level == 3:
+ return __lureLargeMagnet(lure, npcs)
+ elif level == 4:
+ return __lureTenDollar(lure)
+ elif level == 5:
+ return __lureHypnotize(lure, npcs)
+ elif level == 6:
+ return __lureSlideshow(lure, npcs)
+ return None
+
+
+def getSoundTrack(fileName, delay = 0.01, duration = None, node = None):
+ soundEffect = globalBattleSoundCache.getSound(fileName)
+ if duration:
+ return Sequence(Wait(delay), SoundInterval(soundEffect, duration=duration, node=node))
+ else:
+ return Sequence(Wait(delay), SoundInterval(soundEffect, node=node))
+
+
+def __createFishingPoleMultiTrack(lure, dollar, dollarName):
+ toon = lure['toon']
+ target = lure['target']
+ battle = lure['battle']
+ sidestep = lure['sidestep']
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ suit = target['suit']
+ targetPos = suit.getPos(battle)
+ died = target['died']
+ revived = target['revived']
+ reachAnimDuration = 3.5
+ trapProp = suit.battleTrapProp
+ pole = globalPropPool.getProp('fishing-pole')
+ pole2 = MovieUtil.copyProp(pole)
+ poles = [pole, pole2]
+ hands = toon.getRightHands()
+
+ def positionDollar(dollar, suit):
+ dollar.reparentTo(suit)
+ dollar.setPos(0, MovieUtil.SUIT_LURE_DOLLAR_DISTANCE, 0)
+
+ dollarTrack = Sequence(Func(positionDollar, dollar, suit), Func(dollar.wrtReparentTo, battle), ActorInterval(dollar, dollarName, duration=3), getSplicedLerpAnimsTrack(dollar, dollarName, 0.7, 2.0, startTime=3), LerpPosInterval(dollar, 0.2, Point3(0, -10, 7)), Func(MovieUtil.removeProp, dollar))
+ poleTrack = Sequence(Func(MovieUtil.showProps, poles, hands), ActorInterval(pole, 'fishing-pole'), Func(MovieUtil.removeProps, poles))
+ toonTrack = Sequence(Func(toon.headsUp, battle, targetPos), ActorInterval(toon, 'battlecast'), Func(toon.loop, 'neutral'))
+ tracks = Parallel(dollarTrack, poleTrack, toonTrack)
+ if sidestep == 0:
+ if kbbonus == 1 or hp > 0:
+ suitTrack = Sequence()
+ opos, ohpr = battle.getActorPosHpr(suit)
+ reachDist = MovieUtil.SUIT_LURE_DISTANCE
+ reachPos = Point3(opos[0], opos[1] - reachDist, opos[2])
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ suitTrack.append(Wait(3.5))
+ suitName = suit.getStyleName()
+ retardPos, retardHpr = battle.getActorPosHpr(suit)
+ retardPos.setY(retardPos.getY() + MovieUtil.SUIT_EXTRA_REACH_DISTANCE)
+ if suitName in MovieUtil.largeSuits:
+ moveTrack = lerpSuit(suit, 0.0, reachAnimDuration / 2.5, retardPos, battle, trapProp)
+ reachTrack = ActorInterval(suit, 'reach', duration=reachAnimDuration)
+ suitTrack.append(Parallel(moveTrack, reachTrack))
+ else:
+ suitTrack.append(ActorInterval(suit, 'reach', duration=reachAnimDuration))
+ if trapProp:
+ suitTrack.append(Func(trapProp.wrtReparentTo, battle))
+ suitTrack.append(Func(suit.setPos, battle, reachPos))
+ if trapProp:
+ suitTrack.append(Func(trapProp.wrtReparentTo, suit))
+ suit.battleTrapProp = trapProp
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ suitTrack.append(Func(battle.lureSuit, suit))
+ if hp > 0:
+ suitTrack.append(__createSuitDamageTrack(battle, suit, hp, lure, trapProp))
+ if revived != 0:
+ suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle))
+ if died != 0:
+ suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle))
+ tracks.append(suitTrack)
+ else:
+ tracks.append(Sequence(Wait(3.7), Func(MovieUtil.indicateMissed, suit)))
+ tracks.append(getSoundTrack('TL_fishing_pole.mp3', delay=0.5, node=toon))
+ return tracks
+
+
+def __createMagnetMultiTrack(lure, magnet, pos, hpr, scale, isSmallMagnet = 1, npcs = []):
+ toon = lure['toon']
+ if lure.has_key('npc'):
+ toon = lure['npc']
+ battle = lure['battle']
+ sidestep = lure['sidestep']
+ targets = lure['target']
+ tracks = Parallel()
+ tracks.append(Sequence(ActorInterval(toon, 'hold-magnet'), Func(toon.loop, 'neutral')))
+ hands = toon.getLeftHands()
+ magnet2 = MovieUtil.copyProp(magnet)
+ magnets = [magnet, magnet2]
+ magnetTrack = Sequence(Wait(0.7), Func(MovieUtil.showProps, magnets, hands, pos, hpr, scale), Wait(6.3), Func(MovieUtil.removeProps, magnets))
+ tracks.append(magnetTrack)
+ for target in targets:
+ suit = target['suit']
+ trapProp = suit.battleTrapProp
+ if sidestep == 0:
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ died = target['died']
+ revived = target['revived']
+ if kbbonus == 1 or hp > 0:
+ suitDelay = 2.6
+ suitMoveDuration = 0.8
+ suitTrack = Sequence()
+ opos, ohpr = battle.getActorPosHpr(suit)
+ reachDist = MovieUtil.SUIT_LURE_DISTANCE
+ reachPos = Point3(opos[0], opos[1] - reachDist, opos[2])
+ numShakes = 3
+ shakeTotalDuration = 0.8
+ shakeDuration = shakeTotalDuration / float(numShakes)
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ suitTrack.append(Wait(suitDelay))
+ suitTrack.append(ActorInterval(suit, 'landing', startTime=2.37, endTime=1.82))
+ for i in range(0, numShakes):
+ suitTrack.append(ActorInterval(suit, 'landing', startTime=1.82, endTime=1.16, duration=shakeDuration))
+
+ suitTrack.append(ActorInterval(suit, 'landing', startTime=1.16, endTime=0.7))
+ suitTrack.append(ActorInterval(suit, 'landing', startTime=0.7, duration=1.3))
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ suitTrack.append(Func(battle.lureSuit, suit))
+ if hp > 0:
+ suitTrack.append(__createSuitDamageTrack(battle, suit, hp, lure, trapProp))
+ if revived != 0:
+ suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle, npcs))
+ elif died != 0:
+ suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle, npcs))
+ tracks.append(suitTrack)
+ tracks.append(lerpSuit(suit, suitDelay + 0.55 + shakeTotalDuration, suitMoveDuration, reachPos, battle, trapProp))
+ else:
+ tracks.append(Sequence(Wait(3.7), Func(MovieUtil.indicateMissed, suit)))
+
+ if isSmallMagnet == 1:
+ tracks.append(getSoundTrack('TL_small_magnet.mp3', delay=0.7, node=toon))
+ else:
+ tracks.append(getSoundTrack('TL_large_magnet.mp3', delay=0.7, node=toon))
+ return tracks
+
+
+def __createHypnoGogglesMultiTrack(lure, npcs = []):
+ toon = lure['toon']
+ if lure.has_key('npc'):
+ toon = lure['npc']
+ targets = lure['target']
+ battle = lure['battle']
+ sidestep = lure['sidestep']
+ goggles = globalPropPool.getProp('hypno-goggles')
+ goggles2 = MovieUtil.copyProp(goggles)
+ bothGoggles = [goggles, goggles2]
+ pos = Point3(-1.03, 1.04, -0.3)
+ hpr = Point3(-96.55, 36.14, -170.59)
+ scale = Point3(1.5, 1.5, 1.5)
+ hands = toon.getLeftHands()
+ gogglesTrack = Sequence(Wait(0.6), Func(MovieUtil.showProps, bothGoggles, hands, pos, hpr, scale), ActorInterval(goggles, 'hypno-goggles', duration=2.2), Func(MovieUtil.removeProps, bothGoggles))
+ toonTrack = Sequence(ActorInterval(toon, 'hypnotize'), Func(toon.loop, 'neutral'))
+ tracks = Parallel(gogglesTrack, toonTrack)
+ for target in targets:
+ suit = target['suit']
+ trapProp = suit.battleTrapProp
+ if sidestep == 0:
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ died = target['died']
+ revived = target['revived']
+ if kbbonus == 1 or hp > 0:
+ suitTrack = Sequence()
+ suitDelay = 1.6
+ suitAnimDuration = 1.5
+ opos, ohpr = battle.getActorPosHpr(suit)
+ reachDist = MovieUtil.SUIT_LURE_DISTANCE
+ reachPos = Point3(opos[0], opos[1] - reachDist, opos[2])
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ suitTrack.append(Wait(suitDelay))
+ suitTrack.append(ActorInterval(suit, 'hypnotized', duration=3.1))
+ suitTrack.append(Func(suit.setPos, battle, reachPos))
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ suitTrack.append(Func(battle.lureSuit, suit))
+ if hp > 0:
+ suitTrack.append(__createSuitDamageTrack(battle, suit, hp, lure, trapProp))
+ if revived != 0:
+ suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle, npcs))
+ elif died != 0:
+ suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle, npcs))
+ tracks.append(suitTrack)
+ tracks.append(lerpSuit(suit, suitDelay + 1.7, 0.7, reachPos, battle, trapProp))
+ else:
+ tracks.append(Sequence(Wait(2.3), Func(MovieUtil.indicateMissed, suit, 1.1)))
+
+ tracks.append(getSoundTrack('TL_hypnotize.mp3', delay=0.5, node=toon))
+ return tracks
+
+
+def __lureOneDollar(lure):
+ dollarProp = '1dollar'
+ dollar = globalPropPool.getProp(dollarProp)
+ return __createFishingPoleMultiTrack(lure, dollar, dollarProp)
+
+
+def __lureSmallMagnet(lure, npcs = []):
+ magnet = globalPropPool.getProp('small-magnet')
+ pos = Point3(-0.27, 0.19, 0.29)
+ hpr = Point3(-90.0, 84.17, -180.0)
+ scale = Point3(0.85, 0.85, 0.85)
+ return __createMagnetMultiTrack(lure, magnet, pos, hpr, scale, isSmallMagnet=1, npcs=npcs)
+
+
+def __lureFiveDollar(lure):
+ dollarProp = '5dollar'
+ dollar = globalPropPool.getProp(dollarProp)
+ return __createFishingPoleMultiTrack(lure, dollar, dollarProp)
+
+
+def __lureLargeMagnet(lure, npcs = []):
+ magnet = globalPropPool.getProp('big-magnet')
+ pos = Point3(-0.27, 0.08, 0.29)
+ hpr = Point3(-90.0, 84.17, -180)
+ scale = Point3(1.32, 1.32, 1.32)
+ return __createMagnetMultiTrack(lure, magnet, pos, hpr, scale, isSmallMagnet=0, npcs=npcs)
+
+
+def __lureTenDollar(lure):
+ dollarProp = '10dollar'
+ dollar = globalPropPool.getProp(dollarProp)
+ return __createFishingPoleMultiTrack(lure, dollar, dollarProp)
+
+
+def __lureHypnotize(lure, npcs = []):
+ return __createHypnoGogglesMultiTrack(lure, npcs)
+
+
+def __lureSlideshow(lure, npcs):
+ return __createSlideshowMultiTrack(lure, npcs)
+
+
+def __createSuitDamageTrack(battle, suit, hp, lure, trapProp):
+ if trapProp == None or trapProp.isEmpty():
+ return Func(suit.loop, 'neutral')
+ trapProp.wrtReparentTo(battle)
+ trapTrack = ToontownBattleGlobals.TRAP_TRACK
+ trapLevel = suit.battleTrap
+ trapTrackNames = ToontownBattleGlobals.AvProps[trapTrack]
+ trapName = trapTrackNames[trapLevel]
+ result = Sequence()
+
+ def reparentTrap(trapProp = trapProp, battle = battle):
+ if trapProp and not trapProp.isEmpty():
+ trapProp.wrtReparentTo(battle)
+
+ result.append(Func(reparentTrap))
+ parent = battle
+ if suit.battleTrapIsFresh == 1:
+ if trapName == 'quicksand' or trapName == 'trapdoor':
+ trapProp.hide()
+ trapProp.reparentTo(suit)
+ trapProp.setPos(Point3(0, MovieUtil.SUIT_TRAP_DISTANCE, 0))
+ trapProp.setHpr(Point3(0, 0, 0))
+ trapProp.wrtReparentTo(battle)
+ elif trapName == 'rake':
+ trapProp.hide()
+ trapProp.reparentTo(suit)
+ trapProp.setPos(0, MovieUtil.SUIT_TRAP_RAKE_DISTANCE, 0)
+ trapProp.setHpr(Point3(0, 270, 0))
+ trapProp.setScale(Point3(0.7, 0.7, 0.7))
+ rakeOffset = MovieUtil.getSuitRakeOffset(suit)
+ trapProp.setY(trapProp.getY() + rakeOffset)
+ else:
+ parent = render
+ if trapName == 'banana':
+ slidePos = trapProp.getPos(parent)
+ slidePos.setY(slidePos.getY() - 5.1)
+ moveTrack = Sequence(Wait(0.1), LerpPosInterval(trapProp, 0.1, slidePos, other=battle))
+ animTrack = Sequence(ActorInterval(trapProp, 'banana', startTime=3.1), Wait(1.1), LerpScaleInterval(trapProp, 1, Point3(0.01, 0.01, 0.01)))
+ suitTrack = ActorInterval(suit, 'slip-backward')
+ damageTrack = Sequence(Wait(0.5), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp))
+ soundTrack = Sequence(SoundInterval(globalBattleSoundCache.getSound('AA_pie_throw_only.mp3'), duration=0.55, node=suit), SoundInterval(globalBattleSoundCache.getSound('Toon_bodyfall_synergy.mp3'), node=suit))
+ result.append(Parallel(moveTrack, animTrack, suitTrack, damageTrack, soundTrack))
+ elif trapName == 'rake' or trapName == 'rake-react':
+ hpr = trapProp.getHpr(parent)
+ upHpr = Vec3(hpr[0], 179.9999, hpr[2])
+ bounce1Hpr = Vec3(hpr[0], 120, hpr[2])
+ bounce2Hpr = Vec3(hpr[0], 100, hpr[2])
+ rakeTrack = Sequence(Wait(0.5), LerpHprInterval(trapProp, 0.1, upHpr, startHpr=hpr), Wait(0.7), LerpHprInterval(trapProp, 0.4, hpr, startHpr=upHpr), LerpHprInterval(trapProp, 0.15, bounce1Hpr, startHpr=hpr), LerpHprInterval(trapProp, 0.05, hpr, startHpr=bounce1Hpr), LerpHprInterval(trapProp, 0.15, bounce2Hpr, startHpr=hpr), LerpHprInterval(trapProp, 0.05, hpr, startHpr=bounce2Hpr), Wait(0.2), LerpScaleInterval(trapProp, 0.2, Point3(0.01, 0.01, 0.01)))
+ rakeAnimDuration = 3.125
+ suitTrack = ActorInterval(suit, 'rake-react', duration=rakeAnimDuration)
+ damageTrack = Sequence(Wait(0.5), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp))
+ soundTrack = getSoundTrack('TL_step_on_rake.mp3', delay=0.6, node=suit)
+ result.append(Parallel(rakeTrack, suitTrack, damageTrack, soundTrack))
+ elif trapName == 'marbles':
+ slidePos = trapProp.getPos(parent)
+ slidePos.setY(slidePos.getY() - 6.5)
+ moveTrack = Sequence(Wait(0.1), LerpPosInterval(trapProp, 0.8, slidePos, other=battle), Wait(1.1), LerpScaleInterval(trapProp, 1, Point3(0.01, 0.01, 0.01)))
+ animTrack = ActorInterval(trapProp, 'marbles', startTime=3.1)
+ suitTrack = ActorInterval(suit, 'slip-backward')
+ damageTrack = Sequence(Wait(0.5), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp))
+ soundTrack = Sequence(SoundInterval(globalBattleSoundCache.getSound('AA_pie_throw_only.mp3'), duration=0.55, node=suit), SoundInterval(globalBattleSoundCache.getSound('Toon_bodyfall_synergy.mp3'), node=suit))
+ result.append(Parallel(moveTrack, animTrack, suitTrack, damageTrack, soundTrack))
+ elif trapName == 'quicksand':
+ sinkPos1 = trapProp.getPos(battle)
+ sinkPos2 = trapProp.getPos(battle)
+ dropPos = trapProp.getPos(battle)
+ landPos = trapProp.getPos(battle)
+ sinkPos1.setZ(sinkPos1.getZ() - 3.1)
+ sinkPos2.setZ(sinkPos2.getZ() - 9.1)
+ dropPos.setZ(dropPos.getZ() + 15)
+ if base.config.GetBool('want-new-cogs', 0):
+ nameTag = suit.find('**/def_nameTag')
+ else:
+ nameTag = suit.find('**/joint_nameTag')
+ trapTrack = Sequence(Wait(2.4), LerpScaleInterval(trapProp, 0.8, Point3(0.01, 0.01, 0.01)))
+ moveTrack = Sequence(Wait(0.9), LerpPosInterval(suit, 0.9, sinkPos1, other=battle), LerpPosInterval(suit, 0.4, sinkPos2, other=battle), Func(suit.setPos, battle, dropPos), Func(suit.wrtReparentTo, hidden), Wait(1.1), Func(suit.wrtReparentTo, battle), LerpPosInterval(suit, 0.3, landPos, other=battle))
+ animTrack = Sequence(ActorInterval(suit, 'flail'), ActorInterval(suit, 'flail', startTime=1.1), Wait(0.7), ActorInterval(suit, 'slip-forward', duration=2.1))
+ damageTrack = Sequence(Wait(3.5), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp))
+ soundTrack = Sequence(Wait(0.7), SoundInterval(globalBattleSoundCache.getSound('TL_quicksand.mp3'), node=suit), Wait(0.1), SoundInterval(globalBattleSoundCache.getSound('Toon_bodyfall_synergy.mp3'), node=suit))
+ result.append(Parallel(trapTrack, moveTrack, animTrack, damageTrack, soundTrack))
+ elif trapName == 'trapdoor':
+ sinkPos = trapProp.getPos(battle)
+ dropPos = trapProp.getPos(battle)
+ landPos = trapProp.getPos(battle)
+ sinkPos.setZ(sinkPos.getZ() - 9.1)
+ dropPos.setZ(dropPos.getZ() + 15)
+ trapTrack = Sequence(Wait(2.4), LerpScaleInterval(trapProp, 0.8, Point3(0.01, 0.01, 0.01)))
+ moveTrack = Sequence(Wait(2.2), LerpPosInterval(suit, 0.4, sinkPos, other=battle), Func(suit.setPos, battle, dropPos), Func(suit.wrtReparentTo, hidden), Wait(1.6), Func(suit.wrtReparentTo, battle), LerpPosInterval(suit, 0.3, landPos, other=battle))
+ animTrack = Sequence(getSplicedLerpAnimsTrack(suit, 'flail', 0.7, 0.25), Func(trapProp.setColor, Vec4(0, 0, 0, 1)), ActorInterval(suit, 'flail', startTime=0.7, endTime=0), ActorInterval(suit, 'neutral', duration=0.5), ActorInterval(suit, 'flail', startTime=1.1), Wait(1.1), ActorInterval(suit, 'slip-forward', duration=2.1))
+ damageTrack = Sequence(Wait(3.5), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp))
+ soundTrack = Sequence(Wait(0.8), SoundInterval(globalBattleSoundCache.getSound('TL_trap_door.mp3'), node=suit), Wait(0.8), SoundInterval(globalBattleSoundCache.getSound('Toon_bodyfall_synergy.mp3'), node=suit))
+ result.append(Parallel(trapTrack, moveTrack, animTrack, damageTrack, soundTrack))
+ elif trapName == 'tnt':
+ tntTrack = ActorInterval(trapProp, 'tnt')
+ explosionTrack = Sequence(Wait(2.3), createTNTExplosionTrack(battle, trapProp=trapProp, relativeTo=parent))
+ suitTrack = Sequence(ActorInterval(suit, 'flail', duration=0.7), ActorInterval(suit, 'flail', startTime=0.7, endTime=0.0), ActorInterval(suit, 'neutral', duration=0.4), ActorInterval(suit, 'flail', startTime=0.6, endTime=0.7), Wait(0.4), ActorInterval(suit, 'slip-forward', startTime=2.48, duration=0.1), Func(battle.movie.needRestoreColor), Func(suit.setColorScale, Vec4(0.2, 0.2, 0.2, 1)), Func(trapProp.reparentTo, hidden), ActorInterval(suit, 'slip-forward', startTime=2.58), Func(suit.clearColorScale), Func(trapProp.sparksEffect.cleanup), Func(battle.movie.clearRestoreColor))
+ damageTrack = Sequence(Wait(2.3), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp))
+ explosionSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart.mp3')
+ soundTrack = Sequence(SoundInterval(globalBattleSoundCache.getSound('TL_dynamite.mp3'), duration=2.0, node=suit), SoundInterval(explosionSound, duration=0.6, node=suit))
+ result.append(Parallel(tntTrack, suitTrack, damageTrack, explosionTrack, soundTrack))
+ elif trapName == 'traintrack':
+ trainInterval = createIncomingTrainInterval(battle, suit, hp, lure, trapProp)
+ result.append(trainInterval)
+ else:
+ notify.warning('unknown trapName: %s detected on suit: %s' % (trapName, suit))
+ suit.battleTrapProp = trapProp
+ result.append(Func(battle.removeTrap, suit, True))
+ result.append(Func(battle.unlureSuit, suit))
+ result.append(__createSuitResetPosTrack(suit, battle))
+ result.append(Func(suit.loop, 'neutral'))
+ if trapName == 'traintrack':
+ result.append(Func(MovieUtil.removeProp, trapProp))
+ return result
+
+
+def __createSuitResetPosTrack(suit, battle):
+ resetPos, resetHpr = battle.getActorPosHpr(suit)
+ moveDist = Vec3(suit.getPos(battle) - resetPos).length()
+ moveDuration = 0.5
+ walkTrack = Sequence(Func(suit.setHpr, battle, resetHpr), ActorInterval(suit, 'walk', startTime=1, duration=moveDuration, endTime=0.0001), Func(suit.loop, 'neutral'))
+ moveTrack = LerpPosInterval(suit, moveDuration, resetPos, other=battle)
+ return Parallel(walkTrack, moveTrack)
+
+
+def getSplicedLerpAnimsTrack(object, animName, origDuration, newDuration, startTime = 0, fps = 30):
+ track = Sequence()
+ addition = 0
+ numIvals = origDuration * fps
+ timeInterval = newDuration / numIvals
+ animInterval = origDuration / numIvals
+ for i in range(0, numIvals):
+ track.append(Wait(timeInterval))
+ track.append(ActorInterval(object, animName, startTime=startTime + addition, duration=animInterval))
+ addition += animInterval
+
+ return track
+
+
+def lerpSuit(suit, delay, duration, reachPos, battle, trapProp):
+ track = Sequence()
+ if trapProp:
+ track.append(Func(safeWrtReparentTo, trapProp, battle))
+ track.append(Wait(delay))
+ track.append(LerpPosInterval(suit, duration, reachPos, other=battle))
+ if trapProp:
+ if trapProp.getName() == 'traintrack':
+ notify.debug('UBERLURE MovieLure.lerpSuit deliberately not parenting trainTrack to suit')
+ else:
+ track.append(Func(safeWrtReparentTo, trapProp, suit))
+ suit.battleTrapProp = trapProp
+ return track
+
+
+def createTNTExplosionTrack(parent, explosionPoint = None, trapProp = None, relativeTo = render):
+ explosionTrack = Sequence()
+ explosion = BattleProps.globalPropPool.getProp('kapow')
+ explosion.setBillboardPointEye()
+ if not explosionPoint:
+ if trapProp:
+ explosionPoint = trapProp.getPos(relativeTo)
+ explosionPoint.setZ(explosionPoint.getZ() + 2.3)
+ else:
+ explosionPoint = Point3(0, 3.6, 2.1)
+ explosionTrack.append(Func(explosion.reparentTo, parent))
+ explosionTrack.append(Func(explosion.setPos, explosionPoint))
+ explosionTrack.append(Func(explosion.setScale, 0.11))
+ explosionTrack.append(ActorInterval(explosion, 'kapow'))
+ explosionTrack.append(Func(MovieUtil.removeProp, explosion))
+ return explosionTrack
+
+
+TRAIN_STARTING_X = -7.131
+TRAIN_TUNNEL_END_X = 7.1
+TRAIN_TRAVEL_DISTANCE = 45
+TRAIN_SPEED = 35.0
+TRAIN_DURATION = TRAIN_TRAVEL_DISTANCE / TRAIN_SPEED
+TRAIN_MATERIALIZE_TIME = 3
+TOTAL_TRAIN_TIME = TRAIN_DURATION + TRAIN_MATERIALIZE_TIME
+
+def createSuitReactionToTrain(battle, suit, hp, lure, trapProp):
+ toon = lure['toon']
+ retval = Sequence()
+ suitPos, suitHpr = battle.getActorPosHpr(suit)
+ distance = suitPos.getX() - TRAIN_STARTING_X
+ timeToGetHit = distance / TRAIN_SPEED
+ suitTrack = Sequence()
+ showDamage = Func(suit.showHpText, -hp, openEnded=0)
+ updateHealthBar = Func(suit.updateHealthBar, hp)
+ anim = 'flatten'
+ suitReact = ActorInterval(suit, anim)
+ cogGettingHit = getSoundTrack('TL_train_cog.mp3', node=toon)
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ suitTrack.append(Wait(timeToGetHit + TRAIN_MATERIALIZE_TIME))
+ suitTrack.append(updateHealthBar)
+ suitTrack.append(Parallel(suitReact, cogGettingHit))
+ suitTrack.append(showDamage)
+ curDuration = suitTrack.getDuration()
+ timeTillEnd = TOTAL_TRAIN_TIME - curDuration
+ if timeTillEnd > 0:
+ suitTrack.append(Wait(timeTillEnd))
+ retval.append(suitTrack)
+ return retval
+
+
+def createIncomingTrainInterval(battle, suit, hp, lure, trapProp):
+ toon = lure['toon']
+ retval = Parallel()
+ suitTrack = createSuitReactionToTrain(battle, suit, hp, lure, trapProp)
+ retval.append(suitTrack)
+ if not trapProp.find('**/train_gag').isEmpty():
+ return retval
+ clipper = PlaneNode('clipper')
+ clipper.setPlane(Plane(Vec3(1, 0, 0), Point3(TRAIN_STARTING_X, 0, 0)))
+ clipNP = trapProp.attachNewNode(clipper)
+ trapProp.setClipPlane(clipNP)
+ clipper2 = PlaneNode('clipper2')
+ clipper2.setPlane(Plane(Vec3(-1, 0, 0), Point3(TRAIN_TUNNEL_END_X, 0, 0)))
+ clipNP2 = trapProp.attachNewNode(clipper2)
+ trapProp.setClipPlane(clipNP2)
+ train = globalPropPool.getProp('train')
+ train.hide()
+ train.reparentTo(trapProp)
+ tempScale = trapProp.getScale()
+ trainScale = Vec3(1.0 / tempScale[0], 1.0 / tempScale[1], 1.0 / tempScale[2])
+ trainIval = Sequence()
+ trainIval.append(Func(train.setScale, trainScale))
+ trainIval.append(Func(train.setH, 90))
+ trainIval.append(Func(train.setX, TRAIN_STARTING_X))
+ trainIval.append(Func(train.setTransparency, 1))
+ trainIval.append(Func(train.setColorScale, Point4(1, 1, 1, 0)))
+ trainIval.append(Func(train.show))
+ tunnel2 = trapProp.find('**/tunnel3')
+ tunnel3 = trapProp.find('**/tunnel2')
+ tunnels = [tunnel2, tunnel3]
+ for tunnel in tunnels:
+ trainIval.append(Func(tunnel.setTransparency, 1))
+ trainIval.append(Func(tunnel.setColorScale, Point4(1, 1, 1, 0)))
+ trainIval.append(Func(tunnel.setScale, Point3(1.0, 0.01, 0.01)))
+ trainIval.append(Func(tunnel.show))
+
+ materializeIval = Parallel()
+ materializeIval.append(LerpColorScaleInterval(train, TRAIN_MATERIALIZE_TIME, Point4(1, 1, 1, 1)))
+ for tunnel in tunnels:
+ materializeIval.append(LerpColorScaleInterval(tunnel, TRAIN_MATERIALIZE_TIME, Point4(1, 1, 1, 1)))
+
+ for tunnel in tunnels:
+ tunnelScaleIval = Sequence()
+ tunnelScaleIval.append(LerpScaleInterval(tunnel, TRAIN_MATERIALIZE_TIME - 1.0, Point3(1.0, 2.0, 2.5)))
+ tunnelScaleIval.append(LerpScaleInterval(tunnel, 0.5, Point3(1.0, 3.0, 1.5)))
+ tunnelScaleIval.append(LerpScaleInterval(tunnel, 0.5, Point3(1.0, 2.5, 2.0)))
+ materializeIval.append(tunnelScaleIval)
+
+ trainIval.append(materializeIval)
+ endingX = TRAIN_STARTING_X + TRAIN_TRAVEL_DISTANCE
+ trainIval.append(LerpPosInterval(train, TRAIN_DURATION, Point3(endingX, 0, 0), other=battle))
+ trainIval.append(LerpColorScaleInterval(train, TRAIN_MATERIALIZE_TIME, Point4(1, 1, 1, 0)))
+ retval.append(trainIval)
+ trainSoundTrack = getSoundTrack('TL_train.mp3', node=toon)
+ retval.append(trainSoundTrack)
+ return retval
+
+
+def __createSlideshowMultiTrack(lure, npcs = []):
+ toon = lure['toon']
+ battle = lure['battle']
+ sidestep = lure['sidestep']
+ origHpr = toon.getHpr(battle)
+ slideshowDelay = 2.5
+ hands = toon.getLeftHands()
+ endPos = toon.getPos(battle)
+ endPos.setY(endPos.getY() + 4)
+ button = globalPropPool.getProp('button')
+ button2 = MovieUtil.copyProp(button)
+ buttons = [button, button2]
+ toonTrack = Sequence()
+ toonTrack.append(Func(MovieUtil.showProps, buttons, hands))
+ toonTrack.append(Func(toon.headsUp, battle, endPos))
+ toonTrack.append(ActorInterval(toon, 'pushbutton'))
+ toonTrack.append(Func(MovieUtil.removeProps, buttons))
+ toonTrack.append(Func(toon.loop, 'neutral'))
+ toonTrack.append(Func(toon.setHpr, battle, origHpr))
+ slideShowProp = globalPropPool.getProp('slideshow')
+ propTrack = Sequence()
+ propTrack.append(Wait(slideshowDelay))
+ propTrack.append(Func(slideShowProp.show))
+ propTrack.append(Func(slideShowProp.setScale, Point3(0.1, 0.1, 0.1)))
+ propTrack.append(Func(slideShowProp.reparentTo, battle))
+ propTrack.append(Func(slideShowProp.setPos, endPos))
+ propTrack.append(LerpScaleInterval(slideShowProp, 1.2, Point3(1.0, 1.0, 1.0)))
+ shrinkDuration = 0.4
+ totalDuration = 7.1
+ propTrackDurationAtThisPoint = propTrack.getDuration()
+ waitTime = totalDuration - propTrackDurationAtThisPoint - shrinkDuration
+ if waitTime > 0:
+ propTrack.append(Wait(waitTime))
+ propTrack.append(LerpScaleInterval(nodePath=slideShowProp, scale=Point3(1.0, 1.0, 0.1), duration=shrinkDuration))
+ propTrack.append(Func(MovieUtil.removeProp, slideShowProp))
+ tracks = Parallel(propTrack, toonTrack)
+ targets = lure['target']
+ for target in targets:
+ suit = target['suit']
+ trapProp = suit.battleTrapProp
+ if sidestep == 0:
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ died = target['died']
+ revived = target['revived']
+ if kbbonus == 1 or hp > 0:
+ suitTrack = Sequence()
+ suitDelay = 3.8
+ suitAnimDuration = 1.5
+ opos, ohpr = battle.getActorPosHpr(suit)
+ reachDist = MovieUtil.SUIT_LURE_DISTANCE
+ reachPos = Point3(opos[0], opos[1] - reachDist, opos[2])
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ suitTrack.append(Wait(suitDelay))
+ suitTrack.append(ActorInterval(suit, 'hypnotized', duration=3.1))
+ suitTrack.append(Func(suit.setPos, battle, reachPos))
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ suitTrack.append(Func(battle.lureSuit, suit))
+ if hp > 0:
+ suitTrack.append(__createSuitDamageTrack(battle, suit, hp, lure, trapProp))
+ if revived != 0:
+ suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle, npcs))
+ elif died != 0:
+ suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle, npcs))
+ tracks.append(suitTrack)
+ tracks.append(lerpSuit(suit, suitDelay + 1.7, 0.7, reachPos, battle, trapProp))
+ else:
+ tracks.append(Sequence(Wait(2.3), Func(MovieUtil.indicateMissed, suit, 1.1)))
+
+ tracks.append(getSoundTrack('TL_presentation.mp3', delay=2.3, node=toon))
+ tracks.append(getSoundTrack('AA_drop_trigger_box.mp3', delay=slideshowDelay, node=toon))
+ return tracks
diff --git a/toontown/battle/MovieNPCSOS.py b/toontown/battle/MovieNPCSOS.py
new file mode 100644
index 0000000..a444775
--- /dev/null
+++ b/toontown/battle/MovieNPCSOS.py
@@ -0,0 +1,269 @@
+from direct.interval.IntervalGlobal import *
+from BattleProps import *
+from BattleSounds import *
+from direct.directnotify import DirectNotifyGlobal
+import MovieCamera
+import random
+import MovieUtil
+import BattleParticles
+import HealJokes
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase import ToontownBattleGlobals
+from toontown.toon import NPCToons
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieNPCSOS')
+soundFiles = ('AA_heal_tickle.mp3', 'AA_heal_telljoke.mp3', 'AA_heal_smooch.mp3', 'AA_heal_happydance.mp3', 'AA_heal_pixiedust.mp3', 'AA_heal_juggle.mp3')
+offset = Point3(0, 4.0, 0)
+
+def __cogsMiss(attack, level, hp):
+ return __doCogsMiss(attack, level, hp)
+
+
+def __toonsHit(attack, level, hp):
+ return __doToonsHit(attack, level, hp)
+
+
+def __restockGags(attack, level, hp):
+ return __doRestockGags(attack, level, hp)
+
+
+NPCSOSfn_dict = {ToontownBattleGlobals.NPC_COGS_MISS: __cogsMiss,
+ ToontownBattleGlobals.NPC_TOONS_HIT: __toonsHit,
+ ToontownBattleGlobals.NPC_RESTOCK_GAGS: __restockGags}
+
+def doNPCSOSs(NPCSOSs):
+ if len(NPCSOSs) == 0:
+ return (None, None)
+ track = Sequence()
+ textTrack = Sequence()
+ for n in NPCSOSs:
+ ival, textIval = __doNPCSOS(n)
+ if ival:
+ track.append(ival)
+ textTrack.append(textIval)
+
+ camDuration = track.getDuration()
+ if camDuration > 0.0:
+ camTrack = MovieCamera.chooseHealShot(NPCSOSs, camDuration)
+ else:
+ camTrack = Sequence()
+ return (track, Parallel(camTrack, textTrack))
+
+
+def __doNPCSOS(sos):
+ npcId = sos['npcId']
+ track, level, hp = NPCToons.getNPCTrackLevelHp(npcId)
+ if track != None:
+ return NPCSOSfn_dict[track](sos, level, hp)
+ else:
+ return __cogsMiss(sos, 0, 0)
+ return
+
+
+def __healToon(toon, hp, ineffective = 0):
+ notify.debug('healToon() - toon: %d hp: %d ineffective: %d' % (toon.doId, hp, ineffective))
+ if ineffective == 1:
+ laughter = random.choice(TTLocalizer.MovieHealLaughterMisses)
+ else:
+ maxDam = ToontownBattleGlobals.AvPropDamage[0][1][0][1]
+ if hp >= maxDam - 1:
+ laughter = random.choice(TTLocalizer.MovieHealLaughterHits2)
+ else:
+ laughter = random.choice(TTLocalizer.MovieHealLaughterHits1)
+ toon.setChatAbsolute(laughter, CFSpeech | CFTimeout)
+
+
+def __getSoundTrack(level, delay, duration = None, node = None):
+ soundEffect = globalBattleSoundCache.getSound(soundFiles[level])
+ soundIntervals = Sequence()
+ if soundEffect:
+ if duration:
+ playSound = SoundInterval(soundEffect, duration=duration, node=node)
+ else:
+ playSound = SoundInterval(soundEffect, node=node)
+ soundIntervals.append(Wait(delay))
+ soundIntervals.append(playSound)
+ return soundIntervals
+
+
+def teleportIn(attack, npc, pos = Point3(0, 0, 0), hpr = Vec3(180.0, 0.0, 0.0)):
+ a = Func(npc.reparentTo, attack['battle'])
+ b = Func(npc.setPos, pos)
+ c = Func(npc.setHpr, hpr)
+ d = Func(npc.pose, 'teleport', npc.getNumFrames('teleport') - 1)
+ e = npc.getTeleportInTrack()
+ ee = Func(npc.addActive)
+ f = Func(npc.setChatAbsolute, TTLocalizer.MovieNPCSOSGreeting % attack['toon'].getName(), CFSpeech | CFTimeout)
+ g = ActorInterval(npc, 'wave')
+ h = Func(npc.loop, 'neutral')
+ i = Func(npc.clearChat)
+ return Sequence(a, b, c, d, e, ee, f, g, h, i)
+
+
+def teleportOut(attack, npc):
+ if npc.style.getGender() == 'm':
+ a = ActorInterval(npc, 'bow')
+ else:
+ a = ActorInterval(npc, 'curtsy')
+ b = Func(npc.setChatAbsolute, TTLocalizer.MovieNPCSOSGoodbye, CFSpeech | CFTimeout)
+ c = npc.getTeleportOutTrack()
+ d = Func(npc.removeActive)
+ e = Func(npc.detachNode)
+ f = Func(npc.delete)
+ return Sequence(a, b, c, d, e, f)
+
+
+def __getPartTrack(particleEffect, startDelay, durationDelay, partExtraArgs):
+ pEffect = partExtraArgs[0]
+ parent = partExtraArgs[1]
+ if len(partExtraArgs) == 3:
+ worldRelative = partExtraArgs[2]
+ else:
+ worldRelative = 1
+ return Sequence(Wait(startDelay), ParticleInterval(pEffect, parent, worldRelative, duration=durationDelay, cleanup=True))
+
+
+def __doSprinkle(attack, recipients, hp = 0):
+ toon = NPCToons.createLocalNPC(attack['npcId'])
+ if toon == None:
+ return
+ targets = attack[recipients]
+ level = 4
+ battle = attack['battle']
+ track = Sequence(teleportIn(attack, toon))
+
+ def face90(target, toon, battle):
+ vec = Point3(target.getPos(battle) - toon.getPos(battle))
+ vec.setZ(0)
+ temp = vec[0]
+ vec.setX(-vec[1])
+ vec.setY(temp)
+ targetPoint = Point3(toon.getPos(battle) + vec)
+ toon.headsUp(battle, targetPoint)
+
+ delay = 2.5
+ effectTrack = Sequence()
+ for target in targets:
+ sprayEffect = BattleParticles.createParticleEffect(file='pixieSpray')
+ dropEffect = BattleParticles.createParticleEffect(file='pixieDrop')
+ explodeEffect = BattleParticles.createParticleEffect(file='pixieExplode')
+ poofEffect = BattleParticles.createParticleEffect(file='pixiePoof')
+ wallEffect = BattleParticles.createParticleEffect(file='pixieWall')
+ mtrack = Parallel(__getPartTrack(sprayEffect, 1.5, 0.5, [sprayEffect, toon, 0]), __getPartTrack(dropEffect, 1.9, 2.0, [dropEffect, target, 0]), __getPartTrack(explodeEffect, 2.7, 1.0, [explodeEffect, toon, 0]), __getPartTrack(poofEffect, 3.4, 1.0, [poofEffect, target, 0]), __getPartTrack(wallEffect, 4.05, 1.2, [wallEffect, toon, 0]), __getSoundTrack(level, 2, duration=3.1, node=toon), Sequence(Func(face90, target, toon, battle), ActorInterval(toon, 'sprinkle-dust')), Sequence(Wait(delay), Func(__healToon, target, hp)))
+ effectTrack.append(mtrack)
+
+ track.append(effectTrack)
+ track.append(Func(toon.setHpr, Vec3(180.0, 0.0, 0.0)))
+ track.append(teleportOut(attack, toon))
+ return track
+
+
+def __doSmooch(attack, hp = 0):
+ toon = NPCToons.createLocalNPC(attack['npcId'])
+ if toon == None:
+ return
+ targets = attack['toons']
+ level = 2
+ battle = attack['battle']
+ track = Sequence(teleportIn(attack, toon))
+ lipstick = globalPropPool.getProp('lipstick')
+ lipstick2 = MovieUtil.copyProp(lipstick)
+ lipsticks = [lipstick, lipstick2]
+ rightHands = toon.getRightHands()
+ dScale = 0.5
+ lipstickTrack = Sequence(Func(MovieUtil.showProps, lipsticks, rightHands, Point3(-0.27, -0.24, -0.95), Point3(-118, -10.6, -25.9)), MovieUtil.getScaleIntervals(lipsticks, dScale, MovieUtil.PNT3_NEARZERO, MovieUtil.PNT3_ONE), Wait(toon.getDuration('smooch') - 2.0 * dScale), MovieUtil.getScaleIntervals(lipsticks, dScale, MovieUtil.PNT3_ONE, MovieUtil.PNT3_NEARZERO))
+ lips = globalPropPool.getProp('lips')
+ dScale = 0.5
+ tLips = 2.5
+ tThrow = 115.0 / toon.getFrameRate('smooch')
+ dThrow = 0.5
+
+ def getLipPos(toon = toon):
+ toon.pose('smooch', 57)
+ toon.update(0)
+ hand = toon.getRightHands()[0]
+ return hand.getPos(render)
+
+ effectTrack = Sequence()
+ for target in targets:
+ lipcopy = MovieUtil.copyProp(lips)
+ lipsTrack = Sequence(Wait(tLips), Func(MovieUtil.showProp, lipcopy, render, getLipPos), Func(lipcopy.setBillboardPointWorld), LerpScaleInterval(lipcopy, dScale, Point3(3, 3, 3), startScale=MovieUtil.PNT3_NEARZERO), Wait(tThrow - tLips - dScale), LerpPosInterval(lipcopy, dThrow, Point3(target.getPos() + Point3(0, 0, target.getHeight()))), Func(MovieUtil.removeProp, lipcopy))
+ delay = tThrow + dThrow
+ mtrack = Parallel(lipstickTrack, lipsTrack, __getSoundTrack(level, 2, node=toon), Sequence(ActorInterval(toon, 'smooch')), Sequence(Wait(delay), ActorInterval(target, 'conked')), Sequence(Wait(delay), Func(__healToon, target, hp)))
+ effectTrack.append(mtrack)
+
+ effectTrack.append(Func(MovieUtil.removeProps, lipsticks))
+ track.append(effectTrack)
+ track.append(teleportOut(attack, toon))
+ track.append(Func(target.clearChat))
+ return track
+
+
+def __doToonsHit(attack, level, hp):
+ track = __doSprinkle(attack, 'toons', hp)
+ pbpText = attack['playByPlayText']
+ pbpTrack = pbpText.getShowInterval(TTLocalizer.MovieNPCSOSToonsHit, track.getDuration())
+ return (track, pbpTrack)
+
+
+def __doCogsMiss(attack, level, hp):
+ track = __doSprinkle(attack, 'suits', hp)
+ pbpText = attack['playByPlayText']
+ pbpTrack = pbpText.getShowInterval(TTLocalizer.MovieNPCSOSCogsMiss, track.getDuration())
+ return (track, pbpTrack)
+
+
+def __doRestockGags(attack, level, hp):
+ track = __doSmooch(attack, hp)
+ pbpText = attack['playByPlayText']
+ if level == ToontownBattleGlobals.HEAL_TRACK:
+ text = TTLocalizer.MovieNPCSOSHeal
+ elif level == ToontownBattleGlobals.TRAP_TRACK:
+ text = TTLocalizer.MovieNPCSOSTrap
+ elif level == ToontownBattleGlobals.LURE_TRACK:
+ text = TTLocalizer.MovieNPCSOSLure
+ elif level == ToontownBattleGlobals.SOUND_TRACK:
+ text = TTLocalizer.MovieNPCSOSSound
+ elif level == ToontownBattleGlobals.THROW_TRACK:
+ text = TTLocalizer.MovieNPCSOSThrow
+ elif level == ToontownBattleGlobals.SQUIRT_TRACK:
+ text = TTLocalizer.MovieNPCSOSSquirt
+ elif level == ToontownBattleGlobals.DROP_TRACK:
+ text = TTLocalizer.MovieNPCSOSDrop
+ elif level == -1:
+ text = TTLocalizer.MovieNPCSOSAll
+ pbpTrack = pbpText.getShowInterval(TTLocalizer.MovieNPCSOSRestockGags % text, track.getDuration())
+ return (track, pbpTrack)
+
+
+def doNPCTeleports(attacks):
+ npcs = []
+ npcDatas = []
+ arrivals = Sequence()
+ departures = Parallel()
+ for attack in attacks:
+ if attack.has_key('npcId'):
+ npcId = attack['npcId']
+ npc = NPCToons.createLocalNPC(npcId)
+ if npc != None:
+ npcs.append(npc)
+ attack['npc'] = npc
+ toon = attack['toon']
+ battle = attack['battle']
+ pos = toon.getPos(battle) + offset
+ hpr = toon.getHpr(battle)
+ npcDatas.append((npc, battle, hpr))
+ arrival = teleportIn(attack, npc, pos=pos)
+ arrivals.append(arrival)
+ departure = teleportOut(attack, npc)
+ departures.append(departure)
+
+ turns = Parallel()
+ unturns = Parallel()
+ hpr = Vec3(180.0, 0, 0)
+ for npc in npcDatas:
+ turns.append(Func(npc[0].setHpr, npc[1], npc[2]))
+ unturns.append(Func(npc[0].setHpr, npc[1], hpr))
+
+ arrivals.append(turns)
+ unturns.append(departures)
+ return (arrivals, unturns, npcs)
diff --git a/toontown/battle/MoviePetSOS.py b/toontown/battle/MoviePetSOS.py
new file mode 100644
index 0000000..c0f3887
--- /dev/null
+++ b/toontown/battle/MoviePetSOS.py
@@ -0,0 +1,140 @@
+from direct.interval.IntervalGlobal import *
+from BattleProps import *
+from BattleSounds import *
+from direct.directnotify import DirectNotifyGlobal
+import MovieCamera
+import random
+import MovieUtil
+import BattleParticles
+import HealJokes
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase import ToontownBattleGlobals
+from toontown.pets import Pet, PetTricks
+notify = DirectNotifyGlobal.directNotify.newCategory('MoviePetSOS')
+soundFiles = ('AA_heal_tickle.mp3', 'AA_heal_telljoke.mp3', 'AA_heal_smooch.mp3', 'AA_heal_happydance.mp3', 'AA_heal_pixiedust.mp3', 'AA_heal_juggle.mp3')
+offset = Point3(0, 4.0, 0)
+
+def doPetSOSs(PetSOSs):
+ if len(PetSOSs) == 0:
+ return (None, None)
+ track = Sequence()
+ textTrack = Sequence()
+ for p in PetSOSs:
+ ival = __doPetSOS(p)
+ if ival:
+ track.append(ival)
+
+ camDuration = track.getDuration()
+ camTrack = MovieCamera.chooseHealShot(PetSOSs, camDuration)
+ return (track, camTrack)
+
+
+def __doPetSOS(sos):
+ return __healJuggle(sos)
+
+
+def __healToon(toon, hp, gender, callerToonId, ineffective = 0):
+ notify.debug('healToon() - toon: %d hp: %d ineffective: %d' % (toon.doId, hp, ineffective))
+ nolaughter = 0
+ if ineffective == 1:
+ if callerToonId == toon.doId:
+ laughter = TTLocalizer.MoviePetSOSTrickFail
+ else:
+ nolaughter = 1
+ else:
+ maxDam = ToontownBattleGlobals.AvPropDamage[0][1][0][1]
+ if callerToonId == toon.doId:
+ if gender == 1:
+ laughter = TTLocalizer.MoviePetSOSTrickSucceedBoy
+ else:
+ laughter = TTLocalizer.MoviePetSOSTrickSucceedGirl
+ elif hp >= maxDam - 1:
+ laughter = random.choice(TTLocalizer.MovieHealLaughterHits2)
+ else:
+ laughter = random.choice(TTLocalizer.MovieHealLaughterHits1)
+ if nolaughter == 0:
+ toon.setChatAbsolute(laughter, CFSpeech | CFTimeout)
+ if hp > 0 and toon.hp != None:
+ toon.toonUp(hp)
+ else:
+ notify.debug('__healToon() - toon: %d hp: %d' % (toon.doId, hp))
+ return
+
+
+def __teleportIn(attack, pet, pos = Point3(0, 0, 0), hpr = Vec3(180.0, 0.0, 0.0)):
+ a = Func(pet.reparentTo, attack['battle'])
+ b = Func(pet.setPos, pos)
+ c = Func(pet.setHpr, hpr)
+ d = Func(pet.pose, 'reappear', 0)
+ e = pet.getTeleportInTrack()
+ g = Func(pet.loop, 'neutral')
+ return Sequence(a, b, c, d, e, g)
+
+
+def __teleportOut(attack, pet):
+ a = pet.getTeleportOutTrack()
+ c = Func(pet.detachNode)
+ d = Func(pet.delete)
+ return Sequence(a, c)
+
+
+def __doPet(attack, level, hp):
+ track = __doSprinkle(attack, 'suits', hp)
+ pbpText = attack['playByPlayText']
+ pbpTrack = pbpText.getShowInterval(TTLocalizer.MovieNPCSOSCogsMiss, track.getDuration())
+ return (track, pbpTrack)
+
+
+def __healJuggle(heal):
+ petProxyId = heal['petId']
+ pet = Pet.Pet()
+ gender = 0
+ if base.cr.doId2do.has_key(petProxyId):
+ petProxy = base.cr.doId2do[petProxyId]
+ if petProxy == None:
+ return
+ pet.setDNA(petProxy.style)
+ pet.setName(petProxy.petName)
+ gender = petProxy.gender
+ else:
+ pet.setDNA([-1,
+ 0,
+ 0,
+ -1,
+ 2,
+ 0,
+ 4,
+ 0,
+ 1])
+ pet.setName('Smiley')
+ targets = heal['target']
+ ineffective = heal['sidestep']
+ level = heal['level']
+ track = Sequence(__teleportIn(heal, pet))
+ if ineffective:
+ trickTrack = Parallel(Wait(1.0), Func(pet.loop, 'neutralSad'), Func(pet.showMood, 'confusion'))
+ else:
+ trickTrack = PetTricks.getTrickIval(pet, level)
+ track.append(trickTrack)
+ delay = 4.0
+ first = 1
+ targetTrack = Sequence()
+ for target in targets:
+ targetToon = target['toon']
+ hp = target['hp']
+ callerToonId = heal['toonId']
+ reactIval = Func(__healToon, targetToon, hp, gender, callerToonId, ineffective)
+ if first == 1:
+ first = 0
+ targetTrack.append(reactIval)
+
+ mtrack = Parallel(Wait(2.0), targetTrack)
+ track.append(mtrack)
+ track.append(Sequence(Func(pet.clearMood)))
+ track.append(__teleportOut(heal, pet))
+ for target in targets:
+ targetToon = target['toon']
+ track.append(Func(targetToon.clearChat))
+
+ track.append(Func(pet.delete))
+ return track
diff --git a/toontown/battle/MovieSOS.py b/toontown/battle/MovieSOS.py
new file mode 100644
index 0000000..8cfa7e5
--- /dev/null
+++ b/toontown/battle/MovieSOS.py
@@ -0,0 +1,42 @@
+from direct.interval.IntervalGlobal import *
+import MovieCamera
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import TTLocalizer
+from pandac.PandaModules import *
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieSOS')
+
+def doSOSs(calls):
+ if len(calls) == 0:
+ return (None, None)
+
+ def callerFunc(toon, handle):
+ toon.setChatAbsolute(TTLocalizer.MovieSOSCallHelp % handle.getName(), CFSpeech | CFTimeout)
+ handle.d_battleSOS(base.localAvatar.doId)
+
+ def calleeFunc(toon, handle):
+ toon.setChatAbsolute(TTLocalizer.MovieSOSCallHelp % handle.getName(), CFSpeech | CFTimeout)
+
+ def observerFunc(toon):
+ toon.setChatAbsolute(TTLocalizer.MovieSOSObserverHelp, CFSpeech | CFTimeout)
+
+ mtrack = Sequence()
+ for c in calls:
+ toon = c['toon']
+ targetType = c['targetType']
+ handle = c['target']
+ mtrack.append(Wait(0.5))
+ if targetType == 'observer':
+ ival = Func(observerFunc, toon)
+ elif targetType == 'caller':
+ ival = Func(callerFunc, toon, handle)
+ elif targetType == 'callee':
+ ival = Func(calleeFunc, toon, handle)
+ else:
+ notify.error('invalid target type: %s' % targetType)
+ mtrack.append(ival)
+ mtrack.append(Wait(2.0))
+ notify.debug('toon: %s calls for help' % toon.getName())
+
+ camDuration = mtrack.getDuration()
+ camTrack = MovieCamera.chooseSOSShot(toon, camDuration)
+ return (mtrack, camTrack)
diff --git a/toontown/battle/MovieSound.py b/toontown/battle/MovieSound.py
new file mode 100644
index 0000000..2a4bcdd
--- /dev/null
+++ b/toontown/battle/MovieSound.py
@@ -0,0 +1,630 @@
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from BattleProps import *
+from BattleSounds import *
+import BattleParticles
+from RewardPanel import *
+import MovieCamera
+from direct.directnotify import DirectNotifyGlobal
+import MovieUtil
+import MovieNPCSOS
+from toontown.toonbase import ToontownBattleGlobals
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieSound')
+soundFiles = ('AA_sound_bikehorn.mp3', 'AA_sound_whistle.mp3', 'AA_sound_bugle.mp3', 'AA_sound_aoogah.mp3', 'AA_sound_elephant.mp3', 'SZ_DD_foghorn.mp3', 'AA_sound_Opera_Singer.mp3')
+appearSoundFiles = ('MG_tag_1.mp3', 'LB_receive_evidence.mp3', 'm_match_trumpet.mp3', 'TL_step_on_rake.mp3', 'toonbldg_grow.mp3', 'mailbox_full_wobble.mp3', 'mailbox_full_wobble.mp3')
+hitSoundFiles = ('AA_sound_Opera_Singer_Cog_Glass.mp3',)
+tSound = 2.45
+tSuitReact = 2.8
+DISTANCE_TO_WALK_BACK = MovieUtil.SUIT_LURE_DISTANCE * 0.75
+TIME_TO_WALK_BACK = 0.5
+if DISTANCE_TO_WALK_BACK == 0:
+ TIME_TO_WALK_BACK = 0
+INSTRUMENT_SCALE_MODIFIER = 0.5
+BEFORE_STARS = 0.5
+AFTER_STARS = 1.75
+
+def doSounds(sounds):
+ if len(sounds) == 0:
+ return (None, None)
+ npcArrivals, npcDepartures, npcs = MovieNPCSOS.doNPCTeleports(sounds)
+ mtrack = Parallel()
+ hitCount = 0
+ prevLevel = 0
+ prevSounds = [[],
+ [],
+ [],
+ [],
+ [],
+ [],
+ []]
+ for sound in sounds:
+ level = sound['level']
+ prevSounds[level].append(sound)
+ for target in sound['target']:
+ if target['hp'] > 0:
+ hitCount += 1
+ break
+
+ delay = 0.0
+ for soundList in prevSounds:
+ if len(soundList) > 0:
+ mtrack.append(__doSoundsLevel(soundList, delay, hitCount, npcs))
+ delay += TOON_SOUND_DELAY
+
+ soundTrack = Sequence(npcArrivals, mtrack, npcDepartures)
+ targets = sounds[0]['target']
+ camDuration = mtrack.getDuration()
+ enterDuration = npcArrivals.getDuration()
+ exitDuration = npcDepartures.getDuration()
+ camTrack = MovieCamera.chooseSoundShot(sounds, targets, camDuration, enterDuration, exitDuration)
+ return (soundTrack, camTrack)
+
+
+def __getSuitTrack(sound, lastSoundThatHit, delay, hitCount, targets, totalDamage, hpbonus, toon, npcs):
+ tracks = Parallel()
+ attacks = 0
+ uberDelay = 0.0
+ isUber = 0
+ if sound['level'] >= ToontownBattleGlobals.UBER_GAG_LEVEL_INDEX:
+ uberDelay = 3.0
+ isUber = 1
+ for target in targets:
+ suit = target['suit']
+ if totalDamage > 0 and sound == lastSoundThatHit:
+ hp = target['hp']
+ died = target['died']
+ battle = sound['battle']
+ kbbonus = target['kbbonus']
+ suitTrack = Sequence()
+ showDamage = Func(suit.showHpText, -totalDamage, openEnded=0)
+ updateHealthBar = Func(suit.updateHealthBar, totalDamage)
+ if isUber:
+ breakEffect = BattleParticles.createParticleEffect(file='soundBreak')
+ breakEffect.setDepthWrite(0)
+ breakEffect.setDepthTest(0)
+ breakEffect.setTwoSided(1)
+ soundEffect = globalBattleSoundCache.getSound(hitSoundFiles[0])
+ suitTrack.append(Wait(delay + tSuitReact))
+ if isUber:
+ delayTime = random.random()
+ suitTrack.append(Wait(delayTime + 2.0))
+ suitTrack.append(Func(setPosFromOther, breakEffect, suit, Point3(0, 0.0, suit.getHeight() - 1.0)))
+ suitTrack.append(Parallel(showDamage, updateHealthBar, SoundInterval(soundEffect, node=suit), __getPartTrack(breakEffect, 0.0, 1.0, [breakEffect, suit, 0], softStop=-0.5)))
+ else:
+ suitTrack.append(showDamage)
+ suitTrack.append(updateHealthBar)
+ if hitCount == 1:
+ suitTrack.append(Parallel(ActorInterval(suit, 'squirt-small-react'), MovieUtil.createSuitStunInterval(suit, 0.5, 1.8)))
+ else:
+ suitTrack.append(ActorInterval(suit, 'squirt-small-react'))
+ if kbbonus == 0:
+ suitTrack.append(__createSuitResetPosTrack(suit, battle))
+ suitTrack.append(Func(battle.unlureSuit, suit))
+ bonusTrack = None
+ if hpbonus > 0:
+ bonusTrack = Sequence(Wait(delay + tSuitReact + delay + 0.75 + uberDelay), Func(suit.showHpText, -hpbonus, 1, openEnded=0))
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ if bonusTrack == None:
+ tracks.append(suitTrack)
+ else:
+ tracks.append(Parallel(suitTrack, bonusTrack))
+ elif totalDamage <= 0:
+ tracks.append(Sequence(Wait(2.9), Func(MovieUtil.indicateMissed, suit, 1.0)))
+
+ return tracks
+
+
+def __doSoundsLevel(sounds, delay, hitCount, npcs):
+ lastSoundThatHit = None
+ totalDamage = 0
+ for sound in sounds:
+ for target in sound['target']:
+ if target['hp'] > 0:
+ lastSoundThatHit = sound
+ totalDamage += target['hp']
+ break
+
+ mainTrack = Sequence()
+ tracks = Parallel()
+ deathTracks = Parallel()
+ for sound in sounds:
+ toon = sound['toon']
+ if sound.has_key('npc'):
+ toon = sound['npc']
+ level = sound['level']
+ targets = sound['target']
+ hpbonus = sound['hpbonus']
+ attackMTrack = soundfn_array[sound['level']](sound, delay, toon, targets, level)
+ tracks.append(Sequence(Wait(delay), attackMTrack))
+ tracks.append(__getSuitTrack(sound, lastSoundThatHit, delay, hitCount, targets, totalDamage, hpbonus, toon, npcs))
+ for target in targets:
+ battle = sound['battle']
+ suit = target['suit']
+ died = target['died']
+ revived = target['revived']
+ if revived:
+ deathTracks.append(MovieUtil.createSuitReviveTrack(suit, toon, battle, npcs))
+ elif died:
+ deathTracks.append(MovieUtil.createSuitDeathTrack(suit, toon, battle, npcs))
+
+ mainTrack.append(tracks)
+ mainTrack.append(deathTracks)
+ return mainTrack
+
+
+def __createSuitResetPosTrack(suit, battle):
+ resetPos, resetHpr = battle.getActorPosHpr(suit)
+ moveDist = Vec3(suit.getPos(battle) - resetPos).length()
+ moveDuration = 0.5
+ walkTrack = Sequence(Func(suit.setHpr, battle, resetHpr), ActorInterval(suit, 'walk', startTime=1, duration=moveDuration, endTime=0.0001), Func(suit.loop, 'neutral'))
+ moveTrack = LerpPosInterval(suit, moveDuration, resetPos, other=battle)
+ return Parallel(walkTrack, moveTrack)
+
+
+def createSuitResetPosTrack(suit, battle):
+ return __createSuitResetPosTrack(suit, battle)
+
+
+def __createToonInterval(sound, delay, toon, operaInstrument = None):
+ isNPC = 0
+ if sound.get('npc'):
+ isNPC = 1
+ battle = sound['battle']
+ hasLuredSuits = __hasLuredSuits(sound)
+ if not isNPC:
+ oldPos, oldHpr = battle.getActorPosHpr(toon)
+ newPos = Point3(oldPos)
+ newPos.setY(newPos.getY() - DISTANCE_TO_WALK_BACK)
+ retval = Sequence(Wait(delay))
+ if DISTANCE_TO_WALK_BACK and hasLuredSuits and not isNPC:
+ retval.append(Parallel(ActorInterval(toon, 'walk', startTime=1, duration=TIME_TO_WALK_BACK, endTime=0.0001), LerpPosInterval(toon, TIME_TO_WALK_BACK, newPos, other=battle)))
+ if operaInstrument:
+ sprayEffect = BattleParticles.createParticleEffect(file='soundWave')
+ sprayEffect.setDepthWrite(0)
+ sprayEffect.setDepthTest(0)
+ sprayEffect.setTwoSided(1)
+ I1 = 2.8
+ retval.append(ActorInterval(toon, 'sound', playRate=1.0, startTime=0.0, endTime=I1))
+ retval.append(Func(setPosFromOther, sprayEffect, operaInstrument, Point3(0, 1.6, -0.18)))
+ retval.append(__getPartTrack(sprayEffect, 0.0, 6.0, [sprayEffect, toon, 0], softStop=-3.5))
+ retval.append(ActorInterval(toon, 'sound', playRate=1.0, startTime=I1))
+ else:
+ retval.append(ActorInterval(toon, 'sound'))
+ if DISTANCE_TO_WALK_BACK and hasLuredSuits and not isNPC:
+ retval.append(Parallel(ActorInterval(toon, 'walk', startTime=0.0001, duration=TIME_TO_WALK_BACK, endTime=1), LerpPosInterval(toon, TIME_TO_WALK_BACK, oldPos, other=battle)))
+ retval.append(Func(toon.loop, 'neutral'))
+ return retval
+
+
+def __hasLuredSuits(sound):
+ retval = False
+ targets = sound['target']
+ for target in targets:
+ kbbonus = target['kbbonus']
+ if kbbonus == 0:
+ retval = True
+ break
+
+ return retval
+
+
+def __doBikehorn(sound, delay, toon, targets, level):
+ tracks = Parallel()
+ instrMin = Vec3(0.001, 0.001, 0.001)
+ instrMax = Vec3(0.65, 0.65, 0.65)
+ instrMax *= INSTRUMENT_SCALE_MODIFIER
+ instrStretch = Vec3(0.6, 1.1, 0.6)
+ instrStretch *= INSTRUMENT_SCALE_MODIFIER
+ megaphone = globalPropPool.getProp('megaphone')
+ megaphone2 = MovieUtil.copyProp(megaphone)
+ megaphones = [megaphone, megaphone2]
+ instrument = globalPropPool.getProp('bikehorn')
+ instrument2 = MovieUtil.copyProp(instrument)
+ instruments = [instrument, instrument2]
+
+ def setInstrumentStats(instrument = instrument, instrument2 = instrument2):
+ instrument.setPos(-1.1, -1.4, 0.1)
+ instrument.setHpr(145, 0, 0)
+ instrument.setScale(instrMin)
+ instrument2.setPos(-1.1, -1.4, 0.1)
+ instrument2.setHpr(145, 0, 0)
+ instrument2.setScale(instrMin)
+
+ hands = toon.getRightHands()
+ megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats))
+ megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments))
+ instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level])
+ grow = getScaleIntervals(instruments, duration=0.2, startScale=instrMin, endScale=instrMax)
+ instrumentAppear = Parallel(grow, Sequence(Wait(0.15), SoundInterval(instrumentAppearSfx, node=toon)))
+ stretchInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrMax, endScale=instrStretch, blendType='easeOut')
+ backInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrStretch, endScale=instrMax, blendType='easeIn')
+ stretchMega = getScaleBlendIntervals(megaphones, duration=0.2, startScale=megaphone.getScale(), endScale=0.9, blendType='easeOut')
+ backMega = getScaleBlendIntervals(megaphones, duration=0.2, startScale=0.9, endScale=megaphone.getScale(), blendType='easeIn')
+ attackTrack = Parallel(Sequence(stretchInstr, backInstr), Sequence(stretchMega, backMega))
+ hasLuredSuits = __hasLuredSuits(sound)
+ delayTime = delay
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(3.0), megaphoneHide)
+ tracks.append(megaphoneTrack)
+ toonTrack = __createToonInterval(sound, delay, toon)
+ tracks.append(toonTrack)
+ soundEffect = globalBattleSoundCache.getSound(soundFiles[level])
+ instrumentshrink = getScaleIntervals(instruments, duration=0.1, startScale=instrMax, endScale=instrMin)
+ if soundEffect:
+ delayTime = delay + tSound
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ soundTrack = Sequence(Wait(delayTime), Parallel(attackTrack, SoundInterval(soundEffect, node=toon)), Wait(0.2), instrumentshrink)
+ tracks.append(soundTrack)
+ return tracks
+
+
+def __doWhistle(sound, delay, toon, targets, level):
+ tracks = Parallel()
+ instrMin = Vec3(0.001, 0.001, 0.001)
+ instrMax = Vec3(0.2, 0.2, 0.2)
+ instrMax *= INSTRUMENT_SCALE_MODIFIER
+ instrStretch = Vec3(0.25, 0.25, 0.25)
+ instrStretch *= INSTRUMENT_SCALE_MODIFIER
+ megaphone = globalPropPool.getProp('megaphone')
+ megaphone2 = MovieUtil.copyProp(megaphone)
+ megaphones = [megaphone, megaphone2]
+ instrument = globalPropPool.getProp('whistle')
+ instrument2 = MovieUtil.copyProp(instrument)
+ instruments = [instrument, instrument2]
+
+ def setInstrumentStats(instrument = instrument, instrument2 = instrument2):
+ instrument.setPos(-1.2, -1.3, 0.1)
+ instrument.setHpr(145, 0, 85)
+ instrument.setScale(instrMin)
+ instrument2.setPos(-1.2, -1.3, 0.1)
+ instrument2.setHpr(145, 0, 85)
+ instrument2.setScale(instrMin)
+
+ hands = toon.getRightHands()
+ megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats))
+ megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments))
+ instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level])
+ grow = getScaleIntervals(instruments, duration=0.2, startScale=instrMin, endScale=instrMax)
+ instrumentAppear = Parallel(grow, Sequence(Wait(0.05), SoundInterval(instrumentAppearSfx, node=toon)))
+ stretchInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrMax, endScale=instrStretch, blendType='easeOut')
+ backInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrStretch, endScale=instrMax, blendType='easeIn')
+ attackTrack = Sequence(stretchInstr, backInstr)
+ hasLuredSuits = __hasLuredSuits(sound)
+ delayTime = delay
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(3.0), megaphoneHide)
+ tracks.append(megaphoneTrack)
+ toonTrack = __createToonInterval(sound, delay, toon)
+ tracks.append(toonTrack)
+ soundEffect = globalBattleSoundCache.getSound(soundFiles[level])
+ instrumentshrink = getScaleIntervals(instruments, duration=0.1, startScale=instrMax, endScale=instrMin)
+ if soundEffect:
+ delayTime = delay + tSound
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ soundTrack = Sequence(Wait(delayTime), Parallel(attackTrack, SoundInterval(soundEffect, node=toon)), Wait(0.2), instrumentshrink)
+ tracks.append(soundTrack)
+ return tracks
+
+
+def __doBugle(sound, delay, toon, targets, level):
+ tracks = Parallel()
+ instrMin = Vec3(0.001, 0.001, 0.001)
+ instrMax = Vec3(0.4, 0.4, 0.4)
+ instrMax *= INSTRUMENT_SCALE_MODIFIER
+ instrStretch = Vec3(0.5, 0.5, 0.5)
+ instrStretch *= INSTRUMENT_SCALE_MODIFIER
+ megaphone = globalPropPool.getProp('megaphone')
+ megaphone2 = MovieUtil.copyProp(megaphone)
+ megaphones = [megaphone, megaphone2]
+ instrument = globalPropPool.getProp('bugle')
+ instrument2 = MovieUtil.copyProp(instrument)
+ instruments = [instrument, instrument2]
+
+ def setInstrumentStats(instrument = instrument, instrument2 = instrument2):
+ instrument.setPos(-1.3, -1.4, 0.1)
+ instrument.setHpr(145, 0, 85)
+ instrument.setScale(instrMin)
+ instrument2.setPos(-1.3, -1.4, 0.1)
+ instrument2.setHpr(145, 0, 85)
+ instrument2.setScale(instrMin)
+
+ def longshake(models, num):
+ inShake = getScaleBlendIntervals(models, duration=0.2, startScale=instrMax, endScale=instrStretch, blendType='easeInOut')
+ outShake = getScaleBlendIntervals(models, duration=0.2, startScale=instrStretch, endScale=instrMax, blendType='easeInOut')
+ i = 1
+ seq = Sequence()
+ while i < num:
+ if i % 2 == 0:
+ seq.append(inShake)
+ else:
+ seq.append(outShake)
+ i += 1
+
+ seq.start()
+
+ hands = toon.getRightHands()
+ megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats))
+ megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments))
+ instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level])
+ grow = getScaleBlendIntervals(instruments, duration=1, startScale=instrMin, endScale=instrMax, blendType='easeInOut')
+ instrumentshrink = getScaleIntervals(instruments, duration=0.1, startScale=instrMax, endScale=instrMin)
+ instrumentAppear = Sequence(grow, Wait(0), Func(longshake, instruments, 5))
+ hasLuredSuits = __hasLuredSuits(sound)
+ delayTime = delay
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ soundEffect = globalBattleSoundCache.getSound(soundFiles[level])
+ megaphoneTrack = Parallel(Sequence(Wait(delay + 1.7), SoundInterval(soundEffect, node=toon)), Sequence(Wait(delayTime), megaphoneShow, Wait(1.7), instrumentAppear, Wait(1), instrumentshrink, Wait(1.5), megaphoneHide))
+ tracks.append(megaphoneTrack)
+ toonTrack = __createToonInterval(sound, delay, toon)
+ tracks.append(toonTrack)
+ if soundEffect:
+ delayTime = delay + tSound
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ soundTrack = Wait(delayTime)
+ tracks.append(soundTrack)
+ return tracks
+
+
+def __doAoogah(sound, delay, toon, targets, level):
+ tracks = Parallel()
+ instrMin = Vec3(0.001, 0.001, 0.001)
+ instrMax = Vec3(0.5, 0.5, 0.5)
+ instrMax *= INSTRUMENT_SCALE_MODIFIER
+ instrStretch = Vec3(1.1, 0.9, 0.4)
+ instrStretch *= INSTRUMENT_SCALE_MODIFIER
+ megaphone = globalPropPool.getProp('megaphone')
+ megaphone2 = MovieUtil.copyProp(megaphone)
+ megaphones = [megaphone, megaphone2]
+ instrument = globalPropPool.getProp('aoogah')
+ instrument2 = MovieUtil.copyProp(instrument)
+ instruments = [instrument, instrument2]
+
+ def setInstrumentStats(instrument = instrument, instrument2 = instrument2):
+ instrument.setPos(-1.0, -1.5, 0.2)
+ instrument.setHpr(145, 0, 85)
+ instrument.setScale(instrMin)
+ instrument2.setPos(-1.0, -1.5, 0.2)
+ instrument2.setHpr(145, 0, 85)
+ instrument2.setScale(instrMin)
+
+ hands = toon.getRightHands()
+ megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats))
+ megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments))
+ instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level])
+ grow = getScaleIntervals(instruments, duration=0.2, startScale=instrMin, endScale=instrMax)
+ instrumentAppear = Parallel(grow, Sequence(Wait(0.05), SoundInterval(instrumentAppearSfx, node=toon)))
+ stretchInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrMax, endScale=instrStretch, blendType='easeOut')
+ backInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrStretch, endScale=instrMax, blendType='easeInOut')
+ attackTrack = Sequence(stretchInstr, Wait(1), backInstr)
+ hasLuredSuits = __hasLuredSuits(sound)
+ delayTime = delay
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(3.0), megaphoneHide)
+ tracks.append(megaphoneTrack)
+ toonTrack = __createToonInterval(sound, delay, toon)
+ tracks.append(toonTrack)
+ soundEffect = globalBattleSoundCache.getSound(soundFiles[level])
+ instrumentshrink = getScaleIntervals(instruments, duration=0.1, startScale=instrMax, endScale=instrMin)
+ if soundEffect:
+ delayTime = delay + tSound
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ soundTrack = Sequence(Wait(delayTime), Parallel(attackTrack, SoundInterval(soundEffect, node=toon), Sequence(Wait(1.5), instrumentshrink)))
+ tracks.append(soundTrack)
+ return tracks
+
+
+def __doElephant(sound, delay, toon, targets, level):
+ tracks = Parallel()
+ instrMin = Vec3(0.001, 0.001, 0.001)
+ instrMax1 = Vec3(0.3, 0.4, 0.2)
+ instrMax1 *= INSTRUMENT_SCALE_MODIFIER
+ instrMax2 = Vec3(0.3, 0.3, 0.3)
+ instrMax2 *= INSTRUMENT_SCALE_MODIFIER
+ instrStretch1 = Vec3(0.3, 0.5, 0.25)
+ instrStretch1 *= INSTRUMENT_SCALE_MODIFIER
+ instrStretch2 = Vec3(0.3, 0.7, 0.3)
+ instrStretch2 *= INSTRUMENT_SCALE_MODIFIER
+ megaphone = globalPropPool.getProp('megaphone')
+ megaphone2 = MovieUtil.copyProp(megaphone)
+ megaphones = [megaphone, megaphone2]
+ instrument = globalPropPool.getProp('elephant')
+ instrument2 = MovieUtil.copyProp(instrument)
+ instruments = [instrument, instrument2]
+
+ def setInstrumentStats(instrument = instrument, instrument2 = instrument2):
+ instrument.setPos(-.6, -.9, 0.15)
+ instrument.setHpr(145, 0, 85)
+ instrument.setScale(instrMin)
+ instrument2.setPos(-.6, -.9, 0.15)
+ instrument2.setHpr(145, 0, 85)
+ instrument2.setScale(instrMin)
+
+ hands = toon.getRightHands()
+ megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats))
+ megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments))
+ instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level])
+ grow1 = getScaleIntervals(instruments, duration=0.3, startScale=instrMin, endScale=instrMax1)
+ grow2 = getScaleIntervals(instruments, duration=0.3, startScale=instrMax1, endScale=instrMax2)
+ instrumentAppear = Parallel(Sequence(grow1, grow2), Sequence(Wait(0.05), SoundInterval(instrumentAppearSfx, node=toon)))
+ stretchInstr1 = getScaleBlendIntervals(instruments, duration=0.1, startScale=instrMax2, endScale=instrStretch1, blendType='easeOut')
+ stretchInstr2 = getScaleBlendIntervals(instruments, duration=0.1, startScale=instrStretch1, endScale=instrStretch2, blendType='easeOut')
+ stretchInstr = Sequence(stretchInstr1, stretchInstr2)
+ backInstr = getScaleBlendIntervals(instruments, duration=0.1, startScale=instrStretch2, endScale=instrMax2, blendType='easeOut')
+ attackTrack = Sequence(stretchInstr, Wait(1), backInstr)
+ hasLuredSuits = __hasLuredSuits(sound)
+ delayTime = delay
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(3.0), megaphoneHide)
+ tracks.append(megaphoneTrack)
+ toonTrack = __createToonInterval(sound, delay, toon)
+ tracks.append(toonTrack)
+ soundEffect = globalBattleSoundCache.getSound(soundFiles[level])
+ instrumentshrink = getScaleIntervals(instruments, duration=0.1, startScale=instrMax2, endScale=instrMin)
+ if soundEffect:
+ delayTime = delay + tSound
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ soundTrack = Sequence(Wait(delayTime), Parallel(attackTrack, SoundInterval(soundEffect, node=toon), Sequence(Wait(1.5), instrumentshrink)))
+ tracks.append(soundTrack)
+ return tracks
+
+
+def __doFoghorn(sound, delay, toon, targets, level):
+ tracks = Parallel()
+ instrMin = Vec3(0.001, 0.001, 0.001)
+ instrMax1 = Vec3(0.1, 0.1, 0.1)
+ instrMax1 *= INSTRUMENT_SCALE_MODIFIER
+ instrMax2 = Vec3(0.3, 0.3, 0.3)
+ instrMax2 *= INSTRUMENT_SCALE_MODIFIER
+ instrStretch = Vec3(0.4, 0.4, 0.4)
+ instrStretch *= INSTRUMENT_SCALE_MODIFIER
+ megaphone = globalPropPool.getProp('megaphone')
+ megaphone2 = MovieUtil.copyProp(megaphone)
+ megaphones = [megaphone, megaphone2]
+ instrument = globalPropPool.getProp('fog_horn')
+ instrument2 = MovieUtil.copyProp(instrument)
+ instruments = [instrument, instrument2]
+
+ def setInstrumentStats(instrument = instrument, instrument2 = instrument2):
+ instrument.setPos(-.8, -.9, 0.2)
+ instrument.setHpr(145, 0, 0)
+ instrument.setScale(instrMin)
+ instrument2.setPos(-.8, -.9, 0.2)
+ instrument2.setHpr(145, 0, 0)
+ instrument2.setScale(instrMin)
+
+ hands = toon.getRightHands()
+ megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats))
+ megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments))
+ instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level])
+ grow1 = getScaleIntervals(instruments, duration=1, startScale=instrMin, endScale=instrMax1)
+ grow2 = getScaleIntervals(instruments, duration=0.1, startScale=instrMax1, endScale=instrMax2)
+ instrumentAppear = Parallel(Sequence(grow1, grow2), Sequence(Wait(0.05), SoundInterval(instrumentAppearSfx, node=toon)))
+ stretchInstr = getScaleBlendIntervals(instruments, duration=0.3, startScale=instrMax2, endScale=instrStretch, blendType='easeOut')
+ backInstr = getScaleBlendIntervals(instruments, duration=1.0, startScale=instrStretch, endScale=instrMin, blendType='easeIn')
+ spinInstr1 = LerpHprInterval(instrument, duration=1.5, startHpr=Vec3(145, 0, 0), hpr=Vec3(145, 0, 90), blendType='easeInOut')
+ spinInstr2 = LerpHprInterval(instrument2, duration=1.5, startHpr=Vec3(145, 0, 0), hpr=Vec3(145, 0, 90), blendType='easeInOut')
+ spinInstr = Parallel(spinInstr1, spinInstr2)
+ attackTrack = Parallel(Sequence(Wait(0.2), spinInstr), Sequence(stretchInstr, Wait(0.5), backInstr))
+ hasLuredSuits = __hasLuredSuits(sound)
+ delayTime = delay
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(3.0), megaphoneHide)
+ tracks.append(megaphoneTrack)
+ toonTrack = __createToonInterval(sound, delay, toon)
+ tracks.append(toonTrack)
+ soundEffect = globalBattleSoundCache.getSound(soundFiles[level])
+ if soundEffect:
+ delayTime = delay + tSound
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ soundTrack = Sequence(Wait(delayTime), Parallel(attackTrack, SoundInterval(soundEffect, node=toon)))
+ tracks.append(soundTrack)
+ return tracks
+
+
+def __doOpera(sound, delay, toon, targets, level):
+ tracks = Parallel()
+ delay = delay
+ instrMin = Vec3(0.001, 0.001, 0.001)
+ instrMax1 = Vec3(1.7, 1.7, 1.7)
+ instrMax1 *= INSTRUMENT_SCALE_MODIFIER
+ instrMax2 = Vec3(2.2, 2.2, 2.2)
+ instrMax2 *= INSTRUMENT_SCALE_MODIFIER
+ instrStretch = Vec3(0.4, 0.4, 0.4)
+ instrStretch *= INSTRUMENT_SCALE_MODIFIER
+ megaphone = globalPropPool.getProp('megaphone')
+ megaphone2 = MovieUtil.copyProp(megaphone)
+ megaphones = [megaphone, megaphone2]
+ instrument = globalPropPool.getProp('singing')
+ instrument2 = MovieUtil.copyProp(instrument)
+ instruments = [instrument, instrument2]
+ head = instrument2.find('**/opera_singer')
+ head.setPos(0, 0, 0)
+
+ def setInstrumentStats(instrument = instrument, instrument2 = instrument2):
+ notify.debug('setInstrumentStats')
+ newPos = Vec3(-0.8, -0.9, 0.2)
+ newPos *= 1.3
+ instrument.setPos(newPos[0], newPos[1], newPos[2])
+ instrument.setHpr(145, 0, 90)
+ instrument.setScale(instrMin)
+ instrument2.setPos(newPos[0], newPos[1], newPos[2])
+ instrument2.setHpr(145, 0, 90)
+ instrument2.setScale(instrMin)
+
+ hands = toon.getRightHands()
+ megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats))
+ megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments))
+ instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level])
+ grow1 = getScaleBlendIntervals(instruments, duration=1, startScale=instrMin, endScale=instrMax1, blendType='easeOut')
+ grow2 = getScaleBlendIntervals(instruments, duration=1.1, startScale=instrMax1, endScale=instrMax2, blendType='easeIn')
+ shrink2 = getScaleIntervals(instruments, duration=0.1, startScale=instrMax2, endScale=instrMin)
+ instrumentAppear = Parallel(Sequence(grow1, grow2, Wait(6.0), shrink2), Sequence(Wait(0.0), SoundInterval(instrumentAppearSfx, node=toon)))
+ hasLuredSuits = __hasLuredSuits(sound)
+ delayTime = delay
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(2.0), megaphoneHide)
+ tracks.append(megaphoneTrack)
+ toonTrack = __createToonInterval(sound, delay, toon, operaInstrument=instrument)
+ tracks.append(toonTrack)
+ soundEffect = globalBattleSoundCache.getSound(soundFiles[level])
+ if soundEffect:
+ delayTime = delay + tSound - 0.3
+ if hasLuredSuits:
+ delayTime += TIME_TO_WALK_BACK
+ soundTrack = Sequence(Wait(delayTime), SoundInterval(soundEffect, node=toon))
+ tracks.append(Sequence(Wait(0)))
+ tracks.append(soundTrack)
+ return tracks
+
+
+def setPosFromOther(dest, source, offset = Point3(0, 0, 0)):
+ pos = render.getRelativePoint(source, offset)
+ dest.setPos(pos)
+ dest.reparentTo(render)
+
+
+def getScaleIntervals(props, duration, startScale, endScale):
+ tracks = Parallel()
+ for prop in props:
+ tracks.append(LerpScaleInterval(prop, duration, endScale, startScale=startScale))
+
+ return tracks
+
+
+def getScaleBlendIntervals(props, duration, startScale, endScale, blendType):
+ tracks = Parallel()
+ for prop in props:
+ tracks.append(LerpScaleInterval(prop, duration, endScale, startScale=startScale, blendType=blendType))
+
+ return tracks
+
+
+soundfn_array = (__doBikehorn,
+ __doWhistle,
+ __doBugle,
+ __doAoogah,
+ __doElephant,
+ __doFoghorn,
+ __doOpera)
+
+def __getPartTrack(particleEffect, startDelay, durationDelay, partExtraArgs, softStop = 0):
+ pEffect = partExtraArgs[0]
+ parent = partExtraArgs[1]
+ if len(partExtraArgs) == 3:
+ worldRelative = partExtraArgs[2]
+ else:
+ worldRelative = 1
+ return Sequence(Wait(startDelay), ParticleInterval(pEffect, parent, worldRelative, duration=durationDelay, cleanup=True, softStopT=softStop))
diff --git a/toontown/battle/MovieSquirt.py b/toontown/battle/MovieSquirt.py
new file mode 100644
index 0000000..8f1151e
--- /dev/null
+++ b/toontown/battle/MovieSquirt.py
@@ -0,0 +1,746 @@
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from BattleProps import *
+from BattleSounds import *
+from toontown.toon.ToonDNA import *
+from toontown.suit.SuitDNA import *
+import MovieUtil
+import MovieCamera
+from direct.directnotify import DirectNotifyGlobal
+import BattleParticles
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import ToontownBattleGlobals
+import random
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieSquirt')
+hitSoundFiles = ('AA_squirt_flowersquirt.mp3', 'AA_squirt_glasswater.mp3', 'AA_squirt_neonwatergun.mp3', 'AA_squirt_seltzer.mp3', 'firehose_spray.mp3', 'AA_throw_stormcloud.mp3', 'AA_squirt_Geyser.mp3')
+missSoundFiles = ('AA_squirt_flowersquirt_miss.mp3', 'AA_squirt_glasswater_miss.mp3', 'AA_squirt_neonwatergun_miss.mp3', 'AA_squirt_seltzer_miss.mp3', 'firehose_spray.mp3', 'AA_throw_stormcloud_miss.mp3', 'AA_squirt_Geyser.mp3')
+sprayScales = [0.2,
+ 0.3,
+ 0.1,
+ 0.6,
+ 0.8,
+ 1.0,
+ 2.0]
+WaterSprayColor = Point4(0.75, 0.75, 1.0, 0.8)
+
+def doSquirts(squirts):
+ if len(squirts) == 0:
+ return (None, None)
+
+ suitSquirtsDict = {}
+ doneUber = 0
+ skip = 0
+ for squirt in squirts:
+ skip = 0
+ if skip:
+ pass
+ elif type(squirt['target']) == type([]):
+ if 1:
+ target = squirt['target'][0]
+ suitId = target['suit'].doId
+ if suitSquirtsDict.has_key(suitId):
+ suitSquirtsDict[suitId].append(squirt)
+ else:
+ suitSquirtsDict[suitId] = [squirt]
+ else:
+ suitId = squirt['target']['suit'].doId
+ if suitSquirtsDict.has_key(suitId):
+ suitSquirtsDict[suitId].append(squirt)
+ else:
+ suitSquirtsDict[suitId] = [squirt]
+
+ suitSquirts = suitSquirtsDict.values()
+
+ def compFunc(a, b):
+ if len(a) > len(b):
+ return 1
+ elif len(a) < len(b):
+ return -1
+ return 0
+ suitSquirts.sort(compFunc)
+
+ delay = 0.0
+
+ mtrack = Parallel()
+ for st in suitSquirts:
+ if len(st) > 0:
+ ival = __doSuitSquirts(st)
+ if ival:
+ mtrack.append(Sequence(Wait(delay), ival))
+ delay = delay + TOON_SQUIRT_SUIT_DELAY
+
+ camDuration = mtrack.getDuration()
+ camTrack = MovieCamera.chooseSquirtShot(squirts, suitSquirtsDict, camDuration)
+ return (mtrack, camTrack)
+
+
+def __doSuitSquirts(squirts):
+ uberClone = 0
+ toonTracks = Parallel()
+ delay = 0.0
+ if type(squirts[0]['target']) == type([]):
+ for target in squirts[0]['target']:
+ if len(squirts) == 1 and target['hp'] > 0:
+ fShowStun = 1
+ else:
+ fShowStun = 0
+
+ elif len(squirts) == 1 and squirts[0]['target']['hp'] > 0:
+ fShowStun = 1
+ else:
+ fShowStun = 0
+ for s in squirts:
+ tracks = __doSquirt(s, delay, fShowStun, uberClone)
+ if s['level'] >= ToontownBattleGlobals.UBER_GAG_LEVEL_INDEX:
+ uberClone = 1
+ if tracks:
+ for track in tracks:
+ toonTracks.append(track)
+
+ delay = delay + TOON_SQUIRT_DELAY
+
+ return toonTracks
+
+
+def __doSquirt(squirt, delay, fShowStun, uberClone = 0):
+ squirtSequence = Sequence(Wait(delay))
+ if type(squirt['target']) == type([]):
+ for target in squirt['target']:
+ notify.debug('toon: %s squirts prop: %d at suit: %d for hp: %d' % (squirt['toon'].getName(),
+ squirt['level'],
+ target['suit'].doId,
+ target['hp']))
+
+ else:
+ notify.debug('toon: %s squirts prop: %d at suit: %d for hp: %d' % (squirt['toon'].getName(),
+ squirt['level'],
+ squirt['target']['suit'].doId,
+ squirt['target']['hp']))
+ if uberClone:
+ ival = squirtfn_array[squirt['level']](squirt, delay, fShowStun, uberClone)
+ if ival:
+ squirtSequence.append(ival)
+ else:
+ ival = squirtfn_array[squirt['level']](squirt, delay, fShowStun)
+ if ival:
+ squirtSequence.append(ival)
+ return [squirtSequence]
+
+
+def __suitTargetPoint(suit):
+ pnt = suit.getPos(render)
+ pnt.setZ(pnt[2] + suit.getHeight() * 0.66)
+ return Point3(pnt)
+
+
+def __getSplashTrack(point, scale, delay, battle, splashHold = 0.01):
+
+ def prepSplash(splash, point):
+ if callable(point):
+ point = point()
+ splash.reparentTo(render)
+ splash.setPos(point)
+ scale = splash.getScale()
+ splash.setBillboardPointWorld()
+ splash.setScale(scale)
+
+ splash = globalPropPool.getProp('splash-from-splat')
+ splash.setScale(scale)
+ return Sequence(Func(battle.movie.needRestoreRenderProp, splash), Wait(delay), Func(prepSplash, splash, point), ActorInterval(splash, 'splash-from-splat'), Wait(splashHold), Func(MovieUtil.removeProp, splash), Func(battle.movie.clearRenderProp, splash))
+
+
+def __getSuitTrack(suit, tContact, tDodge, hp, hpbonus, kbbonus, anim, died, leftSuits, rightSuits, battle, toon, fShowStun, beforeStun = 0.5, afterStun = 1.8, geyser = 0, uberRepeat = 0, revived = 0):
+ if hp > 0:
+ suitTrack = Sequence()
+ sival = ActorInterval(suit, anim)
+ sival = []
+ if kbbonus > 0 and not geyser:
+ suitPos, suitHpr = battle.getActorPosHpr(suit)
+ suitType = getSuitBodyType(suit.getStyleName())
+ animTrack = Sequence()
+ animTrack.append(ActorInterval(suit, anim, duration=0.2))
+ if suitType == 'a':
+ animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.43))
+ elif suitType == 'b':
+ animTrack.append(ActorInterval(suit, 'slip-forward', startTime=1.94))
+ elif suitType == 'c':
+ animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.58))
+ animTrack.append(Func(battle.unlureSuit, suit))
+ moveTrack = Sequence(Wait(0.2), LerpPosInterval(suit, 0.6, pos=suitPos, other=battle))
+ sival = Parallel(animTrack, moveTrack)
+ elif geyser:
+ suitStartPos = suit.getPos()
+ suitFloat = Point3(0, 0, 14)
+ suitEndPos = Point3(suitStartPos[0] + suitFloat[0], suitStartPos[1] + suitFloat[1], suitStartPos[2] + suitFloat[2])
+ suitType = getSuitBodyType(suit.getStyleName())
+ if suitType == 'a':
+ startFlailFrame = 16
+ endFlailFrame = 16
+ elif suitType == 'b':
+ startFlailFrame = 15
+ endFlailFrame = 15
+ else:
+ startFlailFrame = 15
+ endFlailFrame = 15
+ sival = Sequence(ActorInterval(suit, 'slip-backward', playRate=0.5, startFrame=0, endFrame=startFlailFrame - 1), Func(suit.pingpong, 'slip-backward', fromFrame=startFlailFrame, toFrame=endFlailFrame), Wait(0.5), ActorInterval(suit, 'slip-backward', playRate=1.0, startFrame=endFlailFrame))
+ sUp = LerpPosInterval(suit, 1.1, suitEndPos, startPos=suitStartPos, fluid=1)
+ sDown = LerpPosInterval(suit, 0.6, suitStartPos, startPos=suitEndPos, fluid=1)
+ elif fShowStun == 1:
+ sival = Parallel(ActorInterval(suit, anim), MovieUtil.createSuitStunInterval(suit, beforeStun, afterStun))
+ else:
+ sival = ActorInterval(suit, anim)
+ showDamage = Func(suit.showHpText, -hp, openEnded=0, attackTrack=SQUIRT_TRACK)
+ updateHealthBar = Func(suit.updateHealthBar, hp)
+ suitTrack.append(Wait(tContact))
+ suitTrack.append(showDamage)
+ suitTrack.append(updateHealthBar)
+ if not geyser:
+ suitTrack.append(sival)
+ elif not uberRepeat:
+ geyserMotion = Sequence(sUp, Wait(0.0), sDown)
+ suitLaunch = Parallel(sival, geyserMotion)
+ suitTrack.append(suitLaunch)
+ else:
+ suitTrack.append(Wait(5.5))
+ bonusTrack = Sequence(Wait(tContact))
+ if kbbonus > 0:
+ bonusTrack.append(Wait(0.75))
+ bonusTrack.append(Func(suit.showHpText, -kbbonus, 2, openEnded=0, attackTrack=SQUIRT_TRACK))
+ if hpbonus > 0:
+ bonusTrack.append(Wait(0.75))
+ bonusTrack.append(Func(suit.showHpText, -hpbonus, 1, openEnded=0, attackTrack=SQUIRT_TRACK))
+ if died != 0:
+ suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle))
+ else:
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ if revived != 0:
+ suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle))
+ return Parallel(suitTrack, bonusTrack)
+ else:
+ return MovieUtil.createSuitDodgeMultitrack(tDodge, suit, leftSuits, rightSuits)
+
+
+def say(statement):
+ print statement
+
+
+def __getSoundTrack(level, hitSuit, delay, node = None):
+ if hitSuit:
+ soundEffect = globalBattleSoundCache.getSound(hitSoundFiles[level])
+ else:
+ soundEffect = globalBattleSoundCache.getSound(missSoundFiles[level])
+ soundTrack = Sequence()
+ if soundEffect:
+ soundTrack.append(Wait(delay))
+ soundTrack.append(SoundInterval(soundEffect, node=node))
+ return soundTrack
+
+
+def __doFlower(squirt, delay, fShowStun):
+ toon = squirt['toon']
+ level = squirt['level']
+ hpbonus = squirt['hpbonus']
+ target = squirt['target']
+ suit = target['suit']
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ died = target['died']
+ revived = target['revived']
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ battle = squirt['battle']
+ suitPos = suit.getPos(battle)
+ origHpr = toon.getHpr(battle)
+ hitSuit = hp > 0
+ scale = sprayScales[level]
+ tTotalFlowerToonAnimationTime = 2.5
+ tFlowerFirstAppears = 1.0
+ dFlowerScaleTime = 0.5
+ tSprayStarts = tTotalFlowerToonAnimationTime
+ dSprayScale = 0.2
+ dSprayHold = 0.1
+ tContact = tSprayStarts + dSprayScale
+ tSuitDodges = tTotalFlowerToonAnimationTime
+ tracks = Parallel()
+ button = globalPropPool.getProp('button')
+ button2 = MovieUtil.copyProp(button)
+ buttons = [button, button2]
+ hands = toon.getLeftHands()
+ toonTrack = Sequence(Func(MovieUtil.showProps, buttons, hands), Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'pushbutton'), Func(MovieUtil.removeProps, buttons), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr))
+ tracks.append(toonTrack)
+ tracks.append(__getSoundTrack(level, hitSuit, tTotalFlowerToonAnimationTime - 0.4, toon))
+ flower = globalPropPool.getProp('squirting-flower')
+ flower.setScale(1.5, 1.5, 1.5)
+ targetPoint = lambda suit = suit: __suitTargetPoint(suit)
+
+ def getSprayStartPos(flower = flower):
+ toon.update(0)
+ return flower.getPos(render)
+
+ sprayTrack = MovieUtil.getSprayTrack(battle, WaterSprayColor, getSprayStartPos, targetPoint, dSprayScale, dSprayHold, dSprayScale, horizScale=scale, vertScale=scale)
+ lodnames = toon.getLODNames()
+ toonlod0 = toon.getLOD(lodnames[0])
+ toonlod1 = toon.getLOD(lodnames[1])
+ if base.config.GetBool('want-new-anims', 1):
+ if not toonlod0.find('**/def_joint_attachFlower').isEmpty():
+ flower_joint0 = toonlod0.find('**/def_joint_attachFlower')
+ else:
+ flower_joint0 = toonlod0.find('**/joint_attachFlower')
+ if base.config.GetBool('want-new-anims', 1):
+ if not toonlod1.find('**/def_joint_attachFlower').isEmpty():
+ flower_joint1 = toonlod1.find('**/def_joint_attachFlower')
+ else:
+ flower_joint1 = toonlod1.find('**/joint_attachFlower')
+ flower_jointpath0 = flower_joint0.attachNewNode('attachFlower-InstanceNode')
+ flower_jointpath1 = flower_jointpath0.instanceTo(flower_joint1)
+ flowerTrack = Sequence(Wait(tFlowerFirstAppears), Func(flower.reparentTo, flower_jointpath0), LerpScaleInterval(flower, dFlowerScaleTime, flower.getScale(), startScale=MovieUtil.PNT3_NEARZERO), Wait(tTotalFlowerToonAnimationTime - dFlowerScaleTime - tFlowerFirstAppears))
+ if hp <= 0:
+ flowerTrack.append(Wait(0.5))
+ flowerTrack.append(sprayTrack)
+ flowerTrack.append(LerpScaleInterval(flower, dFlowerScaleTime, MovieUtil.PNT3_NEARZERO))
+ flowerTrack.append(Func(flower_jointpath1.removeNode))
+ flowerTrack.append(Func(flower_jointpath0.removeNode))
+ flowerTrack.append(Func(MovieUtil.removeProp, flower))
+ tracks.append(flowerTrack)
+ if hp > 0:
+ tracks.append(__getSplashTrack(targetPoint, scale, tSprayStarts + dSprayScale, battle))
+ if hp > 0 or delay <= 0:
+ tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'squirt-small-react', died, leftSuits, rightSuits, battle, toon, fShowStun, revived=revived))
+ return tracks
+
+
+def __doWaterGlass(squirt, delay, fShowStun):
+ toon = squirt['toon']
+ level = squirt['level']
+ hpbonus = squirt['hpbonus']
+ target = squirt['target']
+ suit = target['suit']
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ died = target['died']
+ revived = target['revived']
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ battle = squirt['battle']
+ suitPos = suit.getPos(battle)
+ origHpr = toon.getHpr(battle)
+ hitSuit = hp > 0
+ scale = sprayScales[level]
+ dGlassHold = 5.0
+ dGlassScale = 0.5
+ tSpray = 82.0 / toon.getFrameRate('spit')
+ sprayPoseFrame = 88
+ dSprayScale = 0.1
+ dSprayHold = 0.1
+ tContact = tSpray + dSprayScale
+ tSuitDodges = max(tSpray - 0.5, 0.0)
+ tracks = Parallel()
+ tracks.append(ActorInterval(toon, 'spit'))
+ soundTrack = __getSoundTrack(level, hitSuit, 1.7, toon)
+ tracks.append(soundTrack)
+ glass = globalPropPool.getProp('glass')
+ hands = toon.getRightHands()
+ hand_jointpath0 = hands[0].attachNewNode('handJoint0-path')
+ hand_jointpath1 = hand_jointpath0.instanceTo(hands[1])
+ glassTrack = Sequence(Func(MovieUtil.showProp, glass, hand_jointpath0), ActorInterval(glass, 'glass'), Func(hand_jointpath1.removeNode), Func(hand_jointpath0.removeNode), Func(MovieUtil.removeProp, glass))
+ tracks.append(glassTrack)
+ targetPoint = lambda suit = suit: __suitTargetPoint(suit)
+
+ def getSprayStartPos(toon = toon):
+ toon.update(0)
+ lod0 = toon.getLOD(toon.getLODNames()[0])
+ if base.config.GetBool('want-new-anims', 1):
+ if not lod0.find('**/def_head').isEmpty():
+ joint = lod0.find('**/def_head')
+ else:
+ joint = lod0.find('**/joint_head')
+ else:
+ joint = lod0.find('**/joint_head')
+ n = hidden.attachNewNode('pointInFrontOfHead')
+ n.reparentTo(toon)
+ n.setPos(joint.getPos(toon) + Point3(0, 0.3, -0.2))
+ p = n.getPos(render)
+ n.removeNode()
+ del n
+ return p
+
+ sprayTrack = MovieUtil.getSprayTrack(battle, WaterSprayColor, getSprayStartPos, targetPoint, dSprayScale, dSprayHold, dSprayScale, horizScale=scale, vertScale=scale)
+ tracks.append(Sequence(Wait(tSpray), sprayTrack))
+ if hp > 0:
+ tracks.append(__getSplashTrack(targetPoint, scale, tSpray + dSprayScale, battle))
+ if hp > 0 or delay <= 0:
+ tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'squirt-small-react', died, leftSuits, rightSuits, battle, toon, fShowStun, revived=revived))
+ return tracks
+
+
+def __doWaterGun(squirt, delay, fShowStun):
+ toon = squirt['toon']
+ level = squirt['level']
+ hpbonus = squirt['hpbonus']
+ target = squirt['target']
+ suit = target['suit']
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ died = target['died']
+ revived = target['revived']
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ battle = squirt['battle']
+ suitPos = suit.getPos(battle)
+ origHpr = toon.getHpr(battle)
+ hitSuit = hp > 0
+ scale = sprayScales[level]
+ tPistol = 0.0
+ dPistolScale = 0.5
+ dPistolHold = 1.8
+ tSpray = 48.0 / toon.getFrameRate('water-gun')
+ sprayPoseFrame = 63
+ dSprayScale = 0.1
+ dSprayHold = 0.3
+ tContact = tSpray + dSprayScale
+ tSuitDodges = 1.1
+ tracks = Parallel()
+ toonTrack = Sequence(Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'water-gun'), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr))
+ tracks.append(toonTrack)
+ soundTrack = __getSoundTrack(level, hitSuit, 1.8, toon)
+ tracks.append(soundTrack)
+ pistol = globalPropPool.getProp('water-gun')
+ hands = toon.getRightHands()
+ hand_jointpath0 = hands[0].attachNewNode('handJoint0-path')
+ hand_jointpath1 = hand_jointpath0.instanceTo(hands[1])
+ targetPoint = lambda suit = suit: __suitTargetPoint(suit)
+
+ def getSprayStartPos(pistol = pistol, toon = toon):
+ toon.update(0)
+ joint = pistol.find('**/joint_nozzle')
+ p = joint.getPos(render)
+ return p
+
+ sprayTrack = MovieUtil.getSprayTrack(battle, WaterSprayColor, getSprayStartPos, targetPoint, dSprayScale, dSprayHold, dSprayScale, horizScale=scale, vertScale=scale)
+ pistolPos = Point3(0.28, 0.1, 0.08)
+ pistolHpr = VBase3(85.6, -4.44, 94.43)
+ pistolTrack = Sequence(Func(MovieUtil.showProp, pistol, hand_jointpath0, pistolPos, pistolHpr), LerpScaleInterval(pistol, dPistolScale, pistol.getScale(), startScale=MovieUtil.PNT3_NEARZERO), Wait(tSpray - dPistolScale))
+ pistolTrack.append(sprayTrack)
+ pistolTrack.append(Wait(dPistolHold))
+ pistolTrack.append(LerpScaleInterval(pistol, dPistolScale, MovieUtil.PNT3_NEARZERO))
+ pistolTrack.append(Func(hand_jointpath1.removeNode))
+ pistolTrack.append(Func(hand_jointpath0.removeNode))
+ pistolTrack.append(Func(MovieUtil.removeProp, pistol))
+ tracks.append(pistolTrack)
+ if hp > 0:
+ tracks.append(__getSplashTrack(targetPoint, 0.3, tSpray + dSprayScale, battle))
+ if hp > 0 or delay <= 0:
+ tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'squirt-small-react', died, leftSuits, rightSuits, battle, toon, fShowStun, revived=revived))
+ return tracks
+
+
+def __doSeltzerBottle(squirt, delay, fShowStun):
+ toon = squirt['toon']
+ level = squirt['level']
+ hpbonus = squirt['hpbonus']
+ target = squirt['target']
+ suit = target['suit']
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ died = target['died']
+ revived = target['revived']
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ battle = squirt['battle']
+ suitPos = suit.getPos(battle)
+ origHpr = toon.getHpr(battle)
+ hitSuit = hp > 0
+ scale = sprayScales[level]
+ tBottle = 0.0
+ dBottleScale = 0.5
+ dBottleHold = 3.0
+ tSpray = 53.0 / toon.getFrameRate('hold-bottle') + 0.05
+ dSprayScale = 0.2
+ dSprayHold = 0.1
+ tContact = tSpray + dSprayScale
+ tSuitDodges = max(tContact - 0.7, 0.0)
+ tracks = Parallel()
+ toonTrack = Sequence(Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'hold-bottle'), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr))
+ tracks.append(toonTrack)
+ soundTrack = __getSoundTrack(level, hitSuit, tSpray - 0.1, toon)
+ tracks.append(soundTrack)
+ bottle = globalPropPool.getProp('bottle')
+ hands = toon.getRightHands()
+ targetPoint = lambda suit = suit: __suitTargetPoint(suit)
+
+ def getSprayStartPos(bottle = bottle, toon = toon):
+ toon.update(0)
+ joint = bottle.find('**/joint_toSpray')
+ n = hidden.attachNewNode('pointBehindSprayProp')
+ n.reparentTo(toon)
+ n.setPos(joint.getPos(toon) + Point3(0, -0.4, 0))
+ p = n.getPos(render)
+ n.removeNode()
+ del n
+ return p
+
+ sprayTrack = MovieUtil.getSprayTrack(battle, WaterSprayColor, getSprayStartPos, targetPoint, dSprayScale, dSprayHold, dSprayScale, horizScale=scale, vertScale=scale)
+ hand_jointpath0 = hands[0].attachNewNode('handJoint0-path')
+ hand_jointpath1 = hand_jointpath0.instanceTo(hands[1])
+ bottleTrack = Sequence(Func(MovieUtil.showProp, bottle, hand_jointpath0), LerpScaleInterval(bottle, dBottleScale, bottle.getScale(), startScale=MovieUtil.PNT3_NEARZERO), Wait(tSpray - dBottleScale))
+ bottleTrack.append(sprayTrack)
+ bottleTrack.append(Wait(dBottleHold))
+ bottleTrack.append(LerpScaleInterval(bottle, dBottleScale, MovieUtil.PNT3_NEARZERO))
+ bottleTrack.append(Func(hand_jointpath1.removeNode))
+ bottleTrack.append(Func(hand_jointpath0.removeNode))
+ bottleTrack.append(Func(MovieUtil.removeProp, bottle))
+ tracks.append(bottleTrack)
+ if hp > 0:
+ tracks.append(__getSplashTrack(targetPoint, scale, tSpray + dSprayScale, battle))
+ if (hp > 0 or delay <= 0) and suit:
+ tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'squirt-small-react', died, leftSuits, rightSuits, battle, toon, fShowStun, revived=revived))
+ return tracks
+
+
+def __doFireHose(squirt, delay, fShowStun):
+ toon = squirt['toon']
+ level = squirt['level']
+ hpbonus = squirt['hpbonus']
+ target = squirt['target']
+ suit = target['suit']
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ died = target['died']
+ revived = target['revived']
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ battle = squirt['battle']
+ suitPos = suit.getPos(battle)
+ origHpr = toon.getHpr(battle)
+ hitSuit = hp > 0
+ scale = 0.3
+ tAppearDelay = 0.7
+ dHoseHold = 0.7
+ dAnimHold = 5.1
+ tSprayDelay = 2.8
+ tSpray = 0.2
+ dSprayScale = 0.1
+ dSprayHold = 1.8
+ tContact = 2.9
+ tSuitDodges = 2.1
+ tracks = Parallel()
+ toonTrack = Sequence(Wait(tAppearDelay), Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'firehose'), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr))
+ tracks.append(toonTrack)
+ soundTrack = __getSoundTrack(level, hitSuit, tSprayDelay, toon)
+ tracks.append(soundTrack)
+ hose = globalPropPool.getProp('firehose')
+ hydrant = globalPropPool.getProp('hydrant')
+ hose.reparentTo(hydrant)
+ (hose.pose('firehose', 2),)
+ hydrantNode = toon.attachNewNode('hydrantNode')
+ hydrantNode.clearTransform(toon.getGeomNode().getChild(0))
+ hydrantScale = hydrantNode.attachNewNode('hydrantScale')
+ hydrant.reparentTo(hydrantScale)
+ toon.pose('firehose', 30)
+ toon.update(0)
+ torso = toon.getPart('torso', '1000')
+ if toon.style.torso[0] == 'm':
+ hydrant.setPos(torso, 0, 0, -1.85)
+ else:
+ hydrant.setPos(torso, 0, 0, -1.45)
+ hydrant.setPos(0, 0, hydrant.getZ())
+ base = hydrant.find('**/base')
+ base.setColor(1, 1, 1, 0.5)
+ base.setPos(toon, 0, 0, 0)
+ toon.loop('neutral')
+ targetPoint = lambda suit = suit: __suitTargetPoint(suit)
+
+ def getSprayStartPos(hose = hose, toon = toon, targetPoint = targetPoint):
+ toon.update(0)
+ if hose.isEmpty() == 1:
+ if callable(targetPoint):
+ return targetPoint()
+ else:
+ return targetPoint
+ joint = hose.find('**/joint_water_stream')
+ n = hidden.attachNewNode('pointBehindSprayProp')
+ n.reparentTo(toon)
+ n.setPos(joint.getPos(toon) + Point3(0, -0.55, 0))
+ p = n.getPos(render)
+ n.removeNode()
+ del n
+ return p
+
+ sprayTrack = Sequence()
+ sprayTrack.append(Wait(tSprayDelay))
+ sprayTrack.append(MovieUtil.getSprayTrack(battle, WaterSprayColor, getSprayStartPos, targetPoint, dSprayScale, dSprayHold, dSprayScale, horizScale=scale, vertScale=scale))
+ tracks.append(sprayTrack)
+ hydrantNode.detachNode()
+ propTrack = Sequence(Func(battle.movie.needRestoreRenderProp, hydrantNode), Func(hydrantNode.reparentTo, toon), LerpScaleInterval(hydrantScale, tAppearDelay * 0.5, Point3(1, 1, 1.4), startScale=Point3(1, 1, 0.01)), LerpScaleInterval(hydrantScale, tAppearDelay * 0.3, Point3(1, 1, 0.8), startScale=Point3(1, 1, 1.4)), LerpScaleInterval(hydrantScale, tAppearDelay * 0.1, Point3(1, 1, 1.2), startScale=Point3(1, 1, 0.8)), LerpScaleInterval(hydrantScale, tAppearDelay * 0.1, Point3(1, 1, 1), startScale=Point3(1, 1, 1.2)), ActorInterval(hose, 'firehose', duration=dAnimHold), Wait(dHoseHold - 0.2), LerpScaleInterval(hydrantScale, 0.2, Point3(1, 1, 0.01), startScale=Point3(1, 1, 1)), Func(MovieUtil.removeProps, [hydrantNode, hose]), Func(battle.movie.clearRenderProp, hydrantNode))
+ tracks.append(propTrack)
+ if hp > 0:
+ tracks.append(__getSplashTrack(targetPoint, 0.4, 2.7, battle, splashHold=1.5))
+ if hp > 0 or delay <= 0:
+ tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'squirt-small-react', died, leftSuits, rightSuits, battle, toon, fShowStun, revived=revived))
+ return tracks
+
+
+def __doStormCloud(squirt, delay, fShowStun):
+ toon = squirt['toon']
+ level = squirt['level']
+ hpbonus = squirt['hpbonus']
+ target = squirt['target']
+ suit = target['suit']
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ died = target['died']
+ revived = target['revived']
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ battle = squirt['battle']
+ suitPos = suit.getPos(battle)
+ origHpr = toon.getHpr(battle)
+ hitSuit = hp > 0
+ scale = sprayScales[level]
+ tButton = 0.0
+ dButtonScale = 0.5
+ dButtonHold = 3.0
+ tContact = 2.9
+ tSpray = 1
+ tSuitDodges = 1.8
+ tracks = Parallel()
+ soundTrack = __getSoundTrack(level, hitSuit, 2.3, toon)
+ soundTrack2 = __getSoundTrack(level, hitSuit, 4.6, toon)
+ tracks.append(soundTrack)
+ tracks.append(soundTrack2)
+ button = globalPropPool.getProp('button')
+ button2 = MovieUtil.copyProp(button)
+ buttons = [button, button2]
+ hands = toon.getLeftHands()
+ toonTrack = Sequence(Func(MovieUtil.showProps, buttons, hands), Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'pushbutton'), Func(MovieUtil.removeProps, buttons), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr))
+ tracks.append(toonTrack)
+ cloud = globalPropPool.getProp('stormcloud')
+ cloud2 = MovieUtil.copyProp(cloud)
+ BattleParticles.loadParticles()
+ trickleEffect = BattleParticles.createParticleEffect(file='trickleLiquidate')
+ rainEffect = BattleParticles.createParticleEffect(file='liquidate')
+ rainEffect2 = BattleParticles.createParticleEffect(file='liquidate')
+ rainEffect3 = BattleParticles.createParticleEffect(file='liquidate')
+ cloudHeight = suit.height + 3
+ cloudPosPoint = Point3(0, 0, cloudHeight)
+ scaleUpPoint = Point3(3, 3, 3)
+ rainEffects = [rainEffect, rainEffect2, rainEffect3]
+ rainDelay = 1
+ effectDelay = 0.3
+ if hp > 0:
+ cloudHold = 4.7
+ else:
+ cloudHold = 1.7
+
+ def getCloudTrack(cloud, suit, cloudPosPoint, scaleUpPoint, rainEffects, rainDelay, effectDelay, cloudHold, useEffect, battle = battle, trickleEffect = trickleEffect):
+ track = Sequence(Func(MovieUtil.showProp, cloud, suit, cloudPosPoint), Func(cloud.pose, 'stormcloud', 0), LerpScaleInterval(cloud, 1.5, scaleUpPoint, startScale=MovieUtil.PNT3_NEARZERO), Wait(rainDelay))
+ if useEffect == 1:
+ ptrack = Parallel()
+ delay = trickleDuration = cloudHold * 0.25
+ trickleTrack = Sequence(Func(battle.movie.needRestoreParticleEffect, trickleEffect), ParticleInterval(trickleEffect, cloud, worldRelative=0, duration=trickleDuration, cleanup=True), Func(battle.movie.clearRestoreParticleEffect, trickleEffect))
+ track.append(trickleTrack)
+ for i in range(0, 3):
+ dur = cloudHold - 2 * trickleDuration
+ ptrack.append(Sequence(Func(battle.movie.needRestoreParticleEffect, rainEffects[i]), Wait(delay), ParticleInterval(rainEffects[i], cloud, worldRelative=0, duration=dur, cleanup=True), Func(battle.movie.clearRestoreParticleEffect, rainEffects[i])))
+ delay += effectDelay
+
+ ptrack.append(Sequence(Wait(3 * effectDelay), ActorInterval(cloud, 'stormcloud', startTime=1, duration=cloudHold)))
+ track.append(ptrack)
+ else:
+ track.append(ActorInterval(cloud, 'stormcloud', startTime=1, duration=cloudHold))
+ track.append(LerpScaleInterval(cloud, 0.5, MovieUtil.PNT3_NEARZERO))
+ track.append(Func(MovieUtil.removeProp, cloud))
+ return track
+
+ tracks.append(getCloudTrack(cloud, suit, cloudPosPoint, scaleUpPoint, rainEffects, rainDelay, effectDelay, cloudHold, useEffect=1))
+ tracks.append(getCloudTrack(cloud2, suit, cloudPosPoint, scaleUpPoint, rainEffects, rainDelay, effectDelay, cloudHold, useEffect=0))
+ if hp > 0 or delay <= 0:
+ tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'soak', died, leftSuits, rightSuits, battle, toon, fShowStun, beforeStun=2.6, afterStun=2.3, revived=revived))
+ return tracks
+
+
+def __doGeyser(squirt, delay, fShowStun, uberClone = 0):
+ toon = squirt['toon']
+ level = squirt['level']
+ hpbonus = squirt['hpbonus']
+ tracks = Parallel()
+ tButton = 0.0
+ dButtonScale = 0.5
+ dButtonHold = 3.0
+ tContact = 2.9
+ tSpray = 1
+ tSuitDodges = 1.8
+ button = globalPropPool.getProp('button')
+ button2 = MovieUtil.copyProp(button)
+ buttons = [button, button2]
+ hands = toon.getLeftHands()
+ battle = squirt['battle']
+ origHpr = toon.getHpr(battle)
+ suit = squirt['target'][0]['suit']
+ suitPos = suit.getPos(battle)
+ toonTrack = Sequence(Func(MovieUtil.showProps, buttons, hands), Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'pushbutton'), Func(MovieUtil.removeProps, buttons), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr))
+ tracks.append(toonTrack)
+ for target in squirt['target']:
+ suit = target['suit']
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ died = target['died']
+ revived = target['revived']
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ suitPos = suit.getPos(battle)
+ hitSuit = hp > 0
+ scale = sprayScales[level]
+ soundTrack = __getSoundTrack(level, hitSuit, 1.8, toon)
+ delayTime = random.random()
+ tracks.append(Wait(delayTime))
+ tracks.append(soundTrack)
+ cloud = globalPropPool.getProp('geyser')
+ cloud2 = MovieUtil.copyProp(cloud)
+ BattleParticles.loadParticles()
+ geyserHeight = battle.getH()
+ geyserPosPoint = Point3(0, 0, geyserHeight)
+ scaleUpPoint = Point3(1.8, 1.8, 1.8)
+ rainEffects = []
+ rainDelay = 2.5
+ effectDelay = 0.3
+ if hp > 0:
+ geyserHold = 1.5
+ else:
+ geyserHold = 0.5
+
+ def getGeyserTrack(geyser, suit, geyserPosPoint, scaleUpPoint, rainEffects, rainDelay, effectDelay, geyserHold, useEffect, battle = battle):
+ geyserMound = MovieUtil.copyProp(geyser)
+ geyserRemoveM = geyserMound.findAllMatches('**/Splash*')
+ geyserRemoveM.addPathsFrom(geyserMound.findAllMatches('**/spout'))
+ for i in range(geyserRemoveM.getNumPaths()):
+ geyserRemoveM[i].removeNode()
+
+ geyserWater = MovieUtil.copyProp(geyser)
+ geyserRemoveW = geyserWater.findAllMatches('**/hole')
+ geyserRemoveW.addPathsFrom(geyserWater.findAllMatches('**/shadow'))
+ for i in range(geyserRemoveW.getNumPaths()):
+ geyserRemoveW[i].removeNode()
+
+ track = Sequence(Wait(rainDelay), Func(MovieUtil.showProp, geyserMound, battle, suit.getPos(battle)), Func(MovieUtil.showProp, geyserWater, battle, suit.getPos(battle)), LerpScaleInterval(geyserWater, 1.0, scaleUpPoint, startScale=MovieUtil.PNT3_NEARZERO), Wait(geyserHold * 0.5), LerpScaleInterval(geyserWater, 0.5, MovieUtil.PNT3_NEARZERO, startScale=scaleUpPoint))
+ track.append(LerpScaleInterval(geyserMound, 0.5, MovieUtil.PNT3_NEARZERO))
+ track.append(Func(MovieUtil.removeProp, geyserMound))
+ track.append(Func(MovieUtil.removeProp, geyserWater))
+ track.append(Func(MovieUtil.removeProp, geyser))
+ return track
+
+ if not uberClone:
+ tracks.append(Sequence(Wait(delayTime), getGeyserTrack(cloud, suit, geyserPosPoint, scaleUpPoint, rainEffects, rainDelay, effectDelay, geyserHold, useEffect=1)))
+ if hp > 0 or delay <= 0:
+ tracks.append(Sequence(Wait(delayTime), __getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'soak', died, leftSuits, rightSuits, battle, toon, fShowStun, beforeStun=2.6, afterStun=2.3, geyser=1, uberRepeat=uberClone, revived=revived)))
+
+ return tracks
+
+
+squirtfn_array = (__doFlower,
+ __doWaterGlass,
+ __doWaterGun,
+ __doSeltzerBottle,
+ __doFireHose,
+ __doStormCloud,
+ __doGeyser)
\ No newline at end of file
diff --git a/toontown/battle/MovieSuitAttacks.py b/toontown/battle/MovieSuitAttacks.py
new file mode 100644
index 0000000..0dee53a
--- /dev/null
+++ b/toontown/battle/MovieSuitAttacks.py
@@ -0,0 +1,3545 @@
+from toontown.toonbase.ToontownGlobals import *
+from SuitBattleGlobals import *
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from BattleProps import *
+from toontown.suit.SuitDNA import *
+from BattleBase import *
+from BattleSounds import *
+import MovieCamera
+from direct.directnotify import DirectNotifyGlobal
+import MovieUtil
+from direct.particles import ParticleEffect
+import BattleParticles
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieSuitAttacks')
+
+def __doDamage(toon, dmg, died):
+ if dmg > 0 and toon.hp != None:
+ toon.takeDamage(dmg)
+ return
+
+
+def __showProp(prop, parent, pos, hpr = None, scale = None):
+ prop.reparentTo(parent)
+ prop.setPos(pos)
+ if hpr:
+ prop.setHpr(hpr)
+ if scale:
+ prop.setScale(scale)
+
+
+def __animProp(prop, propName, propType = 'actor'):
+ if 'actor' == propType:
+ prop.play(propName)
+ elif 'model' == propType:
+ pass
+ else:
+ self.notify.error('No such propType as: %s' % propType)
+
+
+def __suitFacePoint(suit, zOffset = 0):
+ pnt = suit.getPos()
+ pnt.setZ(pnt[2] + suit.shoulderHeight + 0.3 + zOffset)
+ return Point3(pnt)
+
+
+def __toonFacePoint(toon, zOffset = 0, parent = render):
+ pnt = toon.getPos(parent)
+ pnt.setZ(pnt[2] + toon.shoulderHeight + 0.3 + zOffset)
+ return Point3(pnt)
+
+
+def __toonTorsoPoint(toon, zOffset = 0):
+ pnt = toon.getPos()
+ pnt.setZ(pnt[2] + toon.shoulderHeight - 0.2)
+ return Point3(pnt)
+
+
+def __toonGroundPoint(attack, toon, zOffset = 0, parent = render):
+ pnt = toon.getPos(parent)
+ battle = attack['battle']
+ pnt.setZ(battle.getZ(parent) + zOffset)
+ return Point3(pnt)
+
+
+def __toonGroundMissPoint(attack, prop, toon, zOffset = 0):
+ point = __toonMissPoint(prop, toon)
+ battle = attack['battle']
+ point.setZ(battle.getZ() + zOffset)
+ return Point3(point)
+
+
+def __toonMissPoint(prop, toon, yOffset = 0, parent = None):
+ if parent:
+ p = __toonFacePoint(toon) - prop.getPos(parent)
+ else:
+ p = __toonFacePoint(toon) - prop.getPos()
+ v = Vec3(p)
+ baseDistance = v.length()
+ v.normalize()
+ if parent:
+ endPos = prop.getPos(parent) + v * (baseDistance + 5 + yOffset)
+ else:
+ endPos = prop.getPos() + v * (baseDistance + 5 + yOffset)
+ return Point3(endPos)
+
+
+def __toonMissBehindPoint(toon, parent = render, offset = 0):
+ point = toon.getPos(parent)
+ point.setY(point.getY() - 5 + offset)
+ return point
+
+
+def __throwBounceHitPoint(prop, toon):
+ startPoint = prop.getPos()
+ endPoint = __toonFacePoint(toon)
+ return __throwBouncePoint(startPoint, endPoint)
+
+
+def __throwBounceMissPoint(prop, toon):
+ startPoint = prop.getPos()
+ endPoint = __toonFacePoint(toon)
+ return __throwBouncePoint(startPoint, endPoint)
+
+
+def __throwBouncePoint(startPoint, endPoint):
+ midPoint = startPoint + (endPoint - startPoint) / 2.0
+ midPoint.setZ(0)
+ return Point3(midPoint)
+
+
+def doSuitAttack(attack):
+ notify.debug('building suit attack in doSuitAttack: %s' % attack['name'])
+ name = attack['id']
+ if name == AUDIT:
+ suitTrack = doAudit(attack)
+ elif name == BITE:
+ suitTrack = doBite(attack)
+ elif name == BOUNCE_CHECK:
+ suitTrack = doBounceCheck(attack)
+ elif name == BRAIN_STORM:
+ suitTrack = doBrainStorm(attack)
+ elif name == BUZZ_WORD:
+ suitTrack = doBuzzWord(attack)
+ elif name == CALCULATE:
+ suitTrack = doCalculate(attack)
+ elif name == CANNED:
+ suitTrack = doCanned(attack)
+ elif name == CHOMP:
+ suitTrack = doChomp(attack)
+ elif name == CIGAR_SMOKE:
+ suitTrack = doDefault(attack)
+ elif name == CLIPON_TIE:
+ suitTrack = doClipOnTie(attack)
+ elif name == CRUNCH:
+ suitTrack = doCrunch(attack)
+ elif name == DEMOTION:
+ suitTrack = doDemotion(attack)
+ elif name == DOUBLE_TALK:
+ suitTrack = doDoubleTalk(attack)
+ elif name == DOWNSIZE:
+ suitTrack = doDownsize(attack)
+ elif name == EVICTION_NOTICE:
+ suitTrack = doEvictionNotice(attack)
+ elif name == EVIL_EYE:
+ suitTrack = doEvilEye(attack)
+ elif name == FILIBUSTER:
+ suitTrack = doFilibuster(attack)
+ elif name == FILL_WITH_LEAD:
+ suitTrack = doFillWithLead(attack)
+ elif name == FINGER_WAG:
+ suitTrack = doFingerWag(attack)
+ elif name == FIRED:
+ suitTrack = doFired(attack)
+ elif name == FIVE_O_CLOCK_SHADOW:
+ suitTrack = doDefault(attack)
+ elif name == FLOOD_THE_MARKET:
+ suitTrack = doDefault(attack)
+ elif name == FOUNTAIN_PEN:
+ suitTrack = doFountainPen(attack)
+ elif name == FREEZE_ASSETS:
+ suitTrack = doFreezeAssets(attack)
+ elif name == GAVEL:
+ suitTrack = doDefault(attack)
+ elif name == GLOWER_POWER:
+ suitTrack = doGlowerPower(attack)
+ elif name == GUILT_TRIP:
+ suitTrack = doGuiltTrip(attack)
+ elif name == HALF_WINDSOR:
+ suitTrack = doHalfWindsor(attack)
+ elif name == HANG_UP:
+ suitTrack = doHangUp(attack)
+ elif name == HEAD_SHRINK:
+ suitTrack = doHeadShrink(attack)
+ elif name == HOT_AIR:
+ suitTrack = doHotAir(attack)
+ elif name == JARGON:
+ suitTrack = doJargon(attack)
+ elif name == LEGALESE:
+ suitTrack = doLegalese(attack)
+ elif name == LIQUIDATE:
+ suitTrack = doLiquidate(attack)
+ elif name == MARKET_CRASH:
+ suitTrack = doMarketCrash(attack)
+ elif name == MUMBO_JUMBO:
+ suitTrack = doMumboJumbo(attack)
+ elif name == PARADIGM_SHIFT:
+ suitTrack = doParadigmShift(attack)
+ elif name == PECKING_ORDER:
+ suitTrack = doPeckingOrder(attack)
+ elif name == PICK_POCKET:
+ suitTrack = doPickPocket(attack)
+ elif name == PINK_SLIP:
+ suitTrack = doPinkSlip(attack)
+ elif name == PLAY_HARDBALL:
+ suitTrack = doPlayHardball(attack)
+ elif name == POUND_KEY:
+ suitTrack = doPoundKey(attack)
+ elif name == POWER_TIE:
+ suitTrack = doPowerTie(attack)
+ elif name == POWER_TRIP:
+ suitTrack = doPowerTrip(attack)
+ elif name == QUAKE:
+ suitTrack = doQuake(attack)
+ elif name == RAZZLE_DAZZLE:
+ suitTrack = doRazzleDazzle(attack)
+ elif name == RED_TAPE:
+ suitTrack = doRedTape(attack)
+ elif name == RE_ORG:
+ suitTrack = doReOrg(attack)
+ elif name == RESTRAINING_ORDER:
+ suitTrack = doRestrainingOrder(attack)
+ elif name == ROLODEX:
+ suitTrack = doRolodex(attack)
+ elif name == RUBBER_STAMP:
+ suitTrack = doRubberStamp(attack)
+ elif name == RUB_OUT:
+ suitTrack = doRubOut(attack)
+ elif name == SACKED:
+ suitTrack = doSacked(attack)
+ elif name == SANDTRAP:
+ suitTrack = doDefault(attack)
+ elif name == SCHMOOZE:
+ suitTrack = doSchmooze(attack)
+ elif name == SHAKE:
+ suitTrack = doShake(attack)
+ elif name == SHRED:
+ suitTrack = doShred(attack)
+ elif name == SONG_AND_DANCE:
+ suitTrack = doDefault(attack)
+ elif name == SPIN:
+ suitTrack = doSpin(attack)
+ elif name == SYNERGY:
+ suitTrack = doSynergy(attack)
+ elif name == TABULATE:
+ suitTrack = doTabulate(attack)
+ elif name == TEE_OFF:
+ suitTrack = doTeeOff(attack)
+ elif name == THROW_BOOK:
+ suitTrack = doDefault(attack)
+ elif name == TREMOR:
+ suitTrack = doTremor(attack)
+ elif name == WATERCOOLER:
+ suitTrack = doWatercooler(attack)
+ elif name == WITHDRAWAL:
+ suitTrack = doWithdrawal(attack)
+ elif name == WRITE_OFF:
+ suitTrack = doWriteOff(attack)
+ else:
+ notify.warning('unknown attack: %d substituting Finger Wag' % name)
+ suitTrack = doDefault(attack)
+ camTrack = MovieCamera.chooseSuitShot(attack, suitTrack.getDuration())
+ battle = attack['battle']
+ target = attack['target']
+ groupStatus = attack['group']
+ if groupStatus == ATK_TGT_SINGLE:
+ toon = target['toon']
+ toonHprTrack = Sequence(Func(toon.headsUp, battle, MovieUtil.PNT3_ZERO), Func(toon.loop, 'neutral'))
+ else:
+ toonHprTrack = Parallel()
+ for t in target:
+ toon = t['toon']
+ toonHprTrack.append(Sequence(Func(toon.headsUp, battle, MovieUtil.PNT3_ZERO), Func(toon.loop, 'neutral')))
+
+ suit = attack['suit']
+ neutralIval = Func(suit.loop, 'neutral')
+ suitTrack = Sequence(suitTrack, neutralIval, toonHprTrack)
+ suitPos = suit.getPos(battle)
+ resetPos, resetHpr = battle.getActorPosHpr(suit)
+ if battle.isSuitLured(suit):
+ resetTrack = getResetTrack(suit, battle)
+ resetSuitTrack = Sequence(resetTrack, suitTrack)
+ waitTrack = Sequence(Wait(resetTrack.getDuration()), Func(battle.unlureSuit, suit))
+ resetCamTrack = Sequence(waitTrack, camTrack)
+ return (resetSuitTrack, resetCamTrack)
+ else:
+ return (suitTrack, camTrack)
+
+
+def getResetTrack(suit, battle):
+ resetPos, resetHpr = battle.getActorPosHpr(suit)
+ moveDist = Vec3(suit.getPos(battle) - resetPos).length()
+ moveDuration = 0.5
+ walkTrack = Sequence(Func(suit.setHpr, battle, resetHpr), ActorInterval(suit, 'walk', startTime=1, duration=moveDuration, endTime=1e-05), Func(suit.loop, 'neutral'))
+ moveTrack = LerpPosInterval(suit, moveDuration, resetPos, other=battle)
+ return Parallel(walkTrack, moveTrack)
+
+
+def __makeCancelledNodePath():
+ tn = TextNode('CANCELLED')
+ tn.setFont(getSuitFont())
+ tn.setText(TTLocalizer.MovieSuitCancelled)
+ tn.setAlign(TextNode.ACenter)
+ tntop = hidden.attachNewNode('CancelledTop')
+ tnpath = tntop.attachNewNode(tn)
+ tnpath.setPosHpr(0, 0, 0, 0, 0, 0)
+ tnpath.setScale(1)
+ tnpath.setColor(0.7, 0, 0, 1)
+ tnpathback = tnpath.instanceUnderNode(tntop, 'backside')
+ tnpathback.setPosHpr(0, 0, 0, 180, 0, 0)
+ tnpath.setScale(1)
+ return tntop
+
+
+def doDefault(attack):
+ notify.debug('building suit attack in doDefault')
+ suitName = attack['suitName']
+ if suitName == 'f':
+ attack['id'] = POUND_KEY
+ attack['name'] = 'PoundKey'
+ attack['animName'] = 'phone'
+ return doPoundKey(attack)
+ elif suitName == 'p':
+ attack['id'] = FOUNTAIN_PEN
+ attack['name'] = 'FountainPen'
+ attack['animName'] = 'pen-squirt'
+ return doFountainPen(attack)
+ elif suitName == 'ym':
+ attack['id'] = RUBBER_STAMP
+ attack['name'] = 'RubberStamp'
+ attack['animName'] = 'rubber-stamp'
+ return doRubberStamp(attack)
+ elif suitName == 'mm':
+ attack['id'] = FINGER_WAG
+ attack['name'] = 'FingerWag'
+ attack['animName'] = 'finger-wag'
+ return doFingerWag(attack)
+ elif suitName == 'ds':
+ attack['id'] = DEMOTION
+ attack['name'] = 'Demotion'
+ attack['animName'] = 'magic1'
+ return doDemotion(attack)
+ elif suitName == 'hh':
+ attack['id'] = GLOWER_POWER
+ attack['name'] = 'GlowerPower'
+ attack['animName'] = 'glower'
+ return doGlowerPower(attack)
+ elif suitName == 'cr':
+ attack['id'] = PICK_POCKET
+ attack['name'] = 'PickPocket'
+ attack['animName'] = 'pickpocket'
+ return doPickPocket(attack)
+ elif suitName == 'tbc':
+ attack['id'] = GLOWER_POWER
+ attack['name'] = 'GlowerPower'
+ attack['animName'] = 'glower'
+ return doGlowerPower(attack)
+ elif suitName == 'cc':
+ attack['id'] = POUND_KEY
+ attack['name'] = 'PoundKey'
+ attack['animName'] = 'phone'
+ return doPoundKey(attack)
+ elif suitName == 'tm':
+ attack['id'] = CLIPON_TIE
+ attack['name'] = 'ClipOnTie'
+ attack['animName'] = 'throw-paper'
+ return doClipOnTie(attack)
+ elif suitName == 'nd':
+ attack['id'] = PICK_POCKET
+ attack['name'] = 'PickPocket'
+ attack['animName'] = 'pickpocket'
+ return doPickPocket(attack)
+ elif suitName == 'gh':
+ attack['id'] = FOUNTAIN_PEN
+ attack['name'] = 'FountainPen'
+ attack['animName'] = 'pen-squirt'
+ return doFountainPen(attack)
+ elif suitName == 'ms':
+ attack['id'] = BRAIN_STORM
+ attack['name'] = 'BrainStorm'
+ attack['animName'] = 'effort'
+ return doBrainStorm(attack)
+ elif suitName == 'tf':
+ attack['id'] = RED_TAPE
+ attack['name'] = 'RedTape'
+ attack['animName'] = 'throw-object'
+ return doRedTape(attack)
+ elif suitName == 'm':
+ attack['id'] = BUZZ_WORD
+ attack['name'] = 'BuzzWord'
+ attack['animName'] = 'speak'
+ return doBuzzWord(attack)
+ elif suitName == 'mh':
+ attack['id'] = RAZZLE_DAZZLE
+ attack['name'] = 'RazzleDazzle'
+ attack['animName'] = 'smile'
+ return doRazzleDazzle(attack)
+ elif suitName == 'sc':
+ attack['id'] = WATERCOOLER
+ attack['name'] = 'Watercooler'
+ attack['animName'] = 'water-cooler'
+ return doWatercooler(attack)
+ elif suitName == 'pp':
+ attack['id'] = BOUNCE_CHECK
+ attack['name'] = 'BounceCheck'
+ attack['animName'] = 'throw-paper'
+ return doBounceCheck(attack)
+ elif suitName == 'tw':
+ attack['id'] = GLOWER_POWER
+ attack['name'] = 'GlowerPower'
+ attack['animName'] = 'glower'
+ return doGlowerPower(attack)
+ elif suitName == 'bc':
+ attack['id'] = AUDIT
+ attack['name'] = 'Audit'
+ attack['animName'] = 'phone'
+ return doAudit(attack)
+ elif suitName == 'nc':
+ attack['id'] = RED_TAPE
+ attack['name'] = 'RedTape'
+ attack['animName'] = 'throw-object'
+ return doRedTape(attack)
+ elif suitName == 'mb':
+ attack['id'] = LIQUIDATE
+ attack['name'] = 'Liquidate'
+ attack['animName'] = 'magic1'
+ return doLiquidate(attack)
+ elif suitName == 'ls':
+ attack['id'] = WRITE_OFF
+ attack['name'] = 'WriteOff'
+ attack['animName'] = 'hold-pencil'
+ return doWriteOff(attack)
+ elif suitName == 'rb':
+ attack['id'] = TEE_OFF
+ attack['name'] = 'TeeOff'
+ attack['animName'] = 'golf-club-swing'
+ return doTeeOff(attack)
+ elif suitName == 'bf':
+ attack['id'] = RUBBER_STAMP
+ attack['name'] = 'RubberStamp'
+ attack['animName'] = 'rubber-stamp'
+ return doRubberStamp(attack)
+ elif suitName == 'b':
+ attack['id'] = EVICTION_NOTICE
+ attack['name'] = 'EvictionNotice'
+ attack['animName'] = 'throw-paper'
+ return doEvictionNotice(attack)
+ elif suitName == 'dt':
+ attack['id'] = RUBBER_STAMP
+ attack['name'] = 'RubberStamp'
+ attack['animName'] = 'rubber-stamp'
+ return doRubberStamp(attack)
+ elif suitName == 'ac':
+ attack['id'] = RED_TAPE
+ attack['name'] = 'RedTape'
+ attack['animName'] = 'throw-object'
+ return doRedTape(attack)
+ elif suitName == 'bs':
+ attack['id'] = FINGER_WAG
+ attack['name'] = 'FingerWag'
+ attack['animName'] = 'finger-wag'
+ return doFingerWag(attack)
+ elif suitName == 'sd':
+ attack['id'] = WRITE_OFF
+ attack['name'] = 'WriteOff'
+ attack['animName'] = 'hold-pencil'
+ return doWriteOff(attack)
+ elif suitName == 'le':
+ attack['id'] = JARGON
+ attack['name'] = 'Jargon'
+ attack['animName'] = 'speak'
+ return doJargon(attack)
+ elif suitName == 'bw':
+ attack['id'] = FINGER_WAG
+ attack['name'] = 'FingerWag'
+ attack['animName'] = 'finger-wag'
+ return doFingerWag(attack)
+ else:
+ self.notify.error('doDefault() - unsupported suit type: %s' % suitName)
+ return None
+
+
+def getSuitTrack(attack, delay = 1e-06, splicedAnims = None):
+ suit = attack['suit']
+ battle = attack['battle']
+ tauntIndex = attack['taunt']
+ target = attack['target']
+ toon = target['toon']
+ targetPos = toon.getPos(battle)
+ taunt = getAttackTaunt(attack['name'], tauntIndex)
+ trapStorage = {}
+ trapStorage['trap'] = None
+ track = Sequence(Wait(delay), Func(suit.setChatAbsolute, taunt, CFSpeech | CFTimeout))
+
+ def reparentTrap(suit = suit, battle = battle, trapStorage = trapStorage):
+ trapProp = suit.battleTrapProp
+ if trapProp != None:
+ trapProp.wrtReparentTo(battle)
+ trapStorage['trap'] = trapProp
+ return
+
+ track.append(Func(reparentTrap))
+ track.append(Func(suit.headsUp, battle, targetPos))
+ if splicedAnims:
+ track.append(getSplicedAnimsTrack(splicedAnims, actor=suit))
+ else:
+ track.append(ActorInterval(suit, attack['animName']))
+ origPos, origHpr = battle.getActorPosHpr(suit)
+ track.append(Func(suit.setHpr, battle, origHpr))
+
+ def returnTrapToSuit(suit = suit, trapStorage = trapStorage):
+ trapProp = trapStorage['trap']
+ if trapProp != None:
+ if trapProp.getName() == 'traintrack':
+ notify.debug('deliberately not parenting traintrack to suit')
+ else:
+ trapProp.wrtReparentTo(suit)
+ suit.battleTrapProp = trapProp
+ return
+
+ track.append(Func(returnTrapToSuit))
+ track.append(Func(suit.clearChat))
+ return track
+
+
+def getSuitAnimTrack(attack, delay = 0):
+ suit = attack['suit']
+ tauntIndex = attack['taunt']
+ taunt = getAttackTaunt(attack['name'], tauntIndex)
+ return Sequence(Wait(delay), Func(suit.setChatAbsolute, taunt, CFSpeech | CFTimeout), ActorInterval(attack['suit'], attack['animName']), Func(suit.clearChat))
+
+
+def getPartTrack(particleEffect, startDelay, durationDelay, partExtraArgs):
+ particleEffect = partExtraArgs[0]
+ parent = partExtraArgs[1]
+ if len(partExtraArgs) > 2:
+ worldRelative = partExtraArgs[2]
+ else:
+ worldRelative = 1
+ return Sequence(Wait(startDelay), ParticleInterval(particleEffect, parent, worldRelative, duration=durationDelay, cleanup=True))
+
+
+def getToonTrack(attack, damageDelay = 1e-06, damageAnimNames = None, dodgeDelay = 0.0001, dodgeAnimNames = None, splicedDamageAnims = None, splicedDodgeAnims = None, target = None, showDamageExtraTime = 0.01, showMissedExtraTime = 0.5):
+ if not target:
+ target = attack['target']
+ toon = target['toon']
+ battle = attack['battle']
+ suit = attack['suit']
+ suitPos = suit.getPos(battle)
+ dmg = target['hp']
+ animTrack = Sequence()
+ animTrack.append(Func(toon.headsUp, battle, suitPos))
+ if dmg > 0:
+ animTrack.append(getToonTakeDamageTrack(toon, target['died'], dmg, damageDelay, damageAnimNames, splicedDamageAnims, showDamageExtraTime))
+ return animTrack
+ else:
+ animTrack.append(getToonDodgeTrack(target, dodgeDelay, dodgeAnimNames, splicedDodgeAnims, showMissedExtraTime))
+ indicatorTrack = Sequence(Wait(dodgeDelay + showMissedExtraTime), Func(MovieUtil.indicateMissed, toon))
+ return Parallel(animTrack, indicatorTrack)
+
+
+def getToonTracks(attack, damageDelay = 1e-06, damageAnimNames = None, dodgeDelay = 1e-06, dodgeAnimNames = None, splicedDamageAnims = None, splicedDodgeAnims = None, showDamageExtraTime = 0.01, showMissedExtraTime = 0.5):
+ toonTracks = Parallel()
+ targets = attack['target']
+ for i in range(len(targets)):
+ tgt = targets[i]
+ toonTracks.append(getToonTrack(attack, damageDelay, damageAnimNames, dodgeDelay, dodgeAnimNames, splicedDamageAnims, splicedDodgeAnims, target=tgt, showDamageExtraTime=showDamageExtraTime, showMissedExtraTime=showMissedExtraTime))
+
+ return toonTracks
+
+
+def getToonDodgeTrack(target, dodgeDelay, dodgeAnimNames, splicedDodgeAnims, showMissedExtraTime):
+ toon = target['toon']
+ toonTrack = Sequence()
+ toonTrack.append(Wait(dodgeDelay))
+ if dodgeAnimNames:
+ for d in dodgeAnimNames:
+ if d == 'sidestep':
+ toonTrack.append(getAllyToonsDodgeParallel(target))
+ else:
+ toonTrack.append(ActorInterval(toon, d))
+
+ else:
+ toonTrack.append(getSplicedAnimsTrack(splicedDodgeAnims, actor=toon))
+ toonTrack.append(Func(toon.loop, 'neutral'))
+ return toonTrack
+
+
+def getAllyToonsDodgeParallel(target):
+ toon = target['toon']
+ leftToons = target['leftToons']
+ rightToons = target['rightToons']
+ if len(leftToons) > len(rightToons):
+ PoLR = rightToons
+ PoMR = leftToons
+ else:
+ PoLR = leftToons
+ PoMR = rightToons
+ upper = 1 + 4 * abs(len(leftToons) - len(rightToons))
+ if random.randint(0, upper) > 0:
+ toonDodgeList = PoLR
+ else:
+ toonDodgeList = PoMR
+ if toonDodgeList is leftToons:
+ sidestepAnim = 'sidestep-left'
+ soundEffect = globalBattleSoundCache.getSound('AV_side_step.mp3')
+ else:
+ sidestepAnim = 'sidestep-right'
+ soundEffect = globalBattleSoundCache.getSound('AV_jump_to_side.mp3')
+ toonTracks = Parallel()
+ for t in toonDodgeList:
+ toonTracks.append(Sequence(ActorInterval(t, sidestepAnim), Func(t.loop, 'neutral')))
+
+ toonTracks.append(Sequence(ActorInterval(toon, sidestepAnim), Func(toon.loop, 'neutral')))
+ toonTracks.append(Sequence(Wait(0.5), SoundInterval(soundEffect, node=toon)))
+ return toonTracks
+
+
+def getPropTrack(prop, parent, posPoints, appearDelay, remainDelay, scaleUpPoint = Point3(1), scaleUpTime = 0.5, scaleDownTime = 0.5, startScale = Point3(0.01), anim = 0, propName = 'none', animDuration = 0.0, animStartTime = 0.0):
+ if anim == 1:
+ track = Sequence(Wait(appearDelay), Func(__showProp, prop, parent, *posPoints), LerpScaleInterval(prop, scaleUpTime, scaleUpPoint, startScale=startScale), ActorInterval(prop, propName, duration=animDuration, startTime=animStartTime), Wait(remainDelay), Func(MovieUtil.removeProp, prop))
+ else:
+ track = Sequence(Wait(appearDelay), Func(__showProp, prop, parent, *posPoints), LerpScaleInterval(prop, scaleUpTime, scaleUpPoint, startScale=startScale), Wait(remainDelay), LerpScaleInterval(prop, scaleDownTime, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProp, prop))
+ return track
+
+
+def getPropAppearTrack(prop, parent, posPoints, appearDelay, scaleUpPoint = Point3(1), scaleUpTime = 0.5, startScale = Point3(0.01), poseExtraArgs = None):
+ propTrack = Sequence(Wait(appearDelay), Func(__showProp, prop, parent, *posPoints))
+ if poseExtraArgs:
+ propTrack.append(Func(prop.pose, *poseExtraArgs))
+ propTrack.append(LerpScaleInterval(prop, scaleUpTime, scaleUpPoint, startScale=startScale))
+ return propTrack
+
+
+def getPropThrowTrack(attack, prop, hitPoints = [], missPoints = [], hitDuration = 0.5, missDuration = 0.5, hitPointNames = 'none', missPointNames = 'none', lookAt = 'none', groundPointOffSet = 0, missScaleDown = None, parent = render):
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ battle = attack['battle']
+
+ def getLambdas(list, prop, toon):
+ for i in range(len(list)):
+ if list[i] == 'face':
+ list[i] = lambda toon = toon: __toonFacePoint(toon)
+ elif list[i] == 'miss':
+ list[i] = lambda prop = prop, toon = toon: __toonMissPoint(prop, toon)
+ elif list[i] == 'bounceHit':
+ list[i] = lambda prop = prop, toon = toon: __throwBounceHitPoint(prop, toon)
+ elif list[i] == 'bounceMiss':
+ list[i] = lambda prop = prop, toon = toon: __throwBounceMissPoint(prop, toon)
+
+ return list
+
+ if hitPointNames != 'none':
+ hitPoints = getLambdas(hitPointNames, prop, toon)
+ if missPointNames != 'none':
+ missPoints = getLambdas(missPointNames, prop, toon)
+ propTrack = Sequence()
+ propTrack.append(Func(battle.movie.needRestoreRenderProp, prop))
+ propTrack.append(Func(prop.wrtReparentTo, parent))
+ if lookAt != 'none':
+ propTrack.append(Func(prop.lookAt, lookAt))
+ if dmg > 0:
+ for i in range(len(hitPoints)):
+ pos = hitPoints[i]
+ propTrack.append(LerpPosInterval(prop, hitDuration, pos=pos))
+
+ else:
+ for i in range(len(missPoints)):
+ pos = missPoints[i]
+ propTrack.append(LerpPosInterval(prop, missDuration, pos=pos))
+
+ if missScaleDown:
+ propTrack.append(LerpScaleInterval(prop, missScaleDown, MovieUtil.PNT3_NEARZERO))
+ propTrack.append(Func(MovieUtil.removeProp, prop))
+ propTrack.append(Func(battle.movie.clearRenderProp, prop))
+ return propTrack
+
+
+def getThrowTrack(object, target, duration = 1.0, parent = render, gravity = -32.144):
+ values = {}
+
+ def calcOriginAndVelocity(object = object, target = target, values = values, duration = duration, parent = parent, gravity = gravity):
+ if callable(target):
+ target = target()
+ object.wrtReparentTo(parent)
+ values['origin'] = object.getPos(parent)
+ origin = object.getPos(parent)
+ values['velocity'] = (target[2] - origin[2] - 0.5 * gravity * duration * duration) / duration
+
+ return Sequence(Func(calcOriginAndVelocity), LerpFunctionInterval(throwPos, fromData=0.0, toData=1.0, duration=duration, extraArgs=[object,
+ duration,
+ target,
+ values,
+ gravity]))
+
+
+def throwPos(t, object, duration, target, values, gravity = -32.144):
+ origin = values['origin']
+ velocity = values['velocity']
+ if callable(target):
+ target = target()
+ x = origin[0] * (1 - t) + target[0] * t
+ y = origin[1] * (1 - t) + target[1] * t
+ time = t * duration
+ z = origin[2] + velocity * time + 0.5 * gravity * time * time
+ object.setPos(x, y, z)
+
+
+def getToonTakeDamageTrack(toon, died, dmg, delay, damageAnimNames = None, splicedDamageAnims = None, showDamageExtraTime = 0.01):
+ toonTrack = Sequence()
+ toonTrack.append(Wait(delay))
+ if damageAnimNames:
+ for d in damageAnimNames:
+ toonTrack.append(ActorInterval(toon, d))
+
+ indicatorTrack = Sequence(Wait(delay + showDamageExtraTime), Func(__doDamage, toon, dmg, died))
+ else:
+ splicedAnims = getSplicedAnimsTrack(splicedDamageAnims, actor=toon)
+ toonTrack.append(splicedAnims)
+ indicatorTrack = Sequence(Wait(delay + showDamageExtraTime), Func(__doDamage, toon, dmg, died))
+ toonTrack.append(Func(toon.loop, 'neutral'))
+ if died:
+ toonTrack.append(Wait(5.0))
+ return Parallel(toonTrack, indicatorTrack)
+
+
+def getSplicedAnimsTrack(anims, actor = None):
+ track = Sequence()
+ for nextAnim in anims:
+ delay = 1e-06
+ if len(nextAnim) >= 2:
+ if nextAnim[1] > 0:
+ delay = nextAnim[1]
+ if len(nextAnim) <= 0:
+ track.append(Wait(delay))
+ elif len(nextAnim) == 1:
+ track.append(ActorInterval(actor, nextAnim[0]))
+ elif len(nextAnim) == 2:
+ track.append(Wait(delay))
+ track.append(ActorInterval(actor, nextAnim[0]))
+ elif len(nextAnim) == 3:
+ track.append(Wait(delay))
+ track.append(ActorInterval(actor, nextAnim[0], startTime=nextAnim[2]))
+ elif len(nextAnim) == 4:
+ track.append(Wait(delay))
+ duration = nextAnim[3]
+ if duration < 0:
+ startTime = nextAnim[2]
+ endTime = startTime + duration
+ if endTime <= 0:
+ endTime = 0.01
+ track.append(ActorInterval(actor, nextAnim[0], startTime=startTime, endTime=endTime))
+ else:
+ track.append(ActorInterval(actor, nextAnim[0], startTime=nextAnim[2], duration=duration))
+ elif len(nextAnim) == 5:
+ track.append(Wait(delay))
+ track.append(ActorInterval(nextAnim[4], nextAnim[0], startTime=nextAnim[2], duration=nextAnim[3]))
+
+ return track
+
+
+def getSplicedLerpAnims(animName, origDuration, newDuration, startTime = 0, fps = 30, reverse = 0):
+ anims = []
+ addition = 0
+ numAnims = origDuration * fps
+ timeInterval = newDuration / numAnims
+ animInterval = origDuration / numAnims
+ if reverse == 1:
+ animInterval = -animInterval
+ for i in range(0, numAnims):
+ anims.append([animName,
+ timeInterval,
+ startTime + addition,
+ animInterval])
+ addition += animInterval
+
+ return anims
+
+
+def getSoundTrack(fileName, delay = 0.01, duration = None, node = None):
+ soundEffect = globalBattleSoundCache.getSound(fileName)
+ if duration:
+ return Sequence(Wait(delay), SoundInterval(soundEffect, duration=duration, node=node))
+ else:
+ return Sequence(Wait(delay), SoundInterval(soundEffect, node=node))
+
+
+def doClipOnTie(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ tie = globalPropPool.getProp('clip-on-tie')
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ throwDelay = 2.17
+ damageDelay = 3.3
+ dodgeDelay = 3.1
+ elif suitType == 'b':
+ throwDelay = 2.17
+ damageDelay = 3.3
+ dodgeDelay = 3.1
+ elif suitType == 'c':
+ throwDelay = 1.45
+ damageDelay = 2.61
+ dodgeDelay = 2.34
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(0.66, 0.51, 0.28), VBase3(-69.652, -17.199, 67.96)]
+ tiePropTrack = Sequence(getPropAppearTrack(tie, suit.getRightHand(), posPoints, 0.5, MovieUtil.PNT3_ONE, scaleUpTime=0.5, poseExtraArgs=['clip-on-tie', 0]))
+ if dmg > 0:
+ tiePropTrack.append(ActorInterval(tie, 'clip-on-tie', duration=throwDelay, startTime=1.1))
+ else:
+ tiePropTrack.append(Wait(throwDelay))
+ tiePropTrack.append(Func(tie.setHpr, Point3(0, -90, 0)))
+ tiePropTrack.append(getPropThrowTrack(attack, tie, [__toonFacePoint(toon)], [__toonGroundPoint(attack, toon, 0.1)], hitDuration=0.4, missDuration=0.8, missScaleDown=1.2))
+ toonTrack = getToonTrack(attack, damageDelay, ['conked'], dodgeDelay, ['sidestep'])
+ throwSound = getSoundTrack('SA_powertie_throw.mp3', delay=throwDelay + 1, node=suit)
+ return Parallel(suitTrack, toonTrack, tiePropTrack, throwSound)
+
+
+def doPoundKey(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ phone = globalPropPool.getProp('phone')
+ receiver = globalPropPool.getProp('receiver')
+ BattleParticles.loadParticles()
+ particleEffect = BattleParticles.createParticleEffect('PoundKey')
+ BattleParticles.setEffectTexture(particleEffect, 'poundsign', color=Vec4(0, 0, 0, 1))
+ suitTrack = getSuitTrack(attack)
+ partTrack = getPartTrack(particleEffect, 2.1, 1.55, [particleEffect, suit, 0])
+ phonePosPoints = [Point3(0.23, 0.17, -0.11), VBase3(5.939, 2.763, -177.591)]
+ receiverPosPoints = [Point3(0.23, 0.17, -0.11), VBase3(5.939, 2.763, -177.591)]
+ propTrack = Sequence(Wait(0.3), Func(__showProp, phone, suit.getLeftHand(), phonePosPoints[0], phonePosPoints[1]), Func(__showProp, receiver, suit.getLeftHand(), receiverPosPoints[0], receiverPosPoints[1]), LerpScaleInterval(phone, 0.5, MovieUtil.PNT3_ONE, MovieUtil.PNT3_NEARZERO), Wait(0.74), Func(receiver.wrtReparentTo, suit.getRightHand()), LerpPosHprInterval(receiver, 0.0001, Point3(-0.45, 0.48, -0.62), VBase3(-87.47, -18.21, 7.82)), Wait(3.14), Func(receiver.wrtReparentTo, phone), Wait(0.62), LerpScaleInterval(phone, 0.5, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProps, [receiver, phone]))
+ toonTrack = getToonTrack(attack, 2.7, ['cringe'], 1.9, ['sidestep'])
+ soundTrack = getSoundTrack('SA_hangup.mp3', delay=1.3, node=suit)
+ return Parallel(suitTrack, toonTrack, propTrack, partTrack, soundTrack)
+
+
+def doShred(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ paper = globalPropPool.getProp('shredder-paper')
+ shredder = globalPropPool.getProp('shredder')
+ particleEffect = BattleParticles.createParticleEffect('Shred')
+ suitTrack = getSuitTrack(attack)
+ partTrack = getPartTrack(particleEffect, 3.5, 1.9, [particleEffect, suit, 0])
+ paperPosPoints = [Point3(0.59, -0.31, 0.81), VBase3(79.224, 32.576, -179.449)]
+ paperPropTrack = getPropTrack(paper, suit.getRightHand(), paperPosPoints, 2.4, 1e-05, scaleUpTime=0.2, anim=1, propName='shredder-paper', animDuration=1.5, animStartTime=2.8)
+ shredderPosPoints = [Point3(0, -0.12, -0.34), VBase3(-90.0, -53.77, -0.0)]
+ shredderPropTrack = getPropTrack(shredder, suit.getLeftHand(), shredderPosPoints, 1, 3, scaleUpPoint=Point3(4.81, 4.81, 4.81))
+ toonTrack = getToonTrack(attack, suitTrack.getDuration() - 1.1, ['conked'], suitTrack.getDuration() - 3.1, ['sidestep'])
+ soundTrack = getSoundTrack('SA_shred.mp3', delay=3.4, node=suit)
+ return Parallel(suitTrack, paperPropTrack, shredderPropTrack, partTrack, toonTrack, soundTrack)
+
+
+def doFillWithLead(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ pencil = globalPropPool.getProp('pencil')
+ sharpener = globalPropPool.getProp('sharpener')
+ BattleParticles.loadParticles()
+ sprayEffect = BattleParticles.createParticleEffect(file='fillWithLeadSpray')
+ headSmotherEffect = BattleParticles.createParticleEffect(file='fillWithLeadSmother')
+ torsoSmotherEffect = BattleParticles.createParticleEffect(file='fillWithLeadSmother')
+ legsSmotherEffect = BattleParticles.createParticleEffect(file='fillWithLeadSmother')
+ BattleParticles.setEffectTexture(sprayEffect, 'roll-o-dex', color=Vec4(0, 0, 0, 1))
+ BattleParticles.setEffectTexture(headSmotherEffect, 'roll-o-dex', color=Vec4(0, 0, 0, 1))
+ BattleParticles.setEffectTexture(torsoSmotherEffect, 'roll-o-dex', color=Vec4(0, 0, 0, 1))
+ BattleParticles.setEffectTexture(legsSmotherEffect, 'roll-o-dex', color=Vec4(0, 0, 0, 1))
+ suitTrack = getSuitTrack(attack)
+ sprayTrack = getPartTrack(sprayEffect, 2.5, 1.9, [sprayEffect, suit, 0])
+ pencilPosPoints = [Point3(-0.29, -0.33, -0.13), VBase3(160.565, -11.653, -169.244)]
+ pencilPropTrack = getPropTrack(pencil, suit.getRightHand(), pencilPosPoints, 0.7, 3.2, scaleUpTime=0.2)
+ sharpenerPosPoints = [Point3(0.0, 0.0, -0.03), MovieUtil.PNT3_ZERO]
+ sharpenerPropTrack = getPropTrack(sharpener, suit.getLeftHand(), sharpenerPosPoints, 1.3, 2.3, scaleUpPoint=MovieUtil.PNT3_ONE)
+ damageAnims = []
+ damageAnims.append(['conked',
+ suitTrack.getDuration() - 1.5,
+ 1e-05,
+ 1.4])
+ damageAnims.append(['conked',
+ 1e-05,
+ 0.7,
+ 0.7])
+ damageAnims.append(['conked',
+ 1e-05,
+ 0.7,
+ 0.7])
+ damageAnims.append(['conked', 1e-05, 1.4])
+ toonTrack = getToonTrack(attack, splicedDamageAnims=damageAnims, dodgeDelay=suitTrack.getDuration() - 3.1, dodgeAnimNames=['sidestep'], showDamageExtraTime=4.5, showMissedExtraTime=1.6)
+ animal = toon.style.getAnimal()
+ bodyScale = ToontownGlobals.toonBodyScales[animal]
+ headEffectHeight = __toonFacePoint(toon).getZ()
+ legsHeight = ToontownGlobals.legHeightDict[toon.style.legs] * bodyScale
+ torsoEffectHeight = ToontownGlobals.torsoHeightDict[toon.style.torso] * bodyScale / 2 + legsHeight
+ legsEffectHeight = legsHeight / 2
+ effectX = headSmotherEffect.getX()
+ effectY = headSmotherEffect.getY()
+ headSmotherEffect.setPos(effectX, effectY - 1.5, headEffectHeight)
+ torsoSmotherEffect.setPos(effectX, effectY - 1, torsoEffectHeight)
+ legsSmotherEffect.setPos(effectX, effectY - 0.6, legsEffectHeight)
+ partDelay = 3.5
+ partIvalDelay = 0.7
+ partDuration = 1.0
+ headTrack = getPartTrack(headSmotherEffect, partDelay, partDuration, [headSmotherEffect, toon, 0])
+ torsoTrack = getPartTrack(torsoSmotherEffect, partDelay + partIvalDelay, partDuration, [torsoSmotherEffect, toon, 0])
+ legsTrack = getPartTrack(legsSmotherEffect, partDelay + partIvalDelay * 2, partDuration, [legsSmotherEffect, toon, 0])
+
+ def colorParts(parts):
+ track = Parallel()
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ track.append(Func(nextPart.setColorScale, Vec4(0, 0, 0, 1)))
+
+ return track
+
+ def resetParts(parts):
+ track = Parallel()
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ track.append(Func(nextPart.clearColorScale))
+
+ return track
+
+ if dmg > 0:
+ colorTrack = Sequence()
+ headParts = toon.getHeadParts()
+ torsoParts = toon.getTorsoParts()
+ legsParts = toon.getLegsParts()
+ colorTrack.append(Wait(partDelay + 0.2))
+ colorTrack.append(Func(battle.movie.needRestoreColor))
+ colorTrack.append(colorParts(headParts))
+ colorTrack.append(Wait(partIvalDelay))
+ colorTrack.append(colorParts(torsoParts))
+ colorTrack.append(Wait(partIvalDelay))
+ colorTrack.append(colorParts(legsParts))
+ colorTrack.append(Wait(2.5))
+ colorTrack.append(resetParts(headParts))
+ colorTrack.append(resetParts(torsoParts))
+ colorTrack.append(resetParts(legsParts))
+ colorTrack.append(Func(battle.movie.clearRestoreColor))
+ return Parallel(suitTrack, pencilPropTrack, sharpenerPropTrack, sprayTrack, headTrack, torsoTrack, legsTrack, colorTrack, toonTrack)
+ else:
+ return Parallel(suitTrack, pencilPropTrack, sharpenerPropTrack, sprayTrack, toonTrack)
+
+
+def doFountainPen(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ pen = globalPropPool.getProp('pen')
+
+ def getPenTip(pen = pen):
+ tip = pen.find('**/joint_toSpray')
+ return tip.getPos(render)
+
+ hitPoint = lambda toon = toon: __toonFacePoint(toon)
+ missPoint = lambda prop = pen, toon = toon: __toonMissPoint(prop, toon, 0, parent=render)
+ hitSprayTrack = MovieUtil.getSprayTrack(battle, VBase4(0, 0, 0, 1), getPenTip, hitPoint, 0.2, 0.2, 0.2, horizScale=0.1, vertScale=0.1)
+ missSprayTrack = MovieUtil.getSprayTrack(battle, VBase4(0, 0, 0, 1), getPenTip, missPoint, 0.2, 0.2, 0.2, horizScale=0.1, vertScale=0.1)
+ suitTrack = getSuitTrack(attack)
+ propTrack = Sequence(Wait(0.01), Func(__showProp, pen, suit.getRightHand(), MovieUtil.PNT3_ZERO), LerpScaleInterval(pen, 0.5, Point3(1.5, 1.5, 1.5)), Wait(1.05))
+ if dmg > 0:
+ propTrack.append(hitSprayTrack)
+ else:
+ propTrack.append(missSprayTrack)
+ propTrack += [LerpScaleInterval(pen, 0.5, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProp, pen)]
+ splashTrack = Sequence()
+ if dmg > 0:
+
+ def prepSplash(splash, targetPoint):
+ splash.reparentTo(render)
+ splash.setPos(targetPoint)
+ scale = splash.getScale()
+ splash.setBillboardPointWorld()
+ splash.setScale(scale)
+
+ splash = globalPropPool.getProp('splash-from-splat')
+ splash.setColor(0, 0, 0, 1)
+ splash.setScale(0.15)
+ splashTrack = Sequence(Func(battle.movie.needRestoreRenderProp, splash), Wait(1.65), Func(prepSplash, splash, __toonFacePoint(toon)), ActorInterval(splash, 'splash-from-splat'), Func(MovieUtil.removeProp, splash), Func(battle.movie.clearRenderProp, splash))
+ headParts = toon.getHeadParts()
+ splashTrack.append(Func(battle.movie.needRestoreColor))
+ for partNum in range(0, headParts.getNumPaths()):
+ nextPart = headParts.getPath(partNum)
+ splashTrack.append(Func(nextPart.setColorScale, Vec4(0, 0, 0, 1)))
+
+ splashTrack.append(Func(MovieUtil.removeProp, splash))
+ splashTrack.append(Wait(2.6))
+ for partNum in range(0, headParts.getNumPaths()):
+ nextPart = headParts.getPath(partNum)
+ splashTrack.append(Func(nextPart.clearColorScale))
+
+ splashTrack.append(Func(battle.movie.clearRestoreColor))
+ penSpill = BattleParticles.createParticleEffect(file='penSpill')
+ penSpill.setPos(getPenTip())
+ penSpillTrack = getPartTrack(penSpill, 1.4, 0.7, [penSpill, pen, 0])
+ toonTrack = getToonTrack(attack, 1.81, ['conked'], dodgeDelay=0.11, splicedDodgeAnims=[['duck', 0.01, 0.6]], showMissedExtraTime=1.66)
+ soundTrack = getSoundTrack('SA_fountain_pen.mp3', delay=1.6, node=suit)
+ return Parallel(suitTrack, toonTrack, propTrack, soundTrack, penSpillTrack, splashTrack)
+
+
+def doRubOut(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ pad = globalPropPool.getProp('pad')
+ pencil = globalPropPool.getProp('pencil')
+ headEffect = BattleParticles.createParticleEffect('RubOut', color=toon.style.getHeadColor())
+ torsoEffect = BattleParticles.createParticleEffect('RubOut', color=toon.style.getArmColor())
+ legsEffect = BattleParticles.createParticleEffect('RubOut', color=toon.style.getLegColor())
+ suitTrack = getSuitTrack(attack)
+ padPosPoints = [Point3(-0.66, 0.81, -0.06), VBase3(14.93, -2.29, 180.0)]
+ padPropTrack = getPropTrack(pad, suit.getLeftHand(), padPosPoints, 0.5, 2.57)
+ pencilPosPoints = [Point3(0.04, -0.38, -0.1), VBase3(-170.223, -3.762, -62.929)]
+ pencilPropTrack = getPropTrack(pencil, suit.getRightHand(), pencilPosPoints, 0.5, 2.57)
+ toonTrack = getToonTrack(attack, 2.2, ['conked'], 2.0, ['jump'])
+ hideTrack = Sequence()
+ headParts = toon.getHeadParts()
+ torsoParts = toon.getTorsoParts()
+ legsParts = toon.getLegsParts()
+ animal = toon.style.getAnimal()
+ bodyScale = ToontownGlobals.toonBodyScales[animal]
+ headEffectHeight = __toonFacePoint(toon).getZ()
+ legsHeight = ToontownGlobals.legHeightDict[toon.style.legs] * bodyScale
+ torsoEffectHeight = ToontownGlobals.torsoHeightDict[toon.style.torso] * bodyScale / 2 + legsHeight
+ legsEffectHeight = legsHeight / 2
+ effectX = headEffect.getX()
+ effectY = headEffect.getY()
+ headEffect.setPos(effectX, effectY - 1.5, headEffectHeight)
+ torsoEffect.setPos(effectX, effectY - 1, torsoEffectHeight)
+ legsEffect.setPos(effectX, effectY - 0.6, legsEffectHeight)
+ partDelay = 2.5
+ headTrack = getPartTrack(headEffect, partDelay + 0, 0.5, [headEffect, toon, 0])
+ torsoTrack = getPartTrack(torsoEffect, partDelay + 1.1, 0.5, [torsoEffect, toon, 0])
+ legsTrack = getPartTrack(legsEffect, partDelay + 2.2, 0.5, [legsEffect, toon, 0])
+
+ def hideParts(parts):
+ track = Parallel()
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ track.append(Func(nextPart.setTransparency, 1))
+ track.append(LerpFunctionInterval(nextPart.setAlphaScale, fromData=1, toData=0, duration=0.2))
+
+ return track
+
+ def showParts(parts):
+ track = Parallel()
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ track.append(Func(nextPart.clearColorScale))
+ track.append(Func(nextPart.clearTransparency))
+
+ return track
+
+ soundTrack = getSoundTrack('SA_rubout.mp3', delay=1.7, node=suit)
+ if dmg > 0:
+ hideTrack.append(Wait(2.2))
+ hideTrack.append(Func(battle.movie.needRestoreColor))
+ hideTrack.append(hideParts(headParts))
+ hideTrack.append(Wait(0.4))
+ hideTrack.append(hideParts(torsoParts))
+ hideTrack.append(Wait(0.4))
+ hideTrack.append(hideParts(legsParts))
+ hideTrack.append(Wait(1))
+ hideTrack.append(showParts(headParts))
+ hideTrack.append(showParts(torsoParts))
+ hideTrack.append(showParts(legsParts))
+ hideTrack.append(Func(battle.movie.clearRestoreColor))
+ return Parallel(suitTrack, toonTrack, padPropTrack, pencilPropTrack, soundTrack, hideTrack, headTrack, torsoTrack, legsTrack)
+ else:
+ return Parallel(suitTrack, toonTrack, padPropTrack, pencilPropTrack, soundTrack)
+
+
+def doFingerWag(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ BattleParticles.loadParticles()
+ particleEffect = BattleParticles.createParticleEffect('FingerWag')
+ BattleParticles.setEffectTexture(particleEffect, 'blah', color=Vec4(0.55, 0, 0.55, 1))
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ partDelay = 1.3
+ damageDelay = 2.7
+ dodgeDelay = 1.7
+ elif suitType == 'b':
+ partDelay = 1.3
+ damageDelay = 2.7
+ dodgeDelay = 1.8
+ elif suitType == 'c':
+ partDelay = 1.3
+ damageDelay = 2.7
+ dodgeDelay = 2.0
+ suitTrack = getSuitTrack(attack)
+ partTrack = getPartTrack(particleEffect, partDelay, 2, [particleEffect, suit, 0])
+ suitName = attack['suitName']
+ if suitName == 'mm':
+ particleEffect.setPos(0.167, 1.5, 2.731)
+ elif suitName == 'tw':
+ particleEffect.setPos(0.167, 1.8, 5)
+ particleEffect.setHpr(-90.0, -60.0, 180.0)
+ elif suitName == 'pp':
+ particleEffect.setPos(0.167, 1, 4.1)
+ elif suitName == 'bs':
+ particleEffect.setPos(0.167, 1, 5.1)
+ elif suitName == 'bw':
+ particleEffect.setPos(0.167, 1.9, suit.getHeight() - 1.8)
+ particleEffect.setP(-110)
+ toonTrack = getToonTrack(attack, damageDelay, ['slip-backward'], dodgeDelay, ['sidestep'])
+ soundTrack = getSoundTrack('SA_finger_wag.mp3', delay=1.3, node=suit)
+ return Parallel(suitTrack, toonTrack, partTrack, soundTrack)
+
+
+def doWriteOff(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ pad = globalPropPool.getProp('pad')
+ pencil = globalPropPool.getProp('pencil')
+ BattleParticles.loadParticles()
+ checkmark = MovieUtil.copyProp(BattleParticles.getParticle('checkmark'))
+ checkmark.setBillboardPointEye()
+ suitTrack = getSuitTrack(attack)
+ padPosPoints = [Point3(-0.25, 1.38, -0.08), VBase3(-19.078, -6.603, -171.594)]
+ padPropTrack = getPropTrack(pad, suit.getLeftHand(), padPosPoints, 0.5, 2.57, Point3(1.89, 1.89, 1.89))
+ missPoint = lambda checkmark = checkmark, toon = toon: __toonMissPoint(checkmark, toon)
+ pencilPosPoints = [Point3(-0.47, 1.08, 0.28), VBase3(21.045, 12.702, -176.374)]
+ extraArgsForShowProp = [pencil, suit.getRightHand()]
+ extraArgsForShowProp.extend(pencilPosPoints)
+ pencilPropTrack = Sequence(Wait(0.5), Func(__showProp, *extraArgsForShowProp), LerpScaleInterval(pencil, 0.5, Point3(1.5, 1.5, 1.5), startScale=Point3(0.01)), Wait(2), Func(battle.movie.needRestoreRenderProp, checkmark), Func(checkmark.reparentTo, render), Func(checkmark.setScale, 1.6), Func(checkmark.setPosHpr, pencil, 0, 0, 0, 0, 0, 0), Func(checkmark.setP, 0), Func(checkmark.setR, 0))
+ pencilPropTrack.append(getPropThrowTrack(attack, checkmark, [__toonFacePoint(toon)], [missPoint]))
+ pencilPropTrack.append(Func(MovieUtil.removeProp, checkmark))
+ pencilPropTrack.append(Func(battle.movie.clearRenderProp, checkmark))
+ pencilPropTrack.append(Wait(0.3))
+ pencilPropTrack.append(LerpScaleInterval(pencil, 0.5, MovieUtil.PNT3_NEARZERO))
+ pencilPropTrack.append(Func(MovieUtil.removeProp, pencil))
+ toonTrack = getToonTrack(attack, 3.4, ['slip-forward'], 2.4, ['sidestep'])
+ soundTrack = Sequence(Wait(2.3), SoundInterval(globalBattleSoundCache.getSound('SA_writeoff_pen_only.mp3'), duration=0.9, node=suit), SoundInterval(globalBattleSoundCache.getSound('SA_writeoff_ding_only.mp3'), node=suit))
+ return Parallel(suitTrack, toonTrack, padPropTrack, pencilPropTrack, soundTrack)
+
+
+def doRubberStamp(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ suitTrack = getSuitTrack(attack)
+ stamp = globalPropPool.getProp('rubber-stamp')
+ pad = globalPropPool.getProp('pad')
+ cancelled = __makeCancelledNodePath()
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ padPosPoints = [Point3(-0.65, 0.83, -0.04), VBase3(5.625, 4.456, -165.125)]
+ stampPosPoints = [Point3(-0.64, -0.17, -0.03), MovieUtil.PNT3_ZERO]
+ elif suitType == 'c':
+ padPosPoints = [Point3(0.19, -0.55, -0.21), VBase3(-166.76, -4.001, -1.658)]
+ stampPosPoints = [Point3(-0.64, -0.08, 0.11), MovieUtil.PNT3_ZERO]
+ else:
+ padPosPoints = [Point3(-0.65, 0.83, -0.04), VBase3(5.625, 4.456, -165.125)]
+ stampPosPoints = [Point3(-0.64, -0.17, -0.03), MovieUtil.PNT3_ZERO]
+ padPropTrack = getPropTrack(pad, suit.getLeftHand(), padPosPoints, 1e-06, 3.2)
+ missPoint = lambda cancelled = cancelled, toon = toon: __toonMissPoint(cancelled, toon)
+ propTrack = Sequence(Func(__showProp, stamp, suit.getRightHand(), stampPosPoints[0], stampPosPoints[1]), LerpScaleInterval(stamp, 0.5, MovieUtil.PNT3_ONE), Wait(2.6), Func(battle.movie.needRestoreRenderProp, cancelled), Func(cancelled.reparentTo, render), Func(cancelled.setScale, 0.6), Func(cancelled.setPosHpr, stamp, 0.81, -1.11, -0.16, 0, 0, 90), Func(cancelled.setP, 0), Func(cancelled.setR, 0))
+ propTrack.append(getPropThrowTrack(attack, cancelled, [__toonFacePoint(toon)], [missPoint]))
+ propTrack.append(Func(MovieUtil.removeProp, cancelled))
+ propTrack.append(Func(battle.movie.clearRenderProp, cancelled))
+ propTrack.append(Wait(0.3))
+ propTrack.append(LerpScaleInterval(stamp, 0.5, MovieUtil.PNT3_NEARZERO))
+ propTrack.append(Func(MovieUtil.removeProp, stamp))
+ toonTrack = getToonTrack(attack, 3.4, ['conked'], 1.9, ['sidestep'])
+ soundTrack = getSoundTrack('SA_rubber_stamp.mp3', delay=1.3, duration=1.1, node=suit)
+ return Parallel(suitTrack, toonTrack, propTrack, padPropTrack, soundTrack)
+
+
+def doRazzleDazzle(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ hitSuit = dmg > 0
+ sign = globalPropPool.getProp('smile')
+ BattleParticles.loadParticles()
+ particleEffect = BattleParticles.createParticleEffect('Smile')
+ suitTrack = getSuitTrack(attack)
+ signPosPoints = [Point3(0.0, -0.42, -0.04), VBase3(105.715, 73.977, 65.932)]
+ if hitSuit:
+ hitPoint = lambda toon = toon: __toonFacePoint(toon)
+ else:
+ hitPoint = lambda particleEffect = particleEffect, toon = toon, suit = suit: __toonMissPoint(particleEffect, toon, parent=suit.getRightHand())
+ signPropTrack = Sequence(Wait(0.5), Func(__showProp, sign, suit.getRightHand(), signPosPoints[0], signPosPoints[1]), LerpScaleInterval(sign, 0.5, Point3(1.39, 1.39, 1.39)), Wait(0.5), Func(battle.movie.needRestoreParticleEffect, particleEffect), Func(particleEffect.start, sign), Func(particleEffect.wrtReparentTo, render), LerpPosInterval(particleEffect, 2.0, pos=hitPoint), Func(particleEffect.cleanup), Func(battle.movie.clearRestoreParticleEffect, particleEffect))
+ signPropAnimTrack = ActorInterval(sign, 'smile', duration=4, startTime=0)
+ toonTrack = getToonTrack(attack, 2.6, ['cringe'], 1.9, ['sidestep'])
+ soundTrack = getSoundTrack('SA_razzle_dazzle.mp3', delay=1.6, node=suit)
+ return Sequence(Parallel(suitTrack, signPropTrack, signPropAnimTrack, toonTrack, soundTrack), Func(MovieUtil.removeProp, sign))
+
+
+def doSynergy(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ targets = attack['target']
+ damageDelay = 1.7
+ hitAtleastOneToon = 0
+ for t in targets:
+ if t['hp'] > 0:
+ hitAtleastOneToon = 1
+
+ particleEffect = BattleParticles.createParticleEffect('Synergy')
+ waterfallEffect = BattleParticles.createParticleEffect(file='synergyWaterfall')
+ suitTrack = getSuitAnimTrack(attack)
+ partTrack = getPartTrack(particleEffect, 1.0, 1.9, [particleEffect, suit, 0])
+ waterfallTrack = getPartTrack(waterfallEffect, 0.8, 1.9, [waterfallEffect, suit, 0])
+ damageAnims = [['slip-forward']]
+ dodgeAnims = []
+ dodgeAnims.append(['jump',
+ 0.01,
+ 0,
+ 0.6])
+ dodgeAnims.extend(getSplicedLerpAnims('jump', 0.31, 1.3, startTime=0.6))
+ dodgeAnims.append(['jump', 0, 0.91])
+ toonTracks = getToonTracks(attack, damageDelay=damageDelay, damageAnimNames=['slip-forward'], dodgeDelay=0.91, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=1.0)
+ synergySoundTrack = Sequence(Wait(0.9), SoundInterval(globalBattleSoundCache.getSound('SA_synergy.mp3'), node=suit))
+ if hitAtleastOneToon > 0:
+ fallingSoundTrack = Sequence(Wait(damageDelay + 0.5), SoundInterval(globalBattleSoundCache.getSound('Toon_bodyfall_synergy.mp3'), node=suit))
+ return Parallel(suitTrack, partTrack, waterfallTrack, synergySoundTrack, fallingSoundTrack, toonTracks)
+ else:
+ return Parallel(suitTrack, partTrack, waterfallTrack, synergySoundTrack, toonTracks)
+
+
+def doTeeOff(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ club = globalPropPool.getProp('golf-club')
+ ball = globalPropPool.getProp('golf-ball')
+ suitTrack = getSuitTrack(attack)
+ clubPosPoints = [MovieUtil.PNT3_ZERO, VBase3(63.097, 43.988, -18.435)]
+ clubPropTrack = getPropTrack(club, suit.getLeftHand(), clubPosPoints, 0.5, 5.2, Point3(1.1, 1.1, 1.1))
+ suitName = attack['suitName']
+ if suitName == 'ym':
+ ballPosPoints = [Point3(2.1, 0, 0.1)]
+ elif suitName == 'tbc':
+ ballPosPoints = [Point3(4.1, 0, 0.1)]
+ elif suitName == 'm':
+ ballPosPoints = [Point3(3.2, 0, 0.1)]
+ elif suitName == 'rb':
+ ballPosPoints = [Point3(4.2, 0, 0.1)]
+ else:
+ ballPosPoints = [Point3(2.1, 0, 0.1)]
+ ballPropTrack = Sequence(getPropAppearTrack(ball, suit, ballPosPoints, 1.7, Point3(1.5, 1.5, 1.5)), Func(battle.movie.needRestoreRenderProp, ball), Func(ball.wrtReparentTo, render), Wait(2.15))
+ missPoint = lambda ball = ball, toon = toon: __toonMissPoint(ball, toon)
+ ballPropTrack.append(getPropThrowTrack(attack, ball, [__toonFacePoint(toon)], [missPoint]))
+ ballPropTrack.append(Func(battle.movie.clearRenderProp, ball))
+ dodgeDelay = suitTrack.getDuration() - 4.35
+ toonTrack = getToonTrack(attack, suitTrack.getDuration() - 2.25, ['conked'], dodgeDelay, ['duck'], showMissedExtraTime=1.7)
+ soundTrack = getSoundTrack('SA_tee_off.mp3', delay=4.1, node=suit)
+ return Parallel(suitTrack, toonTrack, clubPropTrack, ballPropTrack, soundTrack)
+
+
+def doBrainStorm(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ BattleParticles.loadParticles()
+ snowEffect = BattleParticles.createParticleEffect('BrainStorm')
+ snowEffect2 = BattleParticles.createParticleEffect('BrainStorm')
+ snowEffect3 = BattleParticles.createParticleEffect('BrainStorm')
+ effectColor = Vec4(0.65, 0.79, 0.93, 0.85)
+ BattleParticles.setEffectTexture(snowEffect, 'brainstorm-box', color=effectColor)
+ BattleParticles.setEffectTexture(snowEffect2, 'brainstorm-env', color=effectColor)
+ BattleParticles.setEffectTexture(snowEffect3, 'brainstorm-track', color=effectColor)
+ cloud = globalPropPool.getProp('stormcloud')
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ partDelay = 1.2
+ damageDelay = 4.5
+ dodgeDelay = 3.3
+ elif suitType == 'b':
+ partDelay = 1.2
+ damageDelay = 4.5
+ dodgeDelay = 3.3
+ elif suitType == 'c':
+ partDelay = 1.2
+ damageDelay = 4.5
+ dodgeDelay = 3.3
+ suitTrack = getSuitTrack(attack, delay=0.9)
+ initialCloudHeight = suit.height + 3
+ cloudPosPoints = [Point3(0, 3, initialCloudHeight), VBase3(180, 0, 0)]
+ cloudPropTrack = Sequence()
+ cloudPropTrack.append(Func(cloud.pose, 'stormcloud', 0))
+ cloudPropTrack.append(getPropAppearTrack(cloud, suit, cloudPosPoints, 1e-06, Point3(3, 3, 3), scaleUpTime=0.7))
+ cloudPropTrack.append(Func(battle.movie.needRestoreRenderProp, cloud))
+ cloudPropTrack.append(Func(cloud.wrtReparentTo, render))
+ targetPoint = __toonFacePoint(toon)
+ targetPoint.setZ(targetPoint[2] + 3)
+ cloudPropTrack.append(Wait(1.1))
+ cloudPropTrack.append(LerpPosInterval(cloud, 1, pos=targetPoint))
+ cloudPropTrack.append(Wait(partDelay))
+ cloudPropTrack.append(Parallel(ParticleInterval(snowEffect, cloud, worldRelative=0, duration=2.2, cleanup=True), Sequence(Wait(0.5), ParticleInterval(snowEffect2, cloud, worldRelative=0, duration=1.7, cleanup=True)), Sequence(Wait(1.0), ParticleInterval(snowEffect3, cloud, worldRelative=0, duration=1.2, cleanup=True)), Sequence(ActorInterval(cloud, 'stormcloud', startTime=3, duration=0.5), ActorInterval(cloud, 'stormcloud', startTime=2.5, duration=0.5), ActorInterval(cloud, 'stormcloud', startTime=1, duration=1.5))))
+ cloudPropTrack.append(Wait(0.4))
+ cloudPropTrack.append(LerpScaleInterval(cloud, 0.5, MovieUtil.PNT3_NEARZERO))
+ cloudPropTrack.append(Func(MovieUtil.removeProp, cloud))
+ cloudPropTrack.append(Func(battle.movie.clearRenderProp, cloud))
+ damageAnims = [['cringe',
+ 0.01,
+ 0.4,
+ 0.8], ['duck', 1e-06, 1.6]]
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'], showMissedExtraTime=1.1)
+ soundTrack = getSoundTrack('SA_brainstorm.mp3', delay=2.6, node=suit)
+ return Parallel(suitTrack, toonTrack, cloudPropTrack, soundTrack)
+
+
+def doBuzzWord(attack):
+ suit = attack['suit']
+ target = attack['target']
+ toon = target['toon']
+ battle = attack['battle']
+ BattleParticles.loadParticles()
+ particleEffects = []
+ texturesList = ['buzzwords-crash',
+ 'buzzwords-inc',
+ 'buzzwords-main',
+ 'buzzwords-over',
+ 'buzzwords-syn']
+ for i in range(0, 5):
+ effect = BattleParticles.createParticleEffect('BuzzWord')
+ if random.random() > 0.5:
+ BattleParticles.setEffectTexture(effect, texturesList[i], color=Vec4(1, 0.94, 0.02, 1))
+ else:
+ BattleParticles.setEffectTexture(effect, texturesList[i], color=Vec4(0, 0, 0, 1))
+ particleEffects.append(effect)
+
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ partDelay = 4.0
+ partDuration = 2.2
+ damageDelay = 4.5
+ dodgeDelay = 3.8
+ elif suitType == 'b':
+ partDelay = 1.3
+ partDuration = 2
+ damageDelay = 2.5
+ dodgeDelay = 1.8
+ elif suitType == 'c':
+ partDelay = 4.0
+ partDuration = 2.2
+ damageDelay = 4.5
+ dodgeDelay = 3.8
+ suitName = suit.getStyleName()
+ if suitName == 'm':
+ for effect in particleEffects:
+ effect.setPos(0, 2.8, suit.getHeight() - 2.5)
+ effect.setHpr(0, -20, 0)
+
+ elif suitName == 'mm':
+ for effect in particleEffects:
+ effect.setPos(0, 2.1, suit.getHeight() - 0.8)
+
+ suitTrack = getSuitTrack(attack)
+ particleTracks = []
+ for effect in particleEffects:
+ particleTracks.append(getPartTrack(effect, partDelay, partDuration, [effect, suit, 0]))
+
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, damageAnimNames=['cringe'], splicedDodgeAnims=[['duck', dodgeDelay, 1.4]], showMissedExtraTime=dodgeDelay + 0.5)
+ soundTrack = getSoundTrack('SA_buzz_word.mp3', delay=3.9, node=suit)
+ return Parallel(suitTrack, toonTrack, soundTrack, *particleTracks)
+
+
+def doDemotion(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ BattleParticles.loadParticles()
+ sprayEffect = BattleParticles.createParticleEffect('DemotionSpray')
+ freezeEffect = BattleParticles.createParticleEffect('DemotionFreeze')
+ unFreezeEffect = BattleParticles.createParticleEffect(file='demotionUnFreeze')
+ BattleParticles.setEffectTexture(sprayEffect, 'snow-particle')
+ BattleParticles.setEffectTexture(freezeEffect, 'snow-particle')
+ BattleParticles.setEffectTexture(unFreezeEffect, 'snow-particle')
+ facePoint = __toonFacePoint(toon)
+ freezeEffect.setPos(0, 0, facePoint.getZ())
+ unFreezeEffect.setPos(0, 0, facePoint.getZ())
+ suitTrack = getSuitTrack(attack)
+ partTrack = getPartTrack(sprayEffect, 0.7, 1.1, [sprayEffect, suit, 0])
+ partTrack2 = getPartTrack(freezeEffect, 1.4, 2.9, [freezeEffect, toon, 0])
+ partTrack3 = getPartTrack(unFreezeEffect, 6.65, 0.5, [unFreezeEffect, toon, 0])
+ dodgeAnims = [['duck', 1e-06, 0.8]]
+ damageAnims = []
+ damageAnims.append(['cringe',
+ 0.01,
+ 0,
+ 0.5])
+ damageAnims.extend(getSplicedLerpAnims('cringe', 0.4, 0.5, startTime=0.5))
+ damageAnims.extend(getSplicedLerpAnims('cringe', 0.3, 0.5, startTime=0.9))
+ damageAnims.extend(getSplicedLerpAnims('cringe', 0.3, 0.6, startTime=1.2))
+ damageAnims.append(['cringe', 2.6, 1.5])
+ toonTrack = getToonTrack(attack, damageDelay=1.0, splicedDamageAnims=damageAnims, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=1.6, showDamageExtraTime=1.3)
+ soundTrack = getSoundTrack('SA_demotion.mp3', delay=1.2, node=suit)
+ if dmg > 0:
+ return Parallel(suitTrack, toonTrack, soundTrack, partTrack, partTrack2, partTrack3)
+ else:
+ return Parallel(suitTrack, toonTrack, soundTrack, partTrack)
+
+
+def doCanned(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ dmg = target['hp']
+ toon = target['toon']
+ hips = toon.getHipsParts()
+ propDelay = 0.8
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'c':
+ suitDelay = 1.13
+ dodgeDelay = 3.1
+ else:
+ suitDelay = 1.83
+ dodgeDelay = 3.6
+ throwDuration = 1.5
+ can = globalPropPool.getProp('can')
+ scale = 26
+ torso = toon.style.torso
+ torso = torso[0]
+ if torso == 's':
+ scaleUpPoint = Point3(scale * 2.63, scale * 2.63, scale * 1.9975)
+ elif torso == 'm':
+ scaleUpPoint = Point3(scale * 2.63, scale * 2.63, scale * 1.7975)
+ elif torso == 'l':
+ scaleUpPoint = Point3(scale * 2.63, scale * 2.63, scale * 2.31)
+ canHpr = VBase3(-173.47, -0.42, 162.09)
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(-0.14, 0.15, 0.08), VBase3(-10.584, 11.945, -161.684)]
+ throwTrack = Sequence(getPropAppearTrack(can, suit.getRightHand(), posPoints, propDelay, Point3(6, 6, 6), scaleUpTime=0.5))
+ propDelay = propDelay + 0.5
+ throwTrack.append(Wait(suitDelay))
+ hitPoint = toon.getPos(battle)
+ hitPoint.setX(hitPoint.getX() + 1.1)
+ hitPoint.setY(hitPoint.getY() - 0.5)
+ hitPoint.setZ(hitPoint.getZ() + toon.height + 1.1)
+ throwTrack.append(Func(battle.movie.needRestoreRenderProp, can))
+ throwTrack.append(getThrowTrack(can, hitPoint, duration=throwDuration, parent=battle))
+ if dmg > 0:
+ can2 = MovieUtil.copyProp(can)
+ hips1 = hips.getPath(2)
+ hips2 = hips.getPath(1)
+ can2Point = Point3(hitPoint.getX(), hitPoint.getY() + 6.4, hitPoint.getZ())
+ can2.setPos(can2Point)
+ can2.setScale(scaleUpPoint)
+ can2.setHpr(canHpr)
+ throwTrack.append(Func(battle.movie.needRestoreHips))
+ throwTrack.append(Func(can.wrtReparentTo, hips1))
+ throwTrack.append(Func(can2.reparentTo, hips2))
+ throwTrack.append(Wait(2.4))
+ throwTrack.append(Func(MovieUtil.removeProp, can2))
+ throwTrack.append(Func(battle.movie.clearRestoreHips))
+ scaleTrack = Sequence(Wait(propDelay + suitDelay), LerpScaleInterval(can, throwDuration, scaleUpPoint))
+ hprTrack = Sequence(Wait(propDelay + suitDelay), LerpHprInterval(can, throwDuration, canHpr))
+ soundTrack = Sequence(Wait(2.6), SoundInterval(globalBattleSoundCache.getSound('SA_canned_tossup_only.mp3'), node=suit), SoundInterval(globalBattleSoundCache.getSound('SA_canned_impact_only.mp3'), node=suit))
+ else:
+ land = toon.getPos(battle)
+ land.setZ(land.getZ() + 0.7)
+ bouncePoint1 = Point3(land.getX(), land.getY() - 1.5, land.getZ() + 2.5)
+ bouncePoint2 = Point3(land.getX(), land.getY() - 2.1, land.getZ() - 0.2)
+ bouncePoint3 = Point3(land.getX(), land.getY() - 3.1, land.getZ() + 1.5)
+ bouncePoint4 = Point3(land.getX(), land.getY() - 4.1, land.getZ() + 0.3)
+ throwTrack.append(LerpPosInterval(can, 0.4, land))
+ throwTrack.append(LerpPosInterval(can, 0.4, bouncePoint1))
+ throwTrack.append(LerpPosInterval(can, 0.3, bouncePoint2))
+ throwTrack.append(LerpPosInterval(can, 0.3, bouncePoint3))
+ throwTrack.append(LerpPosInterval(can, 0.3, bouncePoint4))
+ throwTrack.append(Wait(1.1))
+ throwTrack.append(LerpScaleInterval(can, 0.3, MovieUtil.PNT3_NEARZERO))
+ scaleTrack = Sequence(Wait(propDelay + suitDelay), LerpScaleInterval(can, throwDuration, Point3(11, 11, 11)))
+ hprTrack = Sequence(Wait(propDelay + suitDelay), LerpHprInterval(can, throwDuration, canHpr), Wait(0.4), LerpHprInterval(can, 0.4, Point3(83.27, 19.52, -177.92)), LerpHprInterval(can, 0.3, Point3(95.24, -72.09, 88.65)), LerpHprInterval(can, 0.2, Point3(-96.34, -2.63, 179.89)))
+ soundTrack = getSoundTrack('SA_canned_tossup_only.mp3', delay=2.6, node=suit)
+ canTrack = Sequence(Parallel(throwTrack, scaleTrack, hprTrack), Func(MovieUtil.removeProp, can), Func(battle.movie.clearRenderProp, can))
+ damageAnims = [['struggle',
+ propDelay + suitDelay + throwDuration,
+ 0.01,
+ 0.7], ['slip-backward', 0.01, 0.45]]
+ toonTrack = getToonTrack(attack, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'], showDamageExtraTime=propDelay + suitDelay + 2.4)
+ return Parallel(suitTrack, toonTrack, canTrack, soundTrack)
+
+
+def doDownsize(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ damageDelay = 2.3
+ sprayEffect = BattleParticles.createParticleEffect(file='downsizeSpray')
+ cloudEffect = BattleParticles.createParticleEffect(file='downsizeCloud')
+ toonPos = toon.getPos(toon)
+ cloudPos = Point3(toonPos.getX(), toonPos.getY(), toonPos.getZ() + toon.getHeight() * 0.55)
+ cloudEffect.setPos(cloudPos)
+ suitTrack = getSuitTrack(attack)
+ sprayTrack = getPartTrack(sprayEffect, 1.0, 1.28, [sprayEffect, suit, 0])
+ cloudTrack = getPartTrack(cloudEffect, 2.1, 1.9, [cloudEffect, toon, 0])
+ if dmg > 0:
+ initialScale = toon.getScale()
+ downScale = Vec3(0.4, 0.4, 0.4)
+ shrinkTrack = Sequence(Wait(damageDelay + 0.5), Func(battle.movie.needRestoreToonScale), LerpScaleInterval(toon, 1.0, downScale * 1.1), LerpScaleInterval(toon, 0.1, downScale * 0.9), LerpScaleInterval(toon, 0.1, downScale * 1.05), LerpScaleInterval(toon, 0.1, downScale * 0.95), LerpScaleInterval(toon, 0.1, downScale), Wait(2.1), LerpScaleInterval(toon, 0.5, initialScale * 1.5), LerpScaleInterval(toon, 0.15, initialScale * 0.5), LerpScaleInterval(toon, 0.15, initialScale * 1.2), LerpScaleInterval(toon, 0.15, initialScale * 0.8), LerpScaleInterval(toon, 0.15, initialScale), Func(battle.movie.clearRestoreToonScale))
+ damageAnims = []
+ damageAnims.append(['juggle',
+ 0.01,
+ 0.87,
+ 0.5])
+ damageAnims.append(['lose',
+ 0.01,
+ 2.17,
+ 0.93])
+ damageAnims.append(['lose',
+ 0.01,
+ 3.1,
+ -0.93])
+ damageAnims.append(['struggle',
+ 0.01,
+ 0.8,
+ 1.8])
+ damageAnims.append(['sidestep-right',
+ 0.01,
+ 2.97,
+ 1.49])
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=0.6, dodgeAnimNames=['sidestep'])
+ if dmg > 0:
+ return Parallel(suitTrack, sprayTrack, cloudTrack, shrinkTrack, toonTrack)
+ else:
+ return Parallel(suitTrack, sprayTrack, toonTrack)
+
+
+def doPinkSlip(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ paper = globalPropPool.getProp('pink-slip')
+ throwDelay = 3.03
+ throwDuration = 0.5
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(0.07, -0.06, -0.18), VBase3(-172.075, -26.715, -89.131)]
+ paperAppearTrack = Sequence(getPropAppearTrack(paper, suit.getRightHand(), posPoints, 0.8, Point3(8, 8, 8), scaleUpTime=0.5))
+ paperAppearTrack.append(Wait(1.73))
+ hitPoint = __toonGroundPoint(attack, toon, 0.2, parent=battle)
+ paperAppearTrack.append(Func(battle.movie.needRestoreRenderProp, paper))
+ paperAppearTrack.append(Func(paper.wrtReparentTo, battle))
+ paperAppearTrack.append(LerpPosInterval(paper, throwDuration, hitPoint))
+ if dmg > 0:
+ paperPause = 0.01
+ slidePoint = Point3(hitPoint.getX(), hitPoint.getY() - 5, hitPoint.getZ() + 4)
+ landPoint = Point3(hitPoint.getX(), hitPoint.getY() - 5, hitPoint.getZ())
+ paperAppearTrack.append(Wait(paperPause))
+ paperAppearTrack.append(LerpPosInterval(paper, 0.2, slidePoint))
+ paperAppearTrack.append(LerpPosInterval(paper, 1.1, landPoint))
+ paperSpinTrack = Sequence(Wait(throwDelay), LerpHprInterval(paper, throwDuration, VBase3(300, 0, 0)), Wait(paperPause), LerpHprInterval(paper, 1.3, VBase3(-200, 100, 100)))
+ else:
+ slidePoint = Point3(hitPoint.getX(), hitPoint.getY() - 5, hitPoint.getZ())
+ paperAppearTrack.append(LerpPosInterval(paper, 0.5, slidePoint))
+ paperSpinTrack = Sequence(Wait(throwDelay), LerpHprInterval(paper, throwDuration, VBase3(300, 0, 0)), LerpHprInterval(paper, 0.5, VBase3(10, 0, 0)))
+ propTrack = Sequence()
+ propTrack.append(Parallel(paperAppearTrack, paperSpinTrack))
+ propTrack.append(LerpScaleInterval(paper, 0.4, MovieUtil.PNT3_NEARZERO))
+ propTrack.append(Func(MovieUtil.removeProp, paper))
+ propTrack.append(Func(battle.movie.clearRenderProp, paper))
+ damageAnims = [['jump',
+ 0.01,
+ 0.3,
+ 0.7], ['slip-forward', 0.01]]
+ toonTrack = getToonTrack(attack, damageDelay=2.81, splicedDamageAnims=damageAnims, dodgeDelay=2.8, dodgeAnimNames=['jump'], showDamageExtraTime=0.9)
+ soundTrack = getSoundTrack('SA_pink_slip.mp3', delay=2.9, duration=1.1, node=suit)
+ return Parallel(suitTrack, toonTrack, propTrack, soundTrack)
+
+
+def doReOrg(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ damageDelay = 1.7
+ attackDelay = 1.7
+ sprayEffect = BattleParticles.createParticleEffect(file='reorgSpray')
+ suitTrack = getSuitTrack(attack)
+ partTrack = getPartTrack(sprayEffect, 1.0, 1.9, [sprayEffect, suit, 0])
+ if dmg > 0:
+ headParts = toon.getHeadParts()
+ print '***********headParts pos=', headParts[0].getPos()
+ print '***********headParts hpr=', headParts[0].getHpr()
+ headTracks = Parallel()
+ for partNum in range(0, headParts.getNumPaths()):
+ part = headParts.getPath(partNum)
+ x = part.getX()
+ y = part.getY()
+ z = part.getZ()
+ h = part.getH()
+ p = part.getP()
+ r = part.getR()
+ headTracks.append(Sequence(Wait(attackDelay), LerpPosInterval(part, 0.1, Point3(x - 0.2, y, z - 0.03)), LerpPosInterval(part, 0.1, Point3(x + 0.4, y, z - 0.03)), LerpPosInterval(part, 0.1, Point3(x - 0.4, y, z - 0.03)), LerpPosInterval(part, 0.1, Point3(x + 0.4, y, z - 0.03)), LerpPosInterval(part, 0.1, Point3(x - 0.2, y, z - 0.04)), LerpPosInterval(part, 0.25, Point3(x, y, z + 2.2)), LerpHprInterval(part, 0.4, VBase3(360, 0, 180)), LerpPosInterval(part, 0.3, Point3(x, y, z + 3.1)), LerpPosInterval(part, 0.15, Point3(x, y, z + 0.3)), Wait(0.15), LerpHprInterval(part, 0.6, VBase3(-745, 0, 180), startHpr=VBase3(0, 0, 180)), LerpHprInterval(part, 0.8, VBase3(25, 0, 180), startHpr=VBase3(0, 0, 180)), LerpPosInterval(part, 0.15, Point3(x, y, z + 1)), LerpHprInterval(part, 0.3, VBase3(h, p, r)), Wait(0.2), LerpPosInterval(part, 0.1, Point3(x, y, z)), Wait(0.9)))
+
+ def getChestTrack(part, attackDelay = attackDelay):
+ origScale = part.getScale()
+ return Sequence(Wait(attackDelay), LerpHprInterval(part, 1.1, VBase3(180, 0, 0)), Wait(1.1), LerpHprInterval(part, 1.1, part.getHpr()))
+
+ chestTracks = Parallel()
+ arms = toon.findAllMatches('**/arms')
+ sleeves = toon.findAllMatches('**/sleeves')
+ hands = toon.findAllMatches('**/hands')
+ print '*************arms hpr=', arms[0].getHpr()
+ for partNum in range(0, arms.getNumPaths()):
+ chestTracks.append(getChestTrack(arms.getPath(partNum)))
+ chestTracks.append(getChestTrack(sleeves.getPath(partNum)))
+ chestTracks.append(getChestTrack(hands.getPath(partNum)))
+
+ damageAnims = [['neutral',
+ 0.01,
+ 0.01,
+ 0.5], ['juggle',
+ 0.01,
+ 0.01,
+ 1.48], ['think', 0.01, 2.28]]
+ dodgeAnims = []
+ dodgeAnims.append(['think',
+ 0.01,
+ 0,
+ 0.6])
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=0.01, dodgeAnimNames=['duck'], showDamageExtraTime=2.1, showMissedExtraTime=2.0)
+ if dmg > 0:
+ return Parallel(suitTrack, partTrack, toonTrack, headTracks, chestTracks)
+ else:
+ return Parallel(suitTrack, partTrack, toonTrack)
+
+
+def doSacked(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ dmg = target['hp']
+ toon = target['toon']
+ hips = toon.getHipsParts()
+ propDelay = 0.85
+ suitDelay = 1.93
+ throwDuration = 0.9
+ sack = globalPropPool.getProp('sandbag')
+ initialScale = Point3(0.65, 1.47, 1.28)
+ scaleUpPoint = Point3(1.05, 1.67, 0.98) * 4.1
+ sackHpr = VBase3(-154.33, -6.33, 163.8)
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(0.51, -2.03, -0.73), VBase3(90.0, -24.98, 77.73)]
+ sackAppearTrack = Sequence(getPropAppearTrack(sack, suit.getRightHand(), posPoints, propDelay, initialScale, scaleUpTime=0.2))
+ propDelay = propDelay + 0.2
+ sackAppearTrack.append(Wait(suitDelay))
+ hitPoint = toon.getPos(battle)
+ if dmg > 0:
+ hitPoint.setX(hitPoint.getX() + 2.1)
+ hitPoint.setY(hitPoint.getY() + 0.9)
+ hitPoint.setZ(hitPoint.getZ() + toon.height + 1.2)
+ else:
+ hitPoint.setZ(hitPoint.getZ() - 0.2)
+ sackAppearTrack.append(Func(battle.movie.needRestoreRenderProp, sack))
+ sackAppearTrack.append(getThrowTrack(sack, hitPoint, duration=throwDuration, parent=battle))
+ if dmg > 0:
+ sack2 = MovieUtil.copyProp(sack)
+ hips1 = hips.getPath(2)
+ hips2 = hips.getPath(1)
+ sack2.hide()
+ sack2.reparentTo(battle)
+ sack2.setPos(Point3(hitPoint.getX(), hitPoint.getY(), hitPoint.getZ()))
+ sack2.setScale(scaleUpPoint)
+ sack2.setHpr(sackHpr)
+ sackAppearTrack.append(Func(battle.movie.needRestoreHips))
+ sackAppearTrack.append(Func(sack.wrtReparentTo, hips1))
+ sackAppearTrack.append(Func(sack2.show))
+ sackAppearTrack.append(Func(sack2.wrtReparentTo, hips2))
+ sackAppearTrack.append(Wait(2.4))
+ sackAppearTrack.append(Func(MovieUtil.removeProp, sack2))
+ sackAppearTrack.append(Func(battle.movie.clearRestoreHips))
+ scaleTrack = Sequence(Wait(propDelay + suitDelay), LerpScaleInterval(sack, throwDuration, scaleUpPoint), Wait(1.8), LerpScaleInterval(sack, 0.3, MovieUtil.PNT3_NEARZERO))
+ hprTrack = Sequence(Wait(propDelay + suitDelay), LerpHprInterval(sack, throwDuration, sackHpr))
+ sackTrack = Sequence(Parallel(sackAppearTrack, scaleTrack, hprTrack), Func(MovieUtil.removeProp, sack), Func(battle.movie.clearRenderProp, sack))
+ else:
+ sackAppearTrack.append(Wait(1.1))
+ sackAppearTrack.append(LerpScaleInterval(sack, 0.3, MovieUtil.PNT3_NEARZERO))
+ sackTrack = Sequence(sackAppearTrack, Func(MovieUtil.removeProp, sack), Func(battle.movie.clearRenderProp, sack))
+ damageAnims = [['struggle',
+ 0.01,
+ 0.01,
+ 0.7], ['slip-backward', 0.01, 0.45]]
+ toonTrack = getToonTrack(attack, damageDelay=propDelay + suitDelay + throwDuration, splicedDamageAnims=damageAnims, dodgeDelay=3.0, dodgeAnimNames=['sidestep'], showDamageExtraTime=1.8, showMissedExtraTime=0.8)
+ return Parallel(suitTrack, toonTrack, sackTrack)
+
+
+def doGlowerPower(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ leftKnives = []
+ rightKnives = []
+ for i in range(0, 3):
+ leftKnives.append(globalPropPool.getProp('dagger'))
+ rightKnives.append(globalPropPool.getProp('dagger'))
+
+ suitTrack = getSuitTrack(attack)
+ suitName = suit.getStyleName()
+ if suitName == 'hh':
+ leftPosPoints = [Point3(0.3, 4.3, 5.3), MovieUtil.PNT3_ZERO]
+ rightPosPoints = [Point3(-0.3, 4.3, 5.3), MovieUtil.PNT3_ZERO]
+ elif suitName == 'tbc':
+ leftPosPoints = [Point3(0.6, 4.5, 6), MovieUtil.PNT3_ZERO]
+ rightPosPoints = [Point3(-0.6, 4.5, 6), MovieUtil.PNT3_ZERO]
+ else:
+ leftPosPoints = [Point3(0.4, 3.8, 3.7), MovieUtil.PNT3_ZERO]
+ rightPosPoints = [Point3(-0.4, 3.8, 3.7), MovieUtil.PNT3_ZERO]
+ leftKnifeTracks = Parallel()
+ rightKnifeTracks = Parallel()
+ for i in range(0, 3):
+ knifeDelay = 0.11
+ leftTrack = Sequence()
+ leftTrack.append(Wait(1.1))
+ leftTrack.append(Wait(i * knifeDelay))
+ leftTrack.append(getPropAppearTrack(leftKnives[i], suit, leftPosPoints, 1e-06, Point3(0.4, 0.4, 0.4), scaleUpTime=0.1))
+ leftTrack.append(getPropThrowTrack(attack, leftKnives[i], hitPointNames=['face'], missPointNames=['miss'], hitDuration=0.3, missDuration=0.3))
+ leftKnifeTracks.append(leftTrack)
+ rightTrack = Sequence()
+ rightTrack.append(Wait(1.1))
+ rightTrack.append(Wait(i * knifeDelay))
+ rightTrack.append(getPropAppearTrack(rightKnives[i], suit, rightPosPoints, 1e-06, Point3(0.4, 0.4, 0.4), scaleUpTime=0.1))
+ rightTrack.append(getPropThrowTrack(attack, rightKnives[i], hitPointNames=['face'], missPointNames=['miss'], hitDuration=0.3, missDuration=0.3))
+ rightKnifeTracks.append(rightTrack)
+
+ damageAnims = [['slip-backward', 0.01, 0.35]]
+ toonTrack = getToonTrack(attack, damageDelay=1.6, splicedDamageAnims=damageAnims, dodgeDelay=0.7, dodgeAnimNames=['sidestep'])
+ soundTrack = getSoundTrack('SA_glower_power.mp3', delay=1.1, node=suit)
+ return Parallel(suitTrack, toonTrack, soundTrack, leftKnifeTracks, rightKnifeTracks)
+
+
+def doHalfWindsor(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ tie = globalPropPool.getProp('half-windsor')
+ throwDelay = 2.17
+ damageDelay = 3.4
+ dodgeDelay = 2.4
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(0.02, 0.88, 0.48), VBase3(99, -3, -108.2)]
+ tiePropTrack = getPropAppearTrack(tie, suit.getRightHand(), posPoints, 0.5, Point3(7, 7, 7), scaleUpTime=0.5)
+ tiePropTrack.append(Wait(throwDelay))
+ missPoint = __toonMissBehindPoint(toon, parent=battle)
+ missPoint.setX(missPoint.getX() - 1.1)
+ missPoint.setZ(missPoint.getZ() + 4)
+ hitPoint = __toonFacePoint(toon, parent=battle)
+ hitPoint.setX(hitPoint.getX() - 1.1)
+ hitPoint.setY(hitPoint.getY() - 0.7)
+ hitPoint.setZ(hitPoint.getZ() + 0.9)
+ tiePropTrack.append(getPropThrowTrack(attack, tie, [hitPoint], [missPoint], hitDuration=0.4, missDuration=0.8, missScaleDown=0.3, parent=battle))
+ damageAnims = [['conked',
+ 0.01,
+ 0.01,
+ 0.4], ['cringe', 0.01, 0.7]]
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'])
+ return Parallel(suitTrack, toonTrack, tiePropTrack)
+
+
+def doHeadShrink(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ damageDelay = 2.1
+ dodgeDelay = 1.4
+ shrinkSpray = BattleParticles.createParticleEffect(file='headShrinkSpray')
+ shrinkCloud = BattleParticles.createParticleEffect(file='headShrinkCloud')
+ shrinkDrop = BattleParticles.createParticleEffect(file='headShrinkDrop')
+ suitTrack = getSuitTrack(attack)
+ sprayTrack = getPartTrack(shrinkSpray, 0.3, 1.4, [shrinkSpray, suit, 0])
+ shrinkCloud.reparentTo(battle)
+ adjust = 0.4
+ x = toon.getX(battle)
+ y = toon.getY(battle) - adjust
+ z = 8
+ shrinkCloud.setPos(Point3(x, y, z))
+ shrinkDrop.setPos(Point3(0, 0 - adjust, 7.5))
+ off = 0.7
+ cloudPoints = [Point3(x + off, y, z),
+ Point3(x + off / 2, y + off / 2, z),
+ Point3(x, y + off, z),
+ Point3(x - off / 2, y + off / 2, z),
+ Point3(x - off, y, z),
+ Point3(x - off / 2, y - off / 2, z),
+ Point3(x, y - off, z),
+ Point3(x + off / 2, y - off / 2, z),
+ Point3(x + off, y, z),
+ Point3(x, y, z)]
+ circleTrack = Sequence()
+ for point in cloudPoints:
+ circleTrack.append(LerpPosInterval(shrinkCloud, 0.14, point, other=battle))
+
+ cloudTrack = Sequence()
+ cloudTrack.append(Wait(1.42))
+ cloudTrack.append(Func(battle.movie.needRestoreParticleEffect, shrinkCloud))
+ cloudTrack.append(Func(shrinkCloud.start, battle))
+ cloudTrack.append(circleTrack)
+ cloudTrack.append(circleTrack)
+ cloudTrack.append(LerpFunctionInterval(shrinkCloud.setAlphaScale, fromData=1, toData=0, duration=0.7))
+ cloudTrack.append(Func(shrinkCloud.cleanup))
+ cloudTrack.append(Func(battle.movie.clearRestoreParticleEffect, shrinkCloud))
+ shrinkDelay = 0.8
+ shrinkDuration = 1.1
+ shrinkTrack = Sequence()
+ if dmg > 0:
+ headParts = toon.getHeadParts()
+ initialScale = headParts.getPath(0).getScale()[0]
+ shrinkTrack.append(Wait(damageDelay + shrinkDelay))
+
+ def scaleHeadParallel(scale, duration, headParts = headParts):
+ headTracks = Parallel()
+ for partNum in range(0, headParts.getNumPaths()):
+ nextPart = headParts.getPath(partNum)
+ headTracks.append(LerpScaleInterval(nextPart, duration, Point3(scale, scale, scale)))
+
+ return headTracks
+
+ shrinkTrack.append(Func(battle.movie.needRestoreHeadScale))
+ shrinkTrack.append(scaleHeadParallel(0.6, shrinkDuration))
+ shrinkTrack.append(Wait(1.6))
+ shrinkTrack.append(scaleHeadParallel(initialScale * 3.2, 0.4))
+ shrinkTrack.append(scaleHeadParallel(initialScale * 0.7, 0.4))
+ shrinkTrack.append(scaleHeadParallel(initialScale * 2.5, 0.3))
+ shrinkTrack.append(scaleHeadParallel(initialScale * 0.8, 0.3))
+ shrinkTrack.append(scaleHeadParallel(initialScale * 1.9, 0.2))
+ shrinkTrack.append(scaleHeadParallel(initialScale * 0.85, 0.2))
+ shrinkTrack.append(scaleHeadParallel(initialScale * 1.7, 0.15))
+ shrinkTrack.append(scaleHeadParallel(initialScale * 0.9, 0.15))
+ shrinkTrack.append(scaleHeadParallel(initialScale * 1.3, 0.1))
+ shrinkTrack.append(scaleHeadParallel(initialScale, 0.1))
+ shrinkTrack.append(Func(battle.movie.clearRestoreHeadScale))
+ shrinkTrack.append(Wait(0.7))
+ dropTrack = getPartTrack(shrinkDrop, 1.5, 2.5, [shrinkDrop, toon, 0])
+ damageAnims = []
+ damageAnims.append(['cringe',
+ 0.01,
+ 0.65,
+ 0.2])
+ damageAnims.extend(getSplicedLerpAnims('cringe', 0.64, 1.0, startTime=0.85))
+ damageAnims.append(['cringe', 0.4, 1.49])
+ damageAnims.append(['conked',
+ 0.01,
+ 3.6,
+ -1.6])
+ damageAnims.append(['conked',
+ 0.01,
+ 3.1,
+ 0.4])
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'])
+ if dmg > 0:
+ shrinkSound = globalBattleSoundCache.getSound('SA_head_shrink_only.mp3')
+ growSound = globalBattleSoundCache.getSound('SA_head_grow_back_only.mp3')
+ soundTrack = Sequence(Wait(2.1), SoundInterval(shrinkSound, duration=2.1, node=suit), Wait(1.6), SoundInterval(growSound, node=suit))
+ return Parallel(suitTrack, sprayTrack, cloudTrack, dropTrack, toonTrack, shrinkTrack, soundTrack)
+ else:
+ return Parallel(suitTrack, sprayTrack, cloudTrack, dropTrack, toonTrack)
+
+
+def doRolodex(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ rollodex = globalPropPool.getProp('rollodex')
+ particleEffect2 = BattleParticles.createParticleEffect(file='rollodexWaterfall')
+ particleEffect3 = BattleParticles.createParticleEffect(file='rollodexStream')
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ propPosPoints = [Point3(-0.51, -0.03, -0.1), VBase3(89.673, 2.166, 177.786)]
+ propScale = Point3(1.2, 1.2, 1.2)
+ partDelay = 2.6
+ part2Delay = 2.8
+ part3Delay = 3.2
+ partDuration = 1.6
+ part2Duration = 1.9
+ part3Duration = 1
+ damageDelay = 3.8
+ dodgeDelay = 2.5
+ elif suitType == 'b':
+ propPosPoints = [Point3(0.12, 0.24, 0.01), VBase3(99.032, 5.973, -179.839)]
+ propScale = Point3(0.91, 0.91, 0.91)
+ partDelay = 2.9
+ part2Delay = 3.1
+ part3Delay = 3.5
+ partDuration = 1.6
+ part2Duration = 1.9
+ part3Duration = 1
+ damageDelay = 4
+ dodgeDelay = 2.5
+ elif suitType == 'c':
+ propPosPoints = [Point3(-0.51, -0.03, -0.1), VBase3(89.673, 2.166, 177.786)]
+ propScale = Point3(1.2, 1.2, 1.2)
+ partDelay = 2.3
+ part2Delay = 2.8
+ part3Delay = 3.2
+ partDuration = 1.9
+ part2Duration = 1.9
+ part3Duration = 1
+ damageDelay = 3.5
+ dodgeDelay = 2.5
+ hitPoint = lambda toon = toon: __toonFacePoint(toon)
+ partTrack2 = getPartTrack(particleEffect2, part2Delay, part2Duration, [particleEffect2, suit, 0])
+ partTrack3 = getPartTrack(particleEffect3, part3Delay, part3Duration, [particleEffect3, suit, 0])
+ suitTrack = getSuitTrack(attack)
+ propTrack = getPropTrack(rollodex, suit.getLeftHand(), propPosPoints, 1e-06, 4.7, scaleUpPoint=propScale, anim=0, propName='rollodex', animDuration=0, animStartTime=0)
+ toonTrack = getToonTrack(attack, damageDelay, ['conked'], dodgeDelay, ['sidestep'])
+ soundTrack = getSoundTrack('SA_rolodex.mp3', delay=2.8, node=suit)
+ return Parallel(suitTrack, toonTrack, propTrack, soundTrack, partTrack2, partTrack3)
+
+
+def doEvilEye(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ eye = globalPropPool.getProp('evil-eye')
+ damageDelay = 2.44
+ dodgeDelay = 1.64
+ suitName = suit.getStyleName()
+ if suitName == 'cr':
+ posPoints = [Point3(-0.46, 4.85, 5.28), VBase3(-155.0, -20.0, 0.0)]
+ elif suitName == 'tf':
+ posPoints = [Point3(-0.4, 3.65, 5.01), VBase3(-155.0, -20.0, 0.0)]
+ elif suitName == 'le':
+ posPoints = [Point3(-0.64, 4.45, 5.91), VBase3(-155.0, -20.0, 0.0)]
+ else:
+ posPoints = [Point3(-0.4, 3.65, 5.01), VBase3(-155.0, -20.0, 0.0)]
+ appearDelay = 0.8
+ suitHoldStart = 1.06
+ suitHoldStop = 1.69
+ suitHoldDuration = suitHoldStop - suitHoldStart
+ eyeHoldDuration = 1.1
+ moveDuration = 1.1
+ suitSplicedAnims = []
+ suitSplicedAnims.append(['glower',
+ 0.01,
+ 0.01,
+ suitHoldStart])
+ suitSplicedAnims.extend(getSplicedLerpAnims('glower', suitHoldDuration, 1.1, startTime=suitHoldStart))
+ suitSplicedAnims.append(['glower', 0.01, suitHoldStop])
+ suitTrack = getSuitTrack(attack, splicedAnims=suitSplicedAnims)
+ eyeAppearTrack = Sequence(Wait(suitHoldStart), Func(__showProp, eye, suit, posPoints[0], posPoints[1]), LerpScaleInterval(eye, suitHoldDuration, Point3(11, 11, 11)), Wait(eyeHoldDuration * 0.3), LerpHprInterval(eye, 0.02, Point3(205, 40, 0)), Wait(eyeHoldDuration * 0.7), Func(battle.movie.needRestoreRenderProp, eye), Func(eye.wrtReparentTo, battle))
+ toonFace = __toonFacePoint(toon, parent=battle)
+ if dmg > 0:
+ lerpInterval = LerpPosInterval(eye, moveDuration, toonFace)
+ else:
+ lerpInterval = LerpPosInterval(eye, moveDuration, Point3(toonFace.getX(), toonFace.getY() - 5, toonFace.getZ() - 2))
+ eyeMoveTrack = lerpInterval
+ eyeRollTrack = LerpHprInterval(eye, moveDuration, Point3(0, 0, -180))
+ eyePropTrack = Sequence(eyeAppearTrack, Parallel(eyeMoveTrack, eyeRollTrack), Func(battle.movie.clearRenderProp, eye), Func(MovieUtil.removeProp, eye))
+ damageAnims = [['duck',
+ 0.01,
+ 0.01,
+ 1.4], ['cringe', 0.01, 0.3]]
+ toonTrack = getToonTrack(attack, splicedDamageAnims=damageAnims, damageDelay=damageDelay, dodgeDelay=dodgeDelay, dodgeAnimNames=['duck'], showDamageExtraTime=1.7, showMissedExtraTime=1.7)
+ soundTrack = getSoundTrack('SA_evil_eye.mp3', delay=1.3, node=suit)
+ return Parallel(suitTrack, toonTrack, eyePropTrack, soundTrack)
+
+
+def doPlayHardball(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ ball = globalPropPool.getProp('baseball')
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ suitDelay = 1.09
+ damageDelay = 2.76
+ dodgeDelay = 1.86
+ elif suitType == 'b':
+ suitDelay = 1.79
+ damageDelay = 3.46
+ dodgeDelay = 2.56
+ elif suitType == 'c':
+ suitDelay = 1.09
+ damageDelay = 2.76
+ dodgeDelay = 1.86
+ suitTrack = getSuitTrack(attack)
+ ballPosPoints = [Point3(0.04, 0.03, -0.31), VBase3(-1.152, 86.581, -76.784)]
+ propTrack = Sequence(getPropAppearTrack(ball, suit.getRightHand(), ballPosPoints, 0.8, Point3(5, 5, 5), scaleUpTime=0.5))
+ propTrack.append(Wait(suitDelay))
+ propTrack.append(Func(battle.movie.needRestoreRenderProp, ball))
+ propTrack.append(Func(ball.wrtReparentTo, battle))
+ toonPos = toon.getPos(battle)
+ x = toonPos.getX()
+ y = toonPos.getY()
+ z = toonPos.getZ()
+ z = z + 0.2
+ if dmg > 0:
+ propTrack.append(LerpPosInterval(ball, 0.5, __toonFacePoint(toon, parent=battle)))
+ propTrack.append(LerpPosInterval(ball, 0.5, Point3(x, y + 3, z)))
+ propTrack.append(LerpPosInterval(ball, 0.4, Point3(x, y + 5, z + 2)))
+ propTrack.append(LerpPosInterval(ball, 0.3, Point3(x, y + 6, z)))
+ propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y + 7, z + 1)))
+ propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y + 8, z)))
+ propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y + 8.5, z + 0.6)))
+ propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y + 9, z + 0.2)))
+ propTrack.append(Wait(0.4))
+ soundTrack = getSoundTrack('SA_hardball_impact_only.mp3', delay=2.8, node=suit)
+ else:
+ propTrack.append(LerpPosInterval(ball, 0.5, Point3(x, y + 2, z)))
+ propTrack.append(LerpPosInterval(ball, 0.4, Point3(x, y - 1, z + 2)))
+ propTrack.append(LerpPosInterval(ball, 0.3, Point3(x, y - 3, z)))
+ propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y - 4, z + 1)))
+ propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y - 5, z)))
+ propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y - 5.5, z + 0.6)))
+ propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y - 6, z + 0.2)))
+ propTrack.append(Wait(0.4))
+ soundTrack = getSoundTrack('SA_hardball.mp3', delay=3.1, node=suit)
+ propTrack.append(LerpScaleInterval(ball, 0.3, MovieUtil.PNT3_NEARZERO))
+ propTrack.append(Func(MovieUtil.removeProp, ball))
+ propTrack.append(Func(battle.movie.clearRenderProp, ball))
+ damageAnims = [['conked',
+ damageDelay,
+ 0.01,
+ 0.5], ['slip-backward', 0.01, 0.7]]
+ toonTrack = getToonTrack(attack, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'], showDamageExtraTime=3.9)
+ return Parallel(suitTrack, toonTrack, propTrack, soundTrack)
+
+
+def doPowerTie(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ tie = globalPropPool.getProp('power-tie')
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ throwDelay = 2.17
+ damageDelay = 3.3
+ dodgeDelay = 3.1
+ elif suitType == 'b':
+ throwDelay = 2.17
+ damageDelay = 3.3
+ dodgeDelay = 3.1
+ elif suitType == 'c':
+ throwDelay = 1.45
+ damageDelay = 2.61
+ dodgeDelay = 2.34
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(1.16, 0.24, 0.63), VBase3(171.561, 1.745, -163.443)]
+ tiePropTrack = Sequence(getPropAppearTrack(tie, suit.getRightHand(), posPoints, 0.5, Point3(3.5, 3.5, 3.5), scaleUpTime=0.5))
+ tiePropTrack.append(Wait(throwDelay))
+ tiePropTrack.append(Func(tie.setBillboardPointEye))
+ tiePropTrack.append(getPropThrowTrack(attack, tie, [__toonFacePoint(toon)], [__toonGroundPoint(attack, toon, 0.1)], hitDuration=0.4, missDuration=0.8))
+ toonTrack = getToonTrack(attack, damageDelay, ['conked'], dodgeDelay, ['sidestep'])
+ throwSound = getSoundTrack('SA_powertie_throw.mp3', delay=2.3, node=suit)
+ if dmg > 0:
+ hitSound = getSoundTrack('SA_powertie_impact.mp3', delay=2.9, node=suit)
+ return Parallel(suitTrack, toonTrack, tiePropTrack, throwSound, hitSound)
+ else:
+ return Parallel(suitTrack, toonTrack, tiePropTrack, throwSound)
+
+
+def doDoubleTalk(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ BattleParticles.loadParticles()
+ particleEffect = BattleParticles.createParticleEffect('DoubleTalkLeft')
+ particleEffect2 = BattleParticles.createParticleEffect('DoubleTalkRight')
+ BattleParticles.setEffectTexture(particleEffect, 'doubletalk-double', color=Vec4(0, 1.0, 0.0, 1))
+ BattleParticles.setEffectTexture(particleEffect2, 'doubletalk-good', color=Vec4(0, 1.0, 0.0, 1))
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ partDelay = 3.3
+ damageDelay = 3.5
+ dodgeDelay = 3.3
+ elif suitType == 'b':
+ partDelay = 3.3
+ damageDelay = 3.5
+ dodgeDelay = 3.3
+ elif suitType == 'c':
+ partDelay = 3.3
+ damageDelay = 3.5
+ dodgeDelay = 3.3
+ suitTrack = getSuitTrack(attack)
+ partTrack = getPartTrack(particleEffect, partDelay, 1.8, [particleEffect, suit, 0])
+ partTrack2 = getPartTrack(particleEffect2, partDelay, 1.8, [particleEffect2, suit, 0])
+ damageAnims = [['duck',
+ 0.01,
+ 0.4,
+ 1.05], ['cringe', 1e-06, 0.8]]
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, splicedDodgeAnims=[['duck', 0.01, 1.4]], showMissedExtraTime=0.9, showDamageExtraTime=0.8)
+ soundTrack = getSoundTrack('SA_filibuster.mp3', delay=2.5, node=suit)
+ return Parallel(suitTrack, toonTrack, partTrack, partTrack2, soundTrack)
+
+
+def doFreezeAssets(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ BattleParticles.loadParticles()
+ snowEffect = BattleParticles.createParticleEffect('FreezeAssets')
+ BattleParticles.setEffectTexture(snowEffect, 'snow-particle')
+ cloud = globalPropPool.getProp('stormcloud')
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ partDelay = 0.2
+ damageDelay = 3.5
+ dodgeDelay = 2.3
+ elif suitType == 'b':
+ partDelay = 0.2
+ damageDelay = 3.5
+ dodgeDelay = 2.3
+ elif suitType == 'c':
+ partDelay = 0.2
+ damageDelay = 3.5
+ dodgeDelay = 2.3
+ suitTrack = getSuitTrack(attack, delay=0.9)
+ initialCloudHeight = suit.height + 3
+ cloudPosPoints = [Point3(0, 3, initialCloudHeight), MovieUtil.PNT3_ZERO]
+ cloudPropTrack = Sequence()
+ cloudPropTrack.append(Func(cloud.pose, 'stormcloud', 0))
+ cloudPropTrack.append(getPropAppearTrack(cloud, suit, cloudPosPoints, 1e-06, Point3(3, 3, 3), scaleUpTime=0.7))
+ cloudPropTrack.append(Func(battle.movie.needRestoreRenderProp, cloud))
+ cloudPropTrack.append(Func(cloud.wrtReparentTo, render))
+ targetPoint = __toonFacePoint(toon)
+ targetPoint.setZ(targetPoint[2] + 3)
+ cloudPropTrack.append(Wait(1.1))
+ cloudPropTrack.append(LerpPosInterval(cloud, 1, pos=targetPoint))
+ cloudPropTrack.append(Wait(partDelay))
+ cloudPropTrack.append(ParticleInterval(snowEffect, cloud, worldRelative=0, duration=2.1, cleanup=True))
+ cloudPropTrack.append(Wait(0.4))
+ cloudPropTrack.append(LerpScaleInterval(cloud, 0.5, MovieUtil.PNT3_NEARZERO))
+ cloudPropTrack.append(Func(MovieUtil.removeProp, cloud))
+ cloudPropTrack.append(Func(battle.movie.clearRenderProp, cloud))
+ damageAnims = [['cringe',
+ 0.01,
+ 0.4,
+ 0.8], ['duck', 0.01, 1.6]]
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'], showMissedExtraTime=1.2)
+ return Parallel(suitTrack, toonTrack, cloudPropTrack)
+
+
+def doHotAir(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ BattleParticles.loadParticles()
+ sprayEffect = BattleParticles.createParticleEffect('HotAir')
+ baseFlameEffect = BattleParticles.createParticleEffect(file='firedBaseFlame')
+ flameEffect = BattleParticles.createParticleEffect('FiredFlame')
+ flecksEffect = BattleParticles.createParticleEffect('SpriteFiredFlecks')
+ BattleParticles.setEffectTexture(sprayEffect, 'fire')
+ BattleParticles.setEffectTexture(baseFlameEffect, 'fire')
+ BattleParticles.setEffectTexture(flameEffect, 'fire')
+ BattleParticles.setEffectTexture(flecksEffect, 'roll-o-dex', color=Vec4(0.95, 0.95, 0.0, 1))
+ sprayDelay = 1.3
+ flameDelay = 3.2
+ flameDuration = 2.6
+ flecksDelay = flameDelay + 0.8
+ flecksDuration = flameDuration - 0.8
+ damageDelay = 3.6
+ dodgeDelay = 2.0
+ suitTrack = getSuitTrack(attack)
+ sprayTrack = getPartTrack(sprayEffect, sprayDelay, 2.3, [sprayEffect, suit, 0])
+ baseFlameTrack = getPartTrack(baseFlameEffect, flameDelay, flameDuration, [baseFlameEffect, toon, 0])
+ flameTrack = getPartTrack(flameEffect, flameDelay, flameDuration, [flameEffect, toon, 0])
+ flecksTrack = getPartTrack(flecksEffect, flecksDelay, flecksDuration, [flecksEffect, toon, 0])
+
+ def changeColor(parts):
+ track = Parallel()
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ track.append(Func(nextPart.setColorScale, Vec4(0, 0, 0, 1)))
+
+ return track
+
+ def resetColor(parts):
+ track = Parallel()
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ track.append(Func(nextPart.clearColorScale))
+
+ return track
+
+ if dmg > 0:
+ headParts = toon.getHeadParts()
+ torsoParts = toon.getTorsoParts()
+ legsParts = toon.getLegsParts()
+ colorTrack = Sequence()
+ colorTrack.append(Wait(4.0))
+ colorTrack.append(Func(battle.movie.needRestoreColor))
+ colorTrack.append(changeColor(headParts))
+ colorTrack.append(changeColor(torsoParts))
+ colorTrack.append(changeColor(legsParts))
+ colorTrack.append(Wait(3.5))
+ colorTrack.append(resetColor(headParts))
+ colorTrack.append(resetColor(torsoParts))
+ colorTrack.append(resetColor(legsParts))
+ colorTrack.append(Func(battle.movie.clearRestoreColor))
+ damageAnims = []
+ damageAnims.append(['cringe',
+ 0.01,
+ 0.7,
+ 0.62])
+ damageAnims.append(['slip-forward',
+ 0.01,
+ 0.4,
+ 1.2])
+ damageAnims.append(['slip-forward', 0.01, 1.0])
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'])
+ soundTrack = getSoundTrack('SA_hot_air.mp3', delay=1.6, node=suit)
+ if dmg > 0:
+ return Parallel(suitTrack, toonTrack, sprayTrack, soundTrack, baseFlameTrack, flameTrack, flecksTrack, colorTrack)
+ else:
+ return Parallel(suitTrack, toonTrack, sprayTrack, soundTrack)
+
+
+def doPickPocket(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ dmg = target['hp']
+ bill = globalPropPool.getProp('1dollar')
+ suitTrack = getSuitTrack(attack)
+ billPosPoints = [Point3(-0.01, 0.45, -0.25), VBase3(136.424, -46.434, -129.712)]
+ billPropTrack = getPropTrack(bill, suit.getRightHand(), billPosPoints, 0.6, 0.55, scaleUpPoint=Point3(1.41, 1.41, 1.41))
+ toonTrack = getToonTrack(attack, 0.6, ['cringe'], 0.01, ['sidestep'])
+ multiTrackList = Parallel(suitTrack, toonTrack)
+ if dmg > 0:
+ soundTrack = getSoundTrack('SA_pick_pocket.mp3', delay=0.2, node=suit)
+ multiTrackList.append(billPropTrack)
+ multiTrackList.append(soundTrack)
+ return multiTrackList
+
+
+def doFilibuster(attack):
+ suit = attack['suit']
+ target = attack['target']
+ dmg = target['hp']
+ battle = attack['battle']
+ BattleParticles.loadParticles()
+ sprayEffect = BattleParticles.createParticleEffect(file='filibusterSpray')
+ sprayEffect2 = BattleParticles.createParticleEffect(file='filibusterSpray')
+ sprayEffect3 = BattleParticles.createParticleEffect(file='filibusterSpray')
+ sprayEffect4 = BattleParticles.createParticleEffect(file='filibusterSpray')
+ color = Vec4(0.4, 0, 0, 1)
+ BattleParticles.setEffectTexture(sprayEffect, 'filibuster-cut', color=color)
+ BattleParticles.setEffectTexture(sprayEffect2, 'filibuster-fiscal', color=color)
+ BattleParticles.setEffectTexture(sprayEffect3, 'filibuster-impeach', color=color)
+ BattleParticles.setEffectTexture(sprayEffect4, 'filibuster-inc', color=color)
+ partDelay = 1.3
+ partDuration = 1.15
+ damageDelay = 2.45
+ dodgeDelay = 1.7
+ suitTrack = getSuitTrack(attack)
+ sprayTrack = getPartTrack(sprayEffect, partDelay, partDuration, [sprayEffect, suit, 0])
+ sprayTrack2 = getPartTrack(sprayEffect2, partDelay + 0.8, partDuration, [sprayEffect2, suit, 0])
+ sprayTrack3 = getPartTrack(sprayEffect3, partDelay + 1.6, partDuration, [sprayEffect3, suit, 0])
+ sprayTrack4 = getPartTrack(sprayEffect4, partDelay + 2.4, partDuration, [sprayEffect4, suit, 0])
+ damageAnims = []
+ for i in range(0, 4):
+ damageAnims.append(['cringe',
+ 1e-05,
+ 0.3,
+ 0.8])
+
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'])
+ soundTrack = getSoundTrack('SA_filibuster.mp3', delay=1.1, node=suit)
+ if dmg > 0:
+ return Parallel(suitTrack, toonTrack, soundTrack, sprayTrack, sprayTrack2, sprayTrack3, sprayTrack4)
+ else:
+ return Parallel(suitTrack, toonTrack, soundTrack, sprayTrack, sprayTrack2, sprayTrack3)
+
+
+def doSchmooze(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ BattleParticles.loadParticles()
+ upperEffects = []
+ lowerEffects = []
+ textureNames = ['schmooze-genius',
+ 'schmooze-instant',
+ 'schmooze-master',
+ 'schmooze-viz']
+ for i in range(0, 4):
+ upperEffect = BattleParticles.createParticleEffect(file='schmoozeUpperSpray')
+ lowerEffect = BattleParticles.createParticleEffect(file='schmoozeLowerSpray')
+ BattleParticles.setEffectTexture(upperEffect, textureNames[i], color=Vec4(0, 0, 1, 1))
+ BattleParticles.setEffectTexture(lowerEffect, textureNames[i], color=Vec4(0, 0, 1, 1))
+ upperEffects.append(upperEffect)
+ lowerEffects.append(lowerEffect)
+
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ partDelay = 1.3
+ damageDelay = 1.8
+ dodgeDelay = 1.1
+ elif suitType == 'b':
+ partDelay = 1.3
+ damageDelay = 2.5
+ dodgeDelay = 1.8
+ elif suitType == 'c':
+ partDelay = 1.3
+ damageDelay = partDelay + 1.4
+ dodgeDelay = 0.9
+ suitTrack = getSuitTrack(attack)
+ upperPartTracks = Parallel()
+ lowerPartTracks = Parallel()
+ for i in range(0, 4):
+ upperPartTracks.append(getPartTrack(upperEffects[i], partDelay + i * 0.65, 0.8, [upperEffects[i], suit, 0]))
+ lowerPartTracks.append(getPartTrack(lowerEffects[i], partDelay + i * 0.65 + 0.7, 1.0, [lowerEffects[i], suit, 0]))
+
+ damageAnims = []
+ for i in range(0, 3):
+ damageAnims.append(['conked',
+ 0.01,
+ 0.3,
+ 0.71])
+
+ damageAnims.append(['conked', 0.01, 0.3])
+ dodgeAnims = []
+ dodgeAnims.append(['duck',
+ 0.01,
+ 0.2,
+ 2.7])
+ dodgeAnims.append(['duck',
+ 0.01,
+ 1.22,
+ 1.28])
+ dodgeAnims.append(['duck', 0.01, 3.16])
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=1.9, showDamageExtraTime=1.1)
+ return Parallel(suitTrack, toonTrack, upperPartTracks, lowerPartTracks)
+
+
+def doQuake(attack):
+ suit = attack['suit']
+ suitTrack = getSuitAnimTrack(attack)
+ damageAnims = [['slip-forward'], ['slip-forward', 0.01]]
+ dodgeAnims = [['jump'], ['jump', 0.01], ['jump', 0.01]]
+ toonTracks = getToonTracks(attack, damageDelay=1.8, splicedDamageAnims=damageAnims, dodgeDelay=1.1, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=2.8, showDamageExtraTime=1.1)
+ return Parallel(suitTrack, toonTracks)
+
+
+def doShake(attack):
+ suit = attack['suit']
+ suitTrack = getSuitAnimTrack(attack)
+ damageAnims = [['slip-forward'], ['slip-forward', 0.01]]
+ dodgeAnims = [['jump'], ['jump', 0.01]]
+ toonTracks = getToonTracks(attack, damageDelay=1.1, splicedDamageAnims=damageAnims, dodgeDelay=0.7, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=2.8, showDamageExtraTime=1.1)
+ return Parallel(suitTrack, toonTracks)
+
+
+def doTremor(attack):
+ suit = attack['suit']
+ suitTrack = getSuitAnimTrack(attack)
+ damageAnims = [['slip-forward'], ['slip-forward', 0.01]]
+ dodgeAnims = [['jump'], ['jump', 0.01]]
+ toonTracks = getToonTracks(attack, damageDelay=1.1, splicedDamageAnims=damageAnims, dodgeDelay=0.7, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=2.8, showDamageExtraTime=1.1)
+ soundTrack = getSoundTrack('SA_tremor.mp3', delay=0.9, node=suit)
+ return Parallel(suitTrack, soundTrack, toonTracks)
+
+
+def doHangUp(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ phone = globalPropPool.getProp('phone')
+ receiver = globalPropPool.getProp('receiver')
+ suitTrack = getSuitTrack(attack)
+ suitName = suit.getStyleName()
+ if suitName == 'tf':
+ phonePosPoints = [Point3(-0.23, 0.01, -0.26), VBase3(5.939, 2.763, -177.591)]
+ receiverPosPoints = [Point3(-0.13, -0.07, -0.06), VBase3(-1.854, 2.434, -177.579)]
+ receiverAdjustScale = Point3(0.8, 0.8, 0.8)
+ pickupDelay = 0.44
+ dialDuration = 3.07
+ finalPhoneDelay = 0.01
+ scaleUpPoint = Point3(0.75, 0.75, 0.75)
+ else:
+ phonePosPoints = [Point3(0.23, 0.17, -0.11), VBase3(5.939, 2.763, -177.591)]
+ receiverPosPoints = [Point3(0.23, 0.17, -0.11), VBase3(5.939, 2.763, -177.591)]
+ receiverAdjustScale = MovieUtil.PNT3_ONE
+ pickupDelay = 0.74
+ dialDuration = 3.07
+ finalPhoneDelay = 0.69
+ scaleUpPoint = MovieUtil.PNT3_ONE
+ propTrack = Sequence(Wait(0.3), Func(__showProp, phone, suit.getLeftHand(), phonePosPoints[0], phonePosPoints[1]), Func(__showProp, receiver, suit.getLeftHand(), receiverPosPoints[0], receiverPosPoints[1]), LerpScaleInterval(phone, 0.5, scaleUpPoint, MovieUtil.PNT3_NEARZERO), Wait(pickupDelay), Func(receiver.wrtReparentTo, suit.getRightHand()), LerpScaleInterval(receiver, 0.01, receiverAdjustScale), LerpPosHprInterval(receiver, 0.0001, Point3(-0.53, 0.21, -0.54), VBase3(-99.49, -35.27, 1.84)), Wait(dialDuration), Func(receiver.wrtReparentTo, phone), Wait(finalPhoneDelay), LerpScaleInterval(phone, 0.5, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProps, [receiver, phone]))
+ toonTrack = getToonTrack(attack, 5.5, ['slip-backward'], 4.7, ['jump'])
+ soundTrack = getSoundTrack('SA_hangup.mp3', delay=1.3, node=suit)
+ return Parallel(suitTrack, toonTrack, propTrack, soundTrack)
+
+
+def doRedTape(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ tape = globalPropPool.getProp('redtape')
+ tubes = []
+ for i in range(0, 3):
+ tubes.append(globalPropPool.getProp('redtape-tube'))
+
+ suitTrack = getSuitTrack(attack)
+ suitName = suit.getStyleName()
+ if suitName == 'tf' or suitName == 'nc':
+ tapePosPoints = [Point3(-0.24, 0.09, -0.38), VBase3(-1.152, 86.581, -76.784)]
+ else:
+ tapePosPoints = [Point3(0.24, 0.09, -0.38), VBase3(-1.152, 86.581, -76.784)]
+ tapeScaleUpPoint = Point3(0.9, 0.9, 0.24)
+ propTrack = Sequence(getPropAppearTrack(tape, suit.getRightHand(), tapePosPoints, 0.8, tapeScaleUpPoint, scaleUpTime=0.5))
+ propTrack.append(Wait(1.73))
+ hitPoint = lambda toon = toon: __toonTorsoPoint(toon)
+ propTrack.append(getPropThrowTrack(attack, tape, [hitPoint], [__toonGroundPoint(attack, toon, 0.7)]))
+ hips = toon.getHipsParts()
+ animal = toon.style.getAnimal()
+ scale = ToontownGlobals.toonBodyScales[animal]
+ legs = toon.style.legs
+ torso = toon.style.torso
+ torso = torso[0]
+ animal = animal[0]
+ tubeHeight = -0.8
+ if torso == 's':
+ scaleUpPoint = Point3(scale * 2.03, scale * 2.03, scale * 0.7975)
+ elif torso == 'm':
+ scaleUpPoint = Point3(scale * 2.03, scale * 2.03, scale * 0.7975)
+ elif torso == 'l':
+ scaleUpPoint = Point3(scale * 2.03, scale * 2.03, scale * 1.11)
+ if animal == 'h' or animal == 'd':
+ tubeHeight = -0.87
+ scaleUpPoint = Point3(scale * 1.69, scale * 1.69, scale * 0.67)
+ tubePosPoints = [Point3(0, 0, tubeHeight), MovieUtil.PNT3_ZERO]
+ tubeTracks = Parallel()
+ tubeTracks.append(Func(battle.movie.needRestoreHips))
+ for partNum in range(0, hips.getNumPaths()):
+ nextPart = hips.getPath(partNum)
+ tubeTracks.append(getPropTrack(tubes[partNum], nextPart, tubePosPoints, 3.25, 3.17, scaleUpPoint=scaleUpPoint))
+
+ tubeTracks.append(Func(battle.movie.clearRestoreHips))
+ toonTrack = getToonTrack(attack, 3.4, ['struggle'], 2.8, ['jump'])
+ soundTrack = getSoundTrack('SA_red_tape.mp3', delay=2.9, node=suit)
+ if dmg > 0:
+ return Parallel(suitTrack, toonTrack, propTrack, soundTrack, tubeTracks)
+ else:
+ return Parallel(suitTrack, toonTrack, propTrack, soundTrack)
+
+
+def doParadigmShift(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ targets = attack['target']
+ hitAtleastOneToon = 0
+ for t in targets:
+ if t['hp'] > 0:
+ hitAtleastOneToon = 1
+
+ damageDelay = 1.95
+ dodgeDelay = 0.95
+ sprayEffect = BattleParticles.createParticleEffect('ShiftSpray')
+ suitName = suit.getStyleName()
+ if suitName == 'm':
+ sprayEffect.setPos(Point3(-5.2, 4.6, 2.7))
+ elif suitName == 'sd':
+ sprayEffect.setPos(Point3(-5.2, 4.6, 2.7))
+ else:
+ sprayEffect.setPos(Point3(0.1, 4.6, 2.7))
+ suitTrack = getSuitAnimTrack(attack)
+ sprayTrack = getPartTrack(sprayEffect, 1.0, 1.9, [sprayEffect, suit, 0])
+ liftTracks = Parallel()
+ toonRiseTracks = Parallel()
+ for t in targets:
+ toon = t['toon']
+ dmg = t['hp']
+ if dmg > 0:
+ liftEffect = BattleParticles.createParticleEffect('ShiftLift')
+ liftEffect.setPos(toon.getPos(battle))
+ liftEffect.setZ(liftEffect.getZ() - 1.3)
+ liftTracks.append(getPartTrack(liftEffect, 1.1, 4.1, [liftEffect, battle, 0]))
+ shadow = toon.dropShadow
+ fakeShadow = MovieUtil.copyProp(shadow)
+ x = toon.getX()
+ y = toon.getY()
+ z = toon.getZ()
+ height = 3
+ groundPoint = Point3(x, y, z)
+ risePoint = Point3(x, y, z + height)
+ shakeRight = Point3(x, y + 0.7, z + height)
+ shakeLeft = Point3(x, y - 0.7, z + height)
+ shakeTrack = Sequence()
+ shakeTrack.append(Wait(damageDelay + 0.25))
+ shakeTrack.append(Func(shadow.hide))
+ shakeTrack.append(LerpPosInterval(toon, 1.1, risePoint))
+ for i in range(0, 17):
+ shakeTrack.append(LerpPosInterval(toon, 0.03, shakeLeft))
+ shakeTrack.append(LerpPosInterval(toon, 0.03, shakeRight))
+
+ shakeTrack.append(LerpPosInterval(toon, 0.1, risePoint))
+ shakeTrack.append(LerpPosInterval(toon, 0.1, groundPoint))
+ shakeTrack.append(Func(shadow.show))
+ shadowTrack = Sequence()
+ shadowTrack.append(Func(battle.movie.needRestoreRenderProp, fakeShadow))
+ shadowTrack.append(Wait(damageDelay + 0.25))
+ shadowTrack.append(Func(fakeShadow.hide))
+ shadowTrack.append(Func(fakeShadow.setScale, 0.27))
+ shadowTrack.append(Func(fakeShadow.reparentTo, toon))
+ shadowTrack.append(Func(fakeShadow.setPos, MovieUtil.PNT3_ZERO))
+ shadowTrack.append(Func(fakeShadow.wrtReparentTo, battle))
+ shadowTrack.append(Func(fakeShadow.show))
+ shadowTrack.append(LerpScaleInterval(fakeShadow, 0.4, Point3(0.17, 0.17, 0.17)))
+ shadowTrack.append(Wait(1.81))
+ shadowTrack.append(LerpScaleInterval(fakeShadow, 0.1, Point3(0.27, 0.27, 0.27)))
+ shadowTrack.append(Func(MovieUtil.removeProp, fakeShadow))
+ shadowTrack.append(Func(battle.movie.clearRenderProp, fakeShadow))
+ toonRiseTracks.append(Parallel(shakeTrack, shadowTrack))
+
+ damageAnims = []
+ damageAnims.extend(getSplicedLerpAnims('think', 0.66, 1.9, startTime=2.06))
+ damageAnims.append(['slip-backward', 0.01, 0.5])
+ dodgeAnims = []
+ dodgeAnims.append(['jump',
+ 0.01,
+ 0,
+ 0.6])
+ dodgeAnims.extend(getSplicedLerpAnims('jump', 0.31, 1.0, startTime=0.6))
+ dodgeAnims.append(['jump', 0, 0.91])
+ toonTracks = getToonTracks(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, splicedDodgeAnims=dodgeAnims, showDamageExtraTime=2.7)
+ if hitAtleastOneToon == 1:
+ soundTrack = getSoundTrack('SA_paradigm_shift.mp3', delay=2.1, node=suit)
+ return Parallel(suitTrack, sprayTrack, soundTrack, liftTracks, toonTracks, toonRiseTracks)
+ else:
+ return Parallel(suitTrack, sprayTrack, liftTracks, toonTracks, toonRiseTracks)
+
+
+def doPowerTrip(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ centerColor = Vec4(0.1, 0.1, 0.1, 0.4)
+ edgeColor = Vec4(0.4, 0.1, 0.9, 0.7)
+ powerBar1 = BattleParticles.createParticleEffect(file='powertrip')
+ powerBar2 = BattleParticles.createParticleEffect(file='powertrip2')
+ powerBar1.setPos(0, 6.1, 0.4)
+ powerBar1.setHpr(-60, 0, 0)
+ powerBar2.setPos(0, 6.1, 0.4)
+ powerBar2.setHpr(60, 0, 0)
+ powerBar1Particles = powerBar1.getParticlesNamed('particles-1')
+ powerBar2Particles = powerBar2.getParticlesNamed('particles-1')
+ powerBar1Particles.renderer.setCenterColor(centerColor)
+ powerBar1Particles.renderer.setEdgeColor(edgeColor)
+ powerBar2Particles.renderer.setCenterColor(centerColor)
+ powerBar2Particles.renderer.setEdgeColor(edgeColor)
+ waterfallEffect = BattleParticles.createParticleEffect('Waterfall')
+ waterfallEffect.setScale(11)
+ waterfallParticles = waterfallEffect.getParticlesNamed('particles-1')
+ waterfallParticles.renderer.setCenterColor(centerColor)
+ waterfallParticles.renderer.setEdgeColor(edgeColor)
+ suitName = suit.getStyleName()
+ if suitName == 'mh':
+ waterfallEffect.setPos(0, 4, 3.6)
+ suitTrack = getSuitAnimTrack(attack)
+
+ def getPowerTrack(effect, suit = suit, battle = battle):
+ partTrack = Sequence(Wait(1.0), Func(battle.movie.needRestoreParticleEffect, effect), Func(effect.start, suit), Wait(0.4), LerpPosInterval(effect, 1.0, Point3(0, 15, 0.4)), LerpFunctionInterval(effect.setAlphaScale, fromData=1, toData=0, duration=0.4), Func(effect.cleanup), Func(battle.movie.clearRestoreParticleEffect, effect))
+ return partTrack
+
+ partTrack1 = getPowerTrack(powerBar1)
+ partTrack2 = getPowerTrack(powerBar2)
+ waterfallTrack = getPartTrack(waterfallEffect, 0.6, 1.3, [waterfallEffect, suit, 0])
+ toonTracks = getToonTracks(attack, 1.8, ['slip-forward'], 1.29, ['jump'])
+ return Parallel(suitTrack, partTrack1, partTrack2, waterfallTrack, toonTracks)
+
+
+def getThrowEndPoint(suit, toon, battle, whichBounce):
+ pnt = toon.getPos(toon)
+ if whichBounce == 'one':
+ pnt.setY(pnt[1] + 8)
+ elif whichBounce == 'two':
+ pnt.setY(pnt[1] + 5)
+ elif whichBounce == 'threeHit':
+ pnt.setZ(pnt[2] + toon.shoulderHeight + 0.3)
+ elif whichBounce == 'threeMiss':
+ pass
+ elif whichBounce == 'four':
+ pnt.setY(pnt[1] - 5)
+ return Point3(pnt)
+
+
+def doBounceCheck(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ battle = attack['battle']
+ toon = target['toon']
+ dmg = target['hp']
+ hitSuit = dmg > 0
+ check = globalPropPool.getProp('bounced-check')
+ checkPosPoints = [MovieUtil.PNT3_ZERO, VBase3(95.247, 79.025, 88.849)]
+ bounce1Point = lambda suit = suit, toon = toon, battle = battle: getThrowEndPoint(suit, toon, battle, 'one')
+ bounce2Point = lambda suit = suit, toon = toon, battle = battle: getThrowEndPoint(suit, toon, battle, 'two')
+ hit3Point = lambda suit = suit, toon = toon, battle = battle: getThrowEndPoint(suit, toon, battle, 'threeHit')
+ miss3Point = lambda suit = suit, toon = toon, battle = battle: getThrowEndPoint(suit, toon, battle, 'threeMiss')
+ bounce4Point = lambda suit = suit, toon = toon, battle = battle: getThrowEndPoint(suit, toon, battle, 'four')
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ throwDelay = 2.5
+ dodgeDelay = 4.3
+ damageDelay = 5.1
+ elif suitType == 'b':
+ throwDelay = 1.8
+ dodgeDelay = 3.6
+ damageDelay = 4.4
+ elif suitType == 'c':
+ throwDelay = 1.8
+ dodgeDelay = 3.6
+ damageDelay = 4.4
+ suitTrack = getSuitTrack(attack)
+ checkPropTrack = Sequence(getPropAppearTrack(check, suit.getRightHand(), checkPosPoints, 1e-05, Point3(8.5, 8.5, 8.5), startScale=MovieUtil.PNT3_ONE))
+ checkPropTrack.append(Wait(throwDelay))
+ checkPropTrack.append(Func(check.wrtReparentTo, toon))
+ checkPropTrack.append(Func(check.setHpr, Point3(0, -90, 0)))
+ checkPropTrack.append(getThrowTrack(check, bounce1Point, duration=0.5, parent=toon))
+ checkPropTrack.append(getThrowTrack(check, bounce2Point, duration=0.9, parent=toon))
+ if hitSuit:
+ checkPropTrack.append(getThrowTrack(check, hit3Point, duration=0.7, parent=toon))
+ else:
+ checkPropTrack.append(getThrowTrack(check, miss3Point, duration=0.7, parent=toon))
+ checkPropTrack.append(getThrowTrack(check, bounce4Point, duration=0.7, parent=toon))
+ checkPropTrack.append(LerpScaleInterval(check, 0.3, MovieUtil.PNT3_NEARZERO))
+ checkPropTrack.append(Func(MovieUtil.removeProp, check))
+ toonTrack = getToonTrack(attack, damageDelay, ['conked'], dodgeDelay, ['sidestep'])
+ soundTracks = Sequence(getSoundTrack('SA_pink_slip.mp3', delay=throwDelay + 0.5, duration=0.6, node=suit), getSoundTrack('SA_pink_slip.mp3', delay=0.4, duration=0.6, node=suit))
+ return Parallel(suitTrack, checkPropTrack, toonTrack, soundTracks)
+
+
+def doWatercooler(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ watercooler = globalPropPool.getProp('watercooler')
+
+ def getCoolerSpout(watercooler = watercooler):
+ spout = watercooler.find('**/joint_toSpray')
+ return spout.getPos(render)
+
+ hitPoint = lambda toon = toon: __toonFacePoint(toon)
+ missPoint = lambda prop = watercooler, toon = toon: __toonMissPoint(prop, toon, 0, parent=render)
+ hitSprayTrack = MovieUtil.getSprayTrack(battle, Point4(0.75, 0.75, 1.0, 0.8), getCoolerSpout, hitPoint, 0.2, 0.2, 0.2, horizScale=0.3, vertScale=0.3)
+ missSprayTrack = MovieUtil.getSprayTrack(battle, Point4(0.75, 0.75, 1.0, 0.8), getCoolerSpout, missPoint, 0.2, 0.2, 0.2, horizScale=0.3, vertScale=0.3)
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(0.48, 0.11, -0.92), VBase3(20.403, 33.158, 69.511)]
+ propTrack = Sequence(Wait(1.01), Func(__showProp, watercooler, suit.getLeftHand(), posPoints[0], posPoints[1]), LerpScaleInterval(watercooler, 0.5, Point3(1.15, 1.15, 1.15)), Wait(1.6))
+ if dmg > 0:
+ propTrack.append(hitSprayTrack)
+ else:
+ propTrack.append(missSprayTrack)
+ propTrack += [Wait(0.01), LerpScaleInterval(watercooler, 0.5, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProp, watercooler)]
+ splashTrack = Sequence()
+ if dmg > 0:
+
+ def prepSplash(splash, targetPoint):
+ splash.reparentTo(render)
+ splash.setPos(targetPoint)
+ scale = splash.getScale()
+ splash.setBillboardPointWorld()
+ splash.setScale(scale)
+
+ splash = globalPropPool.getProp('splash-from-splat')
+ splash.setColor(0.75, 0.75, 1, 0.8)
+ splash.setScale(0.3)
+ splashTrack = Sequence(Func(battle.movie.needRestoreRenderProp, splash), Wait(3.2), Func(prepSplash, splash, __toonFacePoint(toon)), ActorInterval(splash, 'splash-from-splat'), Func(MovieUtil.removeProp, splash), Func(battle.movie.clearRenderProp, splash))
+ toonTrack = getToonTrack(attack, suitTrack.getDuration() - 1.5, ['cringe'], 2.4, ['sidestep'])
+ soundTrack = Sequence(Wait(1.1), SoundInterval(globalBattleSoundCache.getSound('SA_watercooler_appear_only.mp3'), node=suit, duration=1.4722), Wait(0.4), SoundInterval(globalBattleSoundCache.getSound('SA_watercooler_spray_only.mp3'), node=suit, duration=2.313))
+ return Parallel(suitTrack, toonTrack, propTrack, soundTrack, splashTrack)
+
+
+def doFired(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ BattleParticles.loadParticles()
+ baseFlameEffect = BattleParticles.createParticleEffect(file='firedBaseFlame')
+ flameEffect = BattleParticles.createParticleEffect('FiredFlame')
+ flecksEffect = BattleParticles.createParticleEffect('SpriteFiredFlecks')
+ BattleParticles.setEffectTexture(baseFlameEffect, 'fire')
+ BattleParticles.setEffectTexture(flameEffect, 'fire')
+ BattleParticles.setEffectTexture(flecksEffect, 'roll-o-dex', color=Vec4(0.8, 0.8, 0.8, 1))
+ baseFlameSmall = BattleParticles.createParticleEffect(file='firedBaseFlame')
+ flameSmall = BattleParticles.createParticleEffect('FiredFlame')
+ flecksSmall = BattleParticles.createParticleEffect('SpriteFiredFlecks')
+ BattleParticles.setEffectTexture(baseFlameSmall, 'fire')
+ BattleParticles.setEffectTexture(flameSmall, 'fire')
+ BattleParticles.setEffectTexture(flecksSmall, 'roll-o-dex', color=Vec4(0.8, 0.8, 0.8, 1))
+ baseFlameSmall.setScale(0.7)
+ flameSmall.setScale(0.7)
+ flecksSmall.setScale(0.7)
+ suitTrack = getSuitTrack(attack)
+ baseFlameTrack = getPartTrack(baseFlameEffect, 1.0, 1.9, [baseFlameEffect, toon, 0])
+ flameTrack = getPartTrack(flameEffect, 1.0, 1.9, [flameEffect, toon, 0])
+ flecksTrack = getPartTrack(flecksEffect, 1.8, 1.1, [flecksEffect, toon, 0])
+ baseFlameSmallTrack = getPartTrack(baseFlameSmall, 1.0, 1.9, [baseFlameSmall, toon, 0])
+ flameSmallTrack = getPartTrack(flameSmall, 1.0, 1.9, [flameSmall, toon, 0])
+ flecksSmallTrack = getPartTrack(flecksSmall, 1.8, 1.1, [flecksSmall, toon, 0])
+
+ def changeColor(parts):
+ track = Parallel()
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ track.append(Func(nextPart.setColorScale, Vec4(0, 0, 0, 1)))
+
+ return track
+
+ def resetColor(parts):
+ track = Parallel()
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ track.append(Func(nextPart.clearColorScale))
+
+ return track
+
+ if dmg > 0:
+ headParts = toon.getHeadParts()
+ torsoParts = toon.getTorsoParts()
+ legsParts = toon.getLegsParts()
+ colorTrack = Sequence()
+ colorTrack.append(Wait(2.0))
+ colorTrack.append(Func(battle.movie.needRestoreColor))
+ colorTrack.append(changeColor(headParts))
+ colorTrack.append(changeColor(torsoParts))
+ colorTrack.append(changeColor(legsParts))
+ colorTrack.append(Wait(3.5))
+ colorTrack.append(resetColor(headParts))
+ colorTrack.append(resetColor(torsoParts))
+ colorTrack.append(resetColor(legsParts))
+ colorTrack.append(Func(battle.movie.clearRestoreColor))
+ damageAnims = []
+ damageAnims.append(['cringe',
+ 0.01,
+ 0.7,
+ 0.62])
+ damageAnims.append(['slip-forward',
+ 1e-05,
+ 0.4,
+ 1.2])
+ damageAnims.extend(getSplicedLerpAnims('slip-forward', 0.31, 0.8, startTime=1.2))
+ toonTrack = getToonTrack(attack, damageDelay=1.5, splicedDamageAnims=damageAnims, dodgeDelay=0.3, dodgeAnimNames=['sidestep'])
+ soundTrack = getSoundTrack('SA_hot_air.mp3', delay=1.0, node=suit)
+ if dmg > 0:
+ return Parallel(suitTrack, baseFlameTrack, flameTrack, flecksTrack, toonTrack, colorTrack, soundTrack)
+ else:
+ return Parallel(suitTrack, baseFlameSmallTrack, flameSmallTrack, flecksSmallTrack, toonTrack, soundTrack)
+
+
+def doAudit(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ calculator = globalPropPool.getProp('calculator')
+ BattleParticles.loadParticles()
+ particleEffect = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect, 'audit-one', color=Vec4(0, 0, 0, 1))
+ particleEffect2 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect2, 'audit-two', color=Vec4(0, 0, 0, 1))
+ particleEffect3 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect3, 'audit-three', color=Vec4(0, 0, 0, 1))
+ particleEffect4 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect4, 'audit-four', color=Vec4(0, 0, 0, 1))
+ particleEffect5 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect5, 'audit-mult', color=Vec4(0, 0, 0, 1))
+ suitTrack = getSuitTrack(attack)
+ partTrack = getPartTrack(particleEffect, 2.1, 1.9, [particleEffect, suit, 0])
+ partTrack2 = getPartTrack(particleEffect2, 2.2, 2.0, [particleEffect2, suit, 0])
+ partTrack3 = getPartTrack(particleEffect3, 2.3, 2.1, [particleEffect3, suit, 0])
+ partTrack4 = getPartTrack(particleEffect4, 2.4, 2.2, [particleEffect4, suit, 0])
+ partTrack5 = getPartTrack(particleEffect5, 2.5, 2.3, [particleEffect5, suit, 0])
+ suitName = attack['suitName']
+ if suitName == 'nc':
+ calcPosPoints = [Point3(-0.15, 0.37, 0.03), VBase3(1.352, -6.518, -6.045)]
+ calcDuration = 0.76
+ scaleUpPoint = Point3(1.1, 1.85, 1.81)
+ else:
+ calcPosPoints = [Point3(0.35, 0.52, 0.03), VBase3(1.352, -6.518, -6.045)]
+ calcDuration = 1.87
+ scaleUpPoint = Point3(1.0, 1.37, 1.31)
+ calcPropTrack = getPropTrack(calculator, suit.getLeftHand(), calcPosPoints, 1e-06, calcDuration, scaleUpPoint=scaleUpPoint, anim=1, propName='calculator', animStartTime=0.5, animDuration=3.4)
+ toonTrack = getToonTrack(attack, 3.2, ['conked'], 0.9, ['duck'], showMissedExtraTime=2.2)
+ soundTrack = getSoundTrack('SA_audit.mp3', delay=1.9, node=suit)
+ return Parallel(suitTrack, toonTrack, calcPropTrack, soundTrack, partTrack, partTrack2, partTrack3, partTrack4, partTrack5)
+
+
+def doCalculate(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ calculator = globalPropPool.getProp('calculator')
+ BattleParticles.loadParticles()
+ particleEffect = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect, 'audit-one', color=Vec4(0, 0, 0, 1))
+ particleEffect2 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect2, 'audit-plus', color=Vec4(0, 0, 0, 1))
+ particleEffect3 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect3, 'audit-mult', color=Vec4(0, 0, 0, 1))
+ particleEffect4 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect4, 'audit-three', color=Vec4(0, 0, 0, 1))
+ particleEffect5 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect5, 'audit-div', color=Vec4(0, 0, 0, 1))
+ suitTrack = getSuitTrack(attack)
+ partTrack = getPartTrack(particleEffect, 2.1, 1.9, [particleEffect, suit, 0])
+ partTrack2 = getPartTrack(particleEffect2, 2.2, 2.0, [particleEffect2, suit, 0])
+ partTrack3 = getPartTrack(particleEffect3, 2.3, 2.1, [particleEffect3, suit, 0])
+ partTrack4 = getPartTrack(particleEffect4, 2.4, 2.2, [particleEffect4, suit, 0])
+ partTrack5 = getPartTrack(particleEffect5, 2.5, 2.3, [particleEffect5, suit, 0])
+ suitName = attack['suitName']
+ if suitName == 'nc':
+ calcPosPoints = [Point3(-0.15, 0.37, 0.03), VBase3(1.352, -6.518, -6.045)]
+ calcDuration = 0.76
+ scaleUpPoint = Point3(1.1, 1.85, 1.81)
+ else:
+ calcPosPoints = [Point3(0.35, 0.52, 0.03), VBase3(1.352, -6.518, -6.045)]
+ calcDuration = 1.87
+ scaleUpPoint = Point3(1.0, 1.37, 1.31)
+ calcPropTrack = getPropTrack(calculator, suit.getLeftHand(), calcPosPoints, 1e-06, calcDuration, scaleUpPoint=scaleUpPoint, anim=1, propName='calculator', animStartTime=0.5, animDuration=3.4)
+ toonTrack = getToonTrack(attack, 3.2, ['conked'], 1.8, ['sidestep'])
+ return Parallel(suitTrack, toonTrack, calcPropTrack, partTrack, partTrack2, partTrack3, partTrack4, partTrack5)
+
+
+def doTabulate(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ calculator = globalPropPool.getProp('calculator')
+ BattleParticles.loadParticles()
+ particleEffect = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect, 'audit-plus', color=Vec4(0, 0, 0, 1))
+ particleEffect2 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect2, 'audit-minus', color=Vec4(0, 0, 0, 1))
+ particleEffect3 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect3, 'audit-mult', color=Vec4(0, 0, 0, 1))
+ particleEffect4 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect4, 'audit-div', color=Vec4(0, 0, 0, 1))
+ particleEffect5 = BattleParticles.createParticleEffect('Calculate')
+ BattleParticles.setEffectTexture(particleEffect5, 'audit-one', color=Vec4(0, 0, 0, 1))
+ suitTrack = getSuitTrack(attack)
+ partTrack = getPartTrack(particleEffect, 2.1, 1.9, [particleEffect, suit, 0])
+ partTrack2 = getPartTrack(particleEffect2, 2.2, 2.0, [particleEffect2, suit, 0])
+ partTrack3 = getPartTrack(particleEffect3, 2.3, 2.1, [particleEffect3, suit, 0])
+ partTrack4 = getPartTrack(particleEffect4, 2.4, 2.2, [particleEffect4, suit, 0])
+ partTrack5 = getPartTrack(particleEffect5, 2.5, 2.3, [particleEffect5, suit, 0])
+ suitName = attack['suitName']
+ if suitName == 'nc':
+ calcPosPoints = [Point3(-0.15, 0.37, 0.03), VBase3(1.352, -6.518, -6.045)]
+ calcDuration = 0.76
+ scaleUpPoint = Point3(1.1, 1.85, 1.81)
+ else:
+ calcPosPoints = [Point3(0.35, 0.52, 0.03), VBase3(1.352, -6.518, -6.045)]
+ calcDuration = 1.87
+ scaleUpPoint = Point3(1.0, 1.37, 1.31)
+ calcPropTrack = getPropTrack(calculator, suit.getLeftHand(), calcPosPoints, 1e-06, calcDuration, scaleUpPoint=scaleUpPoint, anim=1, propName='calculator', animStartTime=0.5, animDuration=3.4)
+ toonTrack = getToonTrack(attack, 3.2, ['conked'], 1.8, ['sidestep'])
+ return Parallel(suitTrack, toonTrack, calcPropTrack, partTrack, partTrack2, partTrack3, partTrack4, partTrack5)
+
+
+def doCrunch(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ throwDuration = 3.03
+ suitTrack = getSuitTrack(attack)
+ numberNames = ['one',
+ 'two',
+ 'three',
+ 'four',
+ 'five',
+ 'six']
+ BattleParticles.loadParticles()
+ numberSpill1 = BattleParticles.createParticleEffect(file='numberSpill')
+ numberSpill2 = BattleParticles.createParticleEffect(file='numberSpill')
+ spillTexture1 = random.choice(numberNames)
+ spillTexture2 = random.choice(numberNames)
+ BattleParticles.setEffectTexture(numberSpill1, 'audit-' + spillTexture1)
+ BattleParticles.setEffectTexture(numberSpill2, 'audit-' + spillTexture2)
+ numberSpillTrack1 = getPartTrack(numberSpill1, 1.1, 2.2, [numberSpill1, suit, 0])
+ numberSpillTrack2 = getPartTrack(numberSpill2, 1.5, 1.0, [numberSpill2, suit, 0])
+ numberSprayTracks = Parallel()
+ numOfNumbers = random.randint(5, 9)
+ for i in range(0, numOfNumbers - 1):
+ nextSpray = BattleParticles.createParticleEffect(file='numberSpray')
+ nextTexture = random.choice(numberNames)
+ BattleParticles.setEffectTexture(nextSpray, 'audit-' + nextTexture)
+ nextStartTime = random.random() * 0.6 + throwDuration
+ nextDuration = random.random() * 0.4 + 1.4
+ nextSprayTrack = getPartTrack(nextSpray, nextStartTime, nextDuration, [nextSpray, suit, 0])
+ numberSprayTracks.append(nextSprayTrack)
+
+ numberTracks = Parallel()
+ for i in range(0, numOfNumbers):
+ texture = random.choice(numberNames)
+ next = MovieUtil.copyProp(BattleParticles.getParticle('audit-' + texture))
+ next.reparentTo(suit.getRightHand())
+ next.setScale(0.01, 0.01, 0.01)
+ next.setColor(Vec4(0.0, 0.0, 0.0, 1.0))
+ next.setPos(random.random() * 0.6 - 0.3, random.random() * 0.6 - 0.3, random.random() * 0.6 - 0.3)
+ next.setHpr(VBase3(-1.15, 86.58, -76.78))
+ numberTrack = Sequence(Wait(0.9), LerpScaleInterval(next, 0.6, MovieUtil.PNT3_ONE), Wait(1.7), Func(MovieUtil.removeProp, next))
+ numberTracks.append(numberTrack)
+
+ damageAnims = []
+ damageAnims.append(['cringe',
+ 0.01,
+ 0.14,
+ 0.28])
+ damageAnims.append(['cringe',
+ 0.01,
+ 0.16,
+ 0.3])
+ damageAnims.append(['cringe',
+ 0.01,
+ 0.13,
+ 0.22])
+ damageAnims.append(['slip-forward', 0.01, 0.6])
+ toonTrack = getToonTrack(attack, damageDelay=4.7, splicedDamageAnims=damageAnims, dodgeDelay=3.6, dodgeAnimNames=['sidestep'])
+ return Parallel(suitTrack, toonTrack, numberSpillTrack1, numberSpillTrack2, numberTracks, numberSprayTracks)
+
+
+def doLiquidate(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ dmg = target['hp']
+ toon = target['toon']
+ BattleParticles.loadParticles()
+ rainEffect = BattleParticles.createParticleEffect(file='liquidate')
+ rainEffect2 = BattleParticles.createParticleEffect(file='liquidate')
+ rainEffect3 = BattleParticles.createParticleEffect(file='liquidate')
+ cloud = globalPropPool.getProp('stormcloud')
+ suitType = getSuitBodyType(attack['suitName'])
+ if suitType == 'a':
+ partDelay = 0.2
+ damageDelay = 3.5
+ dodgeDelay = 2.45
+ elif suitType == 'b':
+ partDelay = 0.2
+ damageDelay = 3.5
+ dodgeDelay = 2.45
+ elif suitType == 'c':
+ partDelay = 0.2
+ damageDelay = 3.5
+ dodgeDelay = 2.45
+ suitTrack = getSuitTrack(attack, delay=0.9)
+ initialCloudHeight = suit.height + 3
+ cloudPosPoints = [Point3(0, 3, initialCloudHeight), VBase3(180, 0, 0)]
+ cloudPropTrack = Sequence()
+ cloudPropTrack.append(Func(cloud.pose, 'stormcloud', 0))
+ cloudPropTrack.append(getPropAppearTrack(cloud, suit, cloudPosPoints, 1e-06, Point3(3, 3, 3), scaleUpTime=0.7))
+ cloudPropTrack.append(Func(battle.movie.needRestoreRenderProp, cloud))
+ cloudPropTrack.append(Func(cloud.wrtReparentTo, render))
+ targetPoint = __toonFacePoint(toon)
+ targetPoint.setZ(targetPoint[2] + 3)
+ cloudPropTrack.append(Wait(1.1))
+ cloudPropTrack.append(LerpPosInterval(cloud, 1, pos=targetPoint))
+ cloudPropTrack.append(Wait(partDelay))
+ cloudPropTrack.append(Parallel(Sequence(ParticleInterval(rainEffect, cloud, worldRelative=0, duration=2.1, cleanup=True)), Sequence(Wait(0.1), ParticleInterval(rainEffect2, cloud, worldRelative=0, duration=2.0, cleanup=True)), Sequence(Wait(0.1), ParticleInterval(rainEffect3, cloud, worldRelative=0, duration=2.0, cleanup=True)), Sequence(ActorInterval(cloud, 'stormcloud', startTime=3, duration=0.1), ActorInterval(cloud, 'stormcloud', startTime=1, duration=2.3))))
+ cloudPropTrack.append(Wait(0.4))
+ cloudPropTrack.append(LerpScaleInterval(cloud, 0.5, MovieUtil.PNT3_NEARZERO))
+ cloudPropTrack.append(Func(MovieUtil.removeProp, cloud))
+ cloudPropTrack.append(Func(battle.movie.clearRenderProp, cloud))
+ damageAnims = [['melt'], ['jump', 1.5, 0.4]]
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'])
+ soundTrack = getSoundTrack('SA_liquidate.mp3', delay=2.0, node=suit)
+ if dmg > 0:
+ puddle = globalPropPool.getProp('quicksand')
+ puddle.setColor(Vec4(0.0, 0.0, 1.0, 1))
+ puddle.setHpr(Point3(120, 0, 0))
+ puddle.setScale(0.01)
+ puddleTrack = Sequence(Func(battle.movie.needRestoreRenderProp, puddle), Wait(damageDelay - 0.7), Func(puddle.reparentTo, battle), Func(puddle.setPos, toon.getPos(battle)), LerpScaleInterval(puddle, 1.7, Point3(1.7, 1.7, 1.7), startScale=MovieUtil.PNT3_NEARZERO), Wait(3.2), LerpFunctionInterval(puddle.setAlphaScale, fromData=1, toData=0, duration=0.8), Func(MovieUtil.removeProp, puddle), Func(battle.movie.clearRenderProp, puddle))
+ return Parallel(suitTrack, toonTrack, cloudPropTrack, soundTrack, puddleTrack)
+ else:
+ return Parallel(suitTrack, toonTrack, cloudPropTrack, soundTrack)
+
+
+def doMarketCrash(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ suitDelay = 1.32
+ propDelay = 0.6
+ throwDuration = 1.5
+ paper = globalPropPool.getProp('newspaper')
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(-0.07, 0.17, -0.13), VBase3(161.867, -33.149, -48.086)]
+ paperTrack = Sequence(getPropAppearTrack(paper, suit.getRightHand(), posPoints, propDelay, Point3(3, 3, 3), scaleUpTime=0.5))
+ paperTrack.append(Wait(suitDelay))
+ hitPoint = toon.getPos(battle)
+ hitPoint.setX(hitPoint.getX() + 1.2)
+ hitPoint.setY(hitPoint.getY() + 1.5)
+ if dmg > 0:
+ hitPoint.setZ(hitPoint.getZ() + 1.1)
+ movePoint = Point3(hitPoint.getX(), hitPoint.getY() - 1.8, hitPoint.getZ() + 0.2)
+ paperTrack.append(Func(battle.movie.needRestoreRenderProp, paper))
+ paperTrack.append(Func(paper.wrtReparentTo, battle))
+ paperTrack.append(getThrowTrack(paper, hitPoint, duration=throwDuration, parent=battle))
+ paperTrack.append(Wait(0.6))
+ paperTrack.append(LerpPosInterval(paper, 0.4, movePoint))
+ spinTrack = Sequence(Wait(propDelay + suitDelay + 0.2), LerpHprInterval(paper, throwDuration, Point3(-360, 0, 0)))
+ sizeTrack = Sequence(Wait(propDelay + suitDelay + 0.2), LerpScaleInterval(paper, throwDuration, Point3(6, 6, 6)), Wait(0.95), LerpScaleInterval(paper, 0.4, MovieUtil.PNT3_NEARZERO))
+ propTrack = Sequence(Parallel(paperTrack, spinTrack, sizeTrack), Func(MovieUtil.removeProp, paper), Func(battle.movie.clearRenderProp, paper))
+ damageAnims = []
+ damageAnims.append(['cringe',
+ 0.01,
+ 0.21,
+ 0.08])
+ damageAnims.append(['slip-forward',
+ 0.01,
+ 0.6,
+ 0.85])
+ damageAnims.extend(getSplicedLerpAnims('slip-forward', 0.31, 0.95, startTime=1.2))
+ damageAnims.append(['slip-forward', 0.01, 1.51])
+ toonTrack = getToonTrack(attack, damageDelay=3.8, splicedDamageAnims=damageAnims, dodgeDelay=2.4, dodgeAnimNames=['sidestep'], showDamageExtraTime=0.4, showMissedExtraTime=1.3)
+ return Parallel(suitTrack, toonTrack, propTrack)
+
+
+def doBite(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ teeth = globalPropPool.getProp('teeth')
+ propDelay = 0.8
+ propScaleUpTime = 0.5
+ suitDelay = 1.73
+ throwDelay = propDelay + propScaleUpTime + suitDelay
+ throwDuration = 0.4
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(-0.05, 0.41, -0.54), VBase3(4.465, -3.563, 51.479)]
+ teethAppearTrack = Sequence(getPropAppearTrack(teeth, suit.getRightHand(), posPoints, propDelay, Point3(3, 3, 3), scaleUpTime=propScaleUpTime))
+ teethAppearTrack.append(Wait(suitDelay))
+ teethAppearTrack.append(Func(battle.movie.needRestoreRenderProp, teeth))
+ teethAppearTrack.append(Func(teeth.wrtReparentTo, battle))
+ if dmg > 0:
+ x = toon.getX(battle)
+ y = toon.getY(battle)
+ z = toon.getZ(battle)
+ toonHeight = z + toon.getHeight()
+ flyPoint = Point3(x, y + 2.7, toonHeight * 0.8)
+ teethAppearTrack.append(LerpPosInterval(teeth, throwDuration, pos=flyPoint))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.4, pos=Point3(x, y + 3.2, toonHeight * 0.7)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.3, pos=Point3(x, y + 4.7, toonHeight * 0.5)))
+ teethAppearTrack.append(Wait(0.2))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.1, pos=Point3(x, y - 0.2, toonHeight * 0.9)))
+ teethAppearTrack.append(Wait(0.4))
+ scaleTrack = Sequence(Wait(throwDelay), LerpScaleInterval(teeth, throwDuration, Point3(8, 8, 8)), Wait(0.9), LerpScaleInterval(teeth, 0.2, Point3(14, 14, 14)), Wait(1.2), LerpScaleInterval(teeth, 0.3, MovieUtil.PNT3_NEARZERO))
+ hprTrack = Sequence(Wait(throwDelay), LerpHprInterval(teeth, 0.3, Point3(180, 0, 0)), Wait(0.2), LerpHprInterval(teeth, 0.4, Point3(180, -35, 0), startHpr=Point3(180, 0, 0)), Wait(0.6), LerpHprInterval(teeth, 0.1, Point3(180, -75, 0), startHpr=Point3(180, -35, 0)))
+ animTrack = Sequence(Wait(throwDelay), ActorInterval(teeth, 'teeth', duration=throwDuration), ActorInterval(teeth, 'teeth', duration=0.3), Func(teeth.pose, 'teeth', 1), Wait(0.7), ActorInterval(teeth, 'teeth', duration=0.9))
+ propTrack = Sequence(Parallel(teethAppearTrack, scaleTrack, hprTrack, animTrack), Func(MovieUtil.removeProp, teeth), Func(battle.movie.clearRenderProp, teeth))
+ else:
+ flyPoint = __toonFacePoint(toon, parent=battle)
+ flyPoint.setY(flyPoint.getY() - 7.1)
+ teethAppearTrack.append(LerpPosInterval(teeth, throwDuration, pos=flyPoint))
+ teethAppearTrack.append(Func(MovieUtil.removeProp, teeth))
+ teethAppearTrack.append(Func(battle.movie.clearRenderProp, teeth))
+ propTrack = teethAppearTrack
+ damageAnims = [['cringe',
+ 0.01,
+ 0.7,
+ 1.2], ['conked',
+ 0.01,
+ 0.2,
+ 2.1], ['conked', 0.01, 3.2]]
+ dodgeAnims = [['cringe',
+ 0.01,
+ 0.7,
+ 0.2], ['duck', 0.01, 1.6]]
+ toonTrack = getToonTrack(attack, damageDelay=3.2, splicedDamageAnims=damageAnims, dodgeDelay=2.9, splicedDodgeAnims=dodgeAnims, showDamageExtraTime=2.4)
+ return Parallel(suitTrack, toonTrack, propTrack)
+
+
+def doChomp(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ teeth = globalPropPool.getProp('teeth')
+ propDelay = 0.8
+ propScaleUpTime = 0.5
+ suitDelay = 1.73
+ throwDelay = propDelay + propScaleUpTime + suitDelay
+ throwDuration = 0.4
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(-0.05, 0.41, -0.54), VBase3(4.465, -3.563, 51.479)]
+ teethAppearTrack = Sequence(getPropAppearTrack(teeth, suit.getRightHand(), posPoints, propDelay, Point3(3, 3, 3), scaleUpTime=propScaleUpTime))
+ teethAppearTrack.append(Wait(suitDelay))
+ teethAppearTrack.append(Func(battle.movie.needRestoreRenderProp, teeth))
+ teethAppearTrack.append(Func(teeth.wrtReparentTo, battle))
+ if dmg > 0:
+ x = toon.getX(battle)
+ y = toon.getY(battle)
+ z = toon.getZ(battle)
+ toonHeight = z + toon.getHeight()
+ flyPoint = Point3(x, y + 2.7, toonHeight * 0.7)
+ teethAppearTrack.append(LerpPosInterval(teeth, throwDuration, pos=flyPoint))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.4, pos=Point3(x, y + 3.2, toonHeight * 0.7)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.3, pos=Point3(x, y + 4.7, toonHeight * 0.5)))
+ teethAppearTrack.append(Wait(0.2))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.1, pos=Point3(x, y, toonHeight + 3)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.1, pos=Point3(x, y - 1.2, toonHeight * 0.7)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.1, pos=Point3(x, y - 0.7, toonHeight * 0.4)))
+ teethAppearTrack.append(Wait(0.4))
+ scaleTrack = Sequence(Wait(throwDelay), LerpScaleInterval(teeth, throwDuration, Point3(6, 6, 6)), Wait(0.9), LerpScaleInterval(teeth, 0.2, Point3(10, 10, 10)), Wait(1.2), LerpScaleInterval(teeth, 0.3, MovieUtil.PNT3_NEARZERO))
+ hprTrack = Sequence(Wait(throwDelay), LerpHprInterval(teeth, 0.3, Point3(180, 0, 0)), Wait(0.2), LerpHprInterval(teeth, 0.4, Point3(180, -35, 0), startHpr=Point3(180, 0, 0)), Wait(0.6), LerpHprInterval(teeth, 0.1, Point3(0, -35, 0), startHpr=Point3(180, -35, 0)))
+ animTrack = Sequence(Wait(throwDelay), ActorInterval(teeth, 'teeth', duration=throwDuration), ActorInterval(teeth, 'teeth', duration=0.3), Func(teeth.pose, 'teeth', 1), Wait(0.7), ActorInterval(teeth, 'teeth', duration=0.9))
+ propTrack = Sequence(Parallel(teethAppearTrack, scaleTrack, hprTrack, animTrack), Func(MovieUtil.removeProp, teeth), Func(battle.movie.clearRenderProp, teeth))
+ else:
+ x = toon.getX(battle)
+ y = toon.getY(battle)
+ z = toon.getZ(battle)
+ z = z + 0.2
+ flyPoint = Point3(x, y - 2.1, z)
+ teethAppearTrack.append(LerpPosInterval(teeth, throwDuration, pos=flyPoint))
+ teethAppearTrack.append(Wait(0.2))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x + 0.5, y - 2.5, z)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x + 1.0, y - 3.0, z + 0.4)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x + 1.3, y - 3.6, z)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x + 0.9, y - 3.1, z + 0.4)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x + 0.3, y - 2.6, z)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x - 0.1, y - 2.2, z + 0.4)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x - 0.4, y - 1.9, z)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x - 0.7, y - 2.1, z + 0.4)))
+ teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x - 0.8, y - 2.3, z)))
+ teethAppearTrack.append(LerpScaleInterval(teeth, 0.6, MovieUtil.PNT3_NEARZERO))
+ hprTrack = Sequence(Wait(throwDelay), LerpHprInterval(teeth, 0.3, Point3(180, 0, 0)), Wait(0.5), LerpHprInterval(teeth, 0.4, Point3(80, 0, 0), startHpr=Point3(180, 0, 0)), LerpHprInterval(teeth, 0.8, Point3(-10, 0, 0), startHpr=Point3(80, 0, 0)))
+ animTrack = Sequence(Wait(throwDelay), ActorInterval(teeth, 'teeth', duration=3.6))
+ propTrack = Sequence(Parallel(teethAppearTrack, hprTrack, animTrack), Func(MovieUtil.removeProp, teeth), Func(battle.movie.clearRenderProp, teeth))
+ damageAnims = [['cringe',
+ 0.01,
+ 0.7,
+ 1.2],
+ ['spit',
+ 0.01,
+ 2.95,
+ 1.47],
+ ['spit',
+ 0.01,
+ 4.42,
+ 0.07],
+ ['spit',
+ 0.08,
+ 4.49,
+ -0.07],
+ ['spit',
+ 0.08,
+ 4.42,
+ 0.07],
+ ['spit',
+ 0.08,
+ 4.49,
+ -0.07],
+ ['spit',
+ 0.08,
+ 4.42,
+ 0.07],
+ ['spit',
+ 0.08,
+ 4.49,
+ -0.07],
+ ['spit', 0.01, 4.42]]
+ dodgeAnims = [['jump', 0.01, 0.01]]
+ toonTrack = getToonTrack(attack, damageDelay=3.2, splicedDamageAnims=damageAnims, dodgeDelay=2.75, splicedDodgeAnims=dodgeAnims, showDamageExtraTime=1.4)
+ return Parallel(suitTrack, toonTrack, propTrack)
+
+
+def doEvictionNotice(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ paper = globalPropPool.getProp('shredder-paper')
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(-0.04, 0.15, -1.38), VBase3(10.584, -11.945, 18.316)]
+ propTrack = Sequence(getPropAppearTrack(paper, suit.getRightHand(), posPoints, 0.8, MovieUtil.PNT3_ONE, scaleUpTime=0.5))
+ propTrack.append(Wait(1.73))
+ hitPoint = __toonFacePoint(toon, parent=battle)
+ hitPoint.setX(hitPoint.getX() - 1.4)
+ missPoint = __toonGroundPoint(attack, toon, 0.7, parent=battle)
+ missPoint.setX(missPoint.getX() - 1.1)
+ propTrack.append(getPropThrowTrack(attack, paper, [hitPoint], [missPoint], parent=battle))
+ toonTrack = getToonTrack(attack, 3.4, ['conked'], 2.8, ['jump'])
+ return Parallel(suitTrack, toonTrack, propTrack)
+
+
+def doWithdrawal(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ BattleParticles.loadParticles()
+ particleEffect = BattleParticles.createParticleEffect('Withdrawal')
+ BattleParticles.setEffectTexture(particleEffect, 'snow-particle')
+ suitTrack = getSuitAnimTrack(attack)
+ partTrack = getPartTrack(particleEffect, 1e-05, suitTrack.getDuration() + 1.2, [particleEffect, suit, 0])
+ toonTrack = getToonTrack(attack, 1.2, ['cringe'], 0.2, splicedDodgeAnims=[['duck', 1e-05, 0.8]], showMissedExtraTime=0.8)
+ headParts = toon.getHeadParts()
+ torsoParts = toon.getTorsoParts()
+ legsParts = toon.getLegsParts()
+
+ def changeColor(parts):
+ track = Parallel()
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ track.append(Func(nextPart.setColorScale, Vec4(0, 0, 0, 1)))
+
+ return track
+
+ def resetColor(parts):
+ track = Parallel()
+ for partNum in range(0, parts.getNumPaths()):
+ nextPart = parts.getPath(partNum)
+ track.append(Func(nextPart.clearColorScale))
+
+ return track
+
+ soundTrack = getSoundTrack('SA_withdrawl.mp3', delay=1.4, node=suit)
+ if dmg > 0:
+ colorTrack = Sequence()
+ colorTrack.append(Wait(1.6))
+ colorTrack.append(Func(battle.movie.needRestoreColor))
+ colorTrack.append(Parallel(changeColor(headParts), changeColor(torsoParts), changeColor(legsParts)))
+ colorTrack.append(Wait(2.9))
+ colorTrack.append(resetColor(headParts))
+ colorTrack.append(resetColor(torsoParts))
+ colorTrack.append(resetColor(legsParts))
+ colorTrack.append(Func(battle.movie.clearRestoreColor))
+ return Parallel(suitTrack, partTrack, toonTrack, soundTrack, colorTrack)
+ else:
+ return Parallel(suitTrack, partTrack, toonTrack, soundTrack)
+
+
+def doJargon(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ BattleParticles.loadParticles()
+ particleEffect = BattleParticles.createParticleEffect(file='jargonSpray')
+ particleEffect2 = BattleParticles.createParticleEffect(file='jargonSpray')
+ particleEffect3 = BattleParticles.createParticleEffect(file='jargonSpray')
+ particleEffect4 = BattleParticles.createParticleEffect(file='jargonSpray')
+ BattleParticles.setEffectTexture(particleEffect, 'jargon-brow', color=Vec4(1, 0, 0, 1))
+ BattleParticles.setEffectTexture(particleEffect2, 'jargon-deep', color=Vec4(0, 0, 0, 1))
+ BattleParticles.setEffectTexture(particleEffect3, 'jargon-hoop', color=Vec4(1, 0, 0, 1))
+ BattleParticles.setEffectTexture(particleEffect4, 'jargon-ipo', color=Vec4(0, 0, 0, 1))
+ damageDelay = 2.2
+ dodgeDelay = 1.5
+ partDelay = 1.1
+ partInterval = 1.2
+ suitTrack = getSuitTrack(attack)
+ partTrack = getPartTrack(particleEffect, partDelay + partInterval * 0, 2, [particleEffect, suit, 0])
+ partTrack2 = getPartTrack(particleEffect2, partDelay + partInterval * 1, 2, [particleEffect2, suit, 0])
+ partTrack3 = getPartTrack(particleEffect3, partDelay + partInterval * 2, 2, [particleEffect3, suit, 0])
+ partTrack4 = getPartTrack(particleEffect4, partDelay + partInterval * 3, 1.0, [particleEffect4, suit, 0])
+ damageAnims = []
+ damageAnims.append(['conked',
+ 0.0001,
+ 0,
+ 0.4])
+ damageAnims.append(['conked',
+ 0.0001,
+ 2.7,
+ 0.85])
+ damageAnims.append(['conked',
+ 0.0001,
+ 0.4,
+ 0.09])
+ damageAnims.append(['conked',
+ 0.0001,
+ 0.4,
+ 0.09])
+ damageAnims.append(['conked',
+ 0.0001,
+ 0.4,
+ 0.66])
+ damageAnims.append(['conked',
+ 0.0001,
+ 0.4,
+ 0.09])
+ damageAnims.append(['conked',
+ 0.0001,
+ 0.4,
+ 0.09])
+ damageAnims.append(['conked',
+ 0.0001,
+ 0.4,
+ 0.86])
+ damageAnims.append(['conked',
+ 0.0001,
+ 0.4,
+ 0.14])
+ damageAnims.append(['conked',
+ 0.0001,
+ 0.4,
+ 0.14])
+ damageAnims.append(['conked', 0.0001, 0.4])
+ dodgeAnims = [['duck', 0.0001, 1.2], ['duck', 0.0001, 1.3]]
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=1.6, showDamageExtraTime=0.7)
+ soundTrack = getSoundTrack('SA_jargon.mp3', delay=2.1, node=suit)
+ return Parallel(suitTrack, toonTrack, soundTrack, partTrack, partTrack2, partTrack3, partTrack4)
+
+
+def doMumboJumbo(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ BattleParticles.loadParticles()
+ particleEffect = BattleParticles.createParticleEffect(file='mumboJumboSpray')
+ particleEffect2 = BattleParticles.createParticleEffect(file='mumboJumboSpray')
+ particleEffect3 = BattleParticles.createParticleEffect(file='mumboJumboSmother')
+ particleEffect4 = BattleParticles.createParticleEffect(file='mumboJumboSmother')
+ particleEffect5 = BattleParticles.createParticleEffect(file='mumboJumboSmother')
+ BattleParticles.setEffectTexture(particleEffect, 'mumbojumbo-boiler', color=Vec4(1, 0, 0, 1))
+ BattleParticles.setEffectTexture(particleEffect2, 'mumbojumbo-creative', color=Vec4(1, 0, 0, 1))
+ BattleParticles.setEffectTexture(particleEffect3, 'mumbojumbo-deben', color=Vec4(1, 0, 0, 1))
+ BattleParticles.setEffectTexture(particleEffect4, 'mumbojumbo-high', color=Vec4(1, 0, 0, 1))
+ BattleParticles.setEffectTexture(particleEffect5, 'mumbojumbo-iron', color=Vec4(1, 0, 0, 1))
+ suitTrack = getSuitTrack(attack)
+ partTrack = getPartTrack(particleEffect, 2.5, 2, [particleEffect, suit, 0])
+ partTrack2 = getPartTrack(particleEffect2, 2.5, 2, [particleEffect2, suit, 0])
+ partTrack3 = getPartTrack(particleEffect3, 3.3, 1.7, [particleEffect3, toon, 0])
+ partTrack4 = getPartTrack(particleEffect4, 3.3, 1.7, [particleEffect4, toon, 0])
+ partTrack5 = getPartTrack(particleEffect5, 3.3, 1.7, [particleEffect5, toon, 0])
+ toonTrack = getToonTrack(attack, 3.2, ['cringe'], 2.2, ['sidestep'])
+ soundTrack = getSoundTrack('SA_mumbo_jumbo.mp3', delay=2.5, node=suit)
+ if dmg > 0:
+ return Parallel(suitTrack, toonTrack, soundTrack, partTrack, partTrack2, partTrack3, partTrack4, partTrack5)
+ else:
+ return Parallel(suitTrack, toonTrack, soundTrack, partTrack, partTrack2)
+
+
+def doGuiltTrip(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ centerColor = Vec4(1.0, 0.2, 0.2, 0.9)
+ edgeColor = Vec4(0.9, 0.9, 0.9, 0.4)
+ powerBar1 = BattleParticles.createParticleEffect(file='guiltTrip')
+ powerBar2 = BattleParticles.createParticleEffect(file='guiltTrip')
+ powerBar1.setPos(0, 6.1, 0.4)
+ powerBar1.setHpr(-90, 0, 0)
+ powerBar2.setPos(0, 6.1, 0.4)
+ powerBar2.setHpr(90, 0, 0)
+ powerBar1.setScale(5)
+ powerBar2.setScale(5)
+ powerBar1Particles = powerBar1.getParticlesNamed('particles-1')
+ powerBar2Particles = powerBar2.getParticlesNamed('particles-1')
+ powerBar1Particles.renderer.setCenterColor(centerColor)
+ powerBar1Particles.renderer.setEdgeColor(edgeColor)
+ powerBar2Particles.renderer.setCenterColor(centerColor)
+ powerBar2Particles.renderer.setEdgeColor(edgeColor)
+ waterfallEffect = BattleParticles.createParticleEffect('Waterfall')
+ waterfallEffect.setScale(11)
+ waterfallParticles = waterfallEffect.getParticlesNamed('particles-1')
+ waterfallParticles.renderer.setCenterColor(centerColor)
+ waterfallParticles.renderer.setEdgeColor(edgeColor)
+ suitTrack = getSuitAnimTrack(attack)
+
+ def getPowerTrack(effect, suit = suit, battle = battle):
+ partTrack = Sequence(Wait(0.7), Func(battle.movie.needRestoreParticleEffect, effect), Func(effect.start, suit), Wait(0.4), LerpPosInterval(effect, 1.0, Point3(0, 15, 0.4)), LerpFunctionInterval(effect.setAlphaScale, fromData=1, toData=0, duration=0.4), Func(effect.cleanup), Func(battle.movie.clearRestoreParticleEffect, effect))
+ return partTrack
+
+ partTrack1 = getPowerTrack(powerBar1)
+ partTrack2 = getPowerTrack(powerBar2)
+ waterfallTrack = getPartTrack(waterfallEffect, 0.6, 0.6, [waterfallEffect, suit, 0])
+ toonTracks = getToonTracks(attack, 1.5, ['slip-forward'], 0.86, ['jump'])
+ soundTrack = getSoundTrack('SA_guilt_trip.mp3', delay=1.1, node=suit)
+ return Parallel(suitTrack, partTrack1, partTrack2, soundTrack, waterfallTrack, toonTracks)
+
+
+def doRestrainingOrder(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ paper = globalPropPool.getProp('shredder-paper')
+ suitTrack = getSuitTrack(attack)
+ posPoints = [Point3(-0.04, 0.15, -1.38), VBase3(10.584, -11.945, 18.316)]
+ propTrack = Sequence(getPropAppearTrack(paper, suit.getRightHand(), posPoints, 0.8, MovieUtil.PNT3_ONE, scaleUpTime=0.5))
+ propTrack.append(Wait(1.73))
+ hitPoint = __toonFacePoint(toon, parent=battle)
+ hitPoint.setX(hitPoint.getX() - 1.4)
+ missPoint = __toonGroundPoint(attack, toon, 0.7, parent=battle)
+ missPoint.setX(missPoint.getX() - 1.1)
+ propTrack.append(getPropThrowTrack(attack, paper, [hitPoint], [missPoint], parent=battle))
+ damageAnims = [['conked',
+ 0.01,
+ 0.3,
+ 0.2], ['struggle', 0.01, 0.2]]
+ toonTrack = getToonTrack(attack, damageDelay=3.4, splicedDamageAnims=damageAnims, dodgeDelay=2.8, dodgeAnimNames=['sidestep'])
+ if dmg > 0:
+ restraintCloud = BattleParticles.createParticleEffect(file='restrainingOrderCloud')
+ restraintCloud.setPos(hitPoint.getX(), hitPoint.getY() + 0.5, hitPoint.getZ())
+ cloudTrack = getPartTrack(restraintCloud, 3.5, 0.2, [restraintCloud, battle, 0])
+ return Parallel(suitTrack, cloudTrack, toonTrack, propTrack)
+ else:
+ return Parallel(suitTrack, toonTrack, propTrack)
+
+
+def doSpin(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ damageDelay = 1.7
+ sprayEffect = BattleParticles.createParticleEffect(file='spinSpray')
+ spinEffect1 = BattleParticles.createParticleEffect(file='spinEffect')
+ spinEffect2 = BattleParticles.createParticleEffect(file='spinEffect')
+ spinEffect3 = BattleParticles.createParticleEffect(file='spinEffect')
+ spinEffect1.reparentTo(toon)
+ spinEffect2.reparentTo(toon)
+ spinEffect3.reparentTo(toon)
+ height1 = toon.getHeight() * (random.random() * 0.2 + 0.7)
+ height2 = toon.getHeight() * (random.random() * 0.2 + 0.4)
+ height3 = toon.getHeight() * (random.random() * 0.2 + 0.1)
+ spinEffect1.setPos(0.8, -0.7, height1)
+ spinEffect1.setHpr(0, 0, -random.random() * 10 - 85)
+ spinEffect1.setHpr(spinEffect1, 0, 50, 0)
+ spinEffect2.setPos(0.8, -0.7, height2)
+ spinEffect2.setHpr(0, 0, -random.random() * 10 - 85)
+ spinEffect2.setHpr(spinEffect2, 0, 50, 0)
+ spinEffect3.setPos(0.8, -0.7, height3)
+ spinEffect3.setHpr(0, 0, -random.random() * 10 - 85)
+ spinEffect3.setHpr(spinEffect3, 0, 50, 0)
+ spinEffect1.wrtReparentTo(battle)
+ spinEffect2.wrtReparentTo(battle)
+ spinEffect3.wrtReparentTo(battle)
+ suitTrack = getSuitTrack(attack)
+ sprayTrack = getPartTrack(sprayEffect, 1.0, 1.9, [sprayEffect, suit, 0])
+ spinTrack1 = getPartTrack(spinEffect1, 2.1, 3.9, [spinEffect1, battle, 0])
+ spinTrack2 = getPartTrack(spinEffect2, 2.1, 3.9, [spinEffect2, battle, 0])
+ spinTrack3 = getPartTrack(spinEffect3, 2.1, 3.9, [spinEffect3, battle, 0])
+ damageAnims = []
+ damageAnims.append(['duck',
+ 0.01,
+ 0.01,
+ 1.1])
+ damageAnims.extend(getSplicedLerpAnims('think', 0.66, 1.1, startTime=2.26))
+ damageAnims.extend(getSplicedLerpAnims('think', 0.66, 1.1, startTime=2.26))
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=0.91, dodgeAnimNames=['sidestep'], showDamageExtraTime=2.1, showMissedExtraTime=1.0)
+ if dmg > 0:
+ toonSpinTrack = Sequence(Wait(damageDelay + 0.9), LerpHprInterval(toon, 0.7, Point3(-10, 0, 0)), LerpHprInterval(toon, 0.5, Point3(-30, 0, 0)), LerpHprInterval(toon, 0.2, Point3(-60, 0, 0)), LerpHprInterval(toon, 0.7, Point3(-700, 0, 0)), LerpHprInterval(toon, 1.0, Point3(-1310, 0, 0)), LerpHprInterval(toon, 0.4, toon.getHpr()), Wait(0.5))
+ return Parallel(suitTrack, sprayTrack, toonTrack, toonSpinTrack, spinTrack1, spinTrack2, spinTrack3)
+ else:
+ return Parallel(suitTrack, sprayTrack, toonTrack)
+
+
+def doLegalese(attack):
+ suit = attack['suit']
+ BattleParticles.loadParticles()
+ sprayEffect1 = BattleParticles.createParticleEffect(file='legaleseSpray')
+ sprayEffect2 = BattleParticles.createParticleEffect(file='legaleseSpray')
+ sprayEffect3 = BattleParticles.createParticleEffect(file='legaleseSpray')
+ color = Vec4(0.4, 0, 0, 1)
+ BattleParticles.setEffectTexture(sprayEffect1, 'legalese-hc', color=color)
+ BattleParticles.setEffectTexture(sprayEffect2, 'legalese-qpq', color=color)
+ BattleParticles.setEffectTexture(sprayEffect3, 'legalese-vd', color=color)
+ partDelay = 1.3
+ partDuration = 1.15
+ damageDelay = 1.9
+ dodgeDelay = 1.1
+ suitTrack = getSuitTrack(attack)
+ sprayTrack1 = getPartTrack(sprayEffect1, partDelay, partDuration, [sprayEffect1, suit, 0])
+ sprayTrack2 = getPartTrack(sprayEffect2, partDelay + 0.8, partDuration, [sprayEffect2, suit, 0])
+ sprayTrack3 = getPartTrack(sprayEffect3, partDelay + 1.6, partDuration, [sprayEffect3, suit, 0])
+ damageAnims = []
+ damageAnims.append(['cringe',
+ 1e-05,
+ 0.3,
+ 0.8])
+ damageAnims.append(['cringe',
+ 1e-05,
+ 0.3,
+ 0.8])
+ damageAnims.append(['cringe', 1e-05, 0.3])
+ toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'], showMissedExtraTime=0.8)
+ return Parallel(suitTrack, toonTrack, sprayTrack1, sprayTrack2, sprayTrack3)
+
+
+def doPeckingOrder(attack):
+ suit = attack['suit']
+ battle = attack['battle']
+ target = attack['target']
+ toon = target['toon']
+ dmg = target['hp']
+ throwDuration = 3.03
+ throwDelay = 3.2
+ suitTrack = getSuitTrack(attack)
+ numBirds = random.randint(4, 7)
+ birdTracks = Parallel()
+ propDelay = 1.5
+ for i in range(0, numBirds):
+ next = globalPropPool.getProp('bird')
+ next.setScale(0.01)
+ next.reparentTo(suit.getRightHand())
+ next.setPos(random.random() * 0.6 - 0.3, random.random() * 0.6 - 0.3, random.random() * 0.6 - 0.3)
+ if dmg > 0:
+ hitPoint = Point3(random.random() * 5 - 2.5, random.random() * 2 - 1 - 6, random.random() * 3 - 1.5 + toon.getHeight() - 0.9)
+ else:
+ hitPoint = Point3(random.random() * 2 - 1, random.random() * 4 - 2 - 15, random.random() * 4 - 2 + 2.2)
+ birdTrack = Sequence(Wait(throwDelay), Func(battle.movie.needRestoreRenderProp, next), Func(next.wrtReparentTo, battle), Func(next.setHpr, Point3(90, 20, 0)), LerpPosInterval(next, 1.1, hitPoint))
+ scaleTrack = Sequence(Wait(throwDelay), LerpScaleInterval(next, 0.15, Point3(9, 9, 9)))
+ birdTracks.append(Sequence(Parallel(birdTrack, scaleTrack), Func(MovieUtil.removeProp, next)))
+
+ damageAnims = []
+ damageAnims.append(['cringe',
+ 0.01,
+ 0.14,
+ 0.21])
+ damageAnims.append(['cringe',
+ 0.01,
+ 0.14,
+ 0.13])
+ damageAnims.append(['cringe', 0.01, 0.43])
+ toonTrack = getToonTrack(attack, damageDelay=4.2, splicedDamageAnims=damageAnims, dodgeDelay=2.8, dodgeAnimNames=['sidestep'], showMissedExtraTime=1.1)
+ return Parallel(suitTrack, toonTrack, birdTracks)
diff --git a/toontown/battle/MovieThrow.py b/toontown/battle/MovieThrow.py
new file mode 100644
index 0000000..5916b5a
--- /dev/null
+++ b/toontown/battle/MovieThrow.py
@@ -0,0 +1,594 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from BattleProps import *
+from BattleSounds import *
+from toontown.toon.ToonDNA import *
+from toontown.suit.SuitDNA import *
+from direct.directnotify import DirectNotifyGlobal
+import random
+import MovieCamera
+import MovieUtil
+from MovieUtil import calcAvgSuitPos
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieThrow')
+hitSoundFiles = ('AA_tart_only.mp3', 'AA_slice_only.mp3', 'AA_slice_only.mp3', 'AA_slice_only.mp3', 'AA_slice_only.mp3', 'AA_wholepie_only.mp3', 'AA_wholepie_only.mp3')
+tPieLeavesHand = 2.7
+tPieHitsSuit = 3.0
+tSuitDodges = 2.45
+ratioMissToHit = 1.5
+tPieShrink = 0.7
+pieFlyTaskName = 'MovieThrow-pieFly'
+
+def addHit(dict, suitId, hitCount):
+ if dict.has_key(suitId):
+ dict[suitId] += hitCount
+ else:
+ dict[suitId] = hitCount
+
+
+def doThrows(throws):
+ if len(throws) == 0:
+ return (None, None)
+ suitThrowsDict = {}
+ for throw in throws:
+ if attackAffectsGroup(throw['track'], throw['level']):
+ pass
+ else:
+ suitId = throw['target']['suit'].doId
+ if suitThrowsDict.has_key(suitId):
+ suitThrowsDict[suitId].append(throw)
+ else:
+ suitThrowsDict[suitId] = [throw]
+
+ suitThrows = suitThrowsDict.values()
+
+ def compFunc(a, b):
+ if len(a) > len(b):
+ return 1
+ elif len(a) < len(b):
+ return -1
+ return 0
+
+ suitThrows.sort(compFunc)
+ totalHitDict = {}
+ singleHitDict = {}
+ groupHitDict = {}
+ for throw in throws:
+ if attackAffectsGroup(throw['track'], throw['level']):
+ for i in range(len(throw['target'])):
+ target = throw['target'][i]
+ suitId = target['suit'].doId
+ if target['hp'] > 0:
+ addHit(groupHitDict, suitId, 1)
+ addHit(totalHitDict, suitId, 1)
+ else:
+ addHit(groupHitDict, suitId, 0)
+ addHit(totalHitDict, suitId, 0)
+
+ else:
+ suitId = throw['target']['suit'].doId
+ if throw['target']['hp'] > 0:
+ addHit(singleHitDict, suitId, 1)
+ addHit(totalHitDict, suitId, 1)
+ else:
+ addHit(singleHitDict, suitId, 0)
+ addHit(totalHitDict, suitId, 0)
+
+ notify.debug('singleHitDict = %s' % singleHitDict)
+ notify.debug('groupHitDict = %s' % groupHitDict)
+ notify.debug('totalHitDict = %s' % totalHitDict)
+ delay = 0.0
+ mtrack = Parallel()
+ for st in suitThrows:
+ if len(st) > 0:
+ ival = __doSuitThrows(st)
+ if ival:
+ mtrack.append(Sequence(Wait(delay), ival))
+ delay = delay + TOON_THROW_SUIT_DELAY
+
+ retTrack = Sequence()
+ retTrack.append(mtrack)
+ groupThrowIvals = Parallel()
+ groupThrows = []
+ for throw in throws:
+ if attackAffectsGroup(throw['track'], throw['level']):
+ groupThrows.append(throw)
+
+ for throw in groupThrows:
+ tracks = None
+ tracks = __throwGroupPie(throw, 0, groupHitDict)
+ if tracks:
+ for track in tracks:
+ groupThrowIvals.append(track)
+
+ retTrack.append(groupThrowIvals)
+ camDuration = retTrack.getDuration()
+ camTrack = MovieCamera.chooseThrowShot(throws, suitThrowsDict, camDuration)
+ return (retTrack, camTrack)
+
+
+def __doSuitThrows(throws):
+ toonTracks = Parallel()
+ delay = 0.0
+ hitCount = 0
+ for throw in throws:
+ if throw['target']['hp'] > 0:
+ hitCount += 1
+ else:
+ break
+
+ for throw in throws:
+ tracks = __throwPie(throw, delay, hitCount)
+ if tracks:
+ for track in tracks:
+ toonTracks.append(track)
+
+ delay = delay + TOON_THROW_DELAY
+
+ return toonTracks
+
+
+def __showProp(prop, parent, pos):
+ prop.reparentTo(parent)
+ prop.setPos(pos)
+
+
+def __animProp(props, propName, propType):
+ if 'actor' == propType:
+ for prop in props:
+ prop.play(propName)
+
+ elif 'model' == propType:
+ pass
+ else:
+ notify.error('No such propType as: %s' % propType)
+
+
+def __billboardProp(prop):
+ scale = prop.getScale()
+ prop.setBillboardPointWorld()
+ prop.setScale(scale)
+
+
+def __suitMissPoint(suit, other = render):
+ pnt = suit.getPos(other)
+ pnt.setZ(pnt[2] + suit.getHeight() * 1.3)
+ return pnt
+
+
+def __propPreflight(props, suit, toon, battle):
+ prop = props[0]
+ toon.update(0)
+ prop.wrtReparentTo(battle)
+ props[1].reparentTo(hidden)
+ for ci in range(prop.getNumChildren()):
+ prop.getChild(ci).setHpr(0, -90, 0)
+
+ targetPnt = MovieUtil.avatarFacePoint(suit, other=battle)
+ prop.lookAt(targetPnt)
+
+
+def __propPreflightGroup(props, suits, toon, battle):
+ prop = props[0]
+ toon.update(0)
+ prop.wrtReparentTo(battle)
+ props[1].reparentTo(hidden)
+ for ci in range(prop.getNumChildren()):
+ prop.getChild(ci).setHpr(0, -90, 0)
+
+ avgTargetPt = Point3(0, 0, 0)
+ for suit in suits:
+ avgTargetPt += MovieUtil.avatarFacePoint(suit, other=battle)
+
+ avgTargetPt /= len(suits)
+ prop.lookAt(avgTargetPt)
+
+
+def __piePreMiss(missDict, pie, suitPoint, other = render):
+ missDict['pie'] = pie
+ missDict['startScale'] = pie.getScale()
+ missDict['startPos'] = pie.getPos(other)
+ v = Vec3(suitPoint - missDict['startPos'])
+ endPos = missDict['startPos'] + v * ratioMissToHit
+ missDict['endPos'] = endPos
+
+
+def __pieMissLerpCallback(t, missDict):
+ pie = missDict['pie']
+ newPos = missDict['startPos'] * (1.0 - t) + missDict['endPos'] * t
+ if t < tPieShrink:
+ tScale = 0.0001
+ else:
+ tScale = (t - tPieShrink) / (1.0 - tPieShrink)
+ newScale = missDict['startScale'] * max(1.0 - tScale, 0.01)
+ pie.setPos(newPos)
+ pie.setScale(newScale)
+
+
+def __piePreMissGroup(missDict, pies, suitPoint, other = render):
+ missDict['pies'] = pies
+ missDict['startScale'] = pies[0].getScale()
+ missDict['startPos'] = pies[0].getPos(other)
+ v = Vec3(suitPoint - missDict['startPos'])
+ endPos = missDict['startPos'] + v * ratioMissToHit
+ missDict['endPos'] = endPos
+ notify.debug('startPos=%s' % missDict['startPos'])
+ notify.debug('v=%s' % v)
+ notify.debug('endPos=%s' % missDict['endPos'])
+
+
+def __pieMissGroupLerpCallback(t, missDict):
+ pies = missDict['pies']
+ newPos = missDict['startPos'] * (1.0 - t) + missDict['endPos'] * t
+ if t < tPieShrink:
+ tScale = 0.0001
+ else:
+ tScale = (t - tPieShrink) / (1.0 - tPieShrink)
+ newScale = missDict['startScale'] * max(1.0 - tScale, 0.01)
+ for pie in pies:
+ pie.setPos(newPos)
+ pie.setScale(newScale)
+
+
+def __getWeddingCakeSoundTrack(level, hitSuit, node = None):
+ throwTrack = Sequence()
+ if hitSuit:
+ throwSound = globalBattleSoundCache.getSound('AA_throw_wedding_cake.mp3')
+ songTrack = Sequence()
+ songTrack.append(Wait(1.0))
+ songTrack.append(SoundInterval(throwSound, node=node))
+ splatSound = globalBattleSoundCache.getSound('AA_throw_wedding_cake_cog.mp3')
+ splatTrack = Sequence()
+ splatTrack.append(Wait(tPieHitsSuit))
+ splatTrack.append(SoundInterval(splatSound, node=node))
+ throwTrack.append(Parallel(songTrack, splatTrack))
+ else:
+ throwSound = globalBattleSoundCache.getSound('AA_throw_wedding_cake_miss.mp3')
+ throwTrack.append(Wait(tSuitDodges))
+ throwTrack.append(SoundInterval(throwSound, node=node))
+ return throwTrack
+
+
+def __getSoundTrack(level, hitSuit, node = None):
+ if level == UBER_GAG_LEVEL_INDEX:
+ return __getWeddingCakeSoundTrack(level, hitSuit, node)
+ throwSound = globalBattleSoundCache.getSound('AA_pie_throw_only.mp3')
+ throwTrack = Sequence(Wait(2.6), SoundInterval(throwSound, node=node))
+ if hitSuit:
+ hitSound = globalBattleSoundCache.getSound(hitSoundFiles[level])
+ hitTrack = Sequence(Wait(tPieLeavesHand), SoundInterval(hitSound, node=node))
+ return Parallel(throwTrack, hitTrack)
+ else:
+ return throwTrack
+
+
+def __throwPie(throw, delay, hitCount):
+ toon = throw['toon']
+ hpbonus = throw['hpbonus']
+ target = throw['target']
+ suit = target['suit']
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ sidestep = throw['sidestep']
+ died = target['died']
+ revived = target['revived']
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ level = throw['level']
+ battle = throw['battle']
+ suitPos = suit.getPos(battle)
+ origHpr = toon.getHpr(battle)
+ notify.debug('toon: %s throws tart at suit: %d for hp: %d died: %d' % (toon.getName(),
+ suit.doId,
+ hp,
+ died))
+ pieName = pieNames[level]
+ hitSuit = hp > 0
+ pie = globalPropPool.getProp(pieName)
+ pieType = globalPropPool.getPropType(pieName)
+ pie2 = MovieUtil.copyProp(pie)
+ pies = [pie, pie2]
+ hands = toon.getRightHands()
+ splatName = 'splat-' + pieName
+ if pieName == 'wedding-cake':
+ splatName = 'splat-birthday-cake'
+ splat = globalPropPool.getProp(splatName)
+ splatType = globalPropPool.getPropType(splatName)
+ toonTrack = Sequence()
+ toonFace = Func(toon.headsUp, battle, suitPos)
+ toonTrack.append(Wait(delay))
+ toonTrack.append(toonFace)
+ toonTrack.append(ActorInterval(toon, 'throw'))
+ toonTrack.append(Func(toon.loop, 'neutral'))
+ toonTrack.append(Func(toon.setHpr, battle, origHpr))
+ pieShow = Func(MovieUtil.showProps, pies, hands)
+ pieAnim = Func(__animProp, pies, pieName, pieType)
+ pieScale1 = LerpScaleInterval(pie, 1.0, pie.getScale(), startScale=MovieUtil.PNT3_NEARZERO)
+ pieScale2 = LerpScaleInterval(pie2, 1.0, pie2.getScale(), startScale=MovieUtil.PNT3_NEARZERO)
+ pieScale = Parallel(pieScale1, pieScale2)
+ piePreflight = Func(__propPreflight, pies, suit, toon, battle)
+ pieTrack = Sequence(Wait(delay), pieShow, pieAnim, pieScale, Func(battle.movie.needRestoreRenderProp, pies[0]), Wait(tPieLeavesHand - 1.0), piePreflight)
+ soundTrack = __getSoundTrack(level, hitSuit, toon)
+ if hitSuit:
+ pieFly = LerpPosInterval(pie, tPieHitsSuit - tPieLeavesHand, pos=MovieUtil.avatarFacePoint(suit, other=battle), name=pieFlyTaskName, other=battle)
+ pieHide = Func(MovieUtil.removeProps, pies)
+ splatShow = Func(__showProp, splat, suit, Point3(0, 0, suit.getHeight()))
+ splatBillboard = Func(__billboardProp, splat)
+ splatAnim = ActorInterval(splat, splatName)
+ splatHide = Func(MovieUtil.removeProp, splat)
+ pieTrack.append(pieFly)
+ pieTrack.append(pieHide)
+ pieTrack.append(Func(battle.movie.clearRenderProp, pies[0]))
+ pieTrack.append(splatShow)
+ pieTrack.append(splatBillboard)
+ pieTrack.append(splatAnim)
+ pieTrack.append(splatHide)
+ else:
+ missDict = {}
+ if sidestep:
+ suitPoint = MovieUtil.avatarFacePoint(suit, other=battle)
+ else:
+ suitPoint = __suitMissPoint(suit, other=battle)
+ piePreMiss = Func(__piePreMiss, missDict, pie, suitPoint, battle)
+ pieMiss = LerpFunctionInterval(__pieMissLerpCallback, extraArgs=[missDict], duration=(tPieHitsSuit - tPieLeavesHand) * ratioMissToHit)
+ pieHide = Func(MovieUtil.removeProps, pies)
+ pieTrack.append(piePreMiss)
+ pieTrack.append(pieMiss)
+ pieTrack.append(pieHide)
+ pieTrack.append(Func(battle.movie.clearRenderProp, pies[0]))
+ if hitSuit:
+ suitResponseTrack = Sequence()
+ showDamage = Func(suit.showHpText, -hp, openEnded=0, attackTrack=THROW_TRACK)
+ updateHealthBar = Func(suit.updateHealthBar, hp)
+ sival = []
+ if kbbonus > 0:
+ suitPos, suitHpr = battle.getActorPosHpr(suit)
+ suitType = getSuitBodyType(suit.getStyleName())
+ animTrack = Sequence()
+ animTrack.append(ActorInterval(suit, 'pie-small-react', duration=0.2))
+ if suitType == 'a':
+ animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.43))
+ elif suitType == 'b':
+ animTrack.append(ActorInterval(suit, 'slip-forward', startTime=1.94))
+ elif suitType == 'c':
+ animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.58))
+ animTrack.append(Func(battle.unlureSuit, suit))
+ moveTrack = Sequence(Wait(0.2), LerpPosInterval(suit, 0.6, pos=suitPos, other=battle))
+ sival = Parallel(animTrack, moveTrack)
+ elif hitCount == 1:
+ sival = Parallel(ActorInterval(suit, 'pie-small-react'), MovieUtil.createSuitStunInterval(suit, 0.3, 1.3))
+ else:
+ sival = ActorInterval(suit, 'pie-small-react')
+ suitResponseTrack.append(Wait(delay + tPieHitsSuit))
+ suitResponseTrack.append(showDamage)
+ suitResponseTrack.append(updateHealthBar)
+ suitResponseTrack.append(sival)
+ bonusTrack = Sequence(Wait(delay + tPieHitsSuit))
+ if kbbonus > 0:
+ bonusTrack.append(Wait(0.75))
+ bonusTrack.append(Func(suit.showHpText, -kbbonus, 2, openEnded=0, attackTrack=THROW_TRACK))
+ if hpbonus > 0:
+ bonusTrack.append(Wait(0.75))
+ bonusTrack.append(Func(suit.showHpText, -hpbonus, 1, openEnded=0, attackTrack=THROW_TRACK))
+ if revived != 0:
+ suitResponseTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle))
+ elif died != 0:
+ suitResponseTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle))
+ else:
+ suitResponseTrack.append(Func(suit.loop, 'neutral'))
+ suitResponseTrack = Parallel(suitResponseTrack, bonusTrack)
+ else:
+ suitResponseTrack = MovieUtil.createSuitDodgeMultitrack(delay + tSuitDodges, suit, leftSuits, rightSuits)
+ if not hitSuit and delay > 0:
+ return [toonTrack, soundTrack, pieTrack]
+ else:
+ return [toonTrack,
+ soundTrack,
+ pieTrack,
+ suitResponseTrack]
+
+
+def __createWeddingCakeFlight(throw, groupHitDict, pie, pies):
+ toon = throw['toon']
+ battle = throw['battle']
+ level = throw['level']
+ sidestep = throw['sidestep']
+ hpbonus = throw['hpbonus']
+ numTargets = len(throw['target'])
+ pieName = pieNames[level]
+ splatName = 'splat-' + pieName
+ if pieName == 'wedding-cake':
+ splatName = 'splat-birthday-cake'
+ splat = globalPropPool.getProp(splatName)
+ splats = [splat]
+ for i in range(numTargets - 1):
+ splats.append(MovieUtil.copyProp(splat))
+
+ splatType = globalPropPool.getPropType(splatName)
+ cakePartStrs = ['cake1',
+ 'cake2',
+ 'cake3',
+ 'caketop']
+ cakeParts = []
+ for part in cakePartStrs:
+ cakeParts.append(pie.find('**/%s' % part))
+
+ cakePartDivisions = {}
+ cakePartDivisions[1] = [[cakeParts[0],
+ cakeParts[1],
+ cakeParts[2],
+ cakeParts[3]]]
+ cakePartDivisions[2] = [[cakeParts[0], cakeParts[1]], [cakeParts[2], cakeParts[3]]]
+ cakePartDivisions[3] = [[cakeParts[0], cakeParts[1]], [cakeParts[2]], [cakeParts[3]]]
+ cakePartDivisions[4] = [[cakeParts[0]],
+ [cakeParts[1]],
+ [cakeParts[2]],
+ [cakeParts[3]]]
+ cakePartDivToUse = cakePartDivisions[len(throw['target'])]
+ groupPieTracks = Parallel()
+ for i in range(numTargets):
+ target = throw['target'][i]
+ suit = target['suit']
+ hitSuit = target['hp'] > 0
+ singlePieTrack = Sequence()
+ if hitSuit:
+ piePartReparent = Func(reparentCakePart, pie, cakePartDivToUse[i])
+ singlePieTrack.append(piePartReparent)
+ cakePartTrack = Parallel()
+ for cakePart in cakePartDivToUse[i]:
+ pieFly = LerpPosInterval(cakePart, tPieHitsSuit - tPieLeavesHand, pos=MovieUtil.avatarFacePoint(suit, other=battle), name=pieFlyTaskName, other=battle)
+ cakePartTrack.append(pieFly)
+
+ singlePieTrack.append(cakePartTrack)
+ pieRemoveCakeParts = Func(MovieUtil.removeProps, cakePartDivToUse[i])
+ pieHide = Func(MovieUtil.removeProps, pies)
+ splatShow = Func(__showProp, splats[i], suit, Point3(0, 0, suit.getHeight()))
+ splatBillboard = Func(__billboardProp, splats[i])
+ splatAnim = ActorInterval(splats[i], splatName)
+ splatHide = Func(MovieUtil.removeProp, splats[i])
+ singlePieTrack.append(pieRemoveCakeParts)
+ singlePieTrack.append(pieHide)
+ singlePieTrack.append(Func(battle.movie.clearRenderProp, pies[0]))
+ singlePieTrack.append(splatShow)
+ singlePieTrack.append(splatBillboard)
+ singlePieTrack.append(splatAnim)
+ singlePieTrack.append(splatHide)
+ else:
+ missDict = {}
+ if sidestep:
+ suitPoint = MovieUtil.avatarFacePoint(suit, other=battle)
+ else:
+ suitPoint = __suitMissPoint(suit, other=battle)
+ piePartReparent = Func(reparentCakePart, pie, cakePartDivToUse[i])
+ piePreMiss = Func(__piePreMissGroup, missDict, cakePartDivToUse[i], suitPoint, battle)
+ pieMiss = LerpFunctionInterval(__pieMissGroupLerpCallback, extraArgs=[missDict], duration=(tPieHitsSuit - tPieLeavesHand) * ratioMissToHit)
+ pieHide = Func(MovieUtil.removeProps, pies)
+ pieRemoveCakeParts = Func(MovieUtil.removeProps, cakePartDivToUse[i])
+ singlePieTrack.append(piePartReparent)
+ singlePieTrack.append(piePreMiss)
+ singlePieTrack.append(pieMiss)
+ singlePieTrack.append(pieRemoveCakeParts)
+ singlePieTrack.append(pieHide)
+ singlePieTrack.append(Func(battle.movie.clearRenderProp, pies[0]))
+ groupPieTracks.append(singlePieTrack)
+
+ return groupPieTracks
+
+
+def __throwGroupPie(throw, delay, groupHitDict):
+ toon = throw['toon']
+ battle = throw['battle']
+ level = throw['level']
+ sidestep = throw['sidestep']
+ hpbonus = throw['hpbonus']
+ numTargets = len(throw['target'])
+ avgSuitPos = calcAvgSuitPos(throw)
+ origHpr = toon.getHpr(battle)
+ toonTrack = Sequence()
+ toonFace = Func(toon.headsUp, battle, avgSuitPos)
+ toonTrack.append(Wait(delay))
+ toonTrack.append(toonFace)
+ toonTrack.append(ActorInterval(toon, 'throw'))
+ toonTrack.append(Func(toon.loop, 'neutral'))
+ toonTrack.append(Func(toon.setHpr, battle, origHpr))
+ suits = []
+ for i in range(numTargets):
+ suits.append(throw['target'][i]['suit'])
+
+ pieName = pieNames[level]
+ pie = globalPropPool.getProp(pieName)
+ pieType = globalPropPool.getPropType(pieName)
+ pie2 = MovieUtil.copyProp(pie)
+ pies = [pie, pie2]
+ hands = toon.getRightHands()
+ pieShow = Func(MovieUtil.showProps, pies, hands)
+ pieAnim = Func(__animProp, pies, pieName, pieType)
+ pieScale1 = LerpScaleInterval(pie, 1.0, pie.getScale() * 1.5, startScale=MovieUtil.PNT3_NEARZERO)
+ pieScale2 = LerpScaleInterval(pie2, 1.0, pie2.getScale() * 1.5, startScale=MovieUtil.PNT3_NEARZERO)
+ pieScale = Parallel(pieScale1, pieScale2)
+ piePreflight = Func(__propPreflightGroup, pies, suits, toon, battle)
+ pieTrack = Sequence(Wait(delay), pieShow, pieAnim, pieScale, Func(battle.movie.needRestoreRenderProp, pies[0]), Wait(tPieLeavesHand - 1.0), piePreflight)
+ if level == UBER_GAG_LEVEL_INDEX:
+ groupPieTracks = __createWeddingCakeFlight(throw, groupHitDict, pie, pies)
+ else:
+ notify.error('unhandled throw level %d' % level)
+ pieTrack.append(groupPieTracks)
+ didThrowHitAnyone = False
+ for i in range(numTargets):
+ target = throw['target'][i]
+ hitSuit = target['hp'] > 0
+ if hitSuit:
+ didThrowHitAnyone = True
+
+ soundTrack = __getSoundTrack(level, didThrowHitAnyone, toon)
+ groupSuitResponseTrack = Parallel()
+ for i in range(numTargets):
+ target = throw['target'][i]
+ suit = target['suit']
+ hitSuit = target['hp'] > 0
+ leftSuits = target['leftSuits']
+ rightSuits = target['rightSuits']
+ hp = target['hp']
+ kbbonus = target['kbbonus']
+ died = target['died']
+ revived = target['revived']
+ if hitSuit:
+ singleSuitResponseTrack = Sequence()
+ showDamage = Func(suit.showHpText, -hp, openEnded=0, attackTrack=THROW_TRACK)
+ updateHealthBar = Func(suit.updateHealthBar, hp)
+ sival = []
+ if kbbonus > 0:
+ suitPos, suitHpr = battle.getActorPosHpr(suit)
+ suitType = getSuitBodyType(suit.getStyleName())
+ animTrack = Sequence()
+ animTrack.append(ActorInterval(suit, 'pie-small-react', duration=0.2))
+ if suitType == 'a':
+ animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.43))
+ elif suitType == 'b':
+ animTrack.append(ActorInterval(suit, 'slip-forward', startTime=1.94))
+ elif suitType == 'c':
+ animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.58))
+ animTrack.append(Func(battle.unlureSuit, suit))
+ moveTrack = Sequence(Wait(0.2), LerpPosInterval(suit, 0.6, pos=suitPos, other=battle))
+ sival = Parallel(animTrack, moveTrack)
+ elif groupHitDict[suit.doId] == 1:
+ sival = Parallel(ActorInterval(suit, 'pie-small-react'), MovieUtil.createSuitStunInterval(suit, 0.3, 1.3))
+ else:
+ sival = ActorInterval(suit, 'pie-small-react')
+ singleSuitResponseTrack.append(Wait(delay + tPieHitsSuit))
+ singleSuitResponseTrack.append(showDamage)
+ singleSuitResponseTrack.append(updateHealthBar)
+ singleSuitResponseTrack.append(sival)
+ bonusTrack = Sequence(Wait(delay + tPieHitsSuit))
+ if kbbonus > 0:
+ bonusTrack.append(Wait(0.75))
+ bonusTrack.append(Func(suit.showHpText, -kbbonus, 2, openEnded=0, attackTrack=THROW_TRACK))
+ if hpbonus > 0:
+ bonusTrack.append(Wait(0.75))
+ bonusTrack.append(Func(suit.showHpText, -hpbonus, 1, openEnded=0, attackTrack=THROW_TRACK))
+ if revived != 0:
+ singleSuitResponseTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle))
+ elif died != 0:
+ singleSuitResponseTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle))
+ else:
+ singleSuitResponseTrack.append(Func(suit.loop, 'neutral'))
+ singleSuitResponseTrack = Parallel(singleSuitResponseTrack, bonusTrack)
+ else:
+ groupHitValues = groupHitDict.values()
+ if groupHitValues.count(0) == len(groupHitValues):
+ singleSuitResponseTrack = MovieUtil.createSuitDodgeMultitrack(delay + tSuitDodges, suit, leftSuits, rightSuits)
+ else:
+ singleSuitResponseTrack = Sequence(Wait(tPieHitsSuit - 0.1), Func(MovieUtil.indicateMissed, suit, 1.0))
+ groupSuitResponseTrack.append(singleSuitResponseTrack)
+
+ return [toonTrack,
+ pieTrack,
+ soundTrack,
+ groupSuitResponseTrack]
+
+
+def reparentCakePart(pie, cakeParts):
+ pieParent = pie.getParent()
+ notify.debug('pieParent = %s' % pieParent)
+ for cakePart in cakeParts:
+ cakePart.wrtReparentTo(pieParent)
diff --git a/toontown/battle/MovieToonVictory.py b/toontown/battle/MovieToonVictory.py
new file mode 100644
index 0000000..7b1d975
--- /dev/null
+++ b/toontown/battle/MovieToonVictory.py
@@ -0,0 +1,129 @@
+from direct.interval.IntervalGlobal import *
+from direct.showbase.DirectObject import DirectObject
+from RewardPanel import *
+from BattleSounds import *
+import MovieCamera
+from direct.directnotify import DirectNotifyGlobal
+import types
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieToonVictory')
+
+def __findToonReward(rewards, toon):
+ for r in rewards:
+ if r['toon'] == toon:
+ return r
+
+ return None
+
+
+class ToonVictorySkipper(DirectObject):
+
+ def __init__(self, numToons, noSkip):
+ self._numToons = numToons
+ self._noSkip = noSkip
+ self._startTimes = {}
+ self._ivals = []
+ self._battle = None
+ return
+
+ def destroy(self):
+ self._ivals = None
+ return
+
+ def getSetupFunc(self, index):
+ return Func(self._setupSkipListen, index)
+
+ def getTeardownFunc(self, index):
+ return Func(self._teardownSkipListen, index)
+
+ def setBattle(self, battle):
+ self._battle = battle
+
+ def setStartTime(self, index, startT):
+ self._startTimes[index] = startT
+
+ def setIvals(self, ivals, timeOffset = 0.0):
+ for index in self._startTimes:
+ self._startTimes[index] += timeOffset
+
+ self._ivals = ivals
+
+ def _setupSkipListen(self, index):
+ if not self._noSkip:
+ func = Functor(self._skipToon, index)
+ self.accept('escape', func)
+ self.accept(RewardPanel.SkipBattleMovieEvent, func)
+
+ def _teardownSkipListen(self, index):
+ if not self._noSkip:
+ self.ignore('escape')
+ self.ignore(RewardPanel.SkipBattleMovieEvent)
+
+ def _skipToon(self, index):
+ nextIndex = index + 1
+ if nextIndex >= self._numToons:
+ for ival in self._ivals:
+ ival.finish()
+
+ if self._battle:
+ self._battle.setSkippingRewardMovie()
+ elif nextIndex in self._startTimes:
+ for ival in self._ivals:
+ ival.setT(self._startTimes[nextIndex])
+
+
+def doToonVictory(localToonActive, toons, rewardToonIds, rewardDicts, deathList, rpanel, allowGroupShot = 1, uberList = [], helpfulToonsList = [], noSkip = False):
+ track = Sequence()
+ if localToonActive == 1:
+ track.append(Func(rpanel.show))
+ track.append(Func(NametagGlobals.setOnscreenChatForced, 1))
+ camTrack = Sequence()
+ endTrack = Sequence()
+ danceSound = globalBattleSoundCache.getSound('ENC_Win.mp3')
+ toonList = []
+ countToons = 0
+ uberListNew = []
+ for t in toons:
+ if isinstance(t, types.IntType):
+ t = base.cr.doId2do.get(t)
+ if t:
+ toonList.append(t)
+ uberListNew.append(uberList[countToons])
+ countToons += 1
+
+ toonId2toon = {}
+ for toon in toonList:
+ toonId2toon[toon.doId] = toon
+
+ rewardToonList = []
+ for id in rewardToonIds:
+ rewardToonList.append(toonId2toon.get(id))
+
+ skipper = ToonVictorySkipper(len(toonList), noSkip)
+ lastListenIndex = 0
+ track.append(skipper.getSetupFunc(lastListenIndex))
+ for tIndex in range(len(toonList)):
+ t = toonList[tIndex]
+ rdict = __findToonReward(rewardDicts, t)
+ if rdict != None:
+ expTrack = rpanel.getExpTrack(t, rdict['origExp'], rdict['earnedExp'], deathList, rdict['origQuests'], rdict['items'], rdict['missedItems'], rdict['origMerits'], rdict['merits'], rdict['parts'], rewardToonList, uberListNew[tIndex], helpfulToonsList, noSkip=noSkip)
+ if expTrack:
+ skipper.setStartTime(tIndex, track.getDuration())
+ track.append(skipper.getTeardownFunc(lastListenIndex))
+ lastListenIndex = tIndex
+ track.append(skipper.getSetupFunc(lastListenIndex))
+ track.append(expTrack)
+ camDuration = expTrack.getDuration()
+ camExpTrack = MovieCamera.chooseRewardShot(t, camDuration)
+ camTrack.append(MovieCamera.chooseRewardShot(t, camDuration, allowGroupShot=allowGroupShot))
+
+ track.append(skipper.getTeardownFunc(lastListenIndex))
+ track.append(Func(skipper.destroy))
+ if localToonActive == 1:
+ track.append(Func(rpanel.hide))
+ track.append(Func(NametagGlobals.setOnscreenChatForced, 0))
+ track.append(endTrack)
+ trackdur = track.getDuration()
+ soundTrack = SoundInterval(danceSound, duration=trackdur, loop=1)
+ mtrack = Parallel(track, soundTrack)
+ skipper.setIvals((mtrack, camTrack))
+ return (mtrack, camTrack, skipper)
diff --git a/toontown/battle/MovieTrap.py b/toontown/battle/MovieTrap.py
new file mode 100644
index 0000000..a5c703a
--- /dev/null
+++ b/toontown/battle/MovieTrap.py
@@ -0,0 +1,582 @@
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from BattleProps import *
+from BattleSounds import *
+import MovieUtil
+import MovieCamera
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownBattleGlobals
+from direct.actor import Actor
+from direct.particles import ParticleEffect
+import BattleParticles
+import BattleProps
+import MovieNPCSOS
+from MovieSound import createSuitResetPosTrack
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieTrap')
+
+def doTraps(traps):
+ if len(traps) == 0:
+ return (None, None)
+ npcArrivals, npcDepartures, npcs = MovieNPCSOS.doNPCTeleports(traps)
+ hasUberTrapConflict = False
+ suitTrapsDict = {}
+ for trap in traps:
+ targets = trap['target']
+ if len(targets) == 1:
+ suitId = targets[0]['suit'].doId
+ if suitTrapsDict.has_key(suitId):
+ suitTrapsDict[suitId].append(trap)
+ else:
+ suitTrapsDict[suitId] = [trap]
+ else:
+ for target in targets:
+ suitId = target['suit'].doId
+ if not suitTrapsDict.has_key(suitId):
+ suitTrapsDict[suitId] = [trap]
+ break
+
+ if trap['level'] == UBER_GAG_LEVEL_INDEX:
+ if len(traps) > 1:
+ hasUberTrapConflict = True
+ for oneTarget in trap['target']:
+ suit = oneTarget['suit']
+ if suit.battleTrap != NO_TRAP:
+ hasUberTrapConflict = True
+
+ suitTrapLists = suitTrapsDict.values()
+ mtrack = Parallel()
+ for trapList in suitTrapLists:
+ trapPropList = []
+ for i in range(len(trapList)):
+ trap = trapList[i]
+ level = trap['level']
+ if level == 0:
+ banana = globalPropPool.getProp('banana')
+ banana2 = MovieUtil.copyProp(banana)
+ trapPropList.append([banana, banana2])
+ elif level == 1:
+ rake = globalPropPool.getProp('rake')
+ rake2 = MovieUtil.copyProp(rake)
+ rake.pose('rake', 0)
+ rake2.pose('rake', 0)
+ trapPropList.append([rake, rake2])
+ elif level == 2:
+ marbles = globalPropPool.getProp('marbles')
+ marbles2 = MovieUtil.copyProp(marbles)
+ trapPropList.append([marbles, marbles2])
+ elif level == 3:
+ trapPropList.append([globalPropPool.getProp('quicksand')])
+ elif level == 4:
+ trapPropList.append([globalPropPool.getProp('trapdoor')])
+ elif level == 5:
+ tnt = globalPropPool.getProp('tnt')
+ tnt2 = MovieUtil.copyProp(tnt)
+ trapPropList.append([tnt, tnt2])
+ elif level == 6:
+ tnt = globalPropPool.getProp('traintrack')
+ tnt2 = MovieUtil.copyProp(tnt)
+ trapPropList.append([tnt, tnt2])
+ else:
+ notify.warning('__doTraps() - Incorrect trap level: %d' % level)
+
+ if len(trapList) == 1 and not hasUberTrapConflict:
+ ival = __doTrapLevel(trapList[0], trapPropList[0])
+ if ival:
+ mtrack.append(ival)
+ else:
+ subMtrack = Parallel()
+ for i in range(len(trapList)):
+ trap = trapList[i]
+ trapProps = trapPropList[i]
+ ival = __doTrapLevel(trap, trapProps, explode=1)
+ if ival:
+ subMtrack.append(ival)
+
+ mtrack.append(subMtrack)
+
+ trapTrack = Sequence(npcArrivals, mtrack, npcDepartures)
+ camDuration = mtrack.getDuration()
+ enterDuration = npcArrivals.getDuration()
+ exitDuration = npcDepartures.getDuration()
+ camTrack = MovieCamera.chooseTrapShot(traps, camDuration, enterDuration, exitDuration)
+ return (trapTrack, camTrack)
+
+
+def __doTrapLevel(trap, trapProps, explode = 0):
+ level = trap['level']
+ if level == 0:
+ return __trapBanana(trap, trapProps, explode)
+ elif level == 1:
+ return __trapRake(trap, trapProps, explode)
+ elif level == 2:
+ return __trapMarbles(trap, trapProps, explode)
+ elif level == 3:
+ return __trapQuicksand(trap, trapProps, explode)
+ elif level == 4:
+ return __trapTrapdoor(trap, trapProps, explode)
+ elif level == 5:
+ return __trapTNT(trap, trapProps, explode)
+ elif level == 6:
+ return __trapTrain(trap, trapProps, explode)
+ return None
+
+
+def getSoundTrack(fileName, delay = 0.01, duration = None, node = None):
+ soundEffect = globalBattleSoundCache.getSound(fileName)
+ if duration:
+ return Sequence(Wait(delay), SoundInterval(soundEffect, duration=duration, node=node))
+ else:
+ return Sequence(Wait(delay), SoundInterval(soundEffect, node=node))
+
+
+def __createThrownTrapMultiTrack(trap, propList, propName, propPos = None, propHpr = None, anim = 0, explode = 0):
+ toon = trap['toon']
+ level = trap['level']
+ battle = trap['battle']
+ target = trap['target']
+ suit = target[0]['suit']
+ targetPos = suit.getPos(battle)
+ thrownProp = propList[0]
+ unthrownProp = propList[1]
+ torso = toon.style.torso
+ torso = torso[0]
+ if torso == 'l':
+ throwDelay = 2.3
+ elif torso == 'm':
+ throwDelay = 2.3
+ else:
+ throwDelay = 1.9
+ throwDuration = 0.9
+ animBreakPoint = throwDelay + throwDuration
+ animDelay = 3.1
+ trapTrack = ToontownBattleGlobals.TRAP_TRACK
+ trapTrackNames = ToontownBattleGlobals.AvProps[trapTrack]
+ trapName = trapTrackNames[level]
+ hands = toon.getRightHands()
+ propTrack = Sequence()
+ if propPos and propHpr:
+ propTrack.append(Func(MovieUtil.showProps, propList, hands, propPos, propHpr))
+ else:
+ propTrack.append(Func(MovieUtil.showProps, propList, hands))
+ if anim == 1:
+ pTracks = Parallel()
+ for prop in propList:
+ pTracks.append(ActorInterval(prop, propName, duration=animBreakPoint))
+
+ propTrack.append(pTracks)
+ throwTrack = Sequence()
+ throwTrack.append(Wait(throwDelay))
+ throwTrack.append(Func(unthrownProp.reparentTo, hidden))
+ throwTrack.append(Func(toon.update))
+ if suit.battleTrap != NO_TRAP:
+ notify.debug('trapSuit() - trap: %d destroyed existing trap: %d' % (level, suit.battleTrap))
+ battle.removeTrap(suit)
+ if trapName == 'rake':
+ trapProp = globalPropPool.getProp('rake-react')
+ else:
+ trapProp = MovieUtil.copyProp(thrownProp)
+ suit.battleTrapProp = trapProp
+ suit.battleTrap = level
+ suit.battleTrapIsFresh = 1
+ if trapName == 'banana':
+ trapPoint, trapHpr = battle.getActorPosHpr(suit)
+ trapPoint.setY(MovieUtil.SUIT_TRAP_DISTANCE)
+ slidePoint = Vec3(trapPoint.getX(), trapPoint.getY() - 2, trapPoint.getZ())
+ throwingTrack = createThrowingTrack(thrownProp, slidePoint, duration=0.9, parent=battle)
+ moveTrack = LerpPosInterval(thrownProp, 0.8, pos=trapPoint, other=battle)
+ animTrack = ActorInterval(thrownProp, propName, startTime=animBreakPoint)
+ slideTrack = Parallel(moveTrack, animTrack)
+ motionTrack = Sequence(throwingTrack, slideTrack)
+ hprTrack = LerpHprInterval(thrownProp, 1.7, hpr=Point3(0, 0, 0))
+ soundTrack = getSoundTrack('TL_banana.mp3', node=toon)
+ scaleTrack = LerpScaleInterval(thrownProp, 1.7, scale=MovieUtil.PNT3_ONE)
+ throwTrack.append(Wait(0.25))
+ throwTrack.append(Func(thrownProp.wrtReparentTo, suit))
+ throwTrack.append(Parallel(motionTrack, hprTrack, scaleTrack, soundTrack))
+ elif trapName == 'tnt':
+ trapPoint, trapHpr = battle.getActorPosHpr(suit)
+ trapPoint.setY(MovieUtil.SUIT_TRAP_TNT_DISTANCE - 3.9)
+ trapPoint.setZ(trapPoint.getZ() + 0.4)
+ throwingTrack = createThrowingTrack(thrownProp, trapPoint, duration=throwDuration, parent=battle)
+ hprTrack = LerpHprInterval(thrownProp, 0.9, hpr=Point3(0, 90, 0))
+ scaleTrack = LerpScaleInterval(thrownProp, 0.9, scale=MovieUtil.PNT3_ONE)
+ soundTrack = getSoundTrack('TL_dynamite.mp3', delay=0.8, duration=0.7, node=suit)
+ throwTrack.append(Wait(0.2))
+ throwTrack.append(Parallel(throwingTrack, hprTrack, scaleTrack, soundTrack))
+ elif trapName == 'marbles':
+ trapPoint, trapHpr = battle.getActorPosHpr(suit)
+ trapPoint.setY(MovieUtil.SUIT_TRAP_MARBLES_DISTANCE)
+ flingDuration = 0.2
+ rollDuration = 1.0
+ throwDuration = flingDuration + rollDuration
+ landPoint = Point3(0, trapPoint.getY() + 2, trapPoint.getZ())
+ throwPoint = Point3(0, trapPoint.getY(), trapPoint.getZ())
+ moveTrack = Sequence(Func(thrownProp.wrtReparentTo, suit), Func(thrownProp.setHpr, Point3(94, 0, 0)), LerpPosInterval(thrownProp, flingDuration, pos=landPoint, other=suit), LerpPosInterval(thrownProp, rollDuration, pos=throwPoint, other=suit))
+ animTrack = ActorInterval(thrownProp, propName, startTime=throwDelay + 0.9)
+ scaleTrack = LerpScaleInterval(thrownProp, throwDuration, scale=MovieUtil.PNT3_ONE)
+ soundTrack = getSoundTrack('TL_marbles.mp3', delay=0.1, node=toon)
+ throwTrack.append(Wait(0.2))
+ throwTrack.append(Parallel(moveTrack, animTrack, scaleTrack, soundTrack))
+ elif trapName == 'rake':
+ trapPoint, trapHpr = battle.getActorPosHpr(suit)
+ trapPoint.setY(MovieUtil.SUIT_TRAP_RAKE_DISTANCE)
+ throwDuration = 1.1
+ throwingTrack = createThrowingTrack(thrownProp, trapPoint, duration=throwDuration, parent=suit)
+ hprTrack = LerpHprInterval(thrownProp, throwDuration, hpr=VBase3(63.43, -90.0, 63.43))
+ scaleTrack = LerpScaleInterval(thrownProp, 0.9, scale=Point3(0.7, 0.7, 0.7))
+ soundTrack = SoundInterval(globalBattleSoundCache.getSound('TL_rake_throw_only.mp3'), duration=1.1, node=suit)
+ throwTrack.append(Wait(0.2))
+ throwTrack.append(Parallel(throwingTrack, hprTrack, scaleTrack, soundTrack))
+ else:
+ notify.warning('__createThrownTrapMultiTrack() - Incorrect trap: %s thrown from toon' % trapName)
+
+ def placeTrap(trapProp, suit, battle = battle, trapName = trapName):
+ if not trapProp or trapProp.isEmpty():
+ return
+ trapProp.wrtReparentTo(suit)
+ trapProp.show()
+ if trapName == 'rake':
+ trapProp.setPos(0, MovieUtil.SUIT_TRAP_RAKE_DISTANCE, 0)
+ trapProp.setHpr(Point3(0, 270, 0))
+ trapProp.setScale(Point3(0.7, 0.7, 0.7))
+ rakeOffset = MovieUtil.getSuitRakeOffset(suit)
+ trapProp.setY(trapProp.getY() + rakeOffset)
+ elif trapName == 'banana':
+ trapProp.setHpr(0, 0, 0)
+ trapProp.setPos(0, MovieUtil.SUIT_TRAP_DISTANCE, -0.35)
+ trapProp.pose(trapName, trapProp.getNumFrames(trapName) - 1)
+ elif trapName == 'marbles':
+ trapProp.setHpr(Point3(94, 0, 0))
+ trapProp.setPos(0, MovieUtil.SUIT_TRAP_MARBLES_DISTANCE, 0)
+ trapProp.pose(trapName, trapProp.getNumFrames(trapName) - 1)
+ elif trapName == 'tnt':
+ trapProp.setHpr(0, 90, 0)
+ trapProp.setPos(0, MovieUtil.SUIT_TRAP_TNT_DISTANCE, 0.4)
+ else:
+ notify.warning('placeTrap() - Incorrect trap: %s placed on a suit' % trapName)
+
+ dustNode = hidden.attachNewNode('DustNode')
+
+ def placeDustExplosion(dustNode = dustNode, thrownProp = thrownProp, battle = battle):
+ dustNode.reparentTo(battle)
+ dustNode.setPos(thrownProp.getPos(battle))
+
+ if explode == 1:
+ throwTrack.append(Func(thrownProp.wrtReparentTo, hidden))
+ throwTrack.append(Func(placeDustExplosion))
+ throwTrack.append(createCartoonExplosionTrack(dustNode, 'dust', explosionPoint=Point3(0, 0, 0)))
+ throwTrack.append(Func(battle.removeTrap, suit))
+ else:
+ throwTrack.append(Func(placeTrap, trapProp, suit))
+ if trapName == 'tnt':
+ tip = trapProp.find('**/joint_attachEmitter')
+ sparks = BattleParticles.createParticleEffect(file='tnt')
+ trapProp.sparksEffect = sparks
+ throwTrack.append(Func(sparks.start, tip))
+ throwTrack.append(Func(MovieUtil.removeProps, propList))
+ toonTrack = Sequence(Func(toon.headsUp, battle, targetPos), ActorInterval(toon, 'toss'), Func(toon.loop, 'neutral'))
+ return Parallel(propTrack, throwTrack, toonTrack)
+
+
+def __createPlacedTrapMultiTrack(trap, prop, propName, propPos = None, propHpr = None, explode = 0, visibleOnlyForThisSuitId = None):
+ toon = trap['toon']
+ if trap.has_key('npc'):
+ toon = trap['npc']
+ level = trap['level']
+ battle = trap['battle']
+ origHpr = toon.getHpr(battle)
+ trapPoint = Point3(0, MovieUtil.SUIT_TRAP_DISTANCE, 0.025)
+ trapDelay = 2.5
+ hands = toon.getLeftHands()
+
+ def placeDustExplosion(dustNode, trapProp, battle):
+ dustNode.reparentTo(battle)
+ dustNode.setPos(trapProp.getPos(battle))
+
+ trapTracks = Parallel()
+ firstTime = 1
+ targets = trap['target']
+ for target in targets:
+ suit = target['suit']
+ suitPos = suit.getPos(battle)
+ targetPos = suitPos
+ trapProp = MovieUtil.copyProp(prop)
+ showThisTrap = True
+ if visibleOnlyForThisSuitId and visibleOnlyForThisSuitId != suit.doId:
+ showThisTrap = False
+ trapTrack = Sequence()
+ trapTrack.append(Wait(trapDelay))
+ if showThisTrap:
+ notify.debug('showing trap %s for %d' % (trapProp.getName(), suit.doId))
+ trapTrack.append(Func(trapProp.show))
+ else:
+ notify.debug('hiding trap %s for %d' % (trapProp.getName(), suit.doId))
+ trapTrack.append(Func(trapProp.hide))
+ trapTrack.append(Func(trapProp.setScale, Point3(0.1, 0.1, 0.1)))
+ trapTrack.append(Func(trapProp.reparentTo, suit))
+ trapTrack.append(Func(trapProp.setPos, trapPoint))
+ trapTrack.append(LerpScaleInterval(trapProp, 1.2, Point3(1.7, 1.7, 1.7)))
+ if explode == 1:
+ dustNode = hidden.attachNewNode('DustNode')
+ trapTrack.append(Func(trapProp.wrtReparentTo, hidden))
+ trapTrack.append(Func(placeDustExplosion, dustNode, trapProp, battle))
+ trapTrack.append(createCartoonExplosionTrack(dustNode, 'dust', explosionPoint=Point3(0, 0, 0)))
+ trapTrack.append(Func(MovieUtil.removeProp, trapProp))
+ trapTrack.append(Func(battle.removeTrap, suit))
+ else:
+ if suit.battleTrap != NO_TRAP:
+ notify.debug('trapSuit() - trap: %d destroyed existing trap: %d' % (level, suit.battleTrap))
+ battle.removeTrap(suit)
+ suit.battleTrapProp = trapProp
+ suit.battleTrap = level
+ suit.battleTrapIsFresh = 1
+ trapTracks.append(trapTrack)
+
+ button = globalPropPool.getProp('button')
+ button2 = MovieUtil.copyProp(button)
+ buttons = [button, button2]
+ toonTrack = Sequence()
+ toonTrack.append(Func(MovieUtil.showProps, buttons, hands))
+ toonTrack.append(Func(toon.headsUp, battle, suitPos))
+ toonTrack.append(ActorInterval(toon, 'pushbutton'))
+ toonTrack.append(Func(MovieUtil.removeProps, buttons))
+ toonTrack.append(Func(toon.loop, 'neutral'))
+ toonTrack.append(Func(toon.setHpr, battle, origHpr))
+ if propName == 'quicksand':
+ propSound = globalBattleSoundCache.getSound('TL_quicksand.mp3')
+ else:
+ propSound = globalBattleSoundCache.getSound('TL_trap_door.mp3')
+ buttonSound = globalBattleSoundCache.getSound('AA_drop_trigger_box.mp3')
+ soundTrack = Sequence(Wait(2.3), SoundInterval(buttonSound, duration=0.67, node=toon), Wait(0.3), SoundInterval(propSound, duration=0.5, node=toon))
+ return Parallel(trapTracks, toonTrack, soundTrack)
+
+
+def __trapBanana(trap, trapProps, explode):
+ toon = trap['toon']
+ suit = trap['target'][0]['suit']
+ notify.debug('toon: %s lays banana peel in front of suit: %d' % (toon.getName(), suit.doId))
+ bananas = trapProps
+ return __createThrownTrapMultiTrack(trap, bananas, 'banana', anim=1, explode=explode)
+
+
+def __trapRake(trap, trapProps, explode):
+ toon = trap['toon']
+ suit = trap['target'][0]['suit']
+ notify.debug('toon: %s lays rake in front of suit: %d' % (toon.getName(), suit.doId))
+ rakes = trapProps
+ return __createThrownTrapMultiTrack(trap, rakes, 'rake', anim=1, explode=explode)
+
+
+def __trapMarbles(trap, trapProps, explode):
+ toon = trap['toon']
+ suit = trap['target'][0]['suit']
+ notify.debug('toon: %s lays marbles in front of suit: %d' % (toon.getName(), suit.doId))
+ bothMarbles = trapProps
+ pos = Point3(0, 0, 0)
+ hpr = Point3(0, 0, -30)
+ return __createThrownTrapMultiTrack(trap, bothMarbles, 'marbles', pos, hpr, anim=1, explode=explode)
+
+
+def __trapQuicksand(trap, trapProps, explode):
+ toon = trap['toon']
+ suit = trap['target'][0]['suit']
+ notify.debug('toon: %s lays quicksand in front of suit: %d' % (toon.getName(), suit.doId))
+ quicksand = trapProps[0]
+ return __createPlacedTrapMultiTrack(trap, quicksand, 'quicksand', explode=explode)
+
+
+def __trapTrapdoor(trap, trapProps, explode):
+ toon = trap['toon']
+ if trap.has_key('npc'):
+ toon = trap['npc']
+ targets = trap['target']
+ for target in targets:
+ suit = target['suit']
+ notify.debug('toon: %s lays trapdoor in front of suit: %d' % (toon.getName(), suit.doId))
+
+ trapdoor = trapProps[0]
+ return __createPlacedTrapMultiTrack(trap, trapdoor, 'trapdoor', explode=explode)
+
+
+def __trapTNT(trap, trapProps, explode):
+ toon = trap['toon']
+ suit = trap['target'][0]['suit']
+ notify.debug('toon: %s lays TNT in front of suit: %d' % (toon.getName(), suit.doId))
+ tnts = trapProps
+ return __createThrownTrapMultiTrack(trap, tnts, 'tnt', anim=0, explode=explode)
+
+
+def __trapTrain(trap, trapProps, explode):
+ toon = trap['toon']
+ if trap.has_key('npc'):
+ toon = trap['npc']
+ targets = trap['target']
+ battle = trap['battle']
+ visibleOnlyForThisSuitId = 0
+ centerSuit = None
+ closestXDistance = 10000
+ for target in targets:
+ suit = target['suit']
+ suitPoint, suitHpr = battle.getActorPosHpr(suit)
+ xDistance = abs(suitPoint.getX())
+ if xDistance < closestXDistance:
+ visibleOnlyForThisSuitId = suit.doId
+ closestXDistance = xDistance
+ centerSuit = suit
+ notify.debug('toon: %s doing traintrack in front of suit: %d' % (toon.getName(), suit.doId))
+
+ traintrack = trapProps[0]
+ return __createPlacedGroupTrapTrack(trap, traintrack, 'traintrack', centerSuit, explode=explode)
+
+
+def createThrowingTrack(object, target, duration = 1.0, parent = render, gravity = -32.144):
+ values = {}
+ values['origin'] = None
+ values['velocity'] = None
+
+ def calcOriginAndVelocity(object = object, target = target, values = values, duration = duration, parent = parent, gravity = gravity):
+ object.wrtReparentTo(parent)
+ values['origin'] = object.getPos(parent)
+ origin = object.getPos(parent)
+ values['velocity'] = (target[2] - origin[2] - 0.5 * gravity * duration * duration) / duration
+
+ def throwPos(t, object, duration, target, values = values, gravity = -32.144):
+ if values['origin'] != None:
+ origin = values['origin']
+ else:
+ origin = object.getPos()
+ if values['velocity'] != None:
+ velocity = values['velocity']
+ else:
+ velocity = 16
+ x = origin[0] * (1 - t) + target[0] * t
+ y = origin[1] * (1 - t) + target[1] * t
+ time = t * duration
+ z = origin[2] + velocity * time + 0.5 * gravity * time * time
+ object.setPos(x, y, z)
+ return
+
+ return Sequence(Func(calcOriginAndVelocity), LerpFunctionInterval(throwPos, fromData=0.0, toData=1.0, duration=duration, extraArgs=[object, duration, target]))
+
+
+def createCartoonExplosionTrack(parent, animName, explosionPoint = None):
+ explosionTrack = Sequence()
+ explosion = BattleProps.globalPropPool.getProp(animName)
+ explosion.setBillboardPointEye()
+ if not explosionPoint:
+ explosionPoint = Point3(0, 3.6, 2.1)
+ if animName == 'dust':
+ scale = Point3(0.1, 0.9, 1)
+ explosionTrack.append(Func(explosion.reparentTo, parent))
+ explosionTrack.append(Func(explosion.setPos, explosionPoint))
+ explosionTrack.append(Func(explosion.setScale, scale))
+ explosionTrack.append(ActorInterval(explosion, animName))
+ explosionTrack.append(Func(MovieUtil.removeProp, explosion))
+ return explosionTrack
+
+
+def __createPlacedGroupTrapTrack(trap, prop, propName, centerSuit, propPos = None, propHpr = None, explode = 0):
+ toon = trap['toon']
+ if trap.has_key('npc'):
+ toon = trap['npc']
+ level = trap['level']
+ battle = trap['battle']
+ origHpr = toon.getHpr(battle)
+ trapPoint = Point3(0, 5 - MovieUtil.SUIT_TRAP_DISTANCE, 0.025)
+ trapDelay = 2.5
+ hands = toon.getLeftHands()
+
+ def placeDustExplosion(dustNode, trapProp, battle):
+ dustNode.reparentTo(battle)
+ dustNode.setPos(trapProp.getPos(battle))
+
+ trapTracks = Parallel()
+ firstTime = 1
+ targets = trap['target']
+ if True:
+ suit = centerSuit
+ suitPos = suit.getPos(battle)
+ targetPos = suitPos
+ trapProp = MovieUtil.copyProp(prop)
+ showThisTrap = True
+ trapTrack = Sequence()
+ trapTrack.append(Wait(trapDelay))
+ if showThisTrap:
+ notify.debug('showing trap %s for %d' % (trapProp.getName(), suit.doId))
+ trapTrack.append(Func(trapProp.show))
+ else:
+ notify.debug('hiding trap %s for %d' % (trapProp.getName(), suit.doId))
+ trapTrack.append(Func(trapProp.hide))
+ trapTrack.append(Func(trapProp.setScale, Point3(0.1, 0.1, 0.1)))
+ trapTrack.append(Func(trapProp.reparentTo, battle))
+ trapTrack.append(Func(trapProp.setPos, trapPoint))
+ trapTrack.append(Func(trapProp.setH, 0))
+ trapTrack.append(LerpScaleInterval(trapProp, 1.2, Point3(1.0, 1.0, 1.0)))
+ if explode == 1:
+ dustNode = hidden.attachNewNode('DustNode')
+ removeTrapsParallel = Parallel()
+ oneTrapTrack = Sequence()
+ oneTrapTrack.append(Func(trapProp.wrtReparentTo, hidden))
+ oneTrapTrack.append(Func(placeDustExplosion, dustNode, trapProp, battle))
+ oneTrapTrack.append(createCartoonExplosionTrack(dustNode, 'dust', explosionPoint=Point3(0, 0, 0)))
+ oneTrapTrack.append(Func(MovieUtil.removeProp, trapProp))
+ removeTrapsParallel.append(oneTrapTrack)
+ for target in trap['target']:
+ otherSuit = target['suit']
+ if otherSuit.battleTrapProp:
+ otherDustNode = hidden.attachNewNode('DustNodeOtherSuit')
+ otherTrapTrack = Sequence()
+ otherTrapTrack.append(Func(otherSuit.battleTrapProp.wrtReparentTo, hidden))
+ otherTrapTrack.append(Func(placeDustExplosion, dustNode, otherSuit.battleTrapProp, battle))
+ otherTrapTrack.append(createCartoonExplosionTrack(otherDustNode, 'dust', explosionPoint=Point3(0, 0, 0)))
+ otherTrapTrack.append(Func(battle.removeTrap, otherSuit))
+ removeTrapsParallel.append(otherTrapTrack)
+
+ trapTrack.append(removeTrapsParallel)
+ else:
+ if suit.battleTrap != NO_TRAP:
+ notify.debug('trapSuit() - trap: %d destroyed existing trap: %d' % (level, suit.battleTrap))
+ battle.removeTrap(suit)
+ suit.battleTrapProp = trapProp
+ suit.battleTrap = level
+ suit.battleTrapIsFresh = 1
+ unlureSuits = Parallel()
+ for target in targets:
+ kbbonus = target['kbbonus']
+ if kbbonus == 0:
+ unluredSuit = target['suit']
+ suitTrack = Sequence()
+ suitTrack.append(createSuitResetPosTrack(unluredSuit, battle))
+ suitTrack.append(Func(battle.unlureSuit, unluredSuit))
+ unlureSuits.append(suitTrack)
+
+ trapTrack.append(unlureSuits)
+ for otherSuit in battle.suits:
+ if not otherSuit == suit:
+ if otherSuit.battleTrap != NO_TRAP:
+ notify.debug('trapSuit() - trap: %d destroyed existing trap: %d' % (level, suit.battleTrap))
+ battle.removeTrap(otherSuit)
+ otherSuit.battleTrapProp = trapProp
+ otherSuit.battleTrap = level
+ otherSuit.battleTrapIsFresh = 1
+
+ trapTracks.append(trapTrack)
+ button = globalPropPool.getProp('button')
+ button2 = MovieUtil.copyProp(button)
+ buttons = [button, button2]
+ toonTrack = Sequence()
+ toonTrack.append(Func(MovieUtil.showProps, buttons, hands))
+ toonTrack.append(Func(toon.headsUp, battle, suitPos))
+ toonTrack.append(ActorInterval(toon, 'pushbutton'))
+ toonTrack.append(Func(MovieUtil.removeProps, buttons))
+ toonTrack.append(Func(toon.loop, 'neutral'))
+ toonTrack.append(Func(toon.setHpr, battle, origHpr))
+ if propName == 'quicksand':
+ propSound = globalBattleSoundCache.getSound('TL_quicksand.mp3')
+ elif propName == 'traintrack':
+ propSound = globalBattleSoundCache.getSound('TL_train_track_appear.mp3')
+ else:
+ propSound = globalBattleSoundCache.getSound('TL_trap_door.mp3')
+ buttonSound = globalBattleSoundCache.getSound('AA_drop_trigger_box.mp3')
+ soundTrack = Sequence(Wait(2.3), Parallel(SoundInterval(buttonSound, duration=0.67, node=toon), SoundInterval(propSound, node=toon)))
+ return Parallel(trapTracks, toonTrack, soundTrack)
diff --git a/toontown/battle/MovieUtil.py b/toontown/battle/MovieUtil.py
new file mode 100644
index 0000000..caa0c21
--- /dev/null
+++ b/toontown/battle/MovieUtil.py
@@ -0,0 +1,603 @@
+from direct.interval.IntervalGlobal import *
+from BattleBase import *
+from BattleProps import *
+from direct.directnotify import DirectNotifyGlobal
+import random
+from direct.particles import ParticleEffect
+import BattleParticles
+import BattleProps
+from toontown.toonbase import TTLocalizer
+notify = DirectNotifyGlobal.directNotify.newCategory('MovieUtil')
+SUIT_LOSE_DURATION = 6.0
+SUIT_LURE_DISTANCE = 2.6
+SUIT_LURE_DOLLAR_DISTANCE = 5.1
+SUIT_EXTRA_REACH_DISTANCE = 0.9
+SUIT_EXTRA_RAKE_DISTANCE = 1.1
+SUIT_TRAP_DISTANCE = 2.6
+SUIT_TRAP_RAKE_DISTANCE = 4.5
+SUIT_TRAP_MARBLES_DISTANCE = 3.7
+SUIT_TRAP_TNT_DISTANCE = 5.1
+PNT3_NEARZERO = Point3(0.01, 0.01, 0.01)
+PNT3_ZERO = Point3(0.0, 0.0, 0.0)
+PNT3_ONE = Point3(1.0, 1.0, 1.0)
+largeSuits = ['f',
+ 'cc',
+ 'gh',
+ 'tw',
+ 'bf',
+ 'sc',
+ 'ds',
+ 'hh',
+ 'cr',
+ 'tbc',
+ 'bs',
+ 'sd',
+ 'le',
+ 'bw',
+ 'nc',
+ 'mb',
+ 'ls',
+ 'rb',
+ 'ms',
+ 'tf',
+ 'm',
+ 'mh']
+shotDirection = 'left'
+
+def avatarDodge(leftAvatars, rightAvatars, leftData, rightData):
+ if len(leftAvatars) > len(rightAvatars):
+ PoLR = rightAvatars
+ PoMR = leftAvatars
+ else:
+ PoLR = leftAvatars
+ PoMR = rightAvatars
+ upper = 1 + 4 * abs(len(leftAvatars) - len(rightAvatars))
+ if random.randint(0, upper) > 0:
+ avDodgeList = PoLR
+ else:
+ avDodgeList = PoMR
+ if avDodgeList is leftAvatars:
+ data = leftData
+ else:
+ data = rightData
+ return (avDodgeList, data)
+
+
+def avatarHide(avatar):
+ notify.debug('avatarHide(%d)' % avatar.doId)
+ if hasattr(avatar, 'battleTrapProp'):
+ notify.debug('avatar.battleTrapProp = %s' % avatar.battleTrapProp)
+ avatar.detachNode()
+
+
+def copyProp(prop):
+ from direct.actor import Actor
+ if isinstance(prop, Actor.Actor):
+ return Actor.Actor(other=prop)
+ else:
+ return prop.copyTo(hidden)
+
+
+def showProp(prop, hand, pos = None, hpr = None, scale = None):
+ prop.reparentTo(hand)
+ if pos:
+ if callable(pos):
+ pos = pos()
+ prop.setPos(pos)
+ if hpr:
+ if callable(hpr):
+ hpr = hpr()
+ prop.setHpr(hpr)
+ if scale:
+ if callable(scale):
+ scale = scale()
+ prop.setScale(scale)
+
+
+def showProps(props, hands, pos = None, hpr = None, scale = None):
+ index = 0
+ for prop in props:
+ prop.reparentTo(hands[index])
+ if pos:
+ prop.setPos(pos)
+ if hpr:
+ prop.setHpr(hpr)
+ if scale:
+ prop.setScale(scale)
+ index += 1
+
+
+def hideProps(props):
+ for prop in props:
+ prop.detachNode()
+
+
+def removeProp(prop):
+ from direct.actor import Actor
+ if prop.isEmpty() == 1 or prop == None:
+ return
+ prop.detachNode()
+ if isinstance(prop, Actor.Actor):
+ prop.cleanup()
+ else:
+ prop.removeNode()
+ return
+
+
+def removeProps(props):
+ for prop in props:
+ removeProp(prop)
+
+
+def getActorIntervals(props, anim):
+ tracks = Parallel()
+ for prop in props:
+ tracks.append(ActorInterval(prop, anim))
+
+ return tracks
+
+
+def getScaleIntervals(props, duration, startScale, endScale):
+ tracks = Parallel()
+ for prop in props:
+ tracks.append(LerpScaleInterval(prop, duration, endScale, startScale=startScale))
+
+ return tracks
+
+
+def avatarFacePoint(av, other = render):
+ pnt = av.getPos(other)
+ pnt.setZ(pnt[2] + av.getHeight())
+ return pnt
+
+
+def insertDeathSuit(suit, deathSuit, battle = None, pos = None, hpr = None):
+ holdParent = suit.getParent()
+ if suit.getVirtual():
+ virtualize(deathSuit)
+ avatarHide(suit)
+ if deathSuit != None and not deathSuit.isEmpty():
+ if holdParent and 0:
+ deathSuit.reparentTo(holdParent)
+ else:
+ deathSuit.reparentTo(render)
+ if battle != None and pos != None:
+ deathSuit.setPos(battle, pos)
+ if battle != None and hpr != None:
+ deathSuit.setHpr(battle, hpr)
+ return
+
+
+def removeDeathSuit(suit, deathSuit):
+ notify.debug('removeDeathSuit()')
+ if not deathSuit.isEmpty():
+ deathSuit.detachNode()
+ suit.cleanupLoseActor()
+
+
+def insertReviveSuit(suit, deathSuit, battle = None, pos = None, hpr = None):
+ holdParent = suit.getParent()
+ if suit.getVirtual():
+ virtualize(deathSuit)
+ suit.hide()
+ if deathSuit != None and not deathSuit.isEmpty():
+ if holdParent and 0:
+ deathSuit.reparentTo(holdParent)
+ else:
+ deathSuit.reparentTo(render)
+ if battle != None and pos != None:
+ deathSuit.setPos(battle, pos)
+ if battle != None and hpr != None:
+ deathSuit.setHpr(battle, hpr)
+ return
+
+
+def removeReviveSuit(suit, deathSuit):
+ notify.debug('removeDeathSuit()')
+ suit.setSkelecog(1)
+ suit.show()
+ if not deathSuit.isEmpty():
+ deathSuit.detachNode()
+ suit.cleanupLoseActor()
+ suit.healthBar.show()
+ suit.reseatHealthBarForSkele()
+
+
+def virtualize(deathsuit):
+ actorNode = deathsuit.find('**/__Actor_modelRoot')
+ actorCollection = actorNode.findAllMatches('*')
+ parts = ()
+ for thingIndex in range(0, actorCollection.getNumPaths()):
+ thing = actorCollection[thingIndex]
+ if thing.getName() not in ('joint_attachMeter', 'joint_nameTag', 'def_nameTag'):
+ thing.setColorScale(1.0, 0.0, 0.0, 1.0)
+ thing.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd))
+ thing.setDepthWrite(False)
+ thing.setBin('fixed', 1)
+
+
+def createTrainTrackAppearTrack(dyingSuit, toon, battle, npcs):
+ retval = Sequence()
+ return retval
+ possibleSuits = []
+ for suitAttack in battle.movie.suitAttackDicts:
+ suit = suitAttack['suit']
+ if not suit == dyingSuit:
+ if hasattr(suit, 'battleTrapProp') and suit.battleTrapProp and suit.battleTrapProp.getName() == 'traintrack':
+ possibleSuits.append(suitAttack['suit'])
+
+ closestXDistance = 10000
+ closestSuit = None
+ for suit in possibleSuits:
+ suitPoint, suitHpr = battle.getActorPosHpr(suit)
+ xDistance = abs(suitPoint.getX())
+ if xDistance < closestXDistance:
+ closestSuit = suit
+ closestXDistance = xDistance
+
+ if closestSuit and closestSuit.battleTrapProp.isHidden():
+ closestSuit.battleTrapProp.setColorScale(1, 1, 1, 0)
+ closestSuit.battleTrapProp.show()
+ newRelativePos = dyingSuit.battleTrapProp.getPos(closestSuit)
+ newHpr = dyingSuit.battleTrapProp.getHpr(closestSuit)
+ closestSuit.battleTrapProp.setPos(newRelativePos)
+ closestSuit.battleTrapProp.setHpr(newHpr)
+ retval.append(LerpColorScaleInterval(closestSuit.battleTrapProp, 3.0, Vec4(1, 1, 1, 1)))
+ else:
+ notify.debug('could not find closest suit, returning empty sequence')
+ return retval
+
+
+def createSuitReviveTrack(suit, toon, battle, npcs = []):
+ suitTrack = Sequence()
+ suitPos, suitHpr = battle.getActorPosHpr(suit)
+ if hasattr(suit, 'battleTrapProp') and suit.battleTrapProp and suit.battleTrapProp.getName() == 'traintrack' and not suit.battleTrapProp.isHidden():
+ suitTrack.append(createTrainTrackAppearTrack(suit, toon, battle, npcs))
+ deathSuit = suit.getLoseActor()
+ suitTrack.append(Func(notify.debug, 'before insertDeathSuit'))
+ suitTrack.append(Func(insertReviveSuit, suit, deathSuit, battle, suitPos, suitHpr))
+ suitTrack.append(Func(notify.debug, 'before actorInterval lose'))
+ suitTrack.append(ActorInterval(deathSuit, 'lose', duration=SUIT_LOSE_DURATION))
+ suitTrack.append(Func(notify.debug, 'before removeDeathSuit'))
+ suitTrack.append(Func(removeReviveSuit, suit, deathSuit, name='remove-death-suit'))
+ suitTrack.append(Func(notify.debug, 'after removeDeathSuit'))
+ suitTrack.append(Func(suit.loop, 'neutral'))
+ spinningSound = base.loadSfx('phase_3.5/audio/sfx/Cog_Death.mp3')
+ deathSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart.mp3')
+ deathSoundTrack = Sequence(Wait(0.8), SoundInterval(spinningSound, duration=1.2, startTime=1.5, volume=0.2, node=suit), SoundInterval(spinningSound, duration=3.0, startTime=0.6, volume=0.8, node=suit), SoundInterval(deathSound, volume=0.32, node=suit))
+ BattleParticles.loadParticles()
+ smallGears = BattleParticles.createParticleEffect(file='gearExplosionSmall')
+ singleGear = BattleParticles.createParticleEffect('GearExplosion', numParticles=1)
+ smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10)
+ bigGearExplosion = BattleParticles.createParticleEffect('BigGearExplosion', numParticles=30)
+ gearPoint = Point3(suitPos.getX(), suitPos.getY(), suitPos.getZ() + suit.height - 0.2)
+ smallGears.setPos(gearPoint)
+ singleGear.setPos(gearPoint)
+ smallGears.setDepthWrite(False)
+ singleGear.setDepthWrite(False)
+ smallGearExplosion.setPos(gearPoint)
+ bigGearExplosion.setPos(gearPoint)
+ smallGearExplosion.setDepthWrite(False)
+ bigGearExplosion.setDepthWrite(False)
+ explosionTrack = Sequence()
+ explosionTrack.append(Wait(5.4))
+ explosionTrack.append(createKapowExplosionTrack(battle, explosionPoint=gearPoint))
+ gears1Track = Sequence(Wait(2.1), ParticleInterval(smallGears, battle, worldRelative=0, duration=4.3, cleanup=True), name='gears1Track')
+ gears2MTrack = Track((0.0, explosionTrack), (0.7, ParticleInterval(singleGear, battle, worldRelative=0, duration=5.7, cleanup=True)), (5.2, ParticleInterval(smallGearExplosion, battle, worldRelative=0, duration=1.2, cleanup=True)), (5.4, ParticleInterval(bigGearExplosion, battle, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack')
+ toonMTrack = Parallel(name='toonMTrack')
+ for mtoon in battle.toons:
+ toonMTrack.append(Sequence(Wait(1.0), ActorInterval(mtoon, 'duck'), ActorInterval(mtoon, 'duck', startTime=1.8), Func(mtoon.loop, 'neutral')))
+
+ for mtoon in npcs:
+ toonMTrack.append(Sequence(Wait(1.0), ActorInterval(mtoon, 'duck'), ActorInterval(mtoon, 'duck', startTime=1.8), Func(mtoon.loop, 'neutral')))
+
+ return Parallel(suitTrack, deathSoundTrack, gears1Track, gears2MTrack, toonMTrack)
+
+
+def createSuitDeathTrack(suit, toon, battle, npcs = []):
+ suitTrack = Sequence()
+ suitPos, suitHpr = battle.getActorPosHpr(suit)
+ if hasattr(suit, 'battleTrapProp') and suit.battleTrapProp and suit.battleTrapProp.getName() == 'traintrack' and not suit.battleTrapProp.isHidden():
+ suitTrack.append(createTrainTrackAppearTrack(suit, toon, battle, npcs))
+ deathSuit = suit.getLoseActor()
+ suitTrack.append(Func(notify.debug, 'before insertDeathSuit'))
+ suitTrack.append(Func(insertDeathSuit, suit, deathSuit, battle, suitPos, suitHpr))
+ suitTrack.append(Func(notify.debug, 'before actorInterval lose'))
+ suitTrack.append(ActorInterval(deathSuit, 'lose', duration=SUIT_LOSE_DURATION))
+ suitTrack.append(Func(notify.debug, 'before removeDeathSuit'))
+ suitTrack.append(Func(removeDeathSuit, suit, deathSuit, name='remove-death-suit'))
+ suitTrack.append(Func(notify.debug, 'after removeDeathSuit'))
+ spinningSound = base.loadSfx('phase_3.5/audio/sfx/Cog_Death.mp3')
+ deathSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart.mp3')
+ deathSoundTrack = Sequence(Wait(0.8), SoundInterval(spinningSound, duration=1.2, startTime=1.5, volume=0.2, node=deathSuit), SoundInterval(spinningSound, duration=3.0, startTime=0.6, volume=0.8, node=deathSuit), SoundInterval(deathSound, volume=0.32, node=deathSuit))
+ BattleParticles.loadParticles()
+ smallGears = BattleParticles.createParticleEffect(file='gearExplosionSmall')
+ singleGear = BattleParticles.createParticleEffect('GearExplosion', numParticles=1)
+ smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10)
+ bigGearExplosion = BattleParticles.createParticleEffect('BigGearExplosion', numParticles=30)
+ gearPoint = Point3(suitPos.getX(), suitPos.getY(), suitPos.getZ() + suit.height - 0.2)
+ smallGears.setPos(gearPoint)
+ singleGear.setPos(gearPoint)
+ smallGears.setDepthWrite(False)
+ singleGear.setDepthWrite(False)
+ smallGearExplosion.setPos(gearPoint)
+ bigGearExplosion.setPos(gearPoint)
+ smallGearExplosion.setDepthWrite(False)
+ bigGearExplosion.setDepthWrite(False)
+ explosionTrack = Sequence()
+ explosionTrack.append(Wait(5.4))
+ explosionTrack.append(createKapowExplosionTrack(battle, explosionPoint=gearPoint))
+ gears1Track = Sequence(Wait(2.1), ParticleInterval(smallGears, battle, worldRelative=0, duration=4.3, cleanup=True), name='gears1Track')
+ gears2MTrack = Track((0.0, explosionTrack), (0.7, ParticleInterval(singleGear, battle, worldRelative=0, duration=5.7, cleanup=True)), (5.2, ParticleInterval(smallGearExplosion, battle, worldRelative=0, duration=1.2, cleanup=True)), (5.4, ParticleInterval(bigGearExplosion, battle, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack')
+ toonMTrack = Parallel(name='toonMTrack')
+ for mtoon in battle.toons:
+ toonMTrack.append(Sequence(Wait(1.0), ActorInterval(mtoon, 'duck'), ActorInterval(mtoon, 'duck', startTime=1.8), Func(mtoon.loop, 'neutral')))
+
+ for mtoon in npcs:
+ toonMTrack.append(Sequence(Wait(1.0), ActorInterval(mtoon, 'duck'), ActorInterval(mtoon, 'duck', startTime=1.8), Func(mtoon.loop, 'neutral')))
+
+ return Parallel(suitTrack, deathSoundTrack, gears1Track, gears2MTrack, toonMTrack)
+
+
+def createSuitDodgeMultitrack(tDodge, suit, leftSuits, rightSuits):
+ suitTracks = Parallel()
+ suitDodgeList, sidestepAnim = avatarDodge(leftSuits, rightSuits, 'sidestep-left', 'sidestep-right')
+ for s in suitDodgeList:
+ suitTracks.append(Sequence(ActorInterval(s, sidestepAnim), Func(s.loop, 'neutral')))
+
+ suitTracks.append(Sequence(ActorInterval(suit, sidestepAnim), Func(suit.loop, 'neutral')))
+ suitTracks.append(Func(indicateMissed, suit))
+ return Sequence(Wait(tDodge), suitTracks)
+
+
+def createToonDodgeMultitrack(tDodge, toon, leftToons, rightToons):
+ toonTracks = Parallel()
+ if len(leftToons) > len(rightToons):
+ PoLR = rightToons
+ PoMR = leftToons
+ else:
+ PoLR = leftToons
+ PoMR = rightToons
+ upper = 1 + 4 * abs(len(leftToons) - len(rightToons))
+ if random.randint(0, upper) > 0:
+ toonDodgeList = PoLR
+ else:
+ toonDodgeList = PoMR
+ if toonDodgeList is leftToons:
+ sidestepAnim = 'sidestep-left'
+ for t in toonDodgeList:
+ toonTracks.append(Sequence(ActorInterval(t, sidestepAnim), Func(t.loop, 'neutral')))
+
+ else:
+ sidestepAnim = 'sidestep-right'
+ toonTracks.append(Sequence(ActorInterval(toon, sidestepAnim), Func(toon.loop, 'neutral')))
+ toonTracks.append(Func(indicateMissed, toon))
+ return Sequence(Wait(tDodge), toonTracks)
+
+
+def createSuitTeaseMultiTrack(suit, delay = 0.01):
+ suitTrack = Sequence(Wait(delay), ActorInterval(suit, 'victory', startTime=0.5, endTime=1.9), Func(suit.loop, 'neutral'))
+ missedTrack = Sequence(Wait(delay + 0.2), Func(indicateMissed, suit, 0.9))
+ return Parallel(suitTrack, missedTrack)
+
+
+SPRAY_LEN = 1.5
+
+def getSprayTrack(battle, color, origin, target, dScaleUp, dHold, dScaleDown, horizScale = 1.0, vertScale = 1.0, parent = render):
+ track = Sequence()
+ sprayProp = globalPropPool.getProp('spray')
+ sprayScale = hidden.attachNewNode('spray-parent')
+ sprayRot = hidden.attachNewNode('spray-rotate')
+ spray = sprayRot
+ spray.setColor(color)
+ if color[3] < 1.0:
+ spray.setTransparency(1)
+
+ def showSpray(sprayScale, sprayRot, sprayProp, origin, target, parent):
+ if callable(origin):
+ origin = origin()
+ if callable(target):
+ target = target()
+ sprayRot.reparentTo(parent)
+ sprayRot.clearMat()
+ sprayScale.reparentTo(sprayRot)
+ sprayScale.clearMat()
+ sprayProp.reparentTo(sprayScale)
+ sprayProp.clearMat()
+ sprayRot.setPos(origin)
+ sprayRot.lookAt(Point3(target))
+
+ track.append(Func(battle.movie.needRestoreRenderProp, sprayProp))
+ track.append(Func(showSpray, sprayScale, sprayRot, sprayProp, origin, target, parent))
+
+ def calcTargetScale(target = target, origin = origin, horizScale = horizScale, vertScale = vertScale):
+ if callable(target):
+ target = target()
+ if callable(origin):
+ origin = origin()
+ distance = Vec3(target - origin).length()
+ yScale = distance / SPRAY_LEN
+ targetScale = Point3(yScale * horizScale, yScale, yScale * vertScale)
+ return targetScale
+
+ track.append(LerpScaleInterval(sprayScale, dScaleUp, calcTargetScale, startScale=PNT3_NEARZERO))
+ track.append(Wait(dHold))
+
+ def prepareToShrinkSpray(spray, sprayProp, origin, target):
+ if callable(target):
+ target = target()
+ if callable(origin):
+ origin = origin()
+ sprayProp.setPos(Point3(0.0, -SPRAY_LEN, 0.0))
+ spray.setPos(target)
+
+ track.append(Func(prepareToShrinkSpray, spray, sprayProp, origin, target))
+ track.append(LerpScaleInterval(sprayScale, dScaleDown, PNT3_NEARZERO))
+
+ def hideSpray(spray, sprayScale, sprayRot, sprayProp, propPool):
+ sprayProp.detachNode()
+ removeProp(sprayProp)
+ sprayRot.removeNode()
+ sprayScale.removeNode()
+
+ track.append(Func(hideSpray, spray, sprayScale, sprayRot, sprayProp, globalPropPool))
+ track.append(Func(battle.movie.clearRenderProp, sprayProp))
+ return track
+
+
+T_HOLE_LEAVES_HAND = 1.708
+T_TELEPORT_ANIM = 3.3
+T_HOLE_CLOSES = 0.3
+
+def getToonTeleportOutInterval(toon):
+ holeActors = toon.getHoleActors()
+ holes = [holeActors[0], holeActors[1]]
+ hole = holes[0]
+ hole2 = holes[1]
+ hands = toon.getRightHands()
+ delay = T_HOLE_LEAVES_HAND
+ dur = T_TELEPORT_ANIM
+ holeTrack = Sequence()
+ holeTrack.append(Func(showProps, holes, hands))
+ (holeTrack.append(Wait(0.5)),)
+ holeTrack.append(Func(base.playSfx, toon.getSoundTeleport()))
+ holeTrack.append(Wait(delay - 0.5))
+ holeTrack.append(Func(hole.reparentTo, toon))
+ holeTrack.append(Func(hole2.reparentTo, hidden))
+ holeAnimTrack = Sequence()
+ holeAnimTrack.append(ActorInterval(hole, 'hole', duration=dur))
+ holeAnimTrack.append(Func(hideProps, holes))
+ runTrack = Sequence(ActorInterval(toon, 'teleport', duration=dur), Wait(T_HOLE_CLOSES), Func(toon.detachNode))
+ return Parallel(runTrack, holeAnimTrack, holeTrack)
+
+
+def getToonTeleportInInterval(toon):
+ hole = toon.getHoleActors()[0]
+ holeAnimTrack = Sequence()
+ holeAnimTrack.append(Func(toon.detachNode))
+ holeAnimTrack.append(Func(hole.reparentTo, toon))
+ pos = Point3(0, -2.4, 0)
+ holeAnimTrack.append(Func(hole.setPos, toon, pos))
+ holeAnimTrack.append(ActorInterval(hole, 'hole', startTime=T_TELEPORT_ANIM, endTime=T_HOLE_LEAVES_HAND))
+ holeAnimTrack.append(ActorInterval(hole, 'hole', startTime=T_HOLE_LEAVES_HAND, endTime=T_TELEPORT_ANIM))
+ holeAnimTrack.append(Func(hole.reparentTo, hidden))
+ delay = T_TELEPORT_ANIM - T_HOLE_LEAVES_HAND
+ jumpTrack = Sequence(Wait(delay), Func(toon.reparentTo, render), ActorInterval(toon, 'jump'))
+ return Parallel(holeAnimTrack, jumpTrack)
+
+
+def getSuitRakeOffset(suit):
+ suitName = suit.getStyleName()
+ if suitName == 'gh':
+ return 1.4
+ elif suitName == 'f':
+ return 1.0
+ elif suitName == 'cc':
+ return 0.7
+ elif suitName == 'tw':
+ return 1.3
+ elif suitName == 'bf':
+ return 1.0
+ elif suitName == 'sc':
+ return 0.8
+ elif suitName == 'ym':
+ return 0.1
+ elif suitName == 'mm':
+ return 0.05
+ elif suitName == 'tm':
+ return 0.07
+ elif suitName == 'nd':
+ return 0.07
+ elif suitName == 'pp':
+ return 0.04
+ elif suitName == 'bc':
+ return 0.36
+ elif suitName == 'b':
+ return 0.41
+ elif suitName == 'dt':
+ return 0.31
+ elif suitName == 'ac':
+ return 0.39
+ elif suitName == 'ds':
+ return 0.41
+ elif suitName == 'hh':
+ return 0.8
+ elif suitName == 'cr':
+ return 2.1
+ elif suitName == 'tbc':
+ return 1.4
+ elif suitName == 'bs':
+ return 0.4
+ elif suitName == 'sd':
+ return 1.02
+ elif suitName == 'le':
+ return 1.3
+ elif suitName == 'bw':
+ return 1.4
+ elif suitName == 'nc':
+ return 0.6
+ elif suitName == 'mb':
+ return 1.85
+ elif suitName == 'ls':
+ return 1.4
+ elif suitName == 'rb':
+ return 1.6
+ elif suitName == 'ms':
+ return 0.7
+ elif suitName == 'tf':
+ return 0.75
+ elif suitName == 'm':
+ return 0.9
+ elif suitName == 'mh':
+ return 1.3
+ else:
+ notify.warning('getSuitRakeOffset(suit) - Unknown suit name: %s' % suitName)
+ return 0
+
+
+def startSparksIval(tntProp):
+ tip = tntProp.find('**/joint_attachEmitter')
+ sparks = BattleParticles.createParticleEffect(file='tnt')
+ return Func(sparks.start, tip)
+
+
+def indicateMissed(actor, duration = 1.1, scale = 0.7):
+ actor.showHpString(TTLocalizer.AttackMissed, duration=duration, scale=scale)
+
+
+def createKapowExplosionTrack(parent, explosionPoint = None, scale = 1.0):
+ explosionTrack = Sequence()
+ explosion = loader.loadModel('phase_3.5/models/props/explosion.bam')
+ explosion.setBillboardPointEye()
+ explosion.setDepthWrite(False)
+ if not explosionPoint:
+ explosionPoint = Point3(0, 3.6, 2.1)
+ explosionTrack.append(Func(explosion.reparentTo, parent))
+ explosionTrack.append(Func(explosion.setPos, explosionPoint))
+ explosionTrack.append(Func(explosion.setScale, 0.4 * scale))
+ explosionTrack.append(Wait(0.6))
+ explosionTrack.append(Func(removeProp, explosion))
+ return explosionTrack
+
+
+def createSuitStunInterval(suit, before, after):
+ p1 = Point3(0)
+ p2 = Point3(0)
+ stars = globalPropPool.getProp('stun')
+ stars.setColor(1, 1, 1, 1)
+ stars.adjustAllPriorities(100)
+ head = suit.getHeadParts()[0]
+ head.calcTightBounds(p1, p2)
+ return Sequence(Wait(before), Func(stars.reparentTo, head), Func(stars.setZ, max(0.0, p2[2] - 1.0)), Func(stars.loop, 'stun'), Wait(after), Func(stars.removeNode))
+
+
+def calcAvgSuitPos(throw):
+ battle = throw['battle']
+ avgSuitPos = Point3(0, 0, 0)
+ numTargets = len(throw['target'])
+ for i in range(numTargets):
+ suit = throw['target'][i]['suit']
+ avgSuitPos += suit.getPos(battle)
+
+ avgSuitPos /= numTargets
+ return avgSuitPos
diff --git a/toontown/battle/PlayByPlayText.py b/toontown/battle/PlayByPlayText.py
new file mode 100644
index 0000000..00fe9e5
--- /dev/null
+++ b/toontown/battle/PlayByPlayText.py
@@ -0,0 +1,32 @@
+from pandac.PandaModules import *
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase.ToontownBattleGlobals import *
+from toontown.toonbase.ToontownGlobals import *
+from SuitBattleGlobals import *
+from direct.interval.IntervalGlobal import *
+from direct.directnotify import DirectNotifyGlobal
+import string
+from direct.gui import OnscreenText
+import BattleBase
+
+class PlayByPlayText(OnscreenText.OnscreenText):
+ notify = DirectNotifyGlobal.directNotify.newCategory('PlayByPlayText')
+
+ def __init__(self):
+ OnscreenText.OnscreenText.__init__(self, mayChange=1, pos=(0.0, 0.75), scale=TTLocalizer.PBPTonscreenText, fg=(1, 0, 0, 1), font=getSignFont(), wordwrap=13)
+
+ def getShowInterval(self, text, duration):
+ return Sequence(Func(self.hide), Wait(duration * 0.3), Func(self.setText, text), Func(self.show), Wait(duration * 0.7), Func(self.hide))
+
+ def getToonsDiedInterval(self, textList, duration):
+ track = Sequence(Func(self.hide), Wait(duration * 0.3))
+ waitGap = 0.6 / len(textList) * duration
+ for text in textList:
+ newList = [Func(self.setText, text),
+ Func(self.show),
+ Wait(waitGap),
+ Func(self.hide)]
+ track += newList
+
+ track.append(Wait(duration * 0.1))
+ return track
diff --git a/toontown/battle/RewardPanel.py b/toontown/battle/RewardPanel.py
new file mode 100644
index 0000000..9736403
--- /dev/null
+++ b/toontown/battle/RewardPanel.py
@@ -0,0 +1,815 @@
+from pandac.PandaModules import *
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from toontown.toonbase import ToontownBattleGlobals
+import BattleBase
+from direct.directnotify import DirectNotifyGlobal
+import random
+import string
+from toontown.quest import Quests
+import copy
+from toontown.suit import SuitDNA
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.toon import NPCToons
+import math
+from toontown.coghq import CogDisguiseGlobals
+from toontown.shtiker import DisguisePage
+import Fanfare
+from otp.otpbase import OTPGlobals
+
+class RewardPanel(DirectFrame):
+ notify = DirectNotifyGlobal.directNotify.newCategory('RewardPanel')
+ SkipBattleMovieEvent = 'skip-battle-movie-event'
+
+ def __init__(self, name):
+ gscale = (TTLocalizer.RPdirectFrame[0], TTLocalizer.RPdirectFrame[1], TTLocalizer.RPdirectFrame[2] * 1.1)
+ DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_pos=Point3(0, 0, -.05), geom_scale=gscale, pos=(0, 0, 0.587))
+ self.initialiseoptions(RewardPanel)
+ self.avNameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.3), text=name, text_scale=0.08)
+ self.gagExpFrame = DirectFrame(parent=self, relief=None, pos=(-0.32, 0, 0.24))
+ self.itemFrame = DirectFrame(parent=self, relief=None, text=TTLocalizer.RewardPanelItems, text_pos=(0, 0.2), text_scale=0.08)
+ self.cogPartFrame = DirectFrame(parent=self, relief=None, text=TTLocalizer.RewardPanelCogPart, text_pos=(0, 0.2), text_scale=0.08)
+ self.missedItemFrame = DirectFrame(parent=self, relief=None, text=TTLocalizer.RewardPanelMissedItems, text_pos=(0, 0.2), text_scale=0.08)
+ self.itemLabel = DirectLabel(parent=self.itemFrame, text='', text_scale=0.06)
+ self.cogPartLabel = DirectLabel(parent=self.cogPartFrame, text='', text_scale=0.06)
+ self.missedItemLabel = DirectLabel(parent=self.missedItemFrame, text='', text_scale=0.06)
+ self.questFrame = DirectFrame(parent=self, relief=None, text=TTLocalizer.RewardPanelToonTasks, text_pos=(0, 0.2), text_scale=0.06)
+ self.questLabelList = []
+ for i in range(ToontownGlobals.MaxQuestCarryLimit):
+ label = DirectLabel(parent=self.questFrame, relief=None, pos=(-0.85, 0, -0.1 * i), text=TTLocalizer.RewardPanelQuestLabel % i, text_scale=0.05, text_align=TextNode.ALeft)
+ label.hide()
+ self.questLabelList.append(label)
+
+ self.newGagFrame = DirectFrame(parent=self, relief=None, pos=(0, 0, 0.24), text='', text_wordwrap=14.4, text_pos=(0, -0.46), text_scale=0.06)
+ self.endTrackFrame = DirectFrame(parent=self, relief=None, pos=(0, 0, 0.24), text='', text_wordwrap=14.4, text_pos=(0, -0.46), text_scale=0.06)
+ self.congratsLeft = DirectLabel(parent=self.newGagFrame, pos=(-0.2, 0, -0.1), text='', text_pos=(0, 0), text_scale=0.06)
+ self.congratsLeft.setHpr(0, 0, -30)
+ self.congratsRight = DirectLabel(parent=self.newGagFrame, pos=(0.2, 0, -0.1), text='', text_pos=(0, 0), text_scale=0.06)
+ self.congratsRight.setHpr(0, 0, 30)
+ self.promotionFrame = DirectFrame(parent=self, relief=None, pos=(0, 0, 0.24), text='', text_wordwrap=14.4, text_pos=(0, -0.46), text_scale=0.06)
+ self.trackLabels = []
+ self.trackIncLabels = []
+ self.trackBars = []
+ self.trackBarsOffset = 0
+ self.meritLabels = []
+ self.meritIncLabels = []
+ self.meritBars = []
+ for i in range(len(SuitDNA.suitDepts)):
+ deptName = TextEncoder.upper(SuitDNA.suitDeptFullnames[SuitDNA.suitDepts[i]])
+ self.meritLabels.append(DirectLabel(parent=self.gagExpFrame, relief=None, text=deptName, text_scale=0.05, text_align=TextNode.ARight, pos=(TTLocalizer.RPmeritLabelPosX, 0, -0.09 * i - 0.125), text_pos=(0, -0.02)))
+ self.meritIncLabels.append(DirectLabel(parent=self.gagExpFrame, relief=None, text='', text_scale=0.05, text_align=TextNode.ALeft, pos=(0.7, 0, -0.09 * i - 0.125), text_pos=(0, -0.02)))
+ self.meritBars.append(DirectWaitBar(parent=self.gagExpFrame, relief=DGG.SUNKEN, frameSize=(-1,
+ 1,
+ -0.15,
+ 0.15), borderWidth=(0.02, 0.02), scale=0.25, frameColor=(DisguisePage.DeptColors[i][0] * 0.7,
+ DisguisePage.DeptColors[i][1] * 0.7,
+ DisguisePage.DeptColors[i][2] * 0.7,
+ 1), barColor=(DisguisePage.DeptColors[i][0],
+ DisguisePage.DeptColors[i][1],
+ DisguisePage.DeptColors[i][2],
+ 1), text='0/0 ' + TTLocalizer.RewardPanelMeritBarLabels[i], text_scale=TTLocalizer.RPmeritBarLabels, text_fg=(0, 0, 0, 1), text_align=TextNode.ALeft, text_pos=(-0.96, -0.05), pos=(TTLocalizer.RPmeritBarsPosX, 0, -0.09 * i - 0.125)))
+
+ for i in range(len(ToontownBattleGlobals.Tracks)):
+ trackName = TextEncoder.upper(ToontownBattleGlobals.Tracks[i])
+ self.trackLabels.append(DirectLabel(parent=self.gagExpFrame, relief=None, text=trackName, text_scale=TTLocalizer.RPtrackLabels, text_align=TextNode.ARight, pos=(0.13, 0, -0.09 * i), text_pos=(0, -0.02)))
+ self.trackIncLabels.append(DirectLabel(parent=self.gagExpFrame, relief=None, text='', text_scale=0.05, text_align=TextNode.ALeft, pos=(0.65, 0, -0.09 * i), text_pos=(0, -0.02)))
+ self.trackBars.append(DirectWaitBar(parent=self.gagExpFrame, relief=DGG.SUNKEN, frameSize=(-1,
+ 1,
+ -0.15,
+ 0.15), borderWidth=(0.02, 0.02), scale=0.25, frameColor=(ToontownBattleGlobals.TrackColors[i][0] * 0.7,
+ ToontownBattleGlobals.TrackColors[i][1] * 0.7,
+ ToontownBattleGlobals.TrackColors[i][2] * 0.7,
+ 1), barColor=(ToontownBattleGlobals.TrackColors[i][0],
+ ToontownBattleGlobals.TrackColors[i][1],
+ ToontownBattleGlobals.TrackColors[i][2],
+ 1), text='0/0', text_scale=0.18, text_fg=(0, 0, 0, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05), pos=(0.4, 0, -0.09 * i)))
+
+ self._battleGui = loader.loadModel('phase_3.5/models/gui/battle_gui')
+ self.skipButton = DirectButton(parent=self, relief=None, image=(self._battleGui.find('**/tt_t_gui_gen_skipSectionUp'),
+ self._battleGui.find('**/tt_t_gui_gen_skipSectionDown'),
+ self._battleGui.find('**/tt_t_gui_gen_skipSectionRollOver'),
+ self._battleGui.find('**/tt_t_gui_gen_skipSectionDisabled')), pos=(0.815, 0, -0.395), scale=(0.39, 1.0, 0.39), text=('',
+ TTLocalizer.RewardPanelSkip,
+ TTLocalizer.RewardPanelSkip,
+ ''), text_scale=TTLocalizer.RPskipScale, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=TTLocalizer.RPskipPos, textMayChange=0, command=self._handleSkip)
+ return
+
+ def getNextExpValue(self, curSkill, trackIndex):
+ retVal = ToontownBattleGlobals.UberSkill
+ for amount in ToontownBattleGlobals.Levels[trackIndex]:
+ if curSkill < amount:
+ retVal = amount
+ return retVal
+
+ return retVal
+
+ def getNextExpValueUber(self, curSkill, trackIndex):
+ return ToontownBattleGlobals.UberSkill
+
+ def getNextMeritValue(self, curMerits, toon, dept):
+ totalMerits = CogDisguiseGlobals.getTotalMerits(toon, dept)
+ retVal = totalMerits
+ if curMerits > totalMerits:
+ retVal = amount
+ return retVal
+
+ def initItemFrame(self, toon):
+ self.endTrackFrame.hide()
+ self.gagExpFrame.hide()
+ self.newGagFrame.hide()
+ self.promotionFrame.hide()
+ self.questFrame.hide()
+ self.itemFrame.show()
+ self.cogPartFrame.hide()
+ self.missedItemFrame.hide()
+
+ def initMissedItemFrame(self, toon):
+ self.endTrackFrame.hide()
+ self.gagExpFrame.hide()
+ self.newGagFrame.hide()
+ self.promotionFrame.hide()
+ self.questFrame.hide()
+ self.itemFrame.hide()
+ self.cogPartFrame.hide()
+ self.missedItemFrame.show()
+
+ def initCogPartFrame(self, toon):
+ self.endTrackFrame.hide()
+ self.gagExpFrame.hide()
+ self.newGagFrame.hide()
+ self.promotionFrame.hide()
+ self.questFrame.hide()
+ self.itemFrame.hide()
+ self.cogPartFrame.show()
+ self.cogPartLabel['text'] = ''
+ self.missedItemFrame.hide()
+
+ def initQuestFrame(self, toon, avQuests):
+ self.endTrackFrame.hide()
+ self.gagExpFrame.hide()
+ self.newGagFrame.hide()
+ self.promotionFrame.hide()
+ self.questFrame.show()
+ self.itemFrame.hide()
+ self.cogPartFrame.hide()
+ self.missedItemFrame.hide()
+ for i in range(ToontownGlobals.MaxQuestCarryLimit):
+ questLabel = self.questLabelList[i]
+ questLabel['text_fg'] = (0, 0, 0, 1)
+ questLabel.hide()
+
+ for i in range(len(avQuests)):
+ questDesc = avQuests[i]
+ questId, npcId, toNpcId, rewardId, toonProgress = questDesc
+ quest = Quests.getQuest(questId)
+ if quest:
+ questString = quest.getString()
+ progressString = quest.getProgressString(toon, questDesc)
+ rewardString = quest.getRewardString(progressString)
+ rewardString = Quests.fillInQuestNames(rewardString, toNpcId=toNpcId)
+ completed = quest.getCompletionStatus(toon, questDesc) == Quests.COMPLETE
+ questLabel = self.questLabelList[i]
+ questLabel.show()
+ if base.localAvatar.tutorialAck:
+ questLabel['text'] = rewardString
+ if completed:
+ questLabel['text_fg'] = (0, 0.3, 0, 1)
+ else:
+ questLabel['text'] = questString + ' :'
+
+ def initGagFrame(self, toon, expList, meritList, noSkip = False):
+ self.avNameLabel['text'] = toon.getName()
+ self.endTrackFrame.hide()
+ self.gagExpFrame.show()
+ self.newGagFrame.hide()
+ self.promotionFrame.hide()
+ self.questFrame.hide()
+ self.itemFrame.hide()
+ self.cogPartFrame.hide()
+ self.missedItemFrame.hide()
+ trackBarOffset = 0
+ self.skipButton['state'] = choice(noSkip, DGG.DISABLED, DGG.NORMAL)
+ for i in range(len(SuitDNA.suitDepts)):
+ meritBar = self.meritBars[i]
+ meritLabel = self.meritLabels[i]
+ totalMerits = CogDisguiseGlobals.getTotalMerits(toon, i)
+ merits = meritList[i]
+ self.meritIncLabels[i].hide()
+ if CogDisguiseGlobals.isSuitComplete(toon.cogParts, i):
+ if not self.trackBarsOffset:
+ trackBarOffset = 0.47
+ self.trackBarsOffset = 1
+ meritBar.show()
+ meritLabel.show()
+ meritLabel.show()
+ if totalMerits:
+ meritBar['range'] = totalMerits
+ meritBar['value'] = merits
+ if merits == totalMerits:
+ meritBar['text'] = TTLocalizer.RewardPanelMeritAlert
+ else:
+ meritBar['text'] = '%s/%s %s' % (merits, totalMerits, TTLocalizer.RewardPanelMeritBarLabels[i])
+ else:
+ meritBar['range'] = 1
+ meritBar['value'] = 1
+ meritBar['text'] = TTLocalizer.RewardPanelMeritsMaxed
+ self.resetMeritBarColor(i)
+ else:
+ meritBar.hide()
+ meritLabel.hide()
+
+ for i in range(len(expList)):
+ curExp = expList[i]
+ trackBar = self.trackBars[i]
+ trackLabel = self.trackLabels[i]
+ trackIncLabel = self.trackIncLabels[i]
+ trackBar.setX(trackBar.getX() - trackBarOffset)
+ trackLabel.setX(trackLabel.getX() - trackBarOffset)
+ trackIncLabel.setX(trackIncLabel.getX() - trackBarOffset)
+ trackIncLabel.hide()
+ if toon.hasTrackAccess(i):
+ trackBar.show()
+ if curExp >= ToontownBattleGlobals.UnpaidMaxSkills[i] and toon.getGameAccess() != OTPGlobals.AccessFull:
+ nextExp = self.getNextExpValue(curExp, i)
+ trackBar['range'] = nextExp
+ trackBar['value'] = ToontownBattleGlobals.UnpaidMaxSkills[i]
+ trackBar['text'] = TTLocalizer.InventoryGuestExp
+ elif curExp >= ToontownBattleGlobals.regMaxSkill:
+ nextExp = self.getNextExpValueUber(curExp, i)
+ trackBar['range'] = nextExp
+ uberCurrExp = curExp - ToontownBattleGlobals.regMaxSkill
+ trackBar['value'] = uberCurrExp
+ trackBar['text'] = TTLocalizer.InventoryUberTrackExp % {'nextExp': ToontownBattleGlobals.MaxSkill - curExp}
+ else:
+ nextExp = self.getNextExpValue(curExp, i)
+ trackBar['range'] = nextExp
+ trackBar['value'] = curExp
+ trackBar['text'] = '%s/%s' % (curExp, nextExp)
+ self.resetBarColor(i)
+ else:
+ trackBar.hide()
+
+ def incrementExp(self, track, newValue, toon):
+ trackBar = self.trackBars[track]
+ oldValue = trackBar['value']
+ newValue = min(ToontownBattleGlobals.MaxSkill, newValue)
+ nextExp = self.getNextExpValue(newValue, track)
+ if newValue >= ToontownBattleGlobals.UnpaidMaxSkills[track] and toon.getGameAccess() != OTPGlobals.AccessFull:
+ newValue = oldValue
+ trackBar['text'] = TTLocalizer.GuestLostExp
+ elif newValue >= ToontownBattleGlobals.regMaxSkill:
+ newValue = newValue - ToontownBattleGlobals.regMaxSkill
+ nextExp = self.getNextExpValueUber(newValue, track)
+ trackBar['text'] = TTLocalizer.InventoryUberTrackExp % {'nextExp': ToontownBattleGlobals.UberSkill - newValue}
+ else:
+ trackBar['text'] = '%s/%s' % (newValue, nextExp)
+ trackBar['range'] = nextExp
+ trackBar['value'] = newValue
+ trackBar['barColor'] = (ToontownBattleGlobals.TrackColors[track][0],
+ ToontownBattleGlobals.TrackColors[track][1],
+ ToontownBattleGlobals.TrackColors[track][2],
+ 1)
+
+ def resetBarColor(self, track):
+ self.trackBars[track]['barColor'] = (ToontownBattleGlobals.TrackColors[track][0] * 0.8,
+ ToontownBattleGlobals.TrackColors[track][1] * 0.8,
+ ToontownBattleGlobals.TrackColors[track][2] * 0.8,
+ 1)
+
+ def incrementMerits(self, toon, dept, newValue, totalMerits):
+ meritBar = self.meritBars[dept]
+ oldValue = meritBar['value']
+ if totalMerits:
+ newValue = min(totalMerits, newValue)
+ meritBar['range'] = totalMerits
+ meritBar['value'] = newValue
+ if newValue == totalMerits:
+ meritBar['text'] = TTLocalizer.RewardPanelMeritAlert
+ meritBar['barColor'] = (DisguisePage.DeptColors[dept][0],
+ DisguisePage.DeptColors[dept][1],
+ DisguisePage.DeptColors[dept][2],
+ 1)
+ else:
+ meritBar['text'] = '%s/%s %s' % (newValue, totalMerits, TTLocalizer.RewardPanelMeritBarLabels[dept])
+
+ def resetMeritBarColor(self, dept):
+ self.meritBars[dept]['barColor'] = (DisguisePage.DeptColors[dept][0] * 0.8,
+ DisguisePage.DeptColors[dept][1] * 0.8,
+ DisguisePage.DeptColors[dept][2] * 0.8,
+ 1)
+
+ def getRandomCongratsPair(self, toon):
+ congratsStrings = TTLocalizer.RewardPanelCongratsStrings
+ numStrings = len(congratsStrings)
+ indexList = range(numStrings)
+ index1 = random.choice(indexList)
+ indexList.remove(index1)
+ index2 = random.choice(indexList)
+ string1 = congratsStrings[index1]
+ string2 = congratsStrings[index2]
+ return (string1, string2)
+
+ def uberGagInterval(self, toon, track, level):
+ self.endTrackFrame.hide()
+ self.gagExpFrame.hide()
+ self.newGagFrame.show()
+ self.promotionFrame.hide()
+ self.questFrame.hide()
+ self.itemFrame.hide()
+ self.missedItemFrame.hide()
+ self.newGagFrame['text'] = TTLocalizer.RewardPanelUberGag % {'gagName': ToontownBattleGlobals.Tracks[track].capitalize(),
+ 'exp': str(ToontownBattleGlobals.UberSkill),
+ 'avName': toon.getName()}
+ self.congratsLeft['text'] = ''
+ self.congratsRight['text'] = ''
+ gagOriginal = base.localAvatar.inventory.buttonLookup(track, level)
+ self.newGagIcon = gagOriginal.copyTo(self.newGagFrame)
+ self.newGagIcon.setPos(0, 0, -0.25)
+ self.newGagIcon.setScale(1.5)
+
+ def newGag(self, toon, track, level):
+ self.endTrackFrame.hide()
+ self.gagExpFrame.hide()
+ self.newGagFrame.show()
+ self.promotionFrame.hide()
+ self.questFrame.hide()
+ self.itemFrame.hide()
+ self.missedItemFrame.hide()
+ self.newGagFrame['text'] = TTLocalizer.RewardPanelNewGag % {'gagName': ToontownBattleGlobals.Tracks[track].capitalize(),
+ 'avName': toon.getName()}
+ self.congratsLeft['text'] = ''
+ self.congratsRight['text'] = ''
+ gagOriginal = base.localAvatar.inventory.buttonLookup(track, level)
+ self.newGagIcon = gagOriginal.copyTo(self.newGagFrame)
+ self.newGagIcon.setPos(0, 0, -0.25)
+ self.newGagIcon.setScale(1.5)
+
+ def cleanupNewGag(self):
+ self.endTrackFrame.hide()
+ if self.newGagIcon:
+ self.newGagIcon.removeNode()
+ self.newGagIcon = None
+ self.gagExpFrame.show()
+ self.newGagFrame.hide()
+ self.promotionFrame.hide()
+ self.questFrame.hide()
+ self.itemFrame.hide()
+ self.missedItemFrame.hide()
+ return
+
+ def getNewGagIntervalList(self, toon, track, level):
+ leftCongratsAnticipate = 1.0
+ rightCongratsAnticipate = 1.0
+ finalDelay = 1.5
+ leftString, rightString = self.getRandomCongratsPair(toon)
+ intervalList = [Func(self.newGag, toon, track, level),
+ Wait(leftCongratsAnticipate),
+ Func(self.congratsLeft.setProp, 'text', leftString),
+ Wait(rightCongratsAnticipate),
+ Func(self.congratsRight.setProp, 'text', rightString),
+ Wait(finalDelay),
+ Func(self.cleanupNewGag)]
+ return intervalList
+
+ def getUberGagIntervalList(self, toon, track, level):
+ leftCongratsAnticipate = 1.0
+ rightCongratsAnticipate = 1.0
+ finalDelay = 1.5
+ leftString, rightString = self.getRandomCongratsPair(toon)
+ intervalList = [Func(self.uberGagInterval, toon, track, level),
+ Wait(leftCongratsAnticipate),
+ Func(self.congratsLeft.setProp, 'text', leftString),
+ Wait(rightCongratsAnticipate),
+ Func(self.congratsRight.setProp, 'text', rightString),
+ Wait(finalDelay),
+ Func(self.cleanupNewGag)]
+ return intervalList
+
+ def vanishFrames(self):
+ self.hide()
+ self.endTrackFrame.hide()
+ self.gagExpFrame.hide()
+ self.newGagFrame.hide()
+ self.promotionFrame.hide()
+ self.questFrame.hide()
+ self.itemFrame.hide()
+ self.missedItemFrame.hide()
+ self.cogPartFrame.hide()
+ self.missedItemFrame.hide()
+
+ def endTrack(self, toon, toonList, track):
+ for t in toonList:
+ if t == base.localAvatar:
+ self.show()
+
+ self.endTrackFrame.show()
+ self.endTrackFrame['text'] = TTLocalizer.RewardPanelEndTrack % {'gagName': ToontownBattleGlobals.Tracks[track].capitalize(),
+ 'avName': toon.getName()}
+ gagLast = base.localAvatar.inventory.buttonLookup(track, ToontownBattleGlobals.UBER_GAG_LEVEL_INDEX)
+ self.gagIcon = gagLast.copyTo(self.endTrackFrame)
+ self.gagIcon.setPos(0, 0, -0.25)
+ self.gagIcon.setScale(1.5)
+
+ def cleanIcon(self):
+ self.gagIcon.removeNode()
+ self.gagIcon = None
+ return
+
+ def cleanupEndTrack(self):
+ self.endTrackFrame.hide()
+ self.gagExpFrame.show()
+ self.newGagFrame.hide()
+ self.promotionFrame.hide()
+ self.questFrame.hide()
+ self.itemFrame.hide()
+ self.missedItemFrame.hide()
+
+ def getEndTrackIntervalList(self, toon, toonList, track):
+ intervalList = [Func(self.endTrack, toon, toonList, track), Wait(2.0), Func(self.cleanIcon)]
+ return intervalList
+
+ def showTrackIncLabel(self, track, earnedSkill, guestWaste = 0):
+ if guestWaste:
+ self.trackIncLabels[track]['text'] = ''
+ elif earnedSkill > 0:
+ self.trackIncLabels[track]['text'] = '+ ' + str(earnedSkill)
+ elif earnedSkill < 0:
+ self.trackIncLabels[track]['text'] = ' ' + str(earnedSkill)
+ self.trackIncLabels[track].show()
+
+ def showMeritIncLabel(self, dept, earnedMerits):
+ self.meritIncLabels[dept]['text'] = '+ ' + str(earnedMerits)
+ self.meritIncLabels[dept].show()
+
+ def getTrackIntervalList(self, toon, track, origSkill, earnedSkill, hasUber, guestWaste = 0):
+ if hasUber < 0:
+ print (toon.doId, 'Reward Panel received an invalid hasUber from an uberList')
+ tickDelay = 1.0 / 60
+ intervalList = []
+ if origSkill + earnedSkill >= ToontownBattleGlobals.UnpaidMaxSkills[track] and toon.getGameAccess() != OTPGlobals.AccessFull:
+ lostExp = origSkill + earnedSkill - ToontownBattleGlobals.UnpaidMaxSkills[track]
+ intervalList.append(Func(self.showTrackIncLabel, track, lostExp, 1))
+ else:
+ intervalList.append(Func(self.showTrackIncLabel, track, earnedSkill))
+ barTime = 0.5
+ numTicks = int(math.ceil(barTime / tickDelay))
+ for i in range(numTicks):
+ t = (i + 1) / float(numTicks)
+ newValue = int(origSkill + t * earnedSkill + 0.5)
+ intervalList.append(Func(self.incrementExp, track, newValue, toon))
+ intervalList.append(Wait(tickDelay))
+
+ intervalList.append(Func(self.resetBarColor, track))
+ intervalList.append(Wait(0.1))
+ nextExpValue = self.getNextExpValue(origSkill, track)
+ finalGagFlag = 0
+ while origSkill + earnedSkill >= nextExpValue and origSkill < nextExpValue and not finalGagFlag:
+ if newValue >= ToontownBattleGlobals.UnpaidMaxSkills[track] and toon.getGameAccess() != OTPGlobals.AccessFull:
+ pass
+ elif nextExpValue != ToontownBattleGlobals.MaxSkill:
+ intervalList += self.getNewGagIntervalList(toon, track, ToontownBattleGlobals.Levels[track].index(nextExpValue))
+ newNextExpValue = self.getNextExpValue(nextExpValue, track)
+ if newNextExpValue == nextExpValue:
+ finalGagFlag = 1
+ else:
+ nextExpValue = newNextExpValue
+
+ uberIndex = ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1
+ currentSkill = origSkill + earnedSkill
+ uberSkill = ToontownBattleGlobals.UberSkill + ToontownBattleGlobals.Levels[track][ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1]
+ if currentSkill >= uberSkill and not hasUber > 0:
+ intervalList += self.getUberGagIntervalList(toon, track, ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1)
+ intervalList.append(Wait(0.1))
+ skillDiff = currentSkill - ToontownBattleGlobals.Levels[track][ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1]
+ barTime = math.log(skillDiff + 1)
+ numTicks = int(math.ceil(barTime / tickDelay))
+ displayedSkillDiff = skillDiff
+ if displayedSkillDiff > ToontownBattleGlobals.UberSkill:
+ displayedSkillDiff = ToontownBattleGlobals.UberSkill
+ intervalList.append(Func(self.showTrackIncLabel, track, -displayedSkillDiff))
+ for i in range(numTicks):
+ t = (i + 1) / float(numTicks)
+ newValue = int(currentSkill - t * skillDiff + 0.5)
+ intervalList.append(Func(self.incrementExp, track, newValue, toon))
+ intervalList.append(Wait(tickDelay * 0.5))
+
+ intervalList.append(Wait(0.1))
+ return intervalList
+
+ def getMeritIntervalList(self, toon, dept, origMerits, earnedMerits):
+ tickDelay = 1.0 / 60
+ intervalList = []
+ totalMerits = CogDisguiseGlobals.getTotalMerits(toon, dept)
+ neededMerits = 0
+ if totalMerits and origMerits != totalMerits:
+ neededMerits = totalMerits - origMerits
+ intervalList.append(Func(self.showMeritIncLabel, dept, min(neededMerits, earnedMerits)))
+ barTime = 0.5
+ numTicks = int(math.ceil(barTime / tickDelay))
+ for i in range(numTicks):
+ t = (i + 1) / float(numTicks)
+ newValue = int(origMerits + t * earnedMerits + 0.5)
+ intervalList.append(Func(self.incrementMerits, toon, dept, newValue, totalMerits))
+ intervalList.append(Wait(tickDelay))
+
+ intervalList.append(Func(self.resetMeritBarColor, dept))
+ intervalList.append(Wait(0.1))
+ if toon.cogLevels[dept] < ToontownGlobals.MaxCogSuitLevel:
+ if neededMerits and toon.readyForPromotion(dept):
+ intervalList.append(Wait(0.4))
+ intervalList += self.getPromotionIntervalList(toon, dept)
+ return intervalList
+
+ def promotion(self, toon, dept):
+ self.endTrackFrame.hide()
+ self.gagExpFrame.hide()
+ self.newGagFrame.hide()
+ self.promotionFrame.show()
+ self.questFrame.hide()
+ self.itemFrame.hide()
+ self.missedItemFrame.hide()
+ name = SuitDNA.suitDepts[dept]
+ self.promotionFrame['text'] = TTLocalizer.RewardPanelPromotion % SuitDNA.suitDeptFullnames[name]
+ icons = loader.loadModel('phase_3/models/gui/cog_icons')
+ if dept == 0:
+ self.deptIcon = icons.find('**/CorpIcon').copyTo(self.promotionFrame)
+ elif dept == 1:
+ self.deptIcon = icons.find('**/LegalIcon').copyTo(self.promotionFrame)
+ elif dept == 2:
+ self.deptIcon = icons.find('**/MoneyIcon').copyTo(self.promotionFrame)
+ elif dept == 3:
+ self.deptIcon = icons.find('**/SalesIcon').copyTo(self.promotionFrame)
+ icons.removeNode()
+ self.deptIcon.setPos(0, 0, -0.225)
+ self.deptIcon.setScale(0.33)
+
+ def cleanupPromotion(self):
+ if not hasattr(self, 'deptIcon'):
+ return
+ self.deptIcon.removeNode()
+ self.deptIcon = None
+ self.endTrackFrame.hide()
+ self.gagExpFrame.show()
+ self.newGagFrame.hide()
+ self.promotionFrame.hide()
+ self.questFrame.hide()
+ self.itemFrame.hide()
+ self.missedItemFrame.hide()
+ return
+
+ def getPromotionIntervalList(self, toon, dept):
+ finalDelay = 2.0
+ intervalList = [Func(self.promotion, toon, dept), Wait(finalDelay), Func(self.cleanupPromotion)]
+ return intervalList
+
+ def getQuestIntervalList(self, toon, deathList, toonList, origQuestsList, itemList, helpfulToonsList = []):
+ avId = toon.getDoId()
+ tickDelay = 0.2
+ intervalList = []
+ toonShortList = []
+ for t in toonList:
+ if t is not None:
+ toonShortList.append(t)
+
+ cogList = []
+ for i in range(0, len(deathList), 4):
+ cogIndex = deathList[i]
+ cogLevel = deathList[i + 1]
+ activeToonBits = deathList[i + 2]
+ flags = deathList[i + 3]
+ activeToonIds = []
+ for j in range(8):
+ if activeToonBits & 1 << j:
+ if toonList[j] is not None:
+ activeToonIds.append(toonList[j].getDoId())
+
+ isSkelecog = flags & ToontownBattleGlobals.DLF_SKELECOG
+ isForeman = flags & ToontownBattleGlobals.DLF_FOREMAN
+ isVP = flags & ToontownBattleGlobals.DLF_VP
+ isCFO = flags & ToontownBattleGlobals.DLF_CFO
+ isSupervisor = flags & ToontownBattleGlobals.DLF_SUPERVISOR
+ isVirtual = flags & ToontownBattleGlobals.DLF_VIRTUAL
+ hasRevives = flags & ToontownBattleGlobals.DLF_REVIVES
+ if isVP or isCFO:
+ cogType = None
+ cogTrack = SuitDNA.suitDepts[cogIndex]
+ else:
+ cogType = SuitDNA.suitHeadTypes[cogIndex]
+ cogTrack = SuitDNA.getSuitDept(cogType)
+ cogList.append({'type': cogType,
+ 'level': cogLevel,
+ 'track': cogTrack,
+ 'isSkelecog': isSkelecog,
+ 'isForeman': isForeman,
+ 'isVP': isVP,
+ 'isCFO': isCFO,
+ 'isSupervisor': isSupervisor,
+ 'isVirtual': isVirtual,
+ 'hasRevives': hasRevives,
+ 'activeToons': activeToonIds})
+
+ try:
+ zoneId = base.cr.playGame.getPlace().getTaskZoneId()
+ except:
+ zoneId = 0
+
+ avQuests = []
+ for i in range(0, len(origQuestsList), 5):
+ avQuests.append(origQuestsList[i:i + 5])
+
+ for i in range(len(avQuests)):
+ questDesc = avQuests[i]
+ questId, npcId, toNpcId, rewardId, toonProgress = questDesc
+ quest = Quests.getQuest(questId)
+ if quest and i < len(self.questLabelList):
+ questString = quest.getString()
+ progressString = quest.getProgressString(toon, questDesc)
+ questLabel = self.questLabelList[i]
+ earned = 0
+ orig = questDesc[4] & pow(2, 16) - 1
+ num = 0
+ if quest.getType() == Quests.RecoverItemQuest:
+ questItem = quest.getItem()
+ if questItem in itemList:
+ earned = itemList.count(questItem)
+ else:
+ for cogDict in cogList:
+ if cogDict['isVP']:
+ num = quest.doesVPCount(avId, cogDict, zoneId, toonShortList)
+ elif cogDict['isCFO']:
+ num = quest.doesCFOCount(avId, cogDict, zoneId, toonShortList)
+ else:
+ num = quest.doesCogCount(avId, cogDict, zoneId, toonShortList)
+ if num:
+ if base.config.GetBool('battle-passing-no-credit', True):
+ if avId in helpfulToonsList:
+ earned += num
+ else:
+ self.notify.debug('avId=%d not getting %d kill cog quest credit' % (avId, num))
+ else:
+ earned += num
+
+ if base.localAvatar.tutorialAck:
+ if earned > 0:
+ earned = min(earned, quest.getNumQuestItems() - questDesc[4])
+ if earned > 0 or base.localAvatar.tutorialAck == 0 and num == 1:
+ barTime = 0.5
+ numTicks = int(math.ceil(barTime / tickDelay))
+ for i in range(numTicks):
+ t = (i + 1) / float(numTicks)
+ newValue = int(orig + t * earned + 0.5)
+ questDesc[4] = newValue
+ progressString = quest.getProgressString(toon, questDesc)
+ str = '%s : %s' % (questString, progressString)
+ if quest.getCompletionStatus(toon, questDesc) == Quests.COMPLETE:
+ intervalList.append(Func(questLabel.setProp, 'text_fg', (0, 0.3, 0, 1)))
+ intervalList.append(Func(questLabel.setProp, 'text', str))
+ intervalList.append(Wait(tickDelay))
+
+ return intervalList
+
+ def getItemIntervalList(self, toon, itemList):
+ intervalList = []
+ for itemId in itemList:
+ itemName = Quests.getItemName(itemId)
+ intervalList.append(Func(self.itemLabel.setProp, 'text', itemName))
+ intervalList.append(Wait(1))
+
+ return intervalList
+
+ def getCogPartIntervalList(self, toon, cogPartList):
+ itemName = CogDisguiseGlobals.getPartName(cogPartList)
+ intervalList = []
+ intervalList.append(Func(self.cogPartLabel.setProp, 'text', itemName))
+ intervalList.append(Wait(1))
+ return intervalList
+
+ def getMissedItemIntervalList(self, toon, missedItemList):
+ intervalList = []
+ for itemId in missedItemList:
+ itemName = Quests.getItemName(itemId)
+ intervalList.append(Func(self.missedItemLabel.setProp, 'text', itemName))
+ intervalList.append(Wait(1))
+
+ return intervalList
+
+ def getExpTrack(self, toon, origExp, earnedExp, deathList, origQuestsList, itemList, missedItemList, origMeritList, meritList, partList, toonList, uberEntry, helpfulToonsList, noSkip = False):
+ track = Sequence(Func(self.initGagFrame, toon, origExp, origMeritList, noSkip=noSkip), Wait(1.0))
+ endTracks = [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0]
+ trackEnded = 0
+ for trackIndex in range(len(earnedExp)):
+ if earnedExp[trackIndex] > 0 or origExp[trackIndex] >= ToontownBattleGlobals.MaxSkill:
+ track += self.getTrackIntervalList(toon, trackIndex, origExp[trackIndex], earnedExp[trackIndex], ToontownBattleGlobals.getUberFlagSafe(uberEntry, trackIndex))
+ maxExp = ToontownBattleGlobals.MaxSkill - ToontownBattleGlobals.UberSkill
+ if origExp[trackIndex] < maxExp and earnedExp[trackIndex] + origExp[trackIndex] >= maxExp:
+ endTracks[trackIndex] = 1
+ trackEnded = 1
+
+ for dept in range(len(SuitDNA.suitDepts)):
+ if meritList[dept]:
+ track += self.getMeritIntervalList(toon, dept, origMeritList[dept], meritList[dept])
+
+ track.append(Wait(0.75))
+ itemInterval = self.getItemIntervalList(toon, itemList)
+ if itemInterval:
+ track.append(Func(self.initItemFrame, toon))
+ track.append(Wait(0.25))
+ track += itemInterval
+ track.append(Wait(0.5))
+ missedItemInterval = self.getMissedItemIntervalList(toon, missedItemList)
+ if missedItemInterval:
+ track.append(Func(self.initMissedItemFrame, toon))
+ track.append(Wait(0.25))
+ track += missedItemInterval
+ track.append(Wait(0.5))
+ self.notify.debug('partList = %s' % partList)
+ newPart = 0
+ for part in partList:
+ if part != 0:
+ newPart = 1
+ break
+
+ if newPart:
+ partList = self.getCogPartIntervalList(toon, partList)
+ if partList:
+ track.append(Func(self.initCogPartFrame, toon))
+ track.append(Wait(0.25))
+ track += partList
+ track.append(Wait(0.5))
+ questList = self.getQuestIntervalList(toon, deathList, toonList, origQuestsList, itemList, helpfulToonsList)
+ if questList:
+ avQuests = []
+ for i in range(0, len(origQuestsList), 5):
+ avQuests.append(origQuestsList[i:i + 5])
+
+ track.append(Func(self.initQuestFrame, toon, copy.deepcopy(avQuests)))
+ track.append(Wait(0.25))
+ track += questList
+ track.append(Wait(0.5))
+ track.append(Wait(0.25))
+ if trackEnded:
+ track.append(Func(self.vanishFrames))
+ track.append(Fanfare.makeFanfare(0, toon)[0])
+ for i in range(len(endTracks)):
+ if endTracks[i] is 1:
+ track += self.getEndTrackIntervalList(toon, toonList, i)
+
+ track.append(Func(self.cleanupEndTrack))
+ return track
+
+ def testMovie(self, otherToons = []):
+ track = Sequence()
+ track.append(Func(self.show))
+ expTrack = self.getExpTrack(base.localAvatar, [1999,
+ 0,
+ 20,
+ 30,
+ 10,
+ 0,
+ 60], [2,
+ 0,
+ 2,
+ 6,
+ 1,
+ 0,
+ 8], [3,
+ 1,
+ 3,
+ 0,
+ 2,
+ 2,
+ 1,
+ 1,
+ 30,
+ 2,
+ 1,
+ 0], [], [], [], [0,
+ 0,
+ 0,
+ 0], [0,
+ 0,
+ 0,
+ 0], [], [base.localAvatar] + otherToons)
+ track.append(expTrack)
+ if len(track) > 0:
+ track.append(Func(self.hide))
+ track.append(Func(base.localAvatar.loop, 'neutral'))
+ track.append(Func(base.localAvatar.startUpdateSmartCamera))
+ track.start()
+ base.localAvatar.loop('victory')
+ base.localAvatar.stopUpdateSmartCamera()
+ camera.setPosHpr(0, 8, base.localAvatar.getHeight() * 0.66, 179, 15, 0)
+ else:
+ self.notify.debug('no experience, no movie.')
+ return None
+
+ def _handleSkip(self):
+ messenger.send(self.SkipBattleMovieEvent)
diff --git a/toontown/battle/SuitBattleGlobals.py b/toontown/battle/SuitBattleGlobals.py
new file mode 100644
index 0000000..ec0b656
--- /dev/null
+++ b/toontown/battle/SuitBattleGlobals.py
@@ -0,0 +1,3062 @@
+from BattleBase import *
+import random
+from direct.directnotify import DirectNotifyGlobal
+from otp.otpbase import OTPLocalizer
+from toontown.toonbase import TTLocalizer
+notify = DirectNotifyGlobal.directNotify.newCategory('SuitBattleGlobals')
+debugAttackSequence = {}
+
+def pickFromFreqList(freqList):
+ randNum = random.randint(0, 99)
+ count = 0
+ index = 0
+ level = None
+ for f in freqList:
+ count = count + f
+ if randNum < count:
+ level = index
+ break
+ index = index + 1
+
+ return level
+
+
+def getActualFromRelativeLevel(name, relLevel):
+ data = SuitAttributes[name]
+ actualLevel = data['level'] + relLevel
+ return actualLevel
+
+
+def getSuitVitals(name, level = -1):
+ data = SuitAttributes[name]
+ if level == -1:
+ level = pickFromFreqList(data['freq'])
+ dict = {}
+ dict['level'] = getActualFromRelativeLevel(name, level)
+ if dict['level'] == 11:
+ level = 0
+ dict['hp'] = data['hp'][level]
+ dict['def'] = data['def'][level]
+ attacks = data['attacks']
+ alist = []
+ for a in attacks:
+ adict = {}
+ name = a[0]
+ adict['name'] = name
+ adict['animName'] = SuitAttacks[name][0]
+ adict['hp'] = a[1][level]
+ adict['acc'] = a[2][level]
+ adict['freq'] = a[3][level]
+ adict['group'] = SuitAttacks[name][1]
+ alist.append(adict)
+
+ dict['attacks'] = alist
+ return dict
+
+
+def pickSuitAttack(attacks, suitLevel):
+ attackNum = None
+ randNum = random.randint(0, 99)
+ notify.debug('pickSuitAttack: rolled %d' % randNum)
+ count = 0
+ index = 0
+ total = 0
+ for c in attacks:
+ total = total + c[3][suitLevel]
+
+ for c in attacks:
+ count = count + c[3][suitLevel]
+ if randNum < count:
+ attackNum = index
+ notify.debug('picking attack %d' % attackNum)
+ break
+ index = index + 1
+
+ configAttackName = simbase.config.GetString('attack-type', 'random')
+ if configAttackName == 'random':
+ return attackNum
+ elif configAttackName == 'sequence':
+ for i in range(len(attacks)):
+ if not debugAttackSequence.has_key(attacks[i]):
+ debugAttackSequence[attacks[i]] = 1
+ return i
+
+ return attackNum
+ else:
+ for i in range(len(attacks)):
+ if attacks[i][0] == configAttackName:
+ return i
+
+ return attackNum
+ return
+
+
+def getSuitAttack(suitName, suitLevel, attackNum = -1):
+ attackChoices = SuitAttributes[suitName]['attacks']
+ if attackNum == -1:
+ notify.debug('getSuitAttack: picking attacking for %s' % suitName)
+ attackNum = pickSuitAttack(attackChoices, suitLevel)
+ attack = attackChoices[attackNum]
+ adict = {}
+ adict['suitName'] = suitName
+ name = attack[0]
+ adict['name'] = name
+ adict['id'] = SuitAttacks.keys().index(name)
+ adict['animName'] = SuitAttacks[name][0]
+ adict['hp'] = attack[1][suitLevel]
+ adict['acc'] = attack[2][suitLevel]
+ adict['freq'] = attack[3][suitLevel]
+ adict['group'] = SuitAttacks[name][1]
+ return adict
+
+
+SuitAttributes = {'f': {'name': TTLocalizer.SuitFlunky,
+ 'singularname': TTLocalizer.SuitFlunkyS,
+ 'pluralname': TTLocalizer.SuitFlunkyP,
+ 'level': 0,
+ 'hp': (6,
+ 12,
+ 20,
+ 30,
+ 42),
+ 'def': (2,
+ 5,
+ 10,
+ 12,
+ 15),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('PoundKey',
+ (2,
+ 2,
+ 3,
+ 4,
+ 6),
+ (75,
+ 75,
+ 80,
+ 80,
+ 90),
+ (30,
+ 35,
+ 40,
+ 45,
+ 50)), ('Shred',
+ (3,
+ 4,
+ 5,
+ 6,
+ 7),
+ (50,
+ 55,
+ 60,
+ 65,
+ 70),
+ (10,
+ 15,
+ 20,
+ 25,
+ 30)), ('ClipOnTie',
+ (1,
+ 1,
+ 2,
+ 2,
+ 3),
+ (75,
+ 80,
+ 85,
+ 90,
+ 95),
+ (60,
+ 50,
+ 40,
+ 30,
+ 20)))},
+ 'p': {'name': TTLocalizer.SuitPencilPusher,
+ 'singularname': TTLocalizer.SuitPencilPusherS,
+ 'pluralname': TTLocalizer.SuitPencilPusherP,
+ 'level': 1,
+ 'hp': (12,
+ 20,
+ 30,
+ 42,
+ 56),
+ 'def': (5,
+ 10,
+ 15,
+ 20,
+ 25),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (45,
+ 50,
+ 55,
+ 60,
+ 65),
+ 'attacks': (('FountainPen',
+ (2,
+ 3,
+ 4,
+ 6,
+ 9),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('RubOut',
+ (4,
+ 5,
+ 6,
+ 8,
+ 12),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('FingerWag',
+ (1,
+ 2,
+ 2,
+ 3,
+ 4),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (35,
+ 30,
+ 25,
+ 20,
+ 15)),
+ ('WriteOff',
+ (4,
+ 6,
+ 8,
+ 10,
+ 12),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (5,
+ 10,
+ 15,
+ 20,
+ 25)),
+ ('FillWithLead',
+ (3,
+ 4,
+ 5,
+ 6,
+ 7),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)))},
+ 'ym': {'name': TTLocalizer.SuitYesman,
+ 'singularname': TTLocalizer.SuitYesmanS,
+ 'pluralname': TTLocalizer.SuitYesmanP,
+ 'level': 2,
+ 'hp': (20,
+ 30,
+ 42,
+ 56,
+ 72),
+ 'def': (10,
+ 15,
+ 20,
+ 25,
+ 30),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (65,
+ 70,
+ 75,
+ 80,
+ 85),
+ 'attacks': (('RubberStamp',
+ (2,
+ 2,
+ 3,
+ 3,
+ 4),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)),
+ ('RazzleDazzle',
+ (1,
+ 1,
+ 1,
+ 1,
+ 1),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50),
+ (25,
+ 20,
+ 15,
+ 10,
+ 5)),
+ ('Synergy',
+ (4,
+ 5,
+ 6,
+ 7,
+ 8),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (5,
+ 10,
+ 15,
+ 20,
+ 25)),
+ ('TeeOff',
+ (3,
+ 3,
+ 4,
+ 4,
+ 5),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)))},
+ 'mm': {'name': TTLocalizer.SuitMicromanager,
+ 'singularname': TTLocalizer.SuitMicromanagerS,
+ 'pluralname': TTLocalizer.SuitMicromanagerP,
+ 'level': 3,
+ 'hp': (30,
+ 42,
+ 56,
+ 72,
+ 90),
+ 'def': (15,
+ 20,
+ 25,
+ 30,
+ 35),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (70,
+ 75,
+ 80,
+ 82,
+ 85),
+ 'attacks': (('Demotion',
+ (6,
+ 8,
+ 12,
+ 15,
+ 18),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)),
+ ('FingerWag',
+ (4,
+ 6,
+ 9,
+ 12,
+ 15),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (10,
+ 10,
+ 10,
+ 10,
+ 10)),
+ ('FountainPen',
+ (3,
+ 4,
+ 6,
+ 8,
+ 10),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('BrainStorm',
+ (4,
+ 6,
+ 9,
+ 12,
+ 15),
+ (5,
+ 5,
+ 5,
+ 5,
+ 5),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('BuzzWord',
+ (4,
+ 6,
+ 9,
+ 12,
+ 15),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)))},
+ 'ds': {'name': TTLocalizer.SuitDownsizer,
+ 'singularname': TTLocalizer.SuitDownsizerS,
+ 'pluralname': TTLocalizer.SuitDownsizerP,
+ 'level': 4,
+ 'hp': (42,
+ 56,
+ 72,
+ 90,
+ 110),
+ 'def': (20,
+ 25,
+ 30,
+ 35,
+ 40),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('Canned',
+ (5,
+ 6,
+ 8,
+ 10,
+ 12),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('Downsize',
+ (8,
+ 9,
+ 11,
+ 13,
+ 15),
+ (50,
+ 65,
+ 70,
+ 75,
+ 80),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)),
+ ('PinkSlip',
+ (4,
+ 5,
+ 6,
+ 7,
+ 8),
+ (60,
+ 65,
+ 75,
+ 80,
+ 85),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('Sacked',
+ (5,
+ 6,
+ 7,
+ 8,
+ 9),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)))},
+ 'hh': {'name': TTLocalizer.SuitHeadHunter,
+ 'singularname': TTLocalizer.SuitHeadHunterS,
+ 'pluralname': TTLocalizer.SuitHeadHunterP,
+ 'level': 5,
+ 'hp': (56,
+ 72,
+ 90,
+ 110,
+ 132),
+ 'def': (25,
+ 30,
+ 35,
+ 40,
+ 45),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('FountainPen',
+ (5,
+ 6,
+ 8,
+ 10,
+ 12),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('GlowerPower',
+ (7,
+ 8,
+ 10,
+ 12,
+ 13),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('HalfWindsor',
+ (8,
+ 10,
+ 12,
+ 14,
+ 16),
+ (60,
+ 65,
+ 70,
+ 75,
+ 80),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('HeadShrink',
+ (10,
+ 12,
+ 15,
+ 18,
+ 21),
+ (65,
+ 75,
+ 80,
+ 85,
+ 95),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)),
+ ('Rolodex',
+ (6,
+ 7,
+ 8,
+ 9,
+ 10),
+ (60,
+ 65,
+ 70,
+ 75,
+ 80),
+ (10,
+ 10,
+ 10,
+ 10,
+ 10)))},
+ 'cr': {'name': TTLocalizer.SuitCorporateRaider,
+ 'singularname': TTLocalizer.SuitCorporateRaiderS,
+ 'pluralname': TTLocalizer.SuitCorporateRaiderP,
+ 'level': 6,
+ 'hp': (72,
+ 90,
+ 110,
+ 132,
+ 156),
+ 'def': (30,
+ 35,
+ 40,
+ 45,
+ 50),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('Canned',
+ (6,
+ 7,
+ 8,
+ 9,
+ 10),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('EvilEye',
+ (12,
+ 15,
+ 18,
+ 21,
+ 24),
+ (60,
+ 70,
+ 75,
+ 80,
+ 90),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)),
+ ('PlayHardball',
+ (7,
+ 8,
+ 12,
+ 15,
+ 16),
+ (60,
+ 65,
+ 70,
+ 75,
+ 80),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)),
+ ('PowerTie',
+ (10,
+ 12,
+ 14,
+ 16,
+ 18),
+ (65,
+ 75,
+ 80,
+ 85,
+ 95),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)))},
+ 'tbc': {'name': TTLocalizer.SuitTheBigCheese,
+ 'singularname': TTLocalizer.SuitTheBigCheeseS,
+ 'pluralname': TTLocalizer.SuitTheBigCheeseP,
+ 'level': 7,
+ 'hp': (90,
+ 110,
+ 132,
+ 156,
+ 200),
+ 'def': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('CigarSmoke',
+ (10,
+ 12,
+ 15,
+ 18,
+ 20),
+ (55,
+ 65,
+ 75,
+ 85,
+ 95),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('FloodTheMarket',
+ (14,
+ 16,
+ 18,
+ 20,
+ 22),
+ (70,
+ 75,
+ 85,
+ 90,
+ 95),
+ (10,
+ 10,
+ 10,
+ 10,
+ 10)),
+ ('SongAndDance',
+ (14,
+ 15,
+ 17,
+ 19,
+ 20),
+ (60,
+ 65,
+ 70,
+ 75,
+ 80),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('TeeOff',
+ (8,
+ 11,
+ 14,
+ 17,
+ 20),
+ (55,
+ 65,
+ 70,
+ 75,
+ 80),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50)))},
+ 'cc': {'name': TTLocalizer.SuitColdCaller,
+ 'singularname': TTLocalizer.SuitColdCallerS,
+ 'pluralname': TTLocalizer.SuitColdCallerP,
+ 'level': 0,
+ 'hp': (6,
+ 12,
+ 20,
+ 30,
+ 42),
+ 'def': (2,
+ 5,
+ 10,
+ 12,
+ 15),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('FreezeAssets',
+ (1,
+ 1,
+ 1,
+ 1,
+ 1),
+ (90,
+ 90,
+ 90,
+ 90,
+ 90),
+ (5,
+ 10,
+ 15,
+ 20,
+ 25)),
+ ('PoundKey',
+ (2,
+ 2,
+ 3,
+ 4,
+ 5),
+ (75,
+ 80,
+ 85,
+ 90,
+ 95),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('DoubleTalk',
+ (2,
+ 3,
+ 4,
+ 6,
+ 8),
+ (50,
+ 55,
+ 60,
+ 65,
+ 70),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('HotAir',
+ (3,
+ 4,
+ 6,
+ 8,
+ 10),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50),
+ (45,
+ 40,
+ 35,
+ 30,
+ 25)))},
+ 'tm': {'name': TTLocalizer.SuitTelemarketer,
+ 'singularname': TTLocalizer.SuitTelemarketerS,
+ 'pluralname': TTLocalizer.SuitTelemarketerP,
+ 'level': 1,
+ 'hp': (12,
+ 20,
+ 30,
+ 42,
+ 56),
+ 'def': (5,
+ 10,
+ 15,
+ 20,
+ 25),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (45,
+ 50,
+ 55,
+ 60,
+ 65),
+ 'attacks': (('ClipOnTie',
+ (2,
+ 2,
+ 3,
+ 3,
+ 4),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('PickPocket',
+ (1,
+ 1,
+ 1,
+ 1,
+ 1),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('Rolodex',
+ (4,
+ 6,
+ 7,
+ 9,
+ 12),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)),
+ ('DoubleTalk',
+ (4,
+ 6,
+ 7,
+ 9,
+ 12),
+ (75,
+ 80,
+ 85,
+ 90,
+ 95),
+ (40,
+ 40,
+ 40,
+ 40,
+ 40)))},
+ 'nd': {'name': TTLocalizer.SuitNameDropper,
+ 'singularname': TTLocalizer.SuitNameDropperS,
+ 'pluralname': TTLocalizer.SuitNameDropperP,
+ 'level': 2,
+ 'hp': (20,
+ 30,
+ 42,
+ 56,
+ 72),
+ 'def': (10,
+ 15,
+ 20,
+ 25,
+ 30),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (65,
+ 70,
+ 75,
+ 80,
+ 85),
+ 'attacks': (('RazzleDazzle',
+ (4,
+ 5,
+ 6,
+ 9,
+ 12),
+ (75,
+ 80,
+ 85,
+ 90,
+ 95),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)),
+ ('Rolodex',
+ (5,
+ 6,
+ 7,
+ 10,
+ 14),
+ (95,
+ 95,
+ 95,
+ 95,
+ 95),
+ (40,
+ 40,
+ 40,
+ 40,
+ 40)),
+ ('Synergy',
+ (3,
+ 4,
+ 6,
+ 9,
+ 12),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('PickPocket',
+ (2,
+ 2,
+ 2,
+ 2,
+ 2),
+ (95,
+ 95,
+ 95,
+ 95,
+ 95),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)))},
+ 'gh': {'name': TTLocalizer.SuitGladHander,
+ 'singularname': TTLocalizer.SuitGladHanderS,
+ 'pluralname': TTLocalizer.SuitGladHanderP,
+ 'level': 3,
+ 'hp': (30,
+ 42,
+ 56,
+ 72,
+ 90),
+ 'def': (15,
+ 20,
+ 25,
+ 30,
+ 35),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (70,
+ 75,
+ 80,
+ 82,
+ 85),
+ 'attacks': (('RubberStamp',
+ (4,
+ 3,
+ 3,
+ 2,
+ 1),
+ (90,
+ 70,
+ 50,
+ 30,
+ 10),
+ (40,
+ 30,
+ 20,
+ 10,
+ 5)),
+ ('FountainPen',
+ (3,
+ 3,
+ 2,
+ 1,
+ 1),
+ (70,
+ 60,
+ 50,
+ 40,
+ 30),
+ (40,
+ 30,
+ 20,
+ 10,
+ 5)),
+ ('Filibuster',
+ (4,
+ 6,
+ 9,
+ 12,
+ 15),
+ (30,
+ 40,
+ 50,
+ 60,
+ 70),
+ (10,
+ 20,
+ 30,
+ 40,
+ 45)),
+ ('Schmooze',
+ (5,
+ 7,
+ 11,
+ 15,
+ 20),
+ (55,
+ 65,
+ 75,
+ 85,
+ 95),
+ (10,
+ 20,
+ 30,
+ 40,
+ 45)))},
+ 'ms': {'name': TTLocalizer.SuitMoverShaker,
+ 'singularname': TTLocalizer.SuitMoverShakerS,
+ 'pluralname': TTLocalizer.SuitMoverShakerP,
+ 'level': 4,
+ 'hp': (42,
+ 56,
+ 72,
+ 90,
+ 110),
+ 'def': (20,
+ 25,
+ 30,
+ 35,
+ 40),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('BrainStorm',
+ (5,
+ 6,
+ 8,
+ 10,
+ 12),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('HalfWindsor',
+ (6,
+ 9,
+ 11,
+ 13,
+ 16),
+ (50,
+ 65,
+ 70,
+ 75,
+ 80),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('Quake',
+ (9,
+ 12,
+ 15,
+ 18,
+ 21),
+ (60,
+ 65,
+ 75,
+ 80,
+ 85),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('Shake',
+ (6,
+ 8,
+ 10,
+ 12,
+ 14),
+ (70,
+ 75,
+ 80,
+ 85,
+ 90),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('Tremor',
+ (5,
+ 6,
+ 7,
+ 8,
+ 9),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)))},
+ 'tf': {'name': TTLocalizer.SuitTwoFace,
+ 'singularname': TTLocalizer.SuitTwoFaceS,
+ 'pluralname': TTLocalizer.SuitTwoFaceP,
+ 'level': 5,
+ 'hp': (56,
+ 72,
+ 90,
+ 110,
+ 132),
+ 'def': (25,
+ 30,
+ 35,
+ 40,
+ 45),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('EvilEye',
+ (10,
+ 12,
+ 14,
+ 16,
+ 18),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)),
+ ('HangUp',
+ (7,
+ 8,
+ 10,
+ 12,
+ 13),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('RazzleDazzle',
+ (8,
+ 10,
+ 12,
+ 14,
+ 16),
+ (60,
+ 65,
+ 70,
+ 75,
+ 80),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)),
+ ('RedTape',
+ (6,
+ 7,
+ 8,
+ 9,
+ 10),
+ (60,
+ 65,
+ 75,
+ 85,
+ 90),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)))},
+ 'm': {'name': TTLocalizer.SuitTheMingler,
+ 'singularname': TTLocalizer.SuitTheMinglerS,
+ 'pluralname': TTLocalizer.SuitTheMinglerP,
+ 'level': 6,
+ 'hp': (72,
+ 90,
+ 110,
+ 132,
+ 156),
+ 'def': (30,
+ 35,
+ 40,
+ 45,
+ 50),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('BuzzWord',
+ (10,
+ 11,
+ 13,
+ 15,
+ 16),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('ParadigmShift',
+ (12,
+ 15,
+ 18,
+ 21,
+ 24),
+ (60,
+ 70,
+ 75,
+ 80,
+ 90),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('PowerTrip',
+ (10,
+ 13,
+ 14,
+ 15,
+ 18),
+ (60,
+ 65,
+ 70,
+ 75,
+ 80),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('Schmooze',
+ (7,
+ 8,
+ 12,
+ 15,
+ 16),
+ (55,
+ 65,
+ 75,
+ 85,
+ 95),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)),
+ ('TeeOff',
+ (8,
+ 9,
+ 10,
+ 11,
+ 12),
+ (70,
+ 75,
+ 80,
+ 85,
+ 95),
+ (10,
+ 10,
+ 10,
+ 10,
+ 10)))},
+ 'mh': {'name': TTLocalizer.SuitMrHollywood,
+ 'singularname': TTLocalizer.SuitMrHollywoodS,
+ 'pluralname': TTLocalizer.SuitMrHollywoodP,
+ 'level': 7,
+ 'hp': (90,
+ 110,
+ 132,
+ 156,
+ 200),
+ 'def': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('PowerTrip',
+ (10,
+ 12,
+ 15,
+ 18,
+ 20),
+ (55,
+ 65,
+ 75,
+ 85,
+ 95),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50)), ('RazzleDazzle',
+ (8,
+ 11,
+ 14,
+ 17,
+ 20),
+ (70,
+ 75,
+ 85,
+ 90,
+ 95),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50)))},
+ 'sc': {'name': TTLocalizer.SuitShortChange,
+ 'singularname': TTLocalizer.SuitShortChangeS,
+ 'pluralname': TTLocalizer.SuitShortChangeP,
+ 'level': 0,
+ 'hp': (6,
+ 12,
+ 20,
+ 30,
+ 42),
+ 'def': (2,
+ 5,
+ 10,
+ 12,
+ 15),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('Watercooler',
+ (2,
+ 2,
+ 3,
+ 4,
+ 6),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('BounceCheck',
+ (3,
+ 5,
+ 7,
+ 9,
+ 11),
+ (75,
+ 80,
+ 85,
+ 90,
+ 95),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('ClipOnTie',
+ (1,
+ 1,
+ 2,
+ 2,
+ 3),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('PickPocket',
+ (2,
+ 2,
+ 3,
+ 4,
+ 6),
+ (95,
+ 95,
+ 95,
+ 95,
+ 95),
+ (40,
+ 40,
+ 40,
+ 40,
+ 40)))},
+ 'pp': {'name': TTLocalizer.SuitPennyPincher,
+ 'singularname': TTLocalizer.SuitPennyPincherS,
+ 'pluralname': TTLocalizer.SuitPennyPincherP,
+ 'level': 1,
+ 'hp': (12,
+ 20,
+ 30,
+ 42,
+ 56),
+ 'def': (5,
+ 10,
+ 15,
+ 20,
+ 25),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (45,
+ 50,
+ 55,
+ 60,
+ 65),
+ 'attacks': (('BounceCheck',
+ (4,
+ 5,
+ 6,
+ 8,
+ 12),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (45,
+ 45,
+ 45,
+ 45,
+ 45)), ('FreezeAssets',
+ (2,
+ 3,
+ 4,
+ 6,
+ 9),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)), ('FingerWag',
+ (1,
+ 2,
+ 3,
+ 4,
+ 6),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)))},
+ 'tw': {'name': TTLocalizer.SuitTightwad,
+ 'singularname': TTLocalizer.SuitTightwadS,
+ 'pluralname': TTLocalizer.SuitTightwadP,
+ 'level': 2,
+ 'hp': (20,
+ 30,
+ 42,
+ 56,
+ 72),
+ 'def': (10,
+ 15,
+ 20,
+ 25,
+ 30),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (65,
+ 70,
+ 75,
+ 80,
+ 85),
+ 'attacks': (('Fired',
+ (3,
+ 4,
+ 5,
+ 5,
+ 6),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (75,
+ 5,
+ 5,
+ 5,
+ 5)),
+ ('GlowerPower',
+ (3,
+ 4,
+ 6,
+ 9,
+ 12),
+ (95,
+ 95,
+ 95,
+ 95,
+ 95),
+ (10,
+ 15,
+ 20,
+ 25,
+ 30)),
+ ('FingerWag',
+ (3,
+ 3,
+ 4,
+ 4,
+ 5),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (5,
+ 70,
+ 5,
+ 5,
+ 5)),
+ ('FreezeAssets',
+ (3,
+ 4,
+ 6,
+ 9,
+ 12),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (5,
+ 5,
+ 65,
+ 5,
+ 30)),
+ ('BounceCheck',
+ (5,
+ 6,
+ 9,
+ 13,
+ 18),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (5,
+ 5,
+ 5,
+ 60,
+ 30)))},
+ 'bc': {'name': TTLocalizer.SuitBeanCounter,
+ 'singularname': TTLocalizer.SuitBeanCounterS,
+ 'pluralname': TTLocalizer.SuitBeanCounterP,
+ 'level': 3,
+ 'hp': (30,
+ 42,
+ 56,
+ 72,
+ 90),
+ 'def': (15,
+ 20,
+ 25,
+ 30,
+ 35),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (70,
+ 75,
+ 80,
+ 82,
+ 85),
+ 'attacks': (('Audit',
+ (4,
+ 6,
+ 9,
+ 12,
+ 15),
+ (95,
+ 95,
+ 95,
+ 95,
+ 95),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('Calculate',
+ (4,
+ 6,
+ 9,
+ 12,
+ 15),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('Tabulate',
+ (4,
+ 6,
+ 9,
+ 12,
+ 15),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('WriteOff',
+ (4,
+ 6,
+ 9,
+ 12,
+ 15),
+ (95,
+ 95,
+ 95,
+ 95,
+ 95),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)))},
+ 'nc': {'name': TTLocalizer.SuitNumberCruncher,
+ 'singularname': TTLocalizer.SuitNumberCruncherS,
+ 'pluralname': TTLocalizer.SuitNumberCruncherP,
+ 'level': 4,
+ 'hp': (42,
+ 56,
+ 72,
+ 90,
+ 110),
+ 'def': (20,
+ 25,
+ 30,
+ 35,
+ 40),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('Audit',
+ (5,
+ 6,
+ 8,
+ 10,
+ 12),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('Calculate',
+ (6,
+ 7,
+ 9,
+ 11,
+ 13),
+ (50,
+ 65,
+ 70,
+ 75,
+ 80),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)),
+ ('Crunch',
+ (8,
+ 9,
+ 11,
+ 13,
+ 15),
+ (60,
+ 65,
+ 75,
+ 80,
+ 85),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)),
+ ('Tabulate',
+ (5,
+ 6,
+ 7,
+ 8,
+ 9),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)))},
+ 'mb': {'name': TTLocalizer.SuitMoneyBags,
+ 'singularname': TTLocalizer.SuitMoneyBagsS,
+ 'pluralname': TTLocalizer.SuitMoneyBagsP,
+ 'level': 5,
+ 'hp': (56,
+ 72,
+ 90,
+ 110,
+ 132),
+ 'def': (25,
+ 30,
+ 35,
+ 40,
+ 45),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('Liquidate',
+ (10,
+ 12,
+ 14,
+ 16,
+ 18),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)), ('MarketCrash',
+ (8,
+ 10,
+ 12,
+ 14,
+ 16),
+ (60,
+ 65,
+ 70,
+ 75,
+ 80),
+ (45,
+ 45,
+ 45,
+ 45,
+ 45)), ('PowerTie',
+ (6,
+ 7,
+ 8,
+ 9,
+ 10),
+ (60,
+ 65,
+ 75,
+ 85,
+ 90),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)))},
+ 'ls': {'name': TTLocalizer.SuitLoanShark,
+ 'singularname': TTLocalizer.SuitLoanSharkS,
+ 'pluralname': TTLocalizer.SuitLoanSharkP,
+ 'level': 6,
+ 'hp': (72,
+ 90,
+ 110,
+ 132,
+ 156),
+ 'def': (30,
+ 35,
+ 40,
+ 45,
+ 50),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('Bite',
+ (10,
+ 11,
+ 13,
+ 15,
+ 16),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)),
+ ('Chomp',
+ (12,
+ 15,
+ 18,
+ 21,
+ 24),
+ (60,
+ 70,
+ 75,
+ 80,
+ 90),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)),
+ ('PlayHardball',
+ (9,
+ 11,
+ 12,
+ 13,
+ 15),
+ (55,
+ 65,
+ 75,
+ 85,
+ 95),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('WriteOff',
+ (6,
+ 8,
+ 10,
+ 12,
+ 14),
+ (70,
+ 75,
+ 80,
+ 85,
+ 95),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)))},
+ 'rb': {'name': TTLocalizer.SuitRobberBaron,
+ 'singularname': TTLocalizer.SuitRobberBaronS,
+ 'pluralname': TTLocalizer.SuitRobberBaronP,
+ 'level': 7,
+ 'hp': (90,
+ 110,
+ 132,
+ 156,
+ 200),
+ 'def': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('PowerTrip',
+ (11,
+ 14,
+ 16,
+ 18,
+ 21),
+ (60,
+ 65,
+ 70,
+ 75,
+ 80),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50)), ('TeeOff',
+ (10,
+ 12,
+ 14,
+ 16,
+ 18),
+ (60,
+ 65,
+ 75,
+ 85,
+ 90),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50)))},
+ 'bf': {'name': TTLocalizer.SuitBottomFeeder,
+ 'singularname': TTLocalizer.SuitBottomFeederS,
+ 'pluralname': TTLocalizer.SuitBottomFeederP,
+ 'level': 0,
+ 'hp': (6,
+ 12,
+ 20,
+ 30,
+ 42),
+ 'def': (2,
+ 5,
+ 10,
+ 12,
+ 15),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('RubberStamp',
+ (2,
+ 3,
+ 4,
+ 5,
+ 6),
+ (75,
+ 80,
+ 85,
+ 90,
+ 95),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('Shred',
+ (2,
+ 4,
+ 6,
+ 8,
+ 10),
+ (50,
+ 55,
+ 60,
+ 65,
+ 70),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('Watercooler',
+ (3,
+ 4,
+ 5,
+ 6,
+ 7),
+ (95,
+ 95,
+ 95,
+ 95,
+ 95),
+ (10,
+ 10,
+ 10,
+ 10,
+ 10)),
+ ('PickPocket',
+ (1,
+ 1,
+ 2,
+ 2,
+ 3),
+ (25,
+ 30,
+ 35,
+ 40,
+ 45),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50)))},
+ 'b': {'name': TTLocalizer.SuitBloodsucker,
+ 'singularname': TTLocalizer.SuitBloodsuckerS,
+ 'pluralname': TTLocalizer.SuitBloodsuckerP,
+ 'level': 1,
+ 'hp': (12,
+ 20,
+ 30,
+ 42,
+ 56),
+ 'def': (5,
+ 10,
+ 15,
+ 20,
+ 25),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (45,
+ 50,
+ 55,
+ 60,
+ 65),
+ 'attacks': (('EvictionNotice',
+ (1,
+ 2,
+ 3,
+ 3,
+ 4),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('RedTape',
+ (2,
+ 3,
+ 4,
+ 6,
+ 9),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('Withdrawal',
+ (6,
+ 8,
+ 10,
+ 12,
+ 14),
+ (95,
+ 95,
+ 95,
+ 95,
+ 95),
+ (10,
+ 10,
+ 10,
+ 10,
+ 10)),
+ ('Liquidate',
+ (2,
+ 3,
+ 4,
+ 6,
+ 9),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50)))},
+ 'dt': {'name': TTLocalizer.SuitDoubleTalker,
+ 'singularname': TTLocalizer.SuitDoubleTalkerS,
+ 'pluralname': TTLocalizer.SuitDoubleTalkerP,
+ 'level': 2,
+ 'hp': (20,
+ 30,
+ 42,
+ 56,
+ 72),
+ 'def': (10,
+ 15,
+ 20,
+ 25,
+ 30),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (65,
+ 70,
+ 75,
+ 80,
+ 85),
+ 'attacks': (('RubberStamp',
+ (1,
+ 1,
+ 1,
+ 1,
+ 1),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (5,
+ 5,
+ 5,
+ 5,
+ 5)),
+ ('BounceCheck',
+ (1,
+ 1,
+ 1,
+ 1,
+ 1),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (5,
+ 5,
+ 5,
+ 5,
+ 5)),
+ ('BuzzWord',
+ (1,
+ 2,
+ 3,
+ 5,
+ 6),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('DoubleTalk',
+ (6,
+ 6,
+ 9,
+ 13,
+ 18),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('Jargon',
+ (3,
+ 4,
+ 6,
+ 9,
+ 12),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)),
+ ('MumboJumbo',
+ (3,
+ 4,
+ 6,
+ 9,
+ 12),
+ (50,
+ 60,
+ 70,
+ 80,
+ 90),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)))},
+ 'ac': {'name': TTLocalizer.SuitAmbulanceChaser,
+ 'singularname': TTLocalizer.SuitAmbulanceChaserS,
+ 'pluralname': TTLocalizer.SuitAmbulanceChaserP,
+ 'level': 3,
+ 'hp': (30,
+ 42,
+ 56,
+ 72,
+ 90),
+ 'def': (15,
+ 20,
+ 25,
+ 30,
+ 35),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (65,
+ 70,
+ 75,
+ 80,
+ 85),
+ 'attacks': (('Shake',
+ (4,
+ 6,
+ 9,
+ 12,
+ 15),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('RedTape',
+ (6,
+ 8,
+ 12,
+ 15,
+ 19),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)),
+ ('Rolodex',
+ (3,
+ 4,
+ 5,
+ 6,
+ 7),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('HangUp',
+ (2,
+ 3,
+ 4,
+ 5,
+ 6),
+ (75,
+ 75,
+ 75,
+ 75,
+ 75),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)))},
+ 'bs': {'name': TTLocalizer.SuitBackStabber,
+ 'singularname': TTLocalizer.SuitBackStabberS,
+ 'pluralname': TTLocalizer.SuitBackStabberP,
+ 'level': 4,
+ 'hp': (42,
+ 56,
+ 72,
+ 90,
+ 110),
+ 'def': (20,
+ 25,
+ 30,
+ 35,
+ 40),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('GuiltTrip',
+ (8,
+ 11,
+ 13,
+ 15,
+ 18),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (40,
+ 40,
+ 40,
+ 40,
+ 40)), ('RestrainingOrder',
+ (6,
+ 7,
+ 9,
+ 11,
+ 13),
+ (50,
+ 65,
+ 70,
+ 75,
+ 90),
+ (25,
+ 25,
+ 25,
+ 25,
+ 25)), ('FingerWag',
+ (5,
+ 6,
+ 7,
+ 8,
+ 9),
+ (50,
+ 55,
+ 65,
+ 75,
+ 80),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)))},
+ 'sd': {'name': TTLocalizer.SuitSpinDoctor,
+ 'singularname': TTLocalizer.SuitSpinDoctorS,
+ 'pluralname': TTLocalizer.SuitSpinDoctorP,
+ 'level': 5,
+ 'hp': (56,
+ 72,
+ 90,
+ 110,
+ 132),
+ 'def': (25,
+ 30,
+ 35,
+ 40,
+ 45),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('ParadigmShift',
+ (9,
+ 10,
+ 13,
+ 16,
+ 17),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)),
+ ('Quake',
+ (8,
+ 10,
+ 12,
+ 14,
+ 16),
+ (60,
+ 65,
+ 70,
+ 75,
+ 80),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('Spin',
+ (10,
+ 12,
+ 15,
+ 18,
+ 20),
+ (70,
+ 75,
+ 80,
+ 85,
+ 90),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)),
+ ('WriteOff',
+ (6,
+ 7,
+ 8,
+ 9,
+ 10),
+ (60,
+ 65,
+ 75,
+ 85,
+ 90),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)))},
+ 'le': {'name': TTLocalizer.SuitLegalEagle,
+ 'singularname': TTLocalizer.SuitLegalEagleS,
+ 'pluralname': TTLocalizer.SuitLegalEagleP,
+ 'level': 6,
+ 'hp': (72,
+ 90,
+ 110,
+ 132,
+ 156),
+ 'def': (30,
+ 35,
+ 40,
+ 45,
+ 50),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('EvilEye',
+ (10,
+ 11,
+ 13,
+ 15,
+ 16),
+ (60,
+ 75,
+ 80,
+ 85,
+ 90),
+ (20,
+ 20,
+ 20,
+ 20,
+ 20)),
+ ('Jargon',
+ (7,
+ 9,
+ 11,
+ 13,
+ 15),
+ (60,
+ 70,
+ 75,
+ 80,
+ 90),
+ (15,
+ 15,
+ 15,
+ 15,
+ 15)),
+ ('Legalese',
+ (11,
+ 13,
+ 16,
+ 19,
+ 21),
+ (55,
+ 65,
+ 75,
+ 85,
+ 95),
+ (35,
+ 35,
+ 35,
+ 35,
+ 35)),
+ ('PeckingOrder',
+ (12,
+ 15,
+ 17,
+ 19,
+ 22),
+ (70,
+ 75,
+ 80,
+ 85,
+ 95),
+ (30,
+ 30,
+ 30,
+ 30,
+ 30)))},
+ 'bw': {'name': TTLocalizer.SuitBigWig,
+ 'singularname': TTLocalizer.SuitBigWigS,
+ 'pluralname': TTLocalizer.SuitBigWigP,
+ 'level': 7,
+ 'hp': (90,
+ 110,
+ 132,
+ 156,
+ 200),
+ 'def': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'freq': (50,
+ 30,
+ 10,
+ 5,
+ 5),
+ 'acc': (35,
+ 40,
+ 45,
+ 50,
+ 55),
+ 'attacks': (('PowerTrip',
+ (10,
+ 11,
+ 13,
+ 15,
+ 16),
+ (75,
+ 80,
+ 85,
+ 90,
+ 95),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50)), ('ThrowBook',
+ (13,
+ 15,
+ 17,
+ 19,
+ 21),
+ (80,
+ 85,
+ 85,
+ 85,
+ 90),
+ (50,
+ 50,
+ 50,
+ 50,
+ 50)))}}
+ATK_TGT_UNKNOWN = 1
+ATK_TGT_SINGLE = 2
+ATK_TGT_GROUP = 3
+SuitAttacks = {'Audit': ('phone', ATK_TGT_SINGLE),
+ 'Bite': ('throw-paper', ATK_TGT_SINGLE),
+ 'BounceCheck': ('throw-paper', ATK_TGT_SINGLE),
+ 'BrainStorm': ('effort', ATK_TGT_SINGLE),
+ 'BuzzWord': ('speak', ATK_TGT_SINGLE),
+ 'Calculate': ('phone', ATK_TGT_SINGLE),
+ 'Canned': ('throw-paper', ATK_TGT_SINGLE),
+ 'Chomp': ('throw-paper', ATK_TGT_SINGLE),
+ 'CigarSmoke': ('cigar-smoke', ATK_TGT_SINGLE),
+ 'ClipOnTie': ('throw-paper', ATK_TGT_SINGLE),
+ 'Crunch': ('throw-object', ATK_TGT_SINGLE),
+ 'Demotion': ('magic1', ATK_TGT_SINGLE),
+ 'DoubleTalk': ('speak', ATK_TGT_SINGLE),
+ 'Downsize': ('magic2', ATK_TGT_SINGLE),
+ 'EvictionNotice': ('throw-paper', ATK_TGT_SINGLE),
+ 'EvilEye': ('glower', ATK_TGT_SINGLE),
+ 'Filibuster': ('speak', ATK_TGT_SINGLE),
+ 'FillWithLead': ('pencil-sharpener', ATK_TGT_SINGLE),
+ 'FingerWag': ('finger-wag', ATK_TGT_SINGLE),
+ 'Fired': ('magic2', ATK_TGT_SINGLE),
+ 'FiveOClockShadow': ('glower', ATK_TGT_SINGLE),
+ 'FloodTheMarket': ('glower', ATK_TGT_SINGLE),
+ 'FountainPen': ('pen-squirt', ATK_TGT_SINGLE),
+ 'FreezeAssets': ('glower', ATK_TGT_SINGLE),
+ 'Gavel': ('gavel', ATK_TGT_SINGLE),
+ 'GlowerPower': ('glower', ATK_TGT_SINGLE),
+ 'GuiltTrip': ('magic1', ATK_TGT_GROUP),
+ 'HalfWindsor': ('throw-paper', ATK_TGT_SINGLE),
+ 'HangUp': ('phone', ATK_TGT_SINGLE),
+ 'HeadShrink': ('magic1', ATK_TGT_SINGLE),
+ 'HotAir': ('speak', ATK_TGT_SINGLE),
+ 'Jargon': ('speak', ATK_TGT_SINGLE),
+ 'Legalese': ('speak', ATK_TGT_SINGLE),
+ 'Liquidate': ('magic1', ATK_TGT_SINGLE),
+ 'MarketCrash': ('throw-paper', ATK_TGT_SINGLE),
+ 'MumboJumbo': ('speak', ATK_TGT_SINGLE),
+ 'ParadigmShift': ('magic2', ATK_TGT_GROUP),
+ 'PeckingOrder': ('throw-object', ATK_TGT_SINGLE),
+ 'PickPocket': ('pickpocket', ATK_TGT_SINGLE),
+ 'PinkSlip': ('throw-paper', ATK_TGT_SINGLE),
+ 'PlayHardball': ('throw-paper', ATK_TGT_SINGLE),
+ 'PoundKey': ('phone', ATK_TGT_SINGLE),
+ 'PowerTie': ('throw-paper', ATK_TGT_SINGLE),
+ 'PowerTrip': ('magic1', ATK_TGT_GROUP),
+ 'Quake': ('quick-jump', ATK_TGT_GROUP),
+ 'RazzleDazzle': ('smile', ATK_TGT_SINGLE),
+ 'RedTape': ('throw-object', ATK_TGT_SINGLE),
+ 'ReOrg': ('magic3', ATK_TGT_SINGLE),
+ 'RestrainingOrder': ('throw-paper', ATK_TGT_SINGLE),
+ 'Rolodex': ('roll-o-dex', ATK_TGT_SINGLE),
+ 'RubberStamp': ('rubber-stamp', ATK_TGT_SINGLE),
+ 'RubOut': ('hold-eraser', ATK_TGT_SINGLE),
+ 'Sacked': ('throw-paper', ATK_TGT_SINGLE),
+ 'SandTrap': ('golf-club-swing', ATK_TGT_SINGLE),
+ 'Schmooze': ('speak', ATK_TGT_SINGLE),
+ 'Shake': ('stomp', ATK_TGT_GROUP),
+ 'Shred': ('shredder', ATK_TGT_SINGLE),
+ 'SongAndDance': ('song-and-dance', ATK_TGT_SINGLE),
+ 'Spin': ('magic3', ATK_TGT_SINGLE),
+ 'Synergy': ('magic3', ATK_TGT_GROUP),
+ 'Tabulate': ('phone', ATK_TGT_SINGLE),
+ 'TeeOff': ('golf-club-swing', ATK_TGT_SINGLE),
+ 'ThrowBook': ('throw-object', ATK_TGT_SINGLE),
+ 'Tremor': ('stomp', ATK_TGT_GROUP),
+ 'Watercooler': ('watercooler', ATK_TGT_SINGLE),
+ 'Withdrawal': ('magic1', ATK_TGT_SINGLE),
+ 'WriteOff': ('hold-pencil', ATK_TGT_SINGLE)}
+AUDIT = SuitAttacks.keys().index('Audit')
+BITE = SuitAttacks.keys().index('Bite')
+BOUNCE_CHECK = SuitAttacks.keys().index('BounceCheck')
+BRAIN_STORM = SuitAttacks.keys().index('BrainStorm')
+BUZZ_WORD = SuitAttacks.keys().index('BuzzWord')
+CALCULATE = SuitAttacks.keys().index('Calculate')
+CANNED = SuitAttacks.keys().index('Canned')
+CHOMP = SuitAttacks.keys().index('Chomp')
+CIGAR_SMOKE = SuitAttacks.keys().index('CigarSmoke')
+CLIPON_TIE = SuitAttacks.keys().index('ClipOnTie')
+CRUNCH = SuitAttacks.keys().index('Crunch')
+DEMOTION = SuitAttacks.keys().index('Demotion')
+DOWNSIZE = SuitAttacks.keys().index('Downsize')
+DOUBLE_TALK = SuitAttacks.keys().index('DoubleTalk')
+EVICTION_NOTICE = SuitAttacks.keys().index('EvictionNotice')
+EVIL_EYE = SuitAttacks.keys().index('EvilEye')
+FILIBUSTER = SuitAttacks.keys().index('Filibuster')
+FILL_WITH_LEAD = SuitAttacks.keys().index('FillWithLead')
+FINGER_WAG = SuitAttacks.keys().index('FingerWag')
+FIRED = SuitAttacks.keys().index('Fired')
+FIVE_O_CLOCK_SHADOW = SuitAttacks.keys().index('FiveOClockShadow')
+FLOOD_THE_MARKET = SuitAttacks.keys().index('FloodTheMarket')
+FOUNTAIN_PEN = SuitAttacks.keys().index('FountainPen')
+FREEZE_ASSETS = SuitAttacks.keys().index('FreezeAssets')
+GAVEL = SuitAttacks.keys().index('Gavel')
+GLOWER_POWER = SuitAttacks.keys().index('GlowerPower')
+GUILT_TRIP = SuitAttacks.keys().index('GuiltTrip')
+HALF_WINDSOR = SuitAttacks.keys().index('HalfWindsor')
+HANG_UP = SuitAttacks.keys().index('HangUp')
+HEAD_SHRINK = SuitAttacks.keys().index('HeadShrink')
+HOT_AIR = SuitAttacks.keys().index('HotAir')
+JARGON = SuitAttacks.keys().index('Jargon')
+LEGALESE = SuitAttacks.keys().index('Legalese')
+LIQUIDATE = SuitAttacks.keys().index('Liquidate')
+MARKET_CRASH = SuitAttacks.keys().index('MarketCrash')
+MUMBO_JUMBO = SuitAttacks.keys().index('MumboJumbo')
+PARADIGM_SHIFT = SuitAttacks.keys().index('ParadigmShift')
+PECKING_ORDER = SuitAttacks.keys().index('PeckingOrder')
+PICK_POCKET = SuitAttacks.keys().index('PickPocket')
+PINK_SLIP = SuitAttacks.keys().index('PinkSlip')
+PLAY_HARDBALL = SuitAttacks.keys().index('PlayHardball')
+POUND_KEY = SuitAttacks.keys().index('PoundKey')
+POWER_TIE = SuitAttacks.keys().index('PowerTie')
+POWER_TRIP = SuitAttacks.keys().index('PowerTrip')
+QUAKE = SuitAttacks.keys().index('Quake')
+RAZZLE_DAZZLE = SuitAttacks.keys().index('RazzleDazzle')
+RED_TAPE = SuitAttacks.keys().index('RedTape')
+RE_ORG = SuitAttacks.keys().index('ReOrg')
+RESTRAINING_ORDER = SuitAttacks.keys().index('RestrainingOrder')
+ROLODEX = SuitAttacks.keys().index('Rolodex')
+RUBBER_STAMP = SuitAttacks.keys().index('RubberStamp')
+RUB_OUT = SuitAttacks.keys().index('RubOut')
+SACKED = SuitAttacks.keys().index('Sacked')
+SANDTRAP = SuitAttacks.keys().index('SandTrap')
+SCHMOOZE = SuitAttacks.keys().index('Schmooze')
+SHAKE = SuitAttacks.keys().index('Shake')
+SHRED = SuitAttacks.keys().index('Shred')
+SONG_AND_DANCE = SuitAttacks.keys().index('SongAndDance')
+SPIN = SuitAttacks.keys().index('Spin')
+SYNERGY = SuitAttacks.keys().index('Synergy')
+TABULATE = SuitAttacks.keys().index('Tabulate')
+TEE_OFF = SuitAttacks.keys().index('TeeOff')
+THROW_BOOK = SuitAttacks.keys().index('ThrowBook')
+TREMOR = SuitAttacks.keys().index('Tremor')
+WATERCOOLER = SuitAttacks.keys().index('Watercooler')
+WITHDRAWAL = SuitAttacks.keys().index('Withdrawal')
+WRITE_OFF = SuitAttacks.keys().index('WriteOff')
+
+def getFaceoffTaunt(suitName, doId):
+ if SuitFaceoffTaunts.has_key(suitName):
+ taunts = SuitFaceoffTaunts[suitName]
+ else:
+ taunts = TTLocalizer.SuitFaceoffDefaultTaunts
+ return taunts[doId % len(taunts)]
+
+
+SuitFaceoffTaunts = OTPLocalizer.SuitFaceoffTaunts
+
+def getAttackTauntIndexFromIndex(suit, attackIndex):
+ adict = getSuitAttack(suit.getStyleName(), suit.getLevel(), attackIndex)
+ return getAttackTauntIndex(adict['name'])
+
+
+def getAttackTauntIndex(attackName):
+ if SuitAttackTaunts.has_key(attackName):
+ taunts = SuitAttackTaunts[attackName]
+ return random.randint(0, len(taunts) - 1)
+ else:
+ return 1
+
+
+def getAttackTaunt(attackName, index = None):
+ if SuitAttackTaunts.has_key(attackName):
+ taunts = SuitAttackTaunts[attackName]
+ else:
+ taunts = TTLocalizer.SuitAttackDefaultTaunts
+ if index != None:
+ if index >= len(taunts):
+ notify.warning('index exceeds length of taunts list in getAttackTaunt')
+ return TTLocalizer.SuitAttackDefaultTaunts[0]
+ return taunts[index]
+ else:
+ return random.choice(taunts)
+ return
+
+
+SuitAttackTaunts = TTLocalizer.SuitAttackTaunts
diff --git a/toontown/battle/__init__.py b/toontown/battle/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/toontown/building/BoardingGroupShow.py b/toontown/building/BoardingGroupShow.py
new file mode 100644
index 0000000..0e93714
--- /dev/null
+++ b/toontown/building/BoardingGroupShow.py
@@ -0,0 +1,220 @@
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownGlobals
+from direct.task.Task import Task
+from direct.interval.IntervalGlobal import *
+from toontown.toonbase import TTLocalizer
+from toontown.effects import DustCloud
+TRACK_TYPE_TELEPORT = 1
+TRACK_TYPE_RUN = 2
+TRACK_TYPE_POOF = 3
+
+class BoardingGroupShow:
+ notify = DirectNotifyGlobal.directNotify.newCategory('BoardingGroupShow')
+ thresholdRunDistance = 25.0
+
+ def __init__(self, toon):
+ self.toon = toon
+ self.avId = self.toon.doId
+ self.dustCloudIval = None
+ return
+
+ def cleanup(self):
+ if localAvatar.doId == self.avId:
+ self.__stopTimer()
+ self.clock.removeNode()
+
+ def startTimer(self):
+ self.clockNode = TextNode('elevatorClock')
+ self.clockNode.setFont(ToontownGlobals.getSignFont())
+ self.clockNode.setAlign(TextNode.ACenter)
+ self.clockNode.setTextColor(0.5, 0.5, 0.5, 1)
+ self.clockNode.setText(str(int(self.countdownDuration)))
+ self.clock = aspect2d.attachNewNode(self.clockNode)
+ self.clock.setPos(0, 0, -0.6)
+ self.clock.setScale(0.15, 0.15, 0.15)
+ self.__countdown(self.countdownDuration, self.__boardingElevatorTimerExpired)
+
+ def __countdown(self, duration, callback):
+ self.countdownTask = Task(self.__timerTask)
+ self.countdownTask.duration = duration
+ self.countdownTask.callback = callback
+ taskMgr.remove(self.uniqueName(self.avId))
+ return taskMgr.add(self.countdownTask, self.uniqueName(self.avId))
+
+ def __timerTask(self, task):
+ countdownTime = int(task.duration - task.time)
+ timeStr = self.timeWarningText + str(countdownTime)
+ if self.clockNode.getText() != timeStr:
+ self.clockNode.setText(timeStr)
+ if task.time >= task.duration:
+ if task.callback:
+ task.callback()
+ return Task.done
+ else:
+ return Task.cont
+
+ def __boardingElevatorTimerExpired(self):
+ self.notify.debug('__boardingElevatorTimerExpired')
+ self.clock.removeNode()
+
+ def __stopTimer(self):
+ if self.countdownTask:
+ self.countdownTask.callback = None
+ taskMgr.remove(self.countdownTask)
+ return
+
+ def uniqueName(self, avId):
+ uniqueName = 'boardingElevatorTimerTask-' + str(avId)
+ return uniqueName
+
+ def getBoardingTrack(self, elevatorModel, offset, offsetWrtRender, wantToonRotation):
+ self.timeWarningText = TTLocalizer.BoardingTimeWarning
+ self.countdownDuration = 6
+ trackType = TRACK_TYPE_TELEPORT
+ boardingTrack = Sequence()
+ if self.toon:
+ if self.avId == localAvatar.doId:
+ boardingTrack.append(Func(self.startTimer))
+ isInThresholdDist = self.__isInThresholdDist(elevatorModel, offset, self.thresholdRunDistance)
+ isRunPathClear = self.__isRunPathClear(elevatorModel, offsetWrtRender)
+ if isInThresholdDist and isRunPathClear:
+ boardingTrack.append(self.__getRunTrack(elevatorModel, offset, wantToonRotation))
+ trackType = TRACK_TYPE_RUN
+ elif self.toon.isDisguised:
+ boardingTrack.append(self.__getPoofTeleportTrack(elevatorModel, offset, wantToonRotation))
+ trackType = TRACK_TYPE_POOF
+ else:
+ boardingTrack.append(self.__getTeleportTrack(elevatorModel, offset, wantToonRotation))
+ boardingTrack.append(Func(self.cleanup))
+ return (boardingTrack, trackType)
+
+ def __getOffsetPos(self, elevatorModel, offset):
+ dest = elevatorModel.getPos(self.toon.getParent())
+ dest += Vec3(*offset)
+ return dest
+
+ def __getTeleportTrack(self, elevatorModel, offset, wantToonRotation):
+ teleportTrack = Sequence()
+ if self.toon:
+ if wantToonRotation:
+ teleportTrack.append(Func(self.toon.headsUp, elevatorModel, offset))
+ teleportTrack.append(Func(self.toon.setAnimState, 'TeleportOut'))
+ teleportTrack.append(Wait(3.5))
+ teleportTrack.append(Func(self.toon.setPos, Point3(offset)))
+ teleportTrack.append(Func(self.toon.setAnimState, 'TeleportIn'))
+ teleportTrack.append(Wait(1))
+ return teleportTrack
+
+ def __getPoofTeleportTrack(self, elevatorModel, offset, wantToonRotation):
+ teleportTrack = Sequence()
+ if wantToonRotation:
+ teleportTrack.append(Func(self.toon.headsUp, elevatorModel, offset))
+
+ def getDustCloudPos():
+ toonPos = self.toon.getPos(render)
+ return Point3(toonPos.getX(), toonPos.getY(), toonPos.getZ() + 3)
+
+ def cleanupDustCloudIval():
+ if self.dustCloudIval:
+ self.dustCloudIval.finish()
+ self.dustCloudIval = None
+ return
+
+ def getDustCloudIval():
+ cleanupDustCloudIval()
+ dustCloud = DustCloud.DustCloud(fBillboard=0, wantSound=1)
+ dustCloud.setBillboardAxis(2.0)
+ dustCloud.setZ(3)
+ dustCloud.setScale(0.4)
+ dustCloud.createTrack()
+ self.dustCloudIval = Sequence(Func(dustCloud.reparentTo, render), Func(dustCloud.setPos, getDustCloudPos()), dustCloud.track, Func(dustCloud.detachNode), Func(dustCloud.destroy), name='dustCloadIval')
+ self.dustCloudIval.start()
+
+ if self.toon:
+ teleportTrack.append(Func(self.toon.setAnimState, 'neutral'))
+ teleportTrack.append(Wait(0.5))
+ teleportTrack.append(Func(getDustCloudIval))
+ teleportTrack.append(Wait(0.25))
+ teleportTrack.append(Func(self.toon.hide))
+ teleportTrack.append(Wait(1.5))
+ teleportTrack.append(Func(self.toon.setPos, Point3(offset)))
+ teleportTrack.append(Func(getDustCloudIval))
+ teleportTrack.append(Wait(0.25))
+ teleportTrack.append(Func(self.toon.show))
+ teleportTrack.append(Wait(0.5))
+ teleportTrack.append(Func(cleanupDustCloudIval))
+ return teleportTrack
+
+ def __getRunTrack(self, elevatorModel, offset, wantToonRotation):
+ runTrack = Sequence()
+ if self.toon:
+ if wantToonRotation:
+ runTrack.append(Func(self.toon.headsUp, elevatorModel, offset))
+ if self.toon.isDisguised:
+ runTrack.append(Func(self.toon.suit.loop, 'walk'))
+ else:
+ runTrack.append(Func(self.toon.setAnimState, 'run'))
+ runTrack.append(LerpPosInterval(self.toon, 1, Point3(offset)))
+ return runTrack
+
+ def __isInThresholdDist(self, elevatorModel, offset, thresholdDist):
+ diff = Point3(offset) - self.toon.getPos()
+ if diff.length() > thresholdDist:
+ return False
+ else:
+ return True
+
+ def __isRunPathClear(self, elevatorModel, offsetWrtRender):
+ pathClear = True
+ source = self.toon.getPos(render)
+ dest = offsetWrtRender
+ collSegment = CollisionSegment(source[0], source[1], source[2], dest[0], dest[1], dest[2])
+ fromObject = render.attachNewNode(CollisionNode('runCollSegment'))
+ fromObject.node().addSolid(collSegment)
+ fromObject.node().setFromCollideMask(ToontownGlobals.WallBitmask)
+ fromObject.node().setIntoCollideMask(BitMask32.allOff())
+ queue = CollisionHandlerQueue()
+ base.cTrav.addCollider(fromObject, queue)
+ base.cTrav.traverse(render)
+ queue.sortEntries()
+ if queue.getNumEntries():
+ for entryNum in xrange(queue.getNumEntries()):
+ entry = queue.getEntry(entryNum)
+ hitObject = entry.getIntoNodePath()
+ if hitObject.getNetTag('pieCode') != '3':
+ pathClear = False
+
+ base.cTrav.removeCollider(fromObject)
+ fromObject.removeNode()
+ return pathClear
+
+ def getGoButtonShow(self, elevatorName):
+ self.elevatorName = elevatorName
+ self.timeWarningText = TTLocalizer.BoardingGoShow % self.elevatorName
+ self.countdownDuration = 4
+ goButtonShow = Sequence()
+ if self.toon:
+ if self.avId == localAvatar.doId:
+ goButtonShow.append(Func(self.startTimer))
+ goButtonShow.append(self.__getTeleportOutTrack())
+ goButtonShow.append(Wait(3))
+ goButtonShow.append(Func(self.cleanup))
+ return goButtonShow
+
+ def __getTeleportOutTrack(self):
+ teleportOutTrack = Sequence()
+ if self.toon and not self.toon.isDisguised:
+ teleportOutTrack.append(Func(self.toon.b_setAnimState, 'TeleportOut'))
+ return teleportOutTrack
+
+ def getGoButtonPreShow(self):
+ self.timeWarningText = TTLocalizer.BoardingGoPreShow
+ self.countdownDuration = 4
+ goButtonPreShow = Sequence()
+ if self.toon:
+ if self.avId == localAvatar.doId:
+ goButtonPreShow.append(Func(self.startTimer))
+ goButtonPreShow.append(Wait(3))
+ goButtonPreShow.append(Func(self.cleanup))
+ return goButtonPreShow
diff --git a/toontown/building/BoardingPartyBase.py b/toontown/building/BoardingPartyBase.py
new file mode 100644
index 0000000..6eac7da
--- /dev/null
+++ b/toontown/building/BoardingPartyBase.py
@@ -0,0 +1,110 @@
+from otp.otpbase import OTPGlobals
+from toontown.toonbase import ToontownGlobals
+import copy
+BOARDCODE_OKAY = 1
+BOARDCODE_MISSING = 0
+BOARDCODE_MINLAFF = -1
+BOARDCODE_PROMOTION = -2
+BOARDCODE_BATTLE = -3
+BOARDCODE_SPACE = -4
+BOARDCODE_NOT_PAID = -5
+BOARDCODE_DIFF_GROUP = -6
+BOARDCODE_PENDING_INVITE = -7
+BOARDCODE_IN_ELEVATOR = -8
+INVITE_ACCEPT_FAIL_GROUP_FULL = -1
+
+class BoardingPartyBase:
+
+ def __init__(self):
+ self.groupListDict = {}
+ self.avIdDict = {}
+
+ def cleanup(self):
+ del self.groupListDict
+ del self.avIdDict
+
+ def getGroupSize(self):
+ return self.maxSize
+
+ def setGroupSize(self, groupSize):
+ self.maxSize = groupSize
+
+ def getGroupLeader(self, avatarId):
+ if self.avIdDict.has_key(avatarId):
+ leaderId = self.avIdDict[avatarId]
+ return leaderId
+ else:
+ return None
+ return None
+
+ def isGroupLeader(self, avatarId):
+ leaderId = self.getGroupLeader(avatarId)
+ if avatarId == leaderId:
+ return True
+ else:
+ return False
+
+ def getGroupMemberList(self, avatarId):
+ if self.avIdDict.has_key(avatarId):
+ leaderId = self.avIdDict[avatarId]
+ group = self.groupListDict.get(leaderId)
+ if group:
+ returnList = copy.copy(group[0])
+ if 0 in returnList:
+ returnList.remove(0)
+ return returnList
+ return []
+
+ def getGroupInviteList(self, avatarId):
+ if self.avIdDict.has_key(avatarId):
+ leaderId = self.avIdDict[avatarId]
+ group = self.groupListDict.get(leaderId)
+ if group:
+ returnList = copy.copy(group[1])
+ if 0 in returnList:
+ returnList.remove(0)
+ return returnList
+ return []
+
+ def getGroupKickList(self, avatarId):
+ if self.avIdDict.has_key(avatarId):
+ leaderId = self.avIdDict[avatarId]
+ group = self.groupListDict.get(leaderId)
+ if group:
+ returnList = copy.copy(group[2])
+ if 0 in returnList:
+ returnList.remove(0)
+ return returnList
+ return []
+
+ def hasActiveGroup(self, avatarId):
+ memberList = self.getGroupMemberList(avatarId)
+ if avatarId in memberList:
+ if len(memberList) > 1:
+ return True
+ return False
+
+ def hasPendingInvite(self, avatarId):
+ pendingInvite = False
+ if self.avIdDict.has_key(avatarId):
+ leaderId = self.avIdDict[avatarId]
+ leaderInviteList = self.getGroupInviteList(leaderId)
+ if leaderId == avatarId:
+ if len(leaderInviteList):
+ pendingInvite = True
+ else:
+ pendingInvite = False
+ elif avatarId in leaderInviteList:
+ pendingInvite = True
+ else:
+ pendingInvite = False
+ if pendingInvite:
+ return True
+ else:
+ return False
+
+ def isInGroup(self, memberId, leaderId):
+ if memberId in self.getGroupMemberList(leaderId) or memberId in self.getGroupInviteList(leaderId):
+ return True
+ else:
+ return False
diff --git a/toontown/building/DistributedAnimBuilding.py b/toontown/building/DistributedAnimBuilding.py
new file mode 100644
index 0000000..f20dc55
--- /dev/null
+++ b/toontown/building/DistributedAnimBuilding.py
@@ -0,0 +1,50 @@
+from pandac.PandaModules import DecalEffect, DepthWriteAttrib
+from direct.directnotify import DirectNotifyGlobal
+from toontown.building import DistributedBuilding
+
+class DistributedAnimBuilding(DistributedBuilding.DistributedBuilding):
+
+ def __init__(self, cr):
+ DistributedBuilding.DistributedBuilding.__init__(self, cr)
+
+ def enterToon(self, ts):
+ DistributedBuilding.DistributedBuilding.enterToon(self, ts)
+ self.fixEffects()
+
+ def fixEffects(self):
+ nodes = self.getNodePaths()
+ for curNode in nodes:
+ mf = curNode.find('**/*mesh_front*')
+ sign_joint = curNode.find('**/sign_origin_joint')
+ if not sign_joint.isEmpty():
+ self.notify.debug('I found sign_origin_joint 1')
+ if not mf.isEmpty():
+ sign = mf.find('**/sign')
+ mf.clearEffect(DecalEffect.getClassType())
+ if not sign.isEmpty():
+ sign.setDepthWrite(1, 1)
+ sign.setEffect(DecalEffect.make())
+ sign_joint = curNode.find('**/sign_origin_joint')
+ allSignJoints = curNode.findAllMatches('**/sign_origin_joint')
+ num = allSignJoints.getNumPaths()
+ if num:
+ sign_joint = allSignJoints.getPath(num - 1)
+ if not sign_joint.isEmpty():
+ self.notify.debug('I found sign_origin_joint 2')
+ sign.wrtReparentTo(sign_joint)
+
+ def setupNametag(self):
+ if not self.wantsNametag():
+ return
+ DistributedBuilding.DistributedBuilding.setupNametag(self)
+
+ def getSbSearchString(self):
+ result = 'landmarkBlocks/sb' + str(self.block) + ':*animated_building_*_DNARoot'
+ return result
+
+ def adjustSbNodepathScale(self, nodePath):
+ nodePath.setScale(0.543667, 1, 1)
+
+ def animToToon(self, timeStamp):
+ DistributedBuilding.DistributedBuilding.animToToon(self, timeStamp)
+ self.fixEffects()
diff --git a/toontown/building/DistributedAnimBuildingAI.py b/toontown/building/DistributedAnimBuildingAI.py
new file mode 100644
index 0000000..08b973d
--- /dev/null
+++ b/toontown/building/DistributedAnimBuildingAI.py
@@ -0,0 +1,13 @@
+from direct.directnotify import DirectNotifyGlobal
+from toontown.building import DistributedBuildingAI
+from toontown.building import DistributedAnimDoorAI
+from toontown.building import DoorTypes
+
+class DistributedAnimBuildingAI(DistributedBuildingAI.DistributedBuildingAI):
+
+ def __init__(self, air, blockNumber, zoneId, trophyMgr):
+ DistributedBuildingAI.DistributedBuildingAI.__init__(self, air, blockNumber, zoneId, trophyMgr)
+
+ def createExteriorDoor(self):
+ result = DistributedAnimDoorAI.DistributedAnimDoorAI(self.air, self.block, DoorTypes.EXT_ANIM_STANDARD)
+ return result
diff --git a/toontown/building/DistributedAnimDoor.py b/toontown/building/DistributedAnimDoor.py
new file mode 100644
index 0000000..2c93313
--- /dev/null
+++ b/toontown/building/DistributedAnimDoor.py
@@ -0,0 +1,143 @@
+from pandac.PandaModules import NodePath, VBase3
+from direct.directnotify import DirectNotifyGlobal
+from direct.interval.IntervalGlobal import Parallel, Sequence, Wait, HprInterval, LerpHprInterval, SoundInterval
+from toontown.building import DistributedDoor
+from toontown.building import DoorTypes
+if (__debug__):
+ import pdb
+
+class DistributedAnimDoor(DistributedDoor.DistributedDoor):
+
+ def __init__(self, cr):
+ DistributedDoor.DistributedDoor.__init__(self, cr)
+ base.animDoor = self
+
+ def getBuilding(self):
+ if not self.__dict__.has_key('building'):
+ if self.doorType == DoorTypes.EXT_ANIM_STANDARD:
+ searchStr = '**/??' + str(self.block) + ':animated_building_*_DNARoot;+s'
+ self.notify.debug('searchStr=%s' % searchStr)
+ self.building = self.cr.playGame.hood.loader.geom.find(searchStr)
+ else:
+ self.notify.error('DistributedAnimDoor.getBuiding with doorType=%s' % self.doorType)
+ return self.building
+
+ def getDoorNodePath(self):
+ if self.doorType == DoorTypes.EXT_ANIM_STANDARD:
+ if hasattr(self, 'tempDoorNodePath'):
+ return self.tempDoorNodePath
+ else:
+ building = self.getBuilding()
+ doorNP = building.find('**/door_origin')
+ self.notify.debug('creating doorOrigin at %s %s' % (str(doorNP.getPos()), str(doorNP.getHpr())))
+ otherNP = NodePath('doorOrigin')
+ otherNP.setPos(doorNP.getPos())
+ otherNP.setHpr(doorNP.getHpr())
+ otherNP.reparentTo(doorNP.getParent())
+ self.tempDoorNodePath = otherNP
+ else:
+ self.notify.error('DistributedAnimDoor.getDoorNodePath with doorType=%s' % self.doorType)
+ return otherNP
+
+ def setTriggerName(self):
+ if self.doorType == DoorTypes.EXT_ANIM_STANDARD:
+ building = self.getBuilding()
+ if not building.isEmpty():
+ doorTrigger = building.find('**/door_0_door_trigger')
+ if not doorTrigger.isEmpty():
+ doorTrigger.node().setName(self.getTriggerName())
+ else:
+ self.notify.warning('setTriggerName failed no building')
+ else:
+ self.notify.error('setTriggerName doorTYpe=%s' % self.doorType)
+
+ def getAnimBuilding(self):
+ if not self.__dict__.has_key('animBuilding'):
+ if self.doorType == DoorTypes.EXT_ANIM_STANDARD:
+ bldg = self.getBuilding()
+ key = bldg.getParent().getParent()
+ animPropList = self.cr.playGame.hood.loader.animPropDict.get(key)
+ if animPropList:
+ for prop in animPropList:
+ if bldg == prop.getActor().getParent():
+ self.animBuilding = prop
+ break
+
+ else:
+ self.notify.error('could not find' + str(key))
+ else:
+ self.notify.error('No such door type as ' + str(self.doorType))
+ return self.animBuilding
+
+ def getBuildingActor(self):
+ result = self.getAnimBuilding().getActor()
+ return result
+
+ def enterOpening(self, ts):
+ bldgActor = self.getBuildingActor()
+ rightDoor = bldgActor.controlJoint(None, 'modelRoot', 'def_right_door')
+ if rightDoor.isEmpty():
+ self.notify.warning('enterOpening(): did not find rightDoor')
+ return
+ otherNP = self.getDoorNodePath()
+ trackName = 'doorOpen-%d' % self.doId
+ if self.rightSwing:
+ h = 100
+ else:
+ h = -100
+ self.finishDoorTrack()
+ self.doorTrack = Parallel(SoundInterval(self.openSfx, node=rightDoor), Sequence(HprInterval(rightDoor, VBase3(0, 0, 0)), Wait(0.4), LerpHprInterval(nodePath=rightDoor, duration=0.6, hpr=VBase3(h, 0, 0), startHpr=VBase3(0, 0, 0), blendType='easeInOut')), name=trackName)
+ self.doorTrack.start(ts)
+ return
+
+ def enterClosing(self, ts):
+ bldgActor = self.getBuildingActor()
+ rightDoor = bldgActor.controlJoint(None, 'modelRoot', 'def_right_door')
+ if rightDoor.isEmpty():
+ self.notify.warning('enterClosing(): did not find rightDoor')
+ return
+ otherNP = self.getDoorNodePath()
+ trackName = 'doorClose-%d' % self.doId
+ if self.rightSwing:
+ h = 100
+ else:
+ h = -100
+ self.finishDoorTrack()
+ self.doorTrack = Sequence(LerpHprInterval(nodePath=rightDoor, duration=1.0, hpr=VBase3(0, 0, 0), startHpr=VBase3(h, 0, 0), blendType='easeInOut'), SoundInterval(self.closeSfx, node=rightDoor), name=trackName)
+ self.doorTrack.start(ts)
+ if hasattr(self, 'done'):
+ request = self.getRequestStatus()
+ messenger.send('doorDoneEvent', [request])
+ return
+
+ def exitDoorEnterOpening(self, ts):
+ bldgActor = self.getBuildingActor()
+ leftDoor = bldgActor.controlJoint(None, 'modelRoot', 'def_left_door')
+ if self.leftSwing:
+ h = -100
+ else:
+ h = 100
+ if not leftDoor.isEmpty():
+ otherNP = self.getDoorNodePath()
+ trackName = 'doorDoorExitTrack-%d' % self.doId
+ self.finishDoorExitTrack()
+ self.doorExitTrack = Parallel(SoundInterval(self.openSfx, node=leftDoor), Sequence(LerpHprInterval(nodePath=leftDoor, duration=0.6, hpr=VBase3(h, 0, 0), startHpr=VBase3(0, 0, 0), blendType='easeInOut')), name=trackName)
+ self.doorExitTrack.start(ts)
+ else:
+ self.notify.warning('exitDoorEnterOpening(): did not find leftDoor')
+ return
+
+ def exitDoorEnterClosing(self, ts):
+ bldgActor = self.getBuildingActor()
+ leftDoor = bldgActor.controlJoint(None, 'modelRoot', 'def_left_door')
+ if self.leftSwing:
+ h = -100
+ else:
+ h = 100
+ if not leftDoor.isEmpty():
+ otherNP = self.getDoorNodePath()
+ trackName = 'doorExitTrack-%d' % self.doId
+ self.finishDoorExitTrack()
+ self.doorExitTrack = Sequence(LerpHprInterval(nodePath=leftDoor, duration=1.0, hpr=VBase3(0, 0, 0), startHpr=VBase3(h, 0, 0), blendType='easeInOut'), SoundInterval(self.closeSfx, node=leftDoor), name=trackName)
+ self.doorExitTrack.start(ts)
+ return
diff --git a/toontown/building/DistributedAnimDoorAI.py b/toontown/building/DistributedAnimDoorAI.py
new file mode 100644
index 0000000..ca3d644
--- /dev/null
+++ b/toontown/building/DistributedAnimDoorAI.py
@@ -0,0 +1,8 @@
+from direct.directnotify import DirectNotifyGlobal
+from toontown.building import DistributedDoorAI
+
+class DistributedAnimDoorAI(DistributedDoorAI.DistributedDoorAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedAnimDoorAI')
+
+ def __init__(self, air, blockNumber, doorType, doorIndex=0, lockValue=0, swing=3):
+ DistributedDoorAI.DistributedDoorAI.__init__(self, air, blockNumber, doorType, doorIndex, lockValue, swing)
diff --git a/toontown/building/DistributedAnimatedProp.py b/toontown/building/DistributedAnimatedProp.py
new file mode 100644
index 0000000..f63f821
--- /dev/null
+++ b/toontown/building/DistributedAnimatedProp.py
@@ -0,0 +1,77 @@
+from pandac.PandaModules import *
+from direct.distributed.ClockDelta import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObject
+
+class DistributedAnimatedProp(DistributedObject.DistributedObject):
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedAnimatedProp', [State.State('off', self.enterOff, self.exitOff, ['playing', 'attract']), State.State('attract', self.enterAttract, self.exitAttract, ['playing']), State.State('playing', self.enterPlaying, self.exitPlaying, ['attract'])], 'off', 'off')
+ self.fsm.enterInitialState()
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ self.setState(self.initialState, self.initialStateTimestamp)
+ del self.initialState
+ del self.initialStateTimestamp
+
+ def disable(self):
+ self.fsm.request('off')
+ DistributedObject.DistributedObject.disable(self)
+
+ def delete(self):
+ del self.fsm
+ DistributedObject.DistributedObject.delete(self)
+
+ def setPropId(self, propId):
+ self.propId = propId
+
+ def setAvatarInteract(self, avatarId):
+ self.avatarId = avatarId
+
+ def setOwnerDoId(self, ownerDoId):
+ self.ownerDoId = ownerDoId
+
+ def setState(self, state, timestamp):
+ if self.isGenerated():
+ self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])
+ else:
+ self.initialState = state
+ self.initialStateTimestamp = timestamp
+
+ def enterTrigger(self, args = None):
+ messenger.send('DistributedAnimatedProp_enterTrigger')
+ self.sendUpdate('requestInteract')
+
+ def exitTrigger(self, args = None):
+ messenger.send('DistributedAnimatedProp_exitTrigger')
+ self.sendUpdate('requestExit')
+
+ def rejectInteract(self):
+ self.cr.playGame.getPlace().setState('walk')
+
+ def avatarExit(self, avatarId):
+ pass
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterAttract(self, ts):
+ pass
+
+ def exitAttract(self):
+ pass
+
+ def enterPlaying(self, ts):
+ pass
+
+ def exitPlaying(self):
+ pass
diff --git a/toontown/building/DistributedAnimatedPropAI.py b/toontown/building/DistributedAnimatedPropAI.py
new file mode 100644
index 0000000..973ab42
--- /dev/null
+++ b/toontown/building/DistributedAnimatedPropAI.py
@@ -0,0 +1,82 @@
+from otp.ai.AIBaseGlobal import *
+from direct.distributed.ClockDelta import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObjectAI
+from direct.fsm import State
+
+class DistributedAnimatedPropAI(DistributedObjectAI.DistributedObjectAI):
+
+ def __init__(self, air, propId):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedAnimatedPropAI', [
+ State.State('off', self.enterOff, self.exitOff, [
+ 'playing']),
+ State.State('attract', self.enterAttract, self.exitAttract, [
+ 'playing']),
+ State.State('playing', self.enterPlaying, self.exitPlaying, [
+ 'attract'])], 'off', 'off')
+ self.fsm.enterInitialState()
+ self.propId = propId
+ self.avatarId = 0
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ del self.fsm
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def getPropId(self):
+ return self.propId
+
+ def getAvatarInteract(self):
+ return self.avatarId
+
+ def getInitialState(self):
+ return [
+ self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]
+
+ def getOwnerDoId(self):
+ return self.ownerDoId
+
+ def requestInteract(self):
+ avatarId = self.air.getAvatarIdFromSender()
+ stateName = self.fsm.getCurrentState().getName()
+ if stateName != 'playing':
+ self.sendUpdate('setAvatarInteract', [avatarId])
+ self.avatarId = avatarId
+ self.fsm.request('playing')
+ else:
+ self.sendUpdateToAvatarId(avatarId, 'rejectInteract', [])
+
+ def requestExit(self):
+ avatarId = self.air.getAvatarIdFromSender()
+ if avatarId == self.avatarId:
+ stateName = self.fsm.getCurrentState().getName()
+ if stateName == 'playing':
+ self.sendUpdate('avatarExit', [avatarId])
+ self.fsm.request('attract')
+
+ def getState(self):
+ return [
+ self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]
+
+ def d_setState(self, state):
+ self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterAttract(self):
+ self.d_setState('attract')
+
+ def exitAttract(self):
+ pass
+
+ def enterPlaying(self):
+ self.d_setState('playing')
+
+ def exitPlaying(self):
+ pass
diff --git a/toontown/building/DistributedBBElevator.py b/toontown/building/DistributedBBElevator.py
new file mode 100644
index 0000000..f79a930
--- /dev/null
+++ b/toontown/building/DistributedBBElevator.py
@@ -0,0 +1,29 @@
+import DistributedElevator
+import DistributedBossElevator
+from ElevatorConstants import *
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import TTLocalizer
+
+class DistributedBBElevator(DistributedBossElevator.DistributedBossElevator):
+
+ def __init__(self, cr):
+ DistributedBossElevator.DistributedBossElevator.__init__(self, cr)
+ self.type = ELEVATOR_BB
+ self.countdownTime = ElevatorData[self.type]['countdown']
+ self.elevatorPoints = BossbotElevatorPoints
+
+ def setupElevator(self):
+ geom = base.cr.playGame.hood.loader.geom
+ self.elevatorModel = loader.loadModel('phase_12/models/bossbotHQ/BB_Elevator')
+ self.leftDoor = self.elevatorModel.find('**/left-door')
+ if self.leftDoor.isEmpty():
+ self.leftDoor = self.elevatorModel.find('**/left_door')
+ self.rightDoor = self.elevatorModel.find('**/right-door')
+ if self.rightDoor.isEmpty():
+ self.rightDoor = self.elevatorModel.find('**/right_door')
+ locator = geom.find('**/elevator_locator')
+ self.elevatorModel.reparentTo(locator)
+ DistributedElevator.DistributedElevator.setupElevator(self)
+
+ def getDestName(self):
+ return TTLocalizer.ElevatorBossBotBoss
diff --git a/toontown/building/DistributedBBElevatorAI.py b/toontown/building/DistributedBBElevatorAI.py
new file mode 100644
index 0000000..433007c
--- /dev/null
+++ b/toontown/building/DistributedBBElevatorAI.py
@@ -0,0 +1,17 @@
+from ElevatorConstants import *
+import DistributedBossElevatorAI
+
+class DistributedBBElevatorAI(DistributedBossElevatorAI.DistributedBossElevatorAI):
+
+ def __init__(self, air, bldg, zone, antiShuffle=0, minLaff=0):
+ DistributedBossElevatorAI.DistributedBossElevatorAI.__init__(self, air, bldg, zone, antiShuffle=antiShuffle, minLaff=0)
+ self.type = ELEVATOR_BB
+ self.countdownTime = ElevatorData[self.type]['countdown']
+
+ def checkBoard(self, av):
+ result = 0
+ if simbase.config.GetBool('allow-ceo-elevator', 1):
+ result = DistributedBossElevatorAI.DistributedBossElevatorAI.checkBoard(self, av)
+ else:
+ result = REJECT_NOT_YET_AVAILABLE
+ return result
diff --git a/toontown/building/DistributedBoardingParty.py b/toontown/building/DistributedBoardingParty.py
new file mode 100644
index 0000000..2bbce05
--- /dev/null
+++ b/toontown/building/DistributedBoardingParty.py
@@ -0,0 +1,574 @@
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase import ToontownGlobals
+from direct.distributed import DistributedObject
+from toontown.toon import GroupInvitee
+from toontown.toon import GroupPanel
+from toontown.toon import BoardingGroupInviterPanels
+from toontown.building import BoardingPartyBase
+from direct.gui.DirectGui import *
+from toontown.toontowngui import TTDialog
+from toontown.hood import ZoneUtil
+from toontown.toontowngui import TeaserPanel
+from direct.interval.IntervalGlobal import *
+import BoardingGroupShow
+
+class DistributedBoardingParty(DistributedObject.DistributedObject, BoardingPartyBase.BoardingPartyBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBoardingParty')
+ InvitationFailedTimeout = 60.0
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ BoardingPartyBase.BoardingPartyBase.__init__(self)
+ self.groupInviteePanel = None
+ self.groupPanel = None
+ self.inviterPanels = BoardingGroupInviterPanels.BoardingGroupInviterPanels()
+ self.lastInvitationFailedMessage = {}
+ self.goToPreShowTrack = None
+ self.goToShowTrack = None
+ return
+
+ def generate(self):
+ self.load()
+ DistributedObject.DistributedObject.generate(self)
+ localAvatar.boardingParty = self
+
+ def announceGenerate(self):
+ canonicalZoneId = ZoneUtil.getCanonicalZoneId(self.zoneId)
+ self.notify.debug('canonicalZoneId = %s' % canonicalZoneId)
+ localAvatar.chatMgr.chatInputSpeedChat.addBoardingGroupMenu(canonicalZoneId)
+ if base.config.GetBool('want-singing', 0):
+ localAvatar.chatMgr.chatInputSpeedChat.addSingingGroupMenu()
+
+ def delete(self):
+ DistributedObject.DistributedObject.delete(self)
+
+ def disable(self):
+ self.finishGoToPreShowTrack()
+ self.finishGoToShowTrack()
+ self.forceCleanupInviteePanel()
+ self.forceCleanupInviterPanels()
+ self.inviterPanels = None
+ if self.groupPanel:
+ self.groupPanel.cleanup()
+ self.groupPanel = None
+ DistributedObject.DistributedObject.disable(self)
+ BoardingPartyBase.BoardingPartyBase.cleanup(self)
+ localAvatar.boardingParty = None
+ localAvatar.chatMgr.chatInputSpeedChat.removeBoardingGroupMenu()
+ self.lastInvitationFailedMessage = {}
+ return
+
+ def getElevatorIdList(self):
+ return self.elevatorIdList
+
+ def setElevatorIdList(self, elevatorIdList):
+ self.notify.debug('setElevatorIdList')
+ self.elevatorIdList = elevatorIdList
+
+ def load(self):
+ pass
+
+ def postGroupInfo(self, leaderId, memberList, inviteeList, kickedList):
+ self.notify.debug('postgroupInfo')
+ isMyGroup = 0
+ removedMemberIdList = []
+ if self.groupListDict.has_key(leaderId):
+ oldGroupEntry = self.groupListDict[leaderId]
+ else:
+ oldGroupEntry = [[], [], []]
+ oldMemberList = oldGroupEntry[0]
+ newGroupEntry = [memberList, inviteeList, kickedList]
+ self.groupListDict[leaderId] = newGroupEntry
+ if not len(oldMemberList) == len(memberList):
+ for oldMember in oldMemberList:
+ if oldMember not in memberList:
+ if oldMember in self.avIdDict:
+ if self.avIdDict[oldMember] == leaderId:
+ self.avIdDict.pop(oldMember)
+ removedMemberIdList.append(oldMember)
+
+ self.avIdDict[leaderId] = leaderId
+ if leaderId == localAvatar.doId:
+ isMyGroup = 1
+ for memberId in memberList:
+ self.avIdDict[memberId] = leaderId
+ if memberId == localAvatar.doId:
+ isMyGroup = 1
+
+ if newGroupEntry[0] == [0] or not newGroupEntry[0]:
+ dgroup = self.groupListDict.pop(leaderId)
+ for memberId in dgroup[0]:
+ if self.avIdDict.has_key(memberId):
+ self.avIdDict.pop(memberId)
+
+ if isMyGroup:
+ self.notify.debug('new info posted on my group')
+ if not self.groupPanel:
+ self.groupPanel = GroupPanel.GroupPanel(self)
+ messenger.send('updateGroupStatus')
+ for removedMemberId in removedMemberIdList:
+ removedMember = base.cr.doId2do.get(removedMemberId)
+ if not removedMember:
+ removedMember = base.cr.identifyFriend(removedMemberId)
+ if removedMember:
+ removedMemberName = removedMember.name
+ messageText = TTLocalizer.BoardingMessageLeftGroup % removedMemberName
+ localAvatar.setSystemMessage(0, messageText, WhisperPopup.WTToontownBoardingGroup)
+
+ elif localAvatar.doId in oldMemberList and localAvatar.doId not in memberList:
+ messenger.send('updateGroupStatus')
+ if self.groupPanel:
+ self.groupPanel.cleanup()
+ self.groupPanel = None
+ else:
+ self.notify.debug('new info posted on some other group')
+ return
+
+ def postInvite(self, leaderId, inviterId):
+ self.notify.debug('post Invite')
+ if not base.cr.avatarFriendsManager.checkIgnored(inviterId):
+ inviter = base.cr.doId2do.get(inviterId)
+ if inviter:
+ if self.inviterPanels.isInvitingPanelUp() or self.inviterPanels.isInvitationRejectedPanelUp():
+ self.inviterPanels.forceCleanup()
+ self.groupInviteePanel = GroupInvitee.GroupInvitee()
+ self.groupInviteePanel.make(self, inviter, leaderId)
+ if base.config.GetBool('reject-boarding-group-invites', 0):
+ self.groupInviteePanel.forceCleanup()
+ self.groupInviteePanel = None
+ return
+
+ def postKick(self, leaderId):
+ self.notify.debug('%s was kicked out of the Boarding Group by %s' % (localAvatar.doId, leaderId))
+ localAvatar.setSystemMessage(0, TTLocalizer.BoardingMessageKickedOut, WhisperPopup.WTToontownBoardingGroup)
+
+ def postSizeReject(self, leaderId, inviterId, inviteeId):
+ self.notify.debug('%s was not invited because the group is full' % inviteeId)
+
+ def postKickReject(self, leaderId, inviterId, inviteeId):
+ self.notify.debug('%s was not invited because %s has kicked them from the group' % (inviteeId, leaderId))
+
+ def postInviteDelcined(self, inviteeId):
+ self.notify.debug("%s delinced %s's Boarding Group invitation." % (inviteeId, localAvatar.doId))
+ invitee = base.cr.doId2do.get(inviteeId)
+ if invitee:
+ self.inviterPanels.createInvitationRejectedPanel(self, inviteeId)
+
+ def postInviteAccepted(self, inviteeId):
+ self.notify.debug("%s accepted %s's Boarding Group invitation." % (inviteeId, localAvatar.doId))
+ if self.inviterPanels.isInvitingPanelIdCorrect(inviteeId):
+ self.inviterPanels.destroyInvitingPanel()
+
+ def postInviteCanceled(self):
+ self.notify.debug('The invitation to the Boarding Group was cancelled')
+ if self.isInviteePanelUp():
+ self.groupInviteePanel.cleanup()
+ self.groupInviteePanel = None
+ return
+
+ def postInviteNotQualify(self, avId, reason, elevatorId):
+ messenger.send('updateGroupStatus')
+ rejectText = ''
+ minLaff = TTLocalizer.BoardingMore
+ if elevatorId:
+ elevator = base.cr.doId2do.get(elevatorId)
+ if elevator:
+ minLaff = elevator.minLaff
+ if avId == localAvatar.doId:
+ if reason == BoardingPartyBase.BOARDCODE_MINLAFF:
+ rejectText = TTLocalizer.BoardingInviteMinLaffInviter % minLaff
+ if reason == BoardingPartyBase.BOARDCODE_PROMOTION:
+ rejectText = TTLocalizer.BoardingInvitePromotionInviter
+ else:
+ avatar = base.cr.doId2do.get(avId)
+ if avatar:
+ avatarNameText = avatar.name
+ else:
+ avatarNameText = ''
+ if reason == BoardingPartyBase.BOARDCODE_MINLAFF:
+ rejectText = TTLocalizer.BoardingInviteMinLaffInvitee % (avatarNameText, minLaff)
+ if reason == BoardingPartyBase.BOARDCODE_PROMOTION:
+ rejectText = TTLocalizer.BoardingInvitePromotionInvitee % avatarNameText
+ if reason == BoardingPartyBase.BOARDCODE_BATTLE:
+ rejectText = TTLocalizer.TeleportPanelNotAvailable % avatarNameText
+ if reason == BoardingPartyBase.BOARDCODE_NOT_PAID:
+ rejectText = TTLocalizer.BoardingInviteNotPaidInvitee % avatarNameText
+ if reason == BoardingPartyBase.BOARDCODE_DIFF_GROUP:
+ rejectText = TTLocalizer.BoardingInviteeInDiffGroup % avatarNameText
+ if reason == BoardingPartyBase.BOARDCODE_PENDING_INVITE:
+ rejectText = TTLocalizer.BoardingInviteePendingIvite % avatarNameText
+ if reason == BoardingPartyBase.BOARDCODE_IN_ELEVATOR:
+ rejectText = TTLocalizer.BoardingInviteeInElevator % avatarNameText
+ if self.inviterPanels.isInvitingPanelIdCorrect(avId) or avId == localAvatar.doId:
+ self.inviterPanels.destroyInvitingPanel()
+ self.showMe(rejectText)
+
+ def postAlreadyInGroup(self):
+ self.showMe(TTLocalizer.BoardingAlreadyInGroup)
+
+ def postGroupAlreadyFull(self):
+ self.showMe(TTLocalizer.BoardingGroupAlreadyFull)
+
+ def postSomethingMissing(self):
+ self.showMe(TTLocalizer.BoardcodeMissing)
+
+ def postRejectBoard(self, elevatorId, reason, avatarsFailingRequirements, avatarsInBattle):
+ self.showRejectMessage(elevatorId, reason, avatarsFailingRequirements, avatarsInBattle)
+ self.enableGoButton()
+
+ def postRejectGoto(self, elevatorId, reason, avatarsFailingRequirements, avatarsInBattle):
+ self.showRejectMessage(elevatorId, reason, avatarsFailingRequirements, avatarsInBattle)
+
+ def postMessageInvited(self, inviteeId, inviterId):
+ inviterName = ''
+ inviteeName = ''
+ inviter = base.cr.doId2do.get(inviterId)
+ if inviter:
+ inviterName = inviter.name
+ invitee = base.cr.doId2do.get(inviteeId)
+ if invitee:
+ inviteeName = invitee.name
+ messageText = TTLocalizer.BoardingMessageInvited % (inviterName, inviteeName)
+ localAvatar.setSystemMessage(0, messageText, WhisperPopup.WTToontownBoardingGroup)
+
+ def postMessageInvitationFailed(self, inviterId):
+ inviterName = ''
+ inviter = base.cr.doId2do.get(inviterId)
+ if inviter:
+ inviterName = inviter.name
+ if self.invitationFailedMessageOk(inviterId):
+ messageText = TTLocalizer.BoardingMessageInvitationFailed % inviterName
+ localAvatar.setSystemMessage(0, messageText, WhisperPopup.WTToontownBoardingGroup)
+
+ def postMessageAcceptanceFailed(self, inviteeId, reason):
+ inviteeName = ''
+ messageText = ''
+ invitee = base.cr.doId2do.get(inviteeId)
+ if invitee:
+ inviteeName = invitee.name
+ if reason == BoardingPartyBase.INVITE_ACCEPT_FAIL_GROUP_FULL:
+ messageText = TTLocalizer.BoardingMessageGroupFull % inviteeName
+ localAvatar.setSystemMessage(0, messageText, WhisperPopup.WTToontownBoardingGroup)
+ if self.inviterPanels.isInvitingPanelIdCorrect(inviteeId):
+ self.inviterPanels.destroyInvitingPanel()
+
+ def invitationFailedMessageOk(self, inviterId):
+ now = globalClock.getFrameTime()
+ lastTime = self.lastInvitationFailedMessage.get(inviterId, None)
+ if lastTime:
+ elapsedTime = now - lastTime
+ if elapsedTime < self.InvitationFailedTimeout:
+ return False
+ self.lastInvitationFailedMessage[inviterId] = now
+ return True
+
+ def showRejectMessage(self, elevatorId, reason, avatarsFailingRequirements, avatarsInBattle):
+ leaderId = localAvatar.doId
+ rejectText = ''
+
+ def getAvatarText(avIdList):
+ avatarText = ''
+ nameList = []
+ for avId in avIdList:
+ avatar = base.cr.doId2do.get(avId)
+ if avatar:
+ nameList.append(avatar.name)
+
+ if len(nameList) > 0:
+ lastName = nameList.pop()
+ avatarText = lastName
+ if len(nameList) > 0:
+ secondLastName = nameList.pop()
+ for name in nameList:
+ avatarText = name + ', '
+
+ avatarText += secondLastName + ' ' + TTLocalizer.And + ' ' + lastName
+ return avatarText
+
+ if reason == BoardingPartyBase.BOARDCODE_MINLAFF:
+ self.notify.debug("%s 's group cannot board because it does not have enough laff points." % leaderId)
+ elevator = base.cr.doId2do.get(elevatorId)
+ if elevator:
+ minLaffPoints = elevator.minLaff
+ else:
+ minLaffPoints = TTLocalizer.BoardingMore
+ if leaderId in avatarsFailingRequirements:
+ rejectText = TTLocalizer.BoardcodeMinLaffLeader % minLaffPoints
+ else:
+ avatarNameText = getAvatarText(avatarsFailingRequirements)
+ if len(avatarsFailingRequirements) == 1:
+ rejectText = TTLocalizer.BoardcodeMinLaffNonLeaderSingular % (avatarNameText, minLaffPoints)
+ else:
+ rejectText = TTLocalizer.BoardcodeMinLaffNonLeaderPlural % (avatarNameText, minLaffPoints)
+ elif reason == BoardingPartyBase.BOARDCODE_PROMOTION:
+ self.notify.debug("%s 's group cannot board because it does not have enough promotion merits." % leaderId)
+ if leaderId in avatarsFailingRequirements:
+ rejectText = TTLocalizer.BoardcodePromotionLeader
+ else:
+ avatarNameText = getAvatarText(avatarsFailingRequirements)
+ if len(avatarsFailingRequirements) == 1:
+ rejectText = TTLocalizer.BoardcodePromotionNonLeaderSingular % avatarNameText
+ else:
+ rejectText = TTLocalizer.BoardcodePromotionNonLeaderPlural % avatarNameText
+ elif reason == BoardingPartyBase.BOARDCODE_BATTLE:
+ self.notify.debug("%s 's group cannot board because it is in a battle" % leaderId)
+ if leaderId in avatarsInBattle:
+ rejectText = TTLocalizer.BoardcodeBattleLeader
+ else:
+ avatarNameText = getAvatarText(avatarsInBattle)
+ if len(avatarsInBattle) == 1:
+ rejectText = TTLocalizer.BoardcodeBattleNonLeaderSingular % avatarNameText
+ else:
+ rejectText = TTLocalizer.BoardcodeBattleNonLeaderPlural % avatarNameText
+ elif reason == BoardingPartyBase.BOARDCODE_SPACE:
+ self.notify.debug("%s 's group cannot board there was not enough room" % leaderId)
+ rejectText = TTLocalizer.BoardcodeSpace
+ elif reason == BoardingPartyBase.BOARDCODE_MISSING:
+ self.notify.debug("%s 's group cannot board because something was missing" % leaderId)
+ rejectText = TTLocalizer.BoardcodeMissing
+ base.localAvatar.elevatorNotifier.showMe(rejectText)
+
+ def postGroupDissolve(self, quitterId, leaderId, memberList, kick):
+ self.notify.debug('%s group has dissolved' % leaderId)
+ isMyGroup = 0
+ if localAvatar.doId == quitterId or localAvatar.doId == leaderId:
+ isMyGroup = 1
+ if self.groupListDict.has_key(leaderId):
+ if leaderId == localAvatar.doId:
+ isMyGroup = 1
+ if self.avIdDict.has_key(leaderId):
+ self.avIdDict.pop(leaderId)
+ dgroup = self.groupListDict.pop(leaderId)
+ for memberId in memberList:
+ if memberId == localAvatar.doId:
+ isMyGroup = 1
+ if self.avIdDict.has_key(memberId):
+ self.avIdDict.pop(memberId)
+
+ if isMyGroup:
+ self.notify.debug('new info posted on my group')
+ messenger.send('updateGroupStatus')
+ groupFormed = False
+ if self.groupPanel:
+ groupFormed = True
+ self.groupPanel.cleanup()
+ self.groupPanel = None
+ if groupFormed:
+ if leaderId == quitterId:
+ if not localAvatar.doId == leaderId:
+ localAvatar.setSystemMessage(0, TTLocalizer.BoardingMessageGroupDissolved, WhisperPopup.WTToontownBoardingGroup)
+ elif not kick:
+ if not localAvatar.doId == quitterId:
+ quitter = base.cr.doId2do.get(quitterId)
+ if quitter:
+ quitterName = quitter.name
+ messageText = TTLocalizer.BoardingMessageLeftGroup % quitterName
+ localAvatar.setSystemMessage(0, messageText, WhisperPopup.WTToontownBoardingGroup)
+ else:
+ messageText = TTLocalizer.BoardingMessageGroupDisbandedGeneric
+ localAvatar.setSystemMessage(0, messageText, WhisperPopup.WTToontownBoardingGroup)
+ return
+
+ def requestInvite(self, inviteeId):
+ self.notify.debug('requestInvite %s' % inviteeId)
+ elevator = base.cr.doId2do.get(self.getElevatorIdList()[0])
+ if elevator:
+ if elevator.allowedToEnter(self.zoneId):
+ if inviteeId in self.getGroupKickList(localAvatar.doId):
+ if not self.isGroupLeader(localAvatar.doId):
+ avatar = base.cr.doId2do.get(inviteeId)
+ if avatar:
+ avatarNameText = avatar.name
+ else:
+ avatarNameText = ''
+ rejectText = TTLocalizer.BoardingInviteeInKickOutList % avatarNameText
+ self.showMe(rejectText)
+ return
+ if self.inviterPanels.isInvitingPanelUp():
+ self.showMe(TTLocalizer.BoardingPendingInvite, pos=(0, 0, 0))
+ elif len(self.getGroupMemberList(localAvatar.doId)) >= self.maxSize:
+ self.showMe(TTLocalizer.BoardingInviteGroupFull)
+ else:
+ invitee = base.cr.doId2do.get(inviteeId)
+ if invitee:
+ self.inviterPanels.createInvitingPanel(self, inviteeId)
+ self.sendUpdate('requestInvite', [inviteeId])
+ else:
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.fsm.request('stopped')
+ self.teaserDialog = TeaserPanel.TeaserPanel(pageName='cogHQ', doneFunc=self.handleOkTeaser)
+
+ def handleOkTeaser(self):
+ self.teaserDialog.destroy()
+ del self.teaserDialog
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.fsm.request('walk')
+
+ def requestCancelInvite(self, inviteeId):
+ self.sendUpdate('requestCancelInvite', [inviteeId])
+
+ def requestAcceptInvite(self, leaderId, inviterId):
+ self.notify.debug('requestAcceptInvite %s %s' % (leaderId, inviterId))
+ self.sendUpdate('requestAcceptInvite', [leaderId, inviterId])
+
+ def requestRejectInvite(self, leaderId, inviterId):
+ self.sendUpdate('requestRejectInvite', [leaderId, inviterId])
+
+ def requestKick(self, kickId):
+ self.sendUpdate('requestKick', [kickId])
+
+ def requestLeave(self):
+ if self.goToShowTrack and self.goToShowTrack.isPlaying():
+ return
+ place = base.cr.playGame.getPlace()
+ if place:
+ if not place.getState() == 'elevator':
+ if self.avIdDict.has_key(localAvatar.doId):
+ leaderId = self.avIdDict[localAvatar.doId]
+ self.sendUpdate('requestLeave', [leaderId])
+
+ def handleEnterElevator(self, elevator):
+ if self.getGroupLeader(localAvatar.doId) == localAvatar.doId:
+ if base.localAvatar.hp > 0:
+ self.cr.playGame.getPlace().detectedElevatorCollision(elevator)
+ self.sendUpdate('requestBoard', [elevator.doId])
+ elevatorId = elevator.doId
+ if elevatorId in self.elevatorIdList:
+ offset = self.elevatorIdList.index(elevatorId)
+ if self.groupPanel:
+ self.groupPanel.scrollToDestination(offset)
+ self.informDestChange(offset)
+ self.disableGoButton()
+
+ def informDestChange(self, offset):
+ self.sendUpdate('informDestinationInfo', [offset])
+
+ def postDestinationInfo(self, offset):
+ if self.groupPanel:
+ self.groupPanel.changeDestination(offset)
+
+ def enableGoButton(self):
+ if self.groupPanel:
+ self.groupPanel.enableGoButton()
+ self.groupPanel.enableDestinationScrolledList()
+
+ def disableGoButton(self):
+ if self.groupPanel:
+ self.groupPanel.disableGoButton()
+ self.groupPanel.disableDestinationScrolledList()
+
+ def isInviteePanelUp(self):
+ if self.groupInviteePanel:
+ if not self.groupInviteePanel.isEmpty():
+ return True
+ self.groupInviteePanel = None
+ return False
+
+ def requestGoToFirstTime(self, elevatorId):
+ self.waitingForFirstResponse = True
+ self.firstRequestAccepted = False
+ self.sendUpdate('requestGoToFirstTime', [elevatorId])
+ self.startGoToPreShow(elevatorId)
+
+ def acceptGoToFirstTime(self, elevatorId):
+ self.waitingForFirstResponse = False
+ self.firstRequestAccepted = True
+
+ def requestGoToSecondTime(self, elevatorId):
+ if not self.waitingForFirstResponse:
+ if self.firstRequestAccepted:
+ self.firstRequestAccepted = False
+ self.disableGoButton()
+ self.sendUpdate('requestGoToSecondTime', [elevatorId])
+ else:
+ self.postRejectGoto(elevatorId, BoardingPartyBase.BOARDCODE_MISSING, [], [])
+ self.cancelGoToElvatorDest()
+
+ def acceptGoToSecondTime(self, elevatorId):
+ self.startGoToShow(elevatorId)
+
+ def rejectGoToRequest(self, elevatorId, reason, avatarsFailingRequirements, avatarsInBattle):
+ self.firstRequestAccepted = False
+ self.waitingForFirstResponse = False
+ self.cancelGoToElvatorDest()
+ self.postRejectGoto(elevatorId, reason, avatarsFailingRequirements, avatarsInBattle)
+
+ def startGoToPreShow(self, elevatorId):
+ self.notify.debug('Starting Go Pre Show.')
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.setState('stopped')
+ goButtonPreShow = BoardingGroupShow.BoardingGroupShow(localAvatar)
+ goButtonPreShowTrack = goButtonPreShow.getGoButtonPreShow()
+ if self.groupPanel:
+ self.groupPanel.changeGoToCancel()
+ self.groupPanel.disableQuitButton()
+ self.groupPanel.disableDestinationScrolledList()
+ self.finishGoToPreShowTrack()
+ self.goToPreShowTrack = Sequence()
+ self.goToPreShowTrack.append(goButtonPreShowTrack)
+ self.goToPreShowTrack.append(Func(self.requestGoToSecondTime, elevatorId))
+ self.goToPreShowTrack.start()
+
+ def finishGoToPreShowTrack(self):
+ if self.goToPreShowTrack:
+ self.goToPreShowTrack.finish()
+ self.goToPreShowTrack = None
+ return
+
+ def startGoToShow(self, elevatorId):
+ self.notify.debug('Starting Go Show.')
+ localAvatar.boardingParty.forceCleanupInviterPanels()
+ elevatorName = self.__getDestName(elevatorId)
+ if self.groupPanel:
+ self.groupPanel.disableQuitButton()
+ goButtonShow = BoardingGroupShow.BoardingGroupShow(localAvatar)
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.setState('stopped')
+ self.goToShowTrack = goButtonShow.getGoButtonShow(elevatorName)
+ self.goToShowTrack.start()
+
+ def finishGoToShowTrack(self):
+ if self.goToShowTrack:
+ self.goToShowTrack.finish()
+ self.goToShowTrack = None
+ return
+
+ def cancelGoToElvatorDest(self):
+ self.notify.debug('%s cancelled the GoTo Button.' % localAvatar.doId)
+ self.firstRequestAccepted = False
+ self.waitingForFirstResponse = False
+ self.finishGoToPreShowTrack()
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.setState('walk')
+ if self.groupPanel:
+ self.groupPanel.changeCancelToGo()
+ self.groupPanel.enableGoButton()
+ self.groupPanel.enableQuitButton()
+ self.groupPanel.enableDestinationScrolledList()
+
+ def __getDestName(self, elevatorId):
+ elevator = base.cr.doId2do.get(elevatorId)
+ destName = ''
+ if elevator:
+ destName = elevator.getDestName()
+ return destName
+
+ def showMe(self, message, pos = None):
+ base.localAvatar.elevatorNotifier.showMeWithoutStopping(message, pos)
+
+ def forceCleanupInviteePanel(self):
+ if self.isInviteePanelUp():
+ self.groupInviteePanel.forceCleanup()
+ self.groupInviteePanel = None
+ return
+
+ def forceCleanupInviterPanels(self):
+ if self.inviterPanels:
+ self.inviterPanels.forceCleanup()
diff --git a/toontown/building/DistributedBoardingPartyAI.py b/toontown/building/DistributedBoardingPartyAI.py
new file mode 100644
index 0000000..d9cad47
--- /dev/null
+++ b/toontown/building/DistributedBoardingPartyAI.py
@@ -0,0 +1,482 @@
+from otp.otpbase import OTPGlobals
+from otp.ai.AIBase import *
+from toontown.toonbase import ToontownGlobals
+from direct.distributed.ClockDelta import *
+from ElevatorConstants import *
+from direct.distributed import DistributedObjectAI
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+from toontown.building import BoardingPartyBase
+from toontown.toonbase import ToontownAccessAI
+GROUPMEMBER = 0
+GROUPINVITE = 1
+
+class DistributedBoardingPartyAI(DistributedObjectAI.DistributedObjectAI, BoardingPartyBase.BoardingPartyBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBoardingPartyAI')
+
+ def __init__(self, air, elevatorList, maxSize=4):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ BoardingPartyBase.BoardingPartyBase.__init__(self)
+ self.setGroupSize(maxSize)
+ self.elevatorIdList = elevatorList
+ self.visibleZones = []
+
+ def delete(self):
+ self.cleanup()
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def generate(self):
+ DistributedObjectAI.DistributedObjectAI.generate(self)
+ for elevatorId in self.elevatorIdList:
+ elevator = simbase.air.doId2do.get(elevatorId)
+ elevator.setBoardingParty(self)
+
+ store = simbase.air.dnaStoreMap.get(self.zoneId)
+ if store:
+ numVisGroups = store.getNumDNAVisGroupsAI()
+ myVisGroup = None
+ for index in range(numVisGroups):
+ if store.getDNAVisGroupAI(index).getName() == str(self.zoneId):
+ myVisGroup = store.getDNAVisGroupAI(index)
+
+ if myVisGroup:
+ numVisibles = myVisGroup.getNumVisibles()
+ for index in range(numVisibles):
+ newVisible = myVisGroup.getVisibleName(index)
+ self.visibleZones.append(int(newVisible))
+
+ else:
+ self.visibleZones = [
+ self.zoneId]
+ else:
+ self.visibleZones = [
+ self.zoneId]
+ return
+
+ def cleanup(self):
+ BoardingPartyBase.BoardingPartyBase.cleanup(self)
+ del self.elevatorIdList
+ del self.visibleZones
+
+ def getElevatorIdList(self):
+ return self.elevatorIdList
+
+ def setElevatorIdList(self, elevatorIdList):
+ self.elevatorIdList = elevatorIdList
+
+ def addWacthAvStatus(self, avId):
+ self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleAvatarDisco, extraArgs=[avId])
+ self.accept(self.staticGetLogicalZoneChangeEvent(avId), self.handleAvatarZoneChange, extraArgs=[avId])
+ messageToonAdded = 'Battle adding toon %s' % avId
+ self.accept(messageToonAdded, self.handleToonJoinedBattle)
+ messageToonReleased = 'Battle releasing toon %s' % avId
+ self.accept(messageToonReleased, self.handleToonLeftBattle)
+
+ def handleToonJoinedBattle(self, avId):
+ self.notify.debug('handleToonJoinedBattle %s' % avId)
+
+ def handleToonLeftBattle(self, avId):
+ self.notify.debug('handleToonLeftBattle %s' % avId)
+
+ def removeWacthAvStatus(self, avId):
+ self.ignore(self.air.getAvatarExitEvent(avId))
+ self.ignore(self.staticGetLogicalZoneChangeEvent(avId))
+
+ def requestInvite(self, inviteeId):
+ self.notify.debug('requestInvite %s' % inviteeId)
+ inviterId = self.air.getAvatarIdFromSender()
+ invitee = simbase.air.doId2do.get(inviteeId)
+ inviter = simbase.air.doId2do.get(inviterId)
+ inviterOkay = self.checkBoard(inviterId, self.elevatorIdList[0])
+ if inviterOkay == REJECT_NOTPAID:
+ reason = BoardingPartyBase.BOARDCODE_NOT_PAID
+ self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason, 0])
+ simbase.air.writeServerEvent('suspicious', inviterId, 'User with rights: %s tried to invite someone to a boarding group' % inviter.getGameAccess())
+ if simbase.config.GetBool('want-ban-boardingparty', True):
+ commentStr = 'User with rights: %s tried to invite someone to a boarding group' % inviter.getGameAccess()
+ dislId = inviter.DISLid
+ simbase.air.banManager.ban(inviterId, dislId, commentStr)
+ return
+ if invitee and invitee.battleId != 0:
+ reason = BoardingPartyBase.BOARDCODE_BATTLE
+ self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason, 0])
+ self.sendUpdateToAvatarId(inviteeId, 'postMessageInvitationFailed', [inviterId])
+ return
+ if self.hasActiveGroup(inviteeId):
+ reason = BoardingPartyBase.BOARDCODE_DIFF_GROUP
+ self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason, 0])
+ self.sendUpdateToAvatarId(inviteeId, 'postMessageInvitationFailed', [inviterId])
+ return
+ if self.hasPendingInvite(inviteeId):
+ reason = BoardingPartyBase.BOARDCODE_PENDING_INVITE
+ self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason, 0])
+ self.sendUpdateToAvatarId(inviteeId, 'postMessageInvitationFailed', [inviterId])
+ return
+ if self.__isInElevator(inviteeId):
+ reason = BoardingPartyBase.BOARDCODE_IN_ELEVATOR
+ self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason, 0])
+ self.sendUpdateToAvatarId(inviteeId, 'postMessageInvitationFailed', [inviterId])
+ return
+ inviteeOkay = self.checkBoard(inviteeId, self.elevatorIdList[0])
+ reason = 0
+ if inviteeOkay == REJECT_NOTPAID:
+ reason = BoardingPartyBase.BOARDCODE_NOT_PAID
+ self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason, 0])
+ return
+ if len(self.elevatorIdList) == 1:
+ if inviteeOkay:
+ if inviteeOkay == REJECT_MINLAFF:
+ reason = BoardingPartyBase.BOARDCODE_MINLAFF
+ elif inviteeOkay == REJECT_PROMOTION:
+ reason = BoardingPartyBase.BOARDCODE_PROMOTION
+ self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason, self.elevatorIdList[0]])
+ return
+ else:
+ inviterOkay = self.checkBoard(inviterId, self.elevatorIdList[0])
+ if inviterOkay:
+ if inviterOkay == REJECT_MINLAFF:
+ reason = BoardingPartyBase.BOARDCODE_MINLAFF
+ elif inviterOkay == REJECT_PROMOTION:
+ reason = BoardingPartyBase.BOARDCODE_PROMOTION
+ self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviterId, reason, self.elevatorIdList[0]])
+ return
+ if self.avIdDict.has_key(inviterId):
+ self.notify.debug('old group')
+ leaderId = self.avIdDict[inviterId]
+ groupList = self.groupListDict.get(leaderId)
+ if groupList:
+ self.notify.debug('got group list')
+ if inviterId == leaderId:
+ if inviteeId in groupList[2]:
+ groupList[2].remove(inviteeId)
+ if len(self.getGroupMemberList(leaderId)) >= self.maxSize:
+ self.sendUpdate('postSizeReject', [leaderId, inviterId, inviteeId])
+ elif inviterId not in groupList[1] and inviterId not in groupList[2]:
+ if inviteeId not in groupList[1]:
+ groupList[1].append(inviteeId)
+ self.groupListDict[leaderId] = groupList
+ if self.avIdDict.has_key(inviteeId):
+ self.notify.warning('inviter %s tried to invite %s who already exists in the avIdDict.' % (inviterId, inviteeId))
+ self.air.writeServerEvent('suspicious: inviter', inviterId, ' tried to invite %s who already exists in the avIdDict.' % inviteeId)
+ self.avIdDict[inviteeId] = leaderId
+ self.sendUpdateToAvatarId(inviteeId, 'postInvite', [leaderId, inviterId])
+ for memberId in groupList[0]:
+ if not memberId == inviterId:
+ self.sendUpdateToAvatarId(memberId, 'postMessageInvited', [inviteeId, inviterId])
+
+ elif inviterId in groupList[2]:
+ self.sendUpdate('postKickReject', [leaderId, inviterId, inviteeId])
+ else:
+ if self.avIdDict.has_key(inviteeId):
+ self.notify.warning('inviter %s tried to invite %s who already exists in avIdDict.' % (inviterId, inviteeId))
+ self.air.writeServerEvent('suspicious: inviter', inviterId, ' tried to invite %s who already exists in the avIdDict.' % inviteeId)
+ self.notify.debug('new group')
+ leaderId = inviterId
+ self.avIdDict[inviterId] = inviterId
+ self.avIdDict[inviteeId] = inviterId
+ self.groupListDict[leaderId] = [[leaderId], [inviteeId], []]
+ self.addWacthAvStatus(leaderId)
+ self.sendUpdateToAvatarId(inviteeId, 'postInvite', [leaderId, inviterId])
+
+ def requestCancelInvite(self, inviteeId):
+ inviterId = self.air.getAvatarIdFromSender()
+ if self.avIdDict.has_key(inviterId):
+ leaderId = self.avIdDict[inviterId]
+ groupList = self.groupListDict.get(leaderId)
+ if groupList:
+ self.removeFromGroup(leaderId, inviteeId)
+ self.sendUpdateToAvatarId(inviteeId, 'postInviteCanceled', [])
+
+ def requestAcceptInvite(self, leaderId, inviterId):
+ inviteeId = self.air.getAvatarIdFromSender()
+ self.notify.debug('requestAcceptInvite leader%s inviter%s invitee%s' % (leaderId, inviterId, inviteeId))
+ if self.avIdDict.has_key(inviteeId):
+ if self.hasActiveGroup(inviteeId):
+ self.sendUpdateToAvatarId(inviteeId, 'postAlreadyInGroup', [])
+ return
+ if not self.avIdDict.has_key(leaderId) or not self.isInGroup(inviteeId, leaderId):
+ self.sendUpdateToAvatarId(inviteeId, 'postSomethingMissing', [])
+ return
+ memberList = self.getGroupMemberList(leaderId)
+ if self.avIdDict[inviteeId]:
+ if self.avIdDict[inviteeId] == leaderId:
+ if inviteeId in memberList:
+ self.notify.debug('invitee already in group, aborting requestAcceptInvite')
+ return
+ else:
+ self.air.writeServerEvent('suspicious: ', inviteeId, " accepted a second invite from %s, in %s's group, while he was in alredy in %s's group." % (inviterId, leaderId, self.avIdDict[inviteeId]))
+ self.removeFromGroup(self.avIdDict[inviteeId], inviteeId, post=0)
+ if len(memberList) >= self.maxSize:
+ self.removeFromGroup(leaderId, inviteeId)
+ self.sendUpdateToAvatarId(inviterId, 'postMessageAcceptanceFailed', [inviteeId, BoardingPartyBase.INVITE_ACCEPT_FAIL_GROUP_FULL])
+ self.sendUpdateToAvatarId(inviteeId, 'postGroupAlreadyFull', [])
+ return
+ self.sendUpdateToAvatarId(inviterId, 'postInviteAccepted', [inviteeId])
+ self.addToGroup(leaderId, inviteeId)
+ else:
+ self.air.writeServerEvent('suspicious: ', inviteeId, " was invited to %s's group by %s, but the invitee didn't have an entry in the avIdDict." % (leaderId, inviterId))
+
+ def requestRejectInvite(self, leaderId, inviterId):
+ inviteeId = self.air.getAvatarIdFromSender()
+ self.removeFromGroup(leaderId, inviteeId)
+ self.sendUpdateToAvatarId(inviterId, 'postInviteDelcined', [inviteeId])
+
+ def requestKick(self, kickId):
+ leaderId = self.air.getAvatarIdFromSender()
+ if self.avIdDict.has_key(kickId):
+ if self.avIdDict[kickId] == leaderId:
+ self.removeFromGroup(leaderId, kickId, kick=1)
+ self.sendUpdateToAvatarId(kickId, 'postKick', [leaderId])
+
+ def requestLeave(self, leaderId):
+ memberId = self.air.getAvatarIdFromSender()
+ if self.avIdDict.has_key(memberId):
+ if leaderId == self.avIdDict[memberId]:
+ self.removeFromGroup(leaderId, memberId)
+
+ def checkBoard(self, avId, elevatorId):
+ elevator = simbase.air.doId2do.get(elevatorId)
+ avatar = simbase.air.doId2do.get(avId)
+ if avatar:
+ if not ToontownAccessAI.canAccess(avId, self.zoneId, 'DistributedBoardingPartyAI.checkBoard'):
+ return REJECT_NOTPAID
+ elif elevator:
+ return elevator.checkBoard(avatar)
+ return REJECT_BOARDINGPARTY
+
+ def testBoard(self, leaderId, elevatorId, needSpace=0):
+ elevator = None
+ boardOkay = BoardingPartyBase.BOARDCODE_MISSING
+ avatarsFailingRequirements = []
+ avatarsInBattle = []
+ if elevatorId in self.elevatorIdList:
+ elevator = simbase.air.doId2do.get(elevatorId)
+ if elevator:
+ if self.avIdDict.has_key(leaderId):
+ if leaderId == self.avIdDict[leaderId]:
+ boardOkay = BoardingPartyBase.BOARDCODE_OKAY
+ for avId in self.getGroupMemberList(leaderId):
+ avatar = simbase.air.doId2do.get(avId)
+ if avatar:
+ if elevator.checkBoard(avatar) != 0:
+ if elevator.checkBoard(avatar) == REJECT_MINLAFF:
+ boardOkay = BoardingPartyBase.BOARDCODE_MINLAFF
+ else:
+ if elevator.checkBoard(avatar) == REJECT_PROMOTION:
+ boardOkay = BoardingPartyBase.BOARDCODE_PROMOTION
+ avatarsFailingRequirements.append(avId)
+ elif avatar.battleId != 0:
+ boardOkay = BoardingPartyBase.BOARDCODE_BATTLE
+ avatarsInBattle.append(avId)
+
+ groupSize = len(self.getGroupMemberList(leaderId))
+ if groupSize > self.maxSize:
+ boardOkay = BoardingPartyBase.BOARDCODE_SPACE
+ if needSpace:
+ if groupSize > elevator.countOpenSeats():
+ boardOkay = BoardingPartyBase.BOARDCODE_SPACE
+ if boardOkay != BoardingPartyBase.BOARDCODE_OKAY:
+ self.notify.debug('Something is wrong with the group board request')
+ if boardOkay == BoardingPartyBase.BOARDCODE_MINLAFF:
+ self.notify.debug('An avatar did not meet the elevator laff requirements')
+ if boardOkay == BoardingPartyBase.BOARDCODE_PROMOTION:
+ self.notify.debug('An avatar did not meet the elevator promotion requirements')
+ elif boardOkay == BoardingPartyBase.BOARDCODE_BATTLE:
+ self.notify.debug('An avatar is in battle')
+ return (boardOkay, avatarsFailingRequirements, avatarsInBattle)
+
+ def requestBoard(self, elevatorId):
+ wantDisableGoButton = False
+ leaderId = self.air.getAvatarIdFromSender()
+ elevator = None
+ if elevatorId in self.elevatorIdList:
+ elevator = simbase.air.doId2do.get(elevatorId)
+ if elevator:
+ if self.avIdDict.has_key(leaderId):
+ if leaderId == self.avIdDict[leaderId]:
+ group = self.groupListDict.get(leaderId)
+ if group:
+ boardOkay, avatarsFailingRequirements, avatarsInBattle = self.testBoard(leaderId, elevatorId, needSpace=1)
+ if boardOkay == BoardingPartyBase.BOARDCODE_OKAY:
+ leader = simbase.air.doId2do.get(leaderId)
+ if leader:
+ elevator.partyAvatarBoard(leader)
+ wantDisableGoButton = True
+ for avId in group[0]:
+ if not avId == leaderId:
+ avatar = simbase.air.doId2do.get(avId)
+ if avatar:
+ elevator.partyAvatarBoard(avatar, wantBoardingShow=1)
+
+ self.air.writeServerEvent('boarding_elevator', self.zoneId, '%s; Sending avatars %s' % (elevatorId, group[0]))
+ else:
+ self.sendUpdateToAvatarId(leaderId, 'postRejectBoard', [elevatorId, boardOkay, avatarsFailingRequirements, avatarsInBattle])
+ return
+ if not wantDisableGoButton:
+ self.sendUpdateToAvatarId(leaderId, 'postRejectBoard', [elevatorId, BoardingPartyBase.BOARDCODE_MISSING, [], []])
+ return
+
+ def testGoButtonRequirements(self, leaderId, elevatorId):
+ if self.avIdDict.has_key(leaderId):
+ if leaderId == self.avIdDict[leaderId]:
+ if elevatorId in self.elevatorIdList:
+ elevator = simbase.air.doId2do.get(elevatorId)
+ if elevator:
+ boardOkay, avatarsFailingRequirements, avatarsInBattle = self.testBoard(leaderId, elevatorId, needSpace=0)
+ if boardOkay == BoardingPartyBase.BOARDCODE_OKAY:
+ avList = self.getGroupMemberList(leaderId)
+ if 0 in avList:
+ avList.remove(0)
+ if leaderId not in elevator.seats:
+ return True
+ else:
+ self.notify.warning('avId: %s has hacked his/her client.' % leaderId)
+ self.air.writeServerEvent('suspicious: ', leaderId, ' pressed the GO Button while inside the elevator.')
+ else:
+ self.sendUpdateToAvatarId(leaderId, 'rejectGoToRequest', [elevatorId, boardOkay, avatarsFailingRequirements, avatarsInBattle])
+ return False
+
+ def requestGoToFirstTime(self, elevatorId):
+ callerId = self.air.getAvatarIdFromSender()
+ if self.testGoButtonRequirements(callerId, elevatorId):
+ self.sendUpdateToAvatarId(callerId, 'acceptGoToFirstTime', [elevatorId])
+
+ def requestGoToSecondTime(self, elevatorId):
+ callerId = self.air.getAvatarIdFromSender()
+ avList = self.getGroupMemberList(callerId)
+ if self.testGoButtonRequirements(callerId, elevatorId):
+ for avId in avList:
+ self.sendUpdateToAvatarId(avId, 'acceptGoToSecondTime', [elevatorId])
+
+ THREE_SECONDS = 3.0
+ taskMgr.doMethodLater(THREE_SECONDS, self.sendAvatarsToDestinationTask, self.uniqueName('sendAvatarsToDestinationTask'), extraArgs=[elevatorId, avList], appendTask=True)
+
+ def sendAvatarsToDestinationTask(self, elevatorId, avList, task):
+ self.notify.debug('entering sendAvatarsToDestinationTask')
+ if len(avList):
+ if elevatorId in self.elevatorIdList:
+ elevator = simbase.air.doId2do.get(elevatorId)
+ if elevator:
+ self.notify.warning('Sending avatars %s' % avList)
+ boardOkay, avatarsFailingRequirements, avatarsInBattle = self.testBoard(avList[0], elevatorId, needSpace=0)
+ if not boardOkay == BoardingPartyBase.BOARDCODE_OKAY:
+ for avId in avatarsFailingRequirements:
+ self.air.writeServerEvent('suspicious: ', avId, ' failed requirements after the second go button request.')
+
+ for avId in avatarsInBattle:
+ self.air.writeServerEvent('suspicious: ', avId, ' joined battle after the second go button request.')
+
+ self.air.writeServerEvent('boarding_go', self.zoneId, '%s; Sending avatars %s' % (elevatorId, avList))
+ elevator.sendAvatarsToDestination(avList)
+ return Task.done
+
+ def handleAvatarDisco(self, avId):
+ self.notify.debug('handleAvatarDisco %s' % avId)
+ if self.avIdDict.has_key(avId):
+ leaderId = self.avIdDict[avId]
+ self.removeFromGroup(leaderId, avId)
+
+ def handleAvatarZoneChange(self, avId, zoneNew, zoneOld):
+ self.notify.debug('handleAvatarZoneChange %s new%s old%s bp%s' % (avId, zoneNew, zoneOld, self.zoneId))
+ if zoneNew in self.visibleZones:
+ self.toonInZone(avId)
+ else:
+ if self.avIdDict.has_key(avId):
+ leaderId = self.avIdDict[avId]
+ self.removeFromGroup(leaderId, avId)
+
+ def toonInZone(self, avId):
+ if self.avIdDict.has_key(avId):
+ leaderId = self.avIdDict[avId]
+ group = self.groupListDict.get(leaderId)
+ if leaderId and group:
+ self.notify.debug('Calling postGroupInfo from toonInZone')
+
+ def addToGroup(self, leaderId, inviteeId, post=1):
+ group = self.groupListDict.get(leaderId)
+ if group:
+ self.avIdDict[inviteeId] = leaderId
+ if inviteeId in group[1]:
+ group[1].remove(inviteeId)
+ if inviteeId not in group[0]:
+ group[0].append(inviteeId)
+ self.groupListDict[leaderId] = group
+ if post:
+ self.notify.debug('Calling postGroupInfo from addToGroup')
+ self.sendUpdate('postGroupInfo', [leaderId, group[0], group[1], group[2]])
+ self.addWacthAvStatus(inviteeId)
+ else:
+ self.sendUpdate('postGroupDissolve', [leaderId, leaderId, [], 0])
+
+ def removeFromGroup(self, leaderId, memberId, kick=0, post=1):
+ self.notify.debug('')
+ self.notify.debug('removeFromGroup leaderId %s memberId %s' % (leaderId, memberId))
+ self.notify.debug('Groups %s' % self.groupListDict)
+ self.notify.debug('avDict %s' % self.avIdDict)
+ if not self.avIdDict.has_key(leaderId):
+ self.sendUpdate('postGroupDissolve', [memberId, leaderId, [], kick])
+ if self.avIdDict.has_key(memberId):
+ self.avIdDict.pop(memberId)
+ return
+ self.removeWacthAvStatus(memberId)
+ group = self.groupListDict.get(leaderId)
+ if group:
+ if memberId in group[0]:
+ group[0].remove(memberId)
+ if memberId in group[1]:
+ group[1].remove(memberId)
+ if memberId in group[2]:
+ group[2].remove(memberId)
+ if kick:
+ group[2].append(memberId)
+ else:
+ return
+ if memberId == leaderId or len(group[0]) < 2:
+ if self.avIdDict.has_key(leaderId):
+ self.avIdDict.pop(leaderId)
+ for inviteeId in group[1]:
+ if self.avIdDict.has_key(inviteeId):
+ self.avIdDict.pop(inviteeId)
+ self.sendUpdateToAvatarId(inviteeId, 'postInviteCanceled', [])
+
+ dgroup = self.groupListDict.pop(leaderId)
+ for dMemberId in dgroup[0]:
+ if self.avIdDict.has_key(dMemberId):
+ self.avIdDict.pop(dMemberId)
+
+ self.notify.debug('postGroupDissolve')
+ dgroup[0].insert(0, memberId)
+ self.sendUpdate('postGroupDissolve', [memberId, leaderId, dgroup[0], kick])
+ else:
+ self.groupListDict[leaderId] = group
+ if post:
+ self.notify.debug('Calling postGroupInfo from removeFromGroup')
+ self.sendUpdate('postGroupInfo', [leaderId, group[0], group[1], group[2]])
+ if self.avIdDict.has_key(memberId):
+ self.avIdDict.pop(memberId)
+ self.notify.debug('Remove from group END')
+ self.notify.debug('Groups %s' % self.groupListDict)
+ self.notify.debug('avDict %s' % self.avIdDict)
+ self.notify.debug('')
+
+ def informDestinationInfo(self, offset):
+ leaderId = self.air.getAvatarIdFromSender()
+ if offset > len(self.elevatorIdList):
+ self.air.writeServerEvent('suspicious: ', leaderId, 'has requested to go to %s elevator which does not exist' % offset)
+ return
+ memberList = self.getGroupMemberList(leaderId)
+ for avId in memberList:
+ if avId != leaderId:
+ self.sendUpdateToAvatarId(avId, 'postDestinationInfo', [offset])
+
+ def __isInElevator(self, avId):
+ inElevator = False
+ for elevatorId in self.elevatorIdList:
+ elevator = simbase.air.doId2do.get(elevatorId)
+ if elevator:
+ if avId in elevator.seats:
+ inElevator = True
+
+ return inElevator
diff --git a/toontown/building/DistributedBossElevator.py b/toontown/building/DistributedBossElevator.py
new file mode 100644
index 0000000..7a89eb1
--- /dev/null
+++ b/toontown/building/DistributedBossElevator.py
@@ -0,0 +1,91 @@
+from pandac.PandaModules import *
+from direct.distributed.ClockDelta import *
+from direct.interval.IntervalGlobal import *
+from ElevatorConstants import *
+from ElevatorUtils import *
+import DistributedElevator
+import DistributedElevatorExt
+from toontown.toonbase import ToontownGlobals
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from toontown.hood import ZoneUtil
+from toontown.toonbase import TTLocalizer
+from toontown.toontowngui import TTDialog
+
+class DistributedBossElevator(DistributedElevatorExt.DistributedElevatorExt):
+
+ def __init__(self, cr):
+ DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr)
+ self.elevatorPoints = BigElevatorPoints
+ self.openSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.mp3')
+ self.finalOpenSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.mp3')
+ self.closeSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.mp3')
+ self.finalCloseSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.mp3')
+ self.type = ELEVATOR_VP
+ self.countdownTime = ElevatorData[self.type]['countdown']
+
+ def disable(self):
+ DistributedElevator.DistributedElevator.disable(self)
+
+ def generate(self):
+ DistributedElevatorExt.DistributedElevatorExt.generate(self)
+
+ def delete(self):
+ self.elevatorModel.removeNode()
+ del self.elevatorModel
+ DistributedElevatorExt.DistributedElevatorExt.delete(self)
+
+ def setupElevator(self):
+ self.elevatorModel = loader.loadModel('phase_9/models/cogHQ/cogHQ_elevator')
+ icon = self.elevatorModel.find('**/big_frame/')
+ icon.hide()
+ self.leftDoor = self.elevatorModel.find('**/left-door')
+ self.rightDoor = self.elevatorModel.find('**/right-door')
+ geom = base.cr.playGame.hood.loader.geom
+ locator = geom.find('**/elevator_locator')
+ self.elevatorModel.reparentTo(locator)
+ self.elevatorModel.setH(180)
+ DistributedElevator.DistributedElevator.setupElevator(self)
+
+ def getElevatorModel(self):
+ return self.elevatorModel
+
+ def gotBldg(self, buildingList):
+ return DistributedElevator.DistributedElevator.gotBldg(self, buildingList)
+
+ def getZoneId(self):
+ return 0
+
+ def __doorsClosed(self, zoneId):
+ pass
+
+ def setBossOfficeZone(self, zoneId):
+ if self.localToonOnBoard:
+ hoodId = self.cr.playGame.hood.hoodId
+ doneStatus = {'loader': 'cogHQLoader',
+ 'where': 'cogHQBossBattle',
+ 'how': 'movie',
+ 'zoneId': zoneId,
+ 'hoodId': hoodId}
+ self.cr.playGame.getPlace().elevator.signalDone(doneStatus)
+
+ def setBossOfficeZoneForce(self, zoneId):
+ place = self.cr.playGame.getPlace()
+ if place:
+ place.fsm.request('elevator', [self, 1])
+ hoodId = self.cr.playGame.hood.hoodId
+ doneStatus = {'loader': 'cogHQLoader',
+ 'where': 'cogHQBossBattle',
+ 'how': 'movie',
+ 'zoneId': zoneId,
+ 'hoodId': hoodId}
+ if hasattr(place, 'elevator') and place.elevator:
+ place.elevator.signalDone(doneStatus)
+ else:
+ self.notify.warning("setMintInteriorZoneForce: Couldn't find playGame.getPlace().elevator, zoneId: %s" % zoneId)
+ else:
+ self.notify.warning("setBossOfficeZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" % zoneId)
+
+ def getDestName(self):
+ return TTLocalizer.ElevatorSellBotBoss
diff --git a/toontown/building/DistributedBossElevatorAI.py b/toontown/building/DistributedBossElevatorAI.py
new file mode 100644
index 0000000..cf06a8a
--- /dev/null
+++ b/toontown/building/DistributedBossElevatorAI.py
@@ -0,0 +1,75 @@
+from otp.ai.AIBase import *
+from toontown.toonbase import ToontownGlobals
+from direct.distributed.ClockDelta import *
+from ElevatorConstants import *
+import DistributedElevatorAI, DistributedElevatorExtAI
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+from toontown.suit import DistributedSellbotBossAI
+
+class DistributedBossElevatorAI(DistributedElevatorExtAI.DistributedElevatorExtAI):
+
+ def __init__(self, air, bldg, zone, antiShuffle=0, minLaff=0):
+ DistributedElevatorExtAI.DistributedElevatorExtAI.__init__(self, air, bldg, numSeats=8, antiShuffle=antiShuffle, minLaff=minLaff)
+ self.zone = zone
+ self.type = ELEVATOR_VP
+ self.countdownTime = ElevatorData[self.type]['countdown']
+
+ def elevatorClosed(self):
+ numPlayers = self.countFullSeats()
+ if numPlayers > 0:
+ bossZone = self.bldg.createBossOffice(self.seats)
+ for seatIndex in range(len(self.seats)):
+ avId = self.seats[seatIndex]
+ if avId:
+ self.sendUpdateToAvatarId(avId, 'setBossOfficeZone', [
+ bossZone])
+ self.clearFullNow(seatIndex)
+
+ else:
+ self.notify.warning('The elevator left, but was empty.')
+ self.fsm.request('closed')
+
+ def sendAvatarsToDestination(self, avIdList):
+ if len(avIdList) > 0:
+ bossZone = self.bldg.createBossOffice(avIdList)
+ for avId in avIdList:
+ if avId:
+ self.sendUpdateToAvatarId(avId, 'setBossOfficeZoneForce', [
+ bossZone])
+
+ def enterClosing(self):
+ DistributedElevatorAI.DistributedElevatorAI.enterClosing(self)
+ taskMgr.doMethodLater(ElevatorData[self.type]['closeTime'], self.elevatorClosedTask, self.uniqueName('closing-timer'))
+
+ def enterClosed(self):
+ DistributedElevatorExtAI.DistributedElevatorExtAI.enterClosed(self)
+ self.fsm.request('opening')
+
+ def enterOpening(self):
+ DistributedElevatorAI.DistributedElevatorAI.enterOpening(self)
+ taskMgr.doMethodLater(ElevatorData[self.type]['openTime'], self.waitEmptyTask, self.uniqueName('opening-timer'))
+
+ def checkBoard(self, av):
+ dept = ToontownGlobals.cogHQZoneId2deptIndex(self.zone)
+ if av.hp < self.minLaff:
+ return REJECT_MINLAFF
+ if not av.readyForPromotion(dept):
+ return REJECT_PROMOTION
+ return 0
+
+ def requestBoard(self, *args):
+ self.notify.debug('requestBoard')
+ avId = self.air.getAvatarIdFromSender()
+ av = self.air.doId2do.get(avId)
+ if av:
+ boardResponse = self.checkBoard(av)
+ newArgs = (avId,) + args + (boardResponse,)
+ if boardResponse == 0:
+ self.acceptingBoardersHandler(*newArgs)
+ else:
+ self.rejectingBoardersHandler(*newArgs)
+ else:
+ self.notify.warning('avid: %s does not exist, but tried to board an elevator' % avId)
diff --git a/toontown/building/DistributedBuilding.py b/toontown/building/DistributedBuilding.py
new file mode 100644
index 0000000..e5e19cb
--- /dev/null
+++ b/toontown/building/DistributedBuilding.py
@@ -0,0 +1,956 @@
+from pandac.PandaModules import *
+from direct.distributed.ClockDelta import *
+from direct.interval.IntervalGlobal import *
+from direct.directtools.DirectGeometry import *
+from ElevatorConstants import *
+from ElevatorUtils import *
+from SuitBuildingGlobals import *
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from toontown.toonbase import ToontownGlobals
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObject
+import random
+from toontown.suit import SuitDNA
+from toontown.toonbase import TTLocalizer
+from toontown.distributed import DelayDelete
+from toontown.toon import TTEmote
+from otp.avatar import Emote
+from toontown.hood import ZoneUtil
+FO_DICT = {'s': 'tt_m_ara_cbe_fieldOfficeMoverShaker',
+ 'l': 'tt_m_ara_cbe_fieldOfficeMoverShaker',
+ 'm': 'tt_m_ara_cbe_fieldOfficeMoverShaker',
+ 'c': 'tt_m_ara_cbe_fieldOfficeMoverShaker'}
+
+class DistributedBuilding(DistributedObject.DistributedObject):
+ SUIT_INIT_HEIGHT = 125
+ TAKEOVER_SFX_PREFIX = 'phase_5/audio/sfx/'
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.interactiveProp = None
+ self.suitDoorOrigin = None
+ self.elevatorModel = None
+ self.fsm = ClassicFSM.ClassicFSM('DistributedBuilding', [State.State('off', self.enterOff, self.exitOff, ['waitForVictors',
+ 'waitForVictorsFromCogdo',
+ 'becomingToon',
+ 'becomingToonFromCogdo',
+ 'toon',
+ 'clearOutToonInterior',
+ 'becomingSuit',
+ 'suit',
+ 'clearOutToonInteriorForCogdo',
+ 'becomingCogdo',
+ 'becomingCogdoFromCogdo',
+ 'cogdo']),
+ State.State('waitForVictors', self.enterWaitForVictors, self.exitWaitForVictors, ['becomingToon']),
+ State.State('waitForVictorsFromCogdo', self.enterWaitForVictorsFromCogdo, self.exitWaitForVictorsFromCogdo, ['becomingToonFromCogdo', 'becomingCogdoFromCogdo']),
+ State.State('becomingToon', self.enterBecomingToon, self.exitBecomingToon, ['toon']),
+ State.State('becomingToonFromCogdo', self.enterBecomingToonFromCogdo, self.exitBecomingToonFromCogdo, ['toon']),
+ State.State('toon', self.enterToon, self.exitToon, ['clearOutToonInterior', 'clearOutToonInteriorForCogdo']),
+ State.State('clearOutToonInterior', self.enterClearOutToonInterior, self.exitClearOutToonInterior, ['becomingSuit']),
+ State.State('becomingSuit', self.enterBecomingSuit, self.exitBecomingSuit, ['suit']),
+ State.State('suit', self.enterSuit, self.exitSuit, ['waitForVictors', 'becomingToon']),
+ State.State('clearOutToonInteriorForCogdo', self.enterClearOutToonInteriorForCogdo, self.exitClearOutToonInteriorForCogdo, ['becomingCogdo']),
+ State.State('becomingCogdo', self.enterBecomingCogdo, self.exitBecomingCogdo, ['cogdo']),
+ State.State('becomingCogdoFromCogdo', self.enterBecomingCogdoFromCogdo, self.exitBecomingCogdoFromCogdo, ['cogdo']),
+ State.State('cogdo', self.enterCogdo, self.exitCogdo, ['waitForVictorsFromCogdo', 'becomingToonFromCogdo'])], 'off', 'off')
+ self.fsm.enterInitialState()
+ self.bossLevel = 0
+ self.transitionTrack = None
+ self.elevatorNodePath = None
+ self.victorList = [0,
+ 0,
+ 0,
+ 0]
+ self.waitingMessage = None
+ self.cogDropSound = None
+ self.cogLandSound = None
+ self.cogSettleSound = None
+ self.cogWeakenSound = None
+ self.toonGrowSound = None
+ self.toonSettleSound = None
+ return
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+ self.mode = 'toon'
+ self.townTopLevel = self.cr.playGame.hood.loader.geom
+
+ def disable(self):
+ self.fsm.request('off')
+ del self.townTopLevel
+ self.stopTransition()
+ DistributedObject.DistributedObject.disable(self)
+
+ def delete(self):
+ if self.elevatorNodePath:
+ self.elevatorNodePath.removeNode()
+ del self.elevatorNodePath
+ del self.elevatorModel
+ if hasattr(self, 'cab'):
+ del self.cab
+ del self.leftDoor
+ del self.rightDoor
+ del self.suitDoorOrigin
+ self.cleanupSuitBuilding()
+ self.unloadSfx()
+ del self.fsm
+ DistributedObject.DistributedObject.delete(self)
+
+ def setBlock(self, block, interiorZoneId):
+ self.block = block
+ self.interiorZoneId = interiorZoneId
+
+ def setSuitData(self, suitTrack, difficulty, numFloors):
+ self.track = suitTrack
+ self.difficulty = difficulty
+ self.numFloors = numFloors
+
+ def setState(self, state, timestamp):
+ self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])
+
+ def getSuitElevatorNodePath(self):
+ if self.mode != 'suit':
+ self.setToSuit()
+ return self.elevatorNodePath
+
+ def getCogdoElevatorNodePath(self):
+ if self.mode != 'cogdo':
+ self.setToCogdo()
+ return self.elevatorNodePath
+
+ def getSuitDoorOrigin(self):
+ if self.mode != 'suit':
+ self.setToSuit()
+ return self.suitDoorOrigin
+
+ def getCogdoDoorOrigin(self):
+ if self.mode != 'cogdo':
+ self.setToCogdo()
+ return self.suitDoorOrigin
+
+ def getBossLevel(self):
+ return self.bossLevel
+
+ def setBossLevel(self, bossLevel):
+ self.bossLevel = bossLevel
+
+ def setVictorList(self, victorList):
+ self.victorList = victorList
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterWaitForVictors(self, ts):
+ if self.mode != 'suit':
+ self.setToSuit()
+ victorCount = self.victorList.count(base.localAvatar.doId)
+ if victorCount == 1:
+ self.acceptOnce('insideVictorElevator', self.handleInsideVictorElevator)
+ camera.reparentTo(render)
+ camera.setPosHpr(self.elevatorNodePath, 0, -32.5, 9.4, 0, 348, 0)
+ base.camLens.setFov(52.0)
+ anyOthers = 0
+ for v in self.victorList:
+ if v != 0 and v != base.localAvatar.doId:
+ anyOthers = 1
+
+ if anyOthers:
+ self.waitingMessage = DirectLabel(text=TTLocalizer.BuildingWaitingForVictors, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.35), scale=0.1)
+ elif victorCount == 0:
+ pass
+ else:
+ self.error('localToon is on the victorList %d times' % victorCount)
+ closeDoors(self.leftDoor, self.rightDoor)
+ for light in self.floorIndicator:
+ if light != None:
+ light.setColor(LIGHT_OFF_COLOR)
+
+ return
+
+ def handleInsideVictorElevator(self):
+ self.notify.info('inside victor elevator')
+ self.sendUpdate('setVictorReady', [])
+
+ def exitWaitForVictors(self):
+ self.ignore('insideVictorElevator')
+ if self.waitingMessage != None:
+ self.waitingMessage.destroy()
+ self.waitingMessage = None
+ return
+
+ def enterWaitForVictorsFromCogdo(self, ts):
+ if self.mode != 'cogdo':
+ self.setToCogdo()
+ victorCount = self.victorList.count(base.localAvatar.doId)
+ if victorCount == 1:
+ self.acceptOnce('insideVictorElevator', self.handleInsideVictorElevatorFromCogdo)
+ camera.reparentTo(render)
+ camera.setPosHpr(self.elevatorNodePath, 0, -32.5, 9.4, 0, 348, 0)
+ base.camLens.setFov(52.0)
+ anyOthers = 0
+ for v in self.victorList:
+ if v != 0 and v != base.localAvatar.doId:
+ anyOthers = 1
+
+ if anyOthers:
+ self.waitingMessage = DirectLabel(text=TTLocalizer.BuildingWaitingForVictors, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.35), scale=0.1)
+ elif victorCount == 0:
+ pass
+ else:
+ self.error('localToon is on the victorList %d times' % victorCount)
+ closeDoors(self.leftDoor, self.rightDoor)
+ for light in self.floorIndicator:
+ if light != None:
+ light.setColor(LIGHT_OFF_COLOR)
+
+ return
+
+ def handleInsideVictorElevatorFromCogdo(self):
+ self.sendUpdate('setVictorReady', [])
+
+ def exitWaitForVictorsFromCogdo(self):
+ self.ignore('insideVictorElevator')
+ if self.waitingMessage != None:
+ self.waitingMessage.destroy()
+ self.waitingMessage = None
+ return
+
+ def enterBecomingToon(self, ts):
+ self.animToToon(ts)
+
+ def exitBecomingToon(self):
+ pass
+
+ def enterBecomingToonFromCogdo(self, ts):
+ self.animToToonFromCogdo(ts)
+
+ def exitBecomingToonFromCogdo(self):
+ pass
+
+ def enterToon(self, ts):
+ if self.getInteractiveProp():
+ self.getInteractiveProp().buildingLiberated(self.doId)
+ self.setToToon()
+
+ def exitToon(self):
+ pass
+
+ def enterClearOutToonInterior(self, ts):
+ pass
+
+ def exitClearOutToonInterior(self):
+ pass
+
+ def enterBecomingSuit(self, ts):
+ self.animToSuit(ts)
+
+ def exitBecomingSuit(self):
+ pass
+
+ def enterSuit(self, ts):
+ self.makePropSad()
+ self.setToSuit()
+
+ def exitSuit(self):
+ pass
+
+ def enterClearOutToonInteriorForCogdo(self, ts):
+ pass
+
+ def exitClearOutToonInteriorForCogdo(self):
+ pass
+
+ def enterBecomingCogdo(self, ts):
+ self.animToCogdo(ts)
+
+ def exitBecomingCogdo(self):
+ pass
+
+ def enterBecomingCogdoFromCogdo(self, ts):
+ self.animToCogdoFromCogdo(ts)
+
+ def exitBecomingCogdoFromCogdo(self):
+ pass
+
+ def enterCogdo(self, ts):
+ self.setToCogdo()
+
+ def exitCogdo(self):
+ pass
+
+ def getNodePaths(self):
+ nodePath = []
+ npc = self.townTopLevel.findAllMatches('**/?b' + str(self.block) + ':*_DNARoot;+s')
+ for i in range(npc.getNumPaths()):
+ nodePath.append(npc.getPath(i))
+
+ return nodePath
+
+ def loadElevator(self, newNP, cogdo = False):
+ self.floorIndicator = [None,
+ None,
+ None,
+ None,
+ None]
+ self.elevatorNodePath = hidden.attachNewNode('elevatorNodePath')
+ if cogdo:
+ self.elevatorModel = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_csa_elevatorB')
+ else:
+ self.elevatorModel = loader.loadModel('phase_4/models/modules/elevator')
+ npc = self.elevatorModel.findAllMatches('**/floor_light_?;+s')
+ for i in range(npc.getNumPaths()):
+ np = npc.getPath(i)
+ floor = int(np.getName()[-1:]) - 1
+ self.floorIndicator[floor] = np
+ if floor < self.numFloors:
+ np.setColor(LIGHT_OFF_COLOR)
+ else:
+ np.hide()
+
+ self.elevatorModel.reparentTo(self.elevatorNodePath)
+ if self.mode == 'suit':
+ self.cab = self.elevatorModel.find('**/elevator')
+ cogIcons = loader.loadModel('phase_3/models/gui/cog_icons')
+ dept = chr(self.track)
+ if dept == 'c':
+ corpIcon = cogIcons.find('**/CorpIcon').copyTo(self.cab)
+ elif dept == 's':
+ corpIcon = cogIcons.find('**/SalesIcon').copyTo(self.cab)
+ elif dept == 'l':
+ corpIcon = cogIcons.find('**/LegalIcon').copyTo(self.cab)
+ elif dept == 'm':
+ corpIcon = cogIcons.find('**/MoneyIcon').copyTo(self.cab)
+ corpIcon.setPos(0, 6.79, 6.8)
+ corpIcon.setScale(3)
+ from toontown.suit import Suit
+ corpIcon.setColor(Suit.Suit.medallionColors[dept])
+ cogIcons.removeNode()
+ self.leftDoor = self.elevatorModel.find('**/left-door')
+ if self.leftDoor.isEmpty():
+ self.leftDoor = self.elevatorModel.find('**/left_door')
+ self.rightDoor = self.elevatorModel.find('**/right-door')
+ if self.rightDoor.isEmpty():
+ self.rightDoor = self.elevatorModel.find('**/right_door')
+ self.suitDoorOrigin = newNP.find('**/*_door_origin')
+ self.elevatorNodePath.reparentTo(self.suitDoorOrigin)
+ self.normalizeElevator()
+ return
+
+ def loadAnimToSuitSfx(self):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: COGBUILDING: Cog Take Over')
+ if self.cogDropSound == None:
+ self.cogDropSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'cogbldg_drop.mp3')
+ self.cogLandSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'cogbldg_land.mp3')
+ self.cogSettleSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'cogbldg_settle.mp3')
+ self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.mp3')
+ return
+
+ def loadAnimToToonSfx(self):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: COGBUILDING: Toon Take Over')
+ if self.cogWeakenSound == None:
+ self.cogWeakenSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'cogbldg_weaken.mp3')
+ self.toonGrowSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'toonbldg_grow.mp3')
+ self.toonSettleSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'toonbldg_settle.mp3')
+ self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.mp3')
+ return
+
+ def unloadSfx(self):
+ if self.cogDropSound != None:
+ self.cogDropSound = None
+ self.cogLandSound = None
+ self.cogSettleSound = None
+ self.openSfx = None
+ if self.cogWeakenSound != None:
+ self.cogWeakenSound = None
+ self.toonGrowSound = None
+ self.toonSettleSound = None
+ self.openSfx = None
+ return
+
+ def _deleteTransitionTrack(self):
+ if self.transitionTrack:
+ DelayDelete.cleanupDelayDeletes(self.transitionTrack)
+ self.transitionTrack = None
+ return
+
+ def animToSuit(self, timeStamp):
+ self.stopTransition()
+ if self.mode != 'toon':
+ self.setToToon()
+ self.loadAnimToSuitSfx()
+ sideBldgNodes = self.getNodePaths()
+ nodePath = hidden.find(self.getSbSearchString())
+ newNP = self.setupSuitBuilding(nodePath)
+ closeDoors(self.leftDoor, self.rightDoor)
+ newNP.stash()
+ sideBldgNodes.append(newNP)
+ soundPlayed = 0
+ tracks = Parallel(name=self.taskName('toSuitTrack'))
+ for i in sideBldgNodes:
+ name = i.getName()
+ timeForDrop = TO_SUIT_BLDG_TIME * 0.85
+ if name[0] == 's':
+ showTrack = Sequence(name=self.taskName('ToSuitFlatsTrack') + '-' + str(sideBldgNodes.index(i)))
+ initPos = Point3(0, 0, self.SUIT_INIT_HEIGHT) + i.getPos()
+ showTrack.append(Func(i.setPos, initPos))
+ showTrack.append(Func(i.unstash))
+ if i == sideBldgNodes[len(sideBldgNodes) - 1]:
+ showTrack.append(Func(self.normalizeElevator))
+ if not soundPlayed:
+ showTrack.append(Func(base.playSfx, self.cogDropSound, 0, 1, None, 0.0))
+ showTrack.append(LerpPosInterval(i, timeForDrop, i.getPos(), name=self.taskName('ToSuitAnim') + '-' + str(sideBldgNodes.index(i))))
+ if not soundPlayed:
+ showTrack.append(Func(base.playSfx, self.cogLandSound, 0, 1, None, 0.0))
+ showTrack.append(self.createBounceTrack(i, 2, 0.65, TO_SUIT_BLDG_TIME - timeForDrop, slowInitBounce=1.0))
+ if not soundPlayed:
+ showTrack.append(Func(base.playSfx, self.cogSettleSound, 0, 1, None, 0.0))
+ tracks.append(showTrack)
+ if not soundPlayed:
+ soundPlayed = 1
+ elif name[0] == 't':
+ hideTrack = Sequence(name=self.taskName('ToSuitToonFlatsTrack'))
+ timeTillSquish = (self.SUIT_INIT_HEIGHT - 20.0) / self.SUIT_INIT_HEIGHT
+ timeTillSquish *= timeForDrop
+ hideTrack.append(LerpFunctionInterval(self.adjustColorScale, fromData=1, toData=0.25, duration=timeTillSquish, extraArgs=[i]))
+ hideTrack.append(LerpScaleInterval(i, timeForDrop - timeTillSquish, Vec3(1, 1, 0.01)))
+ hideTrack.append(Func(i.stash))
+ hideTrack.append(Func(i.setScale, Vec3(1)))
+ hideTrack.append(Func(i.clearColorScale))
+ tracks.append(hideTrack)
+
+ self.stopTransition()
+ self._deleteTransitionTrack()
+ self.transitionTrack = tracks
+ self.transitionTrack.start(timeStamp)
+ return
+
+ def setupSuitBuilding(self, nodePath):
+ dnaStore = self.cr.playGame.dnaStore
+ level = int(self.difficulty / 2) + 1
+ suitNP = dnaStore.findNode('suit_landmark_' + chr(self.track) + str(level))
+ zoneId = dnaStore.getZoneFromBlockNumber(self.block)
+ zoneId = ZoneUtil.getTrueZoneId(zoneId, self.interiorZoneId)
+ newParentNP = base.cr.playGame.hood.loader.zoneDict[zoneId]
+ suitBuildingNP = suitNP.copyTo(newParentNP)
+ buildingTitle = dnaStore.getTitleFromBlockNumber(self.block)
+ if not buildingTitle:
+ buildingTitle = TTLocalizer.CogsInc
+ else:
+ buildingTitle += TTLocalizer.CogsIncExt
+ buildingTitle += '\n%s' % SuitDNA.getDeptFullname(chr(self.track))
+ textNode = TextNode('sign')
+ textNode.setTextColor(1.0, 1.0, 1.0, 1.0)
+ textNode.setFont(ToontownGlobals.getSuitFont())
+ textNode.setAlign(TextNode.ACenter)
+ textNode.setWordwrap(17.0)
+ textNode.setText(buildingTitle)
+ textHeight = textNode.getHeight()
+ zScale = (textHeight + 2) / 3.0
+ signOrigin = suitBuildingNP.find('**/sign_origin;+s')
+ backgroundNP = loader.loadModel('phase_5/models/modules/suit_sign')
+ backgroundNP.reparentTo(signOrigin)
+ backgroundNP.setPosHprScale(0.0, 0.0, textHeight * 0.8 / zScale, 0.0, 0.0, 0.0, 8.0, 8.0, 8.0 * zScale)
+ backgroundNP.node().setEffect(DecalEffect.make())
+ signTextNodePath = backgroundNP.attachNewNode(textNode.generate())
+ signTextNodePath.setPosHprScale(0.0, 0.0, -0.21 + textHeight * 0.1 / zScale, 0.0, 0.0, 0.0, 0.1, 0.1, 0.1 / zScale)
+ signTextNodePath.setColor(1.0, 1.0, 1.0, 1.0)
+ frontNP = suitBuildingNP.find('**/*_front/+GeomNode;+s')
+ backgroundNP.wrtReparentTo(frontNP)
+ frontNP.node().setEffect(DecalEffect.make())
+ suitBuildingNP.setName('sb' + str(self.block) + ':_landmark__DNARoot')
+ suitBuildingNP.setPosHprScale(nodePath, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0)
+ suitBuildingNP.flattenMedium()
+ self.loadElevator(suitBuildingNP)
+ return suitBuildingNP
+
+ def cleanupSuitBuilding(self):
+ if hasattr(self, 'floorIndicator'):
+ del self.floorIndicator
+
+ def adjustColorScale(self, scale, node):
+ node.setColorScale(scale, scale, scale, 1)
+
+ def animToCogdo(self, timeStamp):
+ self.stopTransition()
+ if self.mode != 'toon':
+ self.setToToon()
+ self.loadAnimToSuitSfx()
+ sideBldgNodes = self.getNodePaths()
+ nodePath = hidden.find(self.getSbSearchString())
+ newNP = self.setupCogdo(nodePath)
+ closeDoors(self.leftDoor, self.rightDoor)
+ newNP.stash()
+ sideBldgNodes.append(newNP)
+ for np in sideBldgNodes:
+ if not np.isEmpty():
+ np.setColorScale(0.6, 0.6, 0.6, 1.0)
+
+ soundPlayed = 0
+ tracks = Parallel(name=self.taskName('toCogdoTrack'))
+ for i in sideBldgNodes:
+ name = i.getName()
+ timeForDrop = TO_SUIT_BLDG_TIME * 0.85
+ if name[0] == 'c':
+ showTrack = Sequence(name=self.taskName('ToCogdoFlatsTrack') + '-' + str(sideBldgNodes.index(i)))
+ initPos = Point3(0, 0, self.SUIT_INIT_HEIGHT) + i.getPos()
+ showTrack.append(Func(i.setPos, initPos))
+ showTrack.append(Func(i.unstash))
+ if i == sideBldgNodes[len(sideBldgNodes) - 1]:
+ showTrack.append(Func(self.normalizeElevator))
+ if not soundPlayed:
+ showTrack.append(Func(base.playSfx, self.cogDropSound, 0, 1, None, 0.0))
+ showTrack.append(LerpPosInterval(i, timeForDrop, i.getPos(), name=self.taskName('ToCogdoAnim') + '-' + str(sideBldgNodes.index(i))))
+ if not soundPlayed:
+ showTrack.append(Func(base.playSfx, self.cogLandSound, 0, 1, None, 0.0))
+ showTrack.append(self.createBounceTrack(i, 2, 0.65, TO_SUIT_BLDG_TIME - timeForDrop, slowInitBounce=1.0))
+ if not soundPlayed:
+ showTrack.append(Func(base.playSfx, self.cogSettleSound, 0, 1, None, 0.0))
+ tracks.append(showTrack)
+ if not soundPlayed:
+ soundPlayed = 1
+ elif name[0] == 't':
+ hideTrack = Sequence(name=self.taskName('ToCogdoToonFlatsTrack'))
+ timeTillSquish = (self.SUIT_INIT_HEIGHT - 20.0) / self.SUIT_INIT_HEIGHT
+ timeTillSquish *= timeForDrop
+ hideTrack.append(LerpFunctionInterval(self.adjustColorScale, fromData=1, toData=0.25, duration=timeTillSquish, extraArgs=[i]))
+ hideTrack.append(LerpScaleInterval(i, timeForDrop - timeTillSquish, Vec3(1, 1, 0.01)))
+ hideTrack.append(Func(i.stash))
+ hideTrack.append(Func(i.setScale, Vec3(1)))
+ hideTrack.append(Func(i.clearColorScale))
+ tracks.append(hideTrack)
+
+ self.stopTransition()
+ self._deleteTransitionTrack()
+ self.transitionTrack = tracks
+ self.transitionTrack.start(timeStamp)
+ return
+
+ def setupCogdo(self, nodePath):
+ dnaStore = self.cr.playGame.dnaStore
+ level = int(self.difficulty / 2) + 1
+ suitNP = dnaStore.findNode(FO_DICT[chr(self.track)])
+ zoneId = dnaStore.getZoneFromBlockNumber(self.block)
+ zoneId = ZoneUtil.getTrueZoneId(zoneId, self.interiorZoneId)
+ newParentNP = base.cr.playGame.hood.loader.zoneDict[zoneId]
+ suitBuildingNP = suitNP.copyTo(newParentNP)
+ buildingTitle = dnaStore.getTitleFromBlockNumber(self.block)
+ if not buildingTitle:
+ buildingTitle = TTLocalizer.Cogdominiums
+ else:
+ buildingTitle += TTLocalizer.CogdominiumsExt
+ textNode = TextNode('sign')
+ textNode.setTextColor(1.0, 1.0, 1.0, 1.0)
+ textNode.setFont(ToontownGlobals.getSuitFont())
+ textNode.setAlign(TextNode.ACenter)
+ textNode.setWordwrap(12.0)
+ textNode.setText(buildingTitle)
+ textHeight = textNode.getHeight()
+ zScale = (textHeight + 2) / 3.0
+ signOrigin = suitBuildingNP.find('**/sign_origin;+s')
+ backgroundNP = loader.loadModel('phase_5/models/cogdominium/field_office_sign')
+ backgroundNP.reparentTo(signOrigin)
+ backgroundNP.setPosHprScale(0.0, 0.0, -1.2 + textHeight * 0.8 / zScale, 0.0, 0.0, 0.0, 20.0, 8.0, 8.0 * zScale)
+ backgroundNP.node().setEffect(DecalEffect.make())
+ signTextNodePath = backgroundNP.attachNewNode(textNode.generate())
+ signTextNodePath.setPosHprScale(0.0, 0.0, -0.13 + textHeight * 0.1 / zScale, 0.0, 0.0, 0.0, 0.1 * 8.0 / 20.0, 0.1, 0.1 / zScale)
+ signTextNodePath.setColor(1.0, 1.0, 1.0, 1.0)
+ frontNP = suitBuildingNP.find('**/*_front/+GeomNode;+s')
+ backgroundNP.wrtReparentTo(frontNP)
+ frontNP.node().setEffect(DecalEffect.make())
+ suitBuildingNP.setName('cb' + str(self.block) + ':_landmark__DNARoot')
+ suitBuildingNP.setPosHprScale(nodePath, 15.463, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0)
+ suitBuildingNP.flattenMedium()
+ suitBuildingNP.setColorScale(0.6, 0.6, 0.6, 1.0)
+ self.loadElevator(suitBuildingNP, cogdo=True)
+ return suitBuildingNP
+
+ def animToToon(self, timeStamp):
+ self.stopTransition()
+ if self.mode != 'suit':
+ self.setToSuit()
+ self.loadAnimToToonSfx()
+ suitSoundPlayed = 0
+ toonSoundPlayed = 0
+ bldgNodes = self.getNodePaths()
+ tracks = Parallel()
+ for i in bldgNodes:
+ name = i.getName()
+ if name[0] == 's':
+ hideTrack = Sequence(name=self.taskName('ToToonSuitFlatsTrack'))
+ landmark = name.find('_landmark_') != -1
+ if not suitSoundPlayed:
+ hideTrack.append(Func(base.playSfx, self.cogWeakenSound, 0, 1, None, 0.0))
+ hideTrack.append(self.createBounceTrack(i, 3, 1.2, TO_TOON_BLDG_TIME * 0.05, slowInitBounce=0.0))
+ hideTrack.append(self.createBounceTrack(i, 5, 0.8, TO_TOON_BLDG_TIME * 0.1, slowInitBounce=0.0))
+ hideTrack.append(self.createBounceTrack(i, 7, 1.2, TO_TOON_BLDG_TIME * 0.17, slowInitBounce=0.0))
+ hideTrack.append(self.createBounceTrack(i, 9, 1.2, TO_TOON_BLDG_TIME * 0.18, slowInitBounce=0.0))
+ realScale = i.getScale()
+ hideTrack.append(LerpScaleInterval(i, TO_TOON_BLDG_TIME * 0.1, Vec3(realScale[0], realScale[1], 0.01)))
+ if landmark:
+ hideTrack.append(Func(i.removeNode))
+ else:
+ hideTrack.append(Func(i.stash))
+ hideTrack.append(Func(i.setScale, Vec3(1)))
+ if not suitSoundPlayed:
+ suitSoundPlayed = 1
+ tracks.append(hideTrack)
+ elif name[0] == 't':
+ hideTrack = Sequence(name=self.taskName('ToToonFlatsTrack'))
+ hideTrack.append(Wait(TO_TOON_BLDG_TIME * 0.5))
+ if not toonSoundPlayed:
+ hideTrack.append(Func(base.playSfx, self.toonGrowSound, 0, 1, None, 0.0))
+ hideTrack.append(Func(i.unstash))
+ hideTrack.append(Func(i.setScale, Vec3(1, 1, 0.01)))
+ if not toonSoundPlayed:
+ hideTrack.append(Func(base.playSfx, self.toonSettleSound, 0, 1, None, 0.0))
+ hideTrack.append(self.createBounceTrack(i, 11, 1.2, TO_TOON_BLDG_TIME * 0.5, slowInitBounce=4.0))
+ tracks.append(hideTrack)
+ if not toonSoundPlayed:
+ toonSoundPlayed = 1
+
+ self.stopTransition()
+ bldgMTrack = tracks
+ localToonIsVictor = self.localToonIsVictor()
+ if localToonIsVictor:
+ camTrack = self.walkOutCameraTrack()
+ victoryRunTrack, delayDeletes = self.getVictoryRunTrack()
+ trackName = self.taskName('toToonTrack')
+ self._deleteTransitionTrack()
+ if localToonIsVictor:
+ freedomTrack1 = Func(self.cr.playGame.getPlace().setState, 'walk')
+ freedomTrack2 = Func(base.localAvatar.d_setParent, ToontownGlobals.SPRender)
+ self.transitionTrack = Parallel(camTrack, Sequence(victoryRunTrack, bldgMTrack, freedomTrack1, freedomTrack2), name=trackName)
+ else:
+ self.transitionTrack = Sequence(victoryRunTrack, bldgMTrack, name=trackName)
+ self.transitionTrack.delayDeletes = delayDeletes
+ if localToonIsVictor:
+ self.transitionTrack.start(0)
+ else:
+ self.transitionTrack.start(timeStamp)
+ return
+
+ def animToToonFromCogdo(self, timeStamp):
+ self.stopTransition()
+ if self.mode != 'cogdo':
+ self.setToCogdo()
+ self.loadAnimToToonSfx()
+ suitSoundPlayed = 0
+ toonSoundPlayed = 0
+ bldgNodes = self.getNodePaths()
+ tracks = Parallel()
+ for i in bldgNodes:
+ i.clearColorScale()
+ name = i.getName()
+ if name[0] == 'c':
+ hideTrack = Sequence(name=self.taskName('ToToonCogdoFlatsTrack'))
+ landmark = name.find('_landmark_') != -1
+ if not suitSoundPlayed:
+ hideTrack.append(Func(base.playSfx, self.cogWeakenSound, 0, 1, None, 0.0))
+ hideTrack.append(self.createBounceTrack(i, 3, 1.2, TO_TOON_BLDG_TIME * 0.05, slowInitBounce=0.0))
+ hideTrack.append(self.createBounceTrack(i, 5, 0.8, TO_TOON_BLDG_TIME * 0.1, slowInitBounce=0.0))
+ hideTrack.append(self.createBounceTrack(i, 7, 1.2, TO_TOON_BLDG_TIME * 0.17, slowInitBounce=0.0))
+ hideTrack.append(self.createBounceTrack(i, 9, 1.2, TO_TOON_BLDG_TIME * 0.18, slowInitBounce=0.0))
+ realScale = i.getScale()
+ hideTrack.append(LerpScaleInterval(i, TO_TOON_BLDG_TIME * 0.1, Vec3(realScale[0], realScale[1], 0.01)))
+ if landmark:
+ hideTrack.append(Func(i.removeNode))
+ else:
+ hideTrack.append(Func(i.stash))
+ hideTrack.append(Func(i.setScale, Vec3(1)))
+ if not suitSoundPlayed:
+ suitSoundPlayed = 1
+ tracks.append(hideTrack)
+ elif name[0] == 't':
+ hideTrack = Sequence(name=self.taskName('ToToonFromCogdoFlatsTrack'))
+ hideTrack.append(Wait(TO_TOON_BLDG_TIME * 0.5))
+ if not toonSoundPlayed:
+ hideTrack.append(Func(base.playSfx, self.toonGrowSound, 0, 1, None, 0.0))
+ hideTrack.append(Func(i.unstash))
+ hideTrack.append(Func(i.setScale, Vec3(1, 1, 0.01)))
+ if not toonSoundPlayed:
+ hideTrack.append(Func(base.playSfx, self.toonSettleSound, 0, 1, None, 0.0))
+ hideTrack.append(self.createBounceTrack(i, 11, 1.2, TO_TOON_BLDG_TIME * 0.5, slowInitBounce=4.0))
+ tracks.append(hideTrack)
+ if not toonSoundPlayed:
+ toonSoundPlayed = 1
+
+ self.stopTransition()
+ bldgMTrack = tracks
+ localToonIsVictor = self.localToonIsVictor()
+ if localToonIsVictor:
+ camTrack = self.walkOutCameraTrack()
+ victoryRunTrack, delayDeletes = self.getVictoryRunTrack()
+ trackName = self.taskName('toToonFromCogdoTrack')
+ self._deleteTransitionTrack()
+ if localToonIsVictor:
+ freedomTrack1 = Func(self.cr.playGame.getPlace().setState, 'walk')
+ freedomTrack2 = Func(base.localAvatar.d_setParent, ToontownGlobals.SPRender)
+ self.transitionTrack = Parallel(camTrack, Sequence(victoryRunTrack, bldgMTrack, freedomTrack1, freedomTrack2), name=trackName)
+ else:
+ self.transitionTrack = Sequence(victoryRunTrack, bldgMTrack, name=trackName)
+ self.transitionTrack.delayDeletes = delayDeletes
+ if localToonIsVictor:
+ self.transitionTrack.start(0)
+ else:
+ self.transitionTrack.start(timeStamp)
+ return
+
+ def walkOutCameraTrack(self):
+ track = Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, self.elevatorNodePath, 0, -32.5, 9.4, 0, 348, 0), Func(base.camLens.setFov, 52.0), Wait(VICTORY_RUN_TIME), Func(camera.setPosHpr, self.elevatorNodePath, 0, -32.5, 17, 0, 347, 0), Func(base.camLens.setFov, 75.0), Wait(TO_TOON_BLDG_TIME), Func(base.camLens.setFov, 52.0))
+ return track
+
+ def plantVictorsOutsideBldg(self):
+ retVal = 0
+ for victor in self.victorList:
+ if victor != 0 and self.cr.doId2do.has_key(victor):
+ toon = self.cr.doId2do[victor]
+ toon.setPosHpr(self.elevatorModel, 0, -10, 0, 0, 0, 0)
+ toon.startSmooth()
+ if victor == base.localAvatar.getDoId():
+ retVal = 1
+ self.cr.playGame.getPlace().setState('walk')
+
+ return retVal
+
+ def getVictoryRunTrack(self):
+ origPosTrack = Sequence()
+ delayDeletes = []
+ i = 0
+ for victor in self.victorList:
+ if victor != 0 and self.cr.doId2do.has_key(victor):
+ toon = self.cr.doId2do[victor]
+ delayDeletes.append(DelayDelete.DelayDelete(toon, 'getVictoryRunTrack'))
+ toon.stopSmooth()
+ toon.setParent(ToontownGlobals.SPHidden)
+ origPosTrack.append(Func(toon.setPosHpr, self.elevatorNodePath, apply(Point3, ElevatorPoints[i]), Point3(180, 0, 0)))
+ origPosTrack.append(Func(toon.setParent, ToontownGlobals.SPRender))
+ i += 1
+
+ openDoors = getOpenInterval(self, self.leftDoor, self.rightDoor, self.openSfx, None)
+ toonDoorPosHpr = self.cr.playGame.dnaStore.getDoorPosHprFromBlockNumber(self.block)
+ useFarExitPoints = toonDoorPosHpr.getPos().getZ() > 1.0
+ runOutAll = Parallel()
+ i = 0
+ for victor in self.victorList:
+ if victor != 0 and self.cr.doId2do.has_key(victor):
+ toon = self.cr.doId2do[victor]
+ p0 = Point3(0, 0, 0)
+ p1 = Point3(ElevatorPoints[i][0], ElevatorPoints[i][1] - 5.0, ElevatorPoints[i][2])
+ if useFarExitPoints:
+ p2 = Point3(ElevatorOutPointsFar[i][0], ElevatorOutPointsFar[i][1], ElevatorOutPointsFar[i][2])
+ else:
+ p2 = Point3(ElevatorOutPoints[i][0], ElevatorOutPoints[i][1], ElevatorOutPoints[i][2])
+ runOutSingle = Sequence(Func(Emote.globalEmote.disableBody, toon, 'getVictory'), Func(toon.animFSM.request, 'run'), LerpPosInterval(toon, TOON_VICTORY_EXIT_TIME * 0.25, p1, other=self.elevatorNodePath), Func(toon.headsUp, self.elevatorNodePath, p2), LerpPosInterval(toon, TOON_VICTORY_EXIT_TIME * 0.5, p2, other=self.elevatorNodePath), LerpHprInterval(toon, TOON_VICTORY_EXIT_TIME * 0.25, Point3(0, 0, 0), other=self.elevatorNodePath), Func(toon.animFSM.request, 'neutral'), Func(toon.startSmooth), Func(Emote.globalEmote.releaseBody, toon, 'getVictory'))
+ runOutAll.append(runOutSingle)
+ i += 1
+
+ victoryRunTrack = Sequence(origPosTrack, openDoors, runOutAll)
+ return (victoryRunTrack, delayDeletes)
+
+ def animToCogdoFromCogdo(self, timeStamp):
+ self.stopTransition()
+ if self.mode != 'cogdo':
+ self.setToCogdo()
+ self.loadAnimToToonSfx()
+ localToonIsVictor = self.localToonIsVictor()
+ if localToonIsVictor:
+ camTrack = self.walkOutCameraTrack()
+ victoryRunTrack, delayDeletes = self.getVictoryRunTrack()
+ trackName = self.taskName('toToonFromCogdoTrack')
+ self._deleteTransitionTrack()
+ if localToonIsVictor:
+ freedomTrack1 = Func(self.cr.playGame.getPlace().setState, 'walk')
+ freedomTrack2 = Func(base.localAvatar.d_setParent, ToontownGlobals.SPRender)
+ self.transitionTrack = Parallel(camTrack, Sequence(victoryRunTrack, freedomTrack1, freedomTrack2), name=trackName)
+ else:
+ self.transitionTrack = Sequence(victoryRunTrack, name=trackName)
+ self.transitionTrack.delayDeletes = delayDeletes
+ if localToonIsVictor:
+ self.transitionTrack.start(0)
+ else:
+ self.transitionTrack.start(timeStamp)
+
+ def localToonIsVictor(self):
+ retVal = 0
+ for victor in self.victorList:
+ if victor == base.localAvatar.getDoId():
+ retVal = 1
+
+ return retVal
+
+ def createBounceTrack(self, nodeObj, numBounces, startScale, totalTime, slowInitBounce = 0.0):
+ if not nodeObj or numBounces < 1 or startScale == 0.0 or totalTime == 0:
+ self.notify.warning('createBounceTrack called with invalid parameter')
+ return
+ result = Sequence()
+ numBounces += 1
+ if slowInitBounce:
+ bounceTime = totalTime / (numBounces + slowInitBounce - 1.0)
+ else:
+ bounceTime = totalTime / float(numBounces)
+ if slowInitBounce:
+ currTime = bounceTime * float(slowInitBounce)
+ else:
+ currTime = bounceTime
+ realScale = nodeObj.getScale()
+ currScaleDiff = startScale - realScale[2]
+ for currBounceScale in range(numBounces):
+ if currBounceScale == numBounces - 1:
+ currScale = realScale[2]
+ elif currBounceScale % 2:
+ currScale = realScale[2] - currScaleDiff
+ else:
+ currScale = realScale[2] + currScaleDiff
+ result.append(LerpScaleInterval(nodeObj, currTime, Vec3(realScale[0], realScale[1], currScale), blendType='easeInOut'))
+ currScaleDiff *= 0.5
+ currTime = bounceTime
+
+ return result
+
+ def stopTransition(self):
+ if self.transitionTrack:
+ self.transitionTrack.finish()
+ self._deleteTransitionTrack()
+
+ def setToSuit(self):
+ self.stopTransition()
+ if self.mode == 'suit':
+ return
+ self.mode = 'suit'
+ nodes = self.getNodePaths()
+ for i in nodes:
+ name = i.getName()
+ if name[0] == 's':
+ if name.find('_landmark_') != -1:
+ i.removeNode()
+ else:
+ i.unstash()
+ elif name[0] == 't':
+ if name.find('_landmark_') != -1:
+ i.stash()
+ else:
+ i.stash()
+ elif name[0] == 'c':
+ if name.find('_landmark_') != -1:
+ i.removeNode()
+ else:
+ i.stash()
+
+ npc = hidden.findAllMatches(self.getSbSearchString())
+ for i in range(npc.getNumPaths()):
+ nodePath = npc.getPath(i)
+ self.adjustSbNodepathScale(nodePath)
+ self.notify.debug('net transform = %s' % str(nodePath.getNetTransform()))
+ self.setupSuitBuilding(nodePath)
+
+ def setToCogdo(self):
+ self.stopTransition()
+ if self.mode == 'cogdo':
+ return
+ self.mode = 'cogdo'
+ nodes = self.getNodePaths()
+ for i in nodes:
+ name = i.getName()
+ if name[0] == 'c':
+ if name.find('_landmark_') != -1:
+ i.removeNode()
+ else:
+ i.unstash()
+ elif name[0] == 't':
+ if name.find('_landmark_') != -1:
+ i.stash()
+ else:
+ i.stash()
+ elif name[0] == 's':
+ if name.find('_landmark_') != -1:
+ i.removeNode()
+ else:
+ i.stash()
+
+ for np in nodes:
+ if not np.isEmpty():
+ np.setColorScale(0.6, 0.6, 0.6, 1.0)
+
+ npc = hidden.findAllMatches(self.getSbSearchString())
+ for i in range(npc.getNumPaths()):
+ nodePath = npc.getPath(i)
+ self.adjustSbNodepathScale(nodePath)
+ self.notify.debug('net transform = %s' % str(nodePath.getNetTransform()))
+ self.setupCogdo(nodePath)
+
+ def setToToon(self):
+ self.stopTransition()
+ if self.mode == 'toon':
+ return
+ self.mode = 'toon'
+ self.suitDoorOrigin = None
+ nodes = self.getNodePaths()
+ for i in nodes:
+ i.clearColorScale()
+ name = i.getName()
+ if name[0] == 's':
+ if name.find('_landmark_') != -1:
+ i.removeNode()
+ else:
+ i.stash()
+ elif name[0] == 't':
+ if name.find('_landmark_') != -1:
+ i.unstash()
+ else:
+ i.unstash()
+ elif name[0] == 'c':
+ if name.find('_landmark_') != -1:
+ i.removeNode()
+ else:
+ i.stash()
+
+ return
+
+ def normalizeElevator(self):
+ self.elevatorNodePath.setScale(render, Vec3(1, 1, 1))
+ self.elevatorNodePath.setPosHpr(0, 0, 0, 0, 0, 0)
+
+ def getSbSearchString(self):
+ result = 'landmarkBlocks/sb' + str(self.block) + ':*_landmark_*_DNARoot'
+ return result
+
+ def adjustSbNodepathScale(self, nodePath):
+ pass
+
+ def getVisZoneId(self):
+ exteriorZoneId = base.cr.playGame.hood.dnaStore.getZoneFromBlockNumber(self.block)
+ visZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.zoneId)
+ return visZoneId
+
+ def getInteractiveProp(self):
+ result = None
+ if self.interactiveProp:
+ result = self.interactiveProp
+ else:
+ visZoneId = self.getVisZoneId()
+ if base.cr.playGame.hood:
+ loader = base.cr.playGame.hood.loader
+ if hasattr(loader, 'getInteractiveProp'):
+ self.interactiveProp = loader.getInteractiveProp(visZoneId)
+ result = self.interactiveProp
+ self.notify.debug('self.interactiveProp = %s' % self.interactiveProp)
+ else:
+ self.notify.warning('no loader.getInteractiveProp self.interactiveProp is None')
+ else:
+ self.notify.warning('no hood self.interactiveProp is None')
+ return result
+
+ def makePropSad(self):
+ self.notify.debug('makePropSad')
+ if self.getInteractiveProp():
+ if self.getInteractiveProp().state == 'Sad':
+ pass
+ self.getInteractiveProp().gotoSad(self.doId)
diff --git a/toontown/building/DistributedBuildingAI.py b/toontown/building/DistributedBuildingAI.py
new file mode 100644
index 0000000..9a22337
--- /dev/null
+++ b/toontown/building/DistributedBuildingAI.py
@@ -0,0 +1,590 @@
+from otp.ai.AIBaseGlobal import *
+from direct.distributed.ClockDelta import *
+import types
+from direct.task.Task import Task
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObjectAI
+from direct.fsm import State
+from direct.fsm import ClassicFSM, State
+from toontown.toonbase.ToontownGlobals import ToonHall
+import DistributedToonInteriorAI, DistributedToonHallInteriorAI, DistributedSuitInteriorAI, DistributedDoorAI, DoorTypes, DistributedElevatorExtAI, DistributedKnockKnockDoorAI, SuitPlannerInteriorAI, SuitBuildingGlobals, FADoorCodes
+from toontown.hood import ZoneUtil
+import random, time
+from toontown.cogdominium.DistributedCogdoInteriorAI import DistributedCogdoInteriorAI
+from toontown.cogdominium.SuitPlannerCogdoInteriorAI import SuitPlannerCogdoInteriorAI
+from toontown.cogdominium.CogdoLayout import CogdoLayout
+from toontown.cogdominium.DistributedCogdoElevatorExtAI import DistributedCogdoElevatorExtAI
+
+class DistributedBuildingAI(DistributedObjectAI.DistributedObjectAI):
+ FieldOfficeNumFloors = 1
+
+ def __init__(self, air, blockNumber, zoneId, trophyMgr):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ self.block = blockNumber
+ self.zoneId = zoneId
+ self.canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId)
+ self.trophyMgr = trophyMgr
+ self.victorResponses = None
+ self.fsm = ClassicFSM.ClassicFSM('DistributedBuildingAI', [
+ State.State('off', self.enterOff, self.exitOff, [
+ 'waitForVictors', 'becomingToon', 'toon', 'clearOutToonInterior', 'becomingSuit', 'suit', 'clearOutToonInteriorForCogdo', 'becomingCogdo', 'becomingCogdoFromCogdo', 'cogdo']),
+ State.State('waitForVictors', self.enterWaitForVictors, self.exitWaitForVictors, [
+ 'becomingToon']),
+ State.State('waitForVictorsFromCogdo', self.enterWaitForVictorsFromCogdo, self.exitWaitForVictorsFromCogdo, [
+ 'becomingToonFromCogdo', 'becomingCogdoFromCogdo']),
+ State.State('becomingToon', self.enterBecomingToon, self.exitBecomingToon, [
+ 'toon']),
+ State.State('becomingToonFromCogdo', self.enterBecomingToonFromCogdo, self.exitBecomingToonFromCogdo, [
+ 'toon']),
+ State.State('toon', self.enterToon, self.exitToon, [
+ 'clearOutToonInterior', 'clearOutToonInteriorForCogdo']),
+ State.State('clearOutToonInterior', self.enterClearOutToonInterior, self.exitClearOutToonInterior, [
+ 'becomingSuit']),
+ State.State('becomingSuit', self.enterBecomingSuit, self.exitBecomingSuit, [
+ 'suit']),
+ State.State('suit', self.enterSuit, self.exitSuit, [
+ 'waitForVictors', 'becomingToon']),
+ State.State('clearOutToonInteriorForCogdo', self.enterClearOutToonInteriorForCogdo, self.exitClearOutToonInteriorForCogdo, [
+ 'becomingCogdo']),
+ State.State('becomingCogdo', self.enterBecomingCogdo, self.exitBecomingCogdo, [
+ 'cogdo']),
+ State.State('becomingCogdoFromCogdo', self.enterBecomingCogdoFromCogdo, self.exitBecomingCogdoFromCogdo, [
+ 'cogdo']),
+ State.State('cogdo', self.enterCogdo, self.exitCogdo, [
+ 'waitForVictorsFromCogdo', 'becomingToonFromCogdo'])], 'off', 'off')
+ self.fsm.enterInitialState()
+ self.track = 'c'
+ self.difficulty = 1
+ self.numFloors = 0
+ self.savedBy = None
+ self.becameSuitTime = 0
+ self.frontDoorPoint = None
+ self.suitPlannerExt = None
+ self.fSkipElevatorOpening = False
+ return
+
+ def cleanup(self):
+ if self.isDeleted():
+ return
+ self.fsm.requestFinalState()
+ if hasattr(self, 'interior'):
+ self.interior.requestDelete()
+ del self.interior
+ if hasattr(self, 'door'):
+ self.door.requestDelete()
+ del self.door
+ self.insideDoor.requestDelete()
+ del self.insideDoor
+ self.knockKnock.requestDelete()
+ del self.knockKnock
+ if hasattr(self, 'elevator'):
+ self.elevator.requestDelete()
+ del self.elevator
+ self.requestDelete()
+
+ def delete(self):
+ taskMgr.remove(self.taskName('suitbldg-time-out'))
+ taskMgr.remove(self.taskName(str(self.block) + '_becomingToon-timer'))
+ taskMgr.remove(self.taskName(str(self.block) + '_becomingSuit-timer'))
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+ del self.fsm
+
+ def getPickleData(self):
+ pickleData = {'state': str(self.fsm.getCurrentState().getName()), 'block': str(self.block), 'track': str(self.track), 'difficulty': str(self.difficulty), 'numFloors': str(self.numFloors), 'savedBy': self.savedBy, 'becameSuitTime': self.becameSuitTime}
+ return pickleData
+
+ def _getMinMaxFloors(self, difficulty):
+ return SuitBuildingGlobals.SuitBuildingInfo[difficulty][0]
+
+ def suitTakeOver(self, suitTrack, difficulty, buildingHeight):
+ if not self.isToonBlock():
+ return
+ self.updateSavedBy(None)
+ difficulty = min(difficulty, len(SuitBuildingGlobals.SuitBuildingInfo) - 1)
+ minFloors, maxFloors = self._getMinMaxFloors(difficulty)
+ if buildingHeight == None:
+ numFloors = random.randint(minFloors, maxFloors)
+ else:
+ numFloors = buildingHeight + 1
+ if numFloors < minFloors or numFloors > maxFloors:
+ numFloors = random.randint(minFloors, maxFloors)
+ self.track = suitTrack
+ self.difficulty = difficulty
+ self.numFloors = numFloors
+ self.becameSuitTime = time.time()
+ self.fsm.request('clearOutToonInterior')
+ return
+
+ def cogdoTakeOver(self, suitTrack, difficulty, buildingHeight):
+ if not self.isToonBlock():
+ return
+ self.updateSavedBy(None)
+ numFloors = self.FieldOfficeNumFloors
+ self.track = suitTrack
+ self.difficulty = difficulty
+ self.numFloors = numFloors
+ self.becameSuitTime = time.time()
+ self.fsm.request('clearOutToonInteriorForCogdo')
+ return
+
+ def toonTakeOver(self):
+ isCogdo = 'cogdo' in self.fsm.getCurrentState().getName().lower()
+ takenOver = True
+ if isCogdo:
+ if self.buildingDefeated:
+ self.fsm.request('becomingToonFromCogdo')
+ else:
+ self.fsm.request('becomingCogdoFromCogdo')
+ takenOver = False
+ else:
+ self.fsm.request('becomingToon')
+ if takenOver and self.suitPlannerExt:
+ self.suitPlannerExt.recycleBuilding(isCogdo)
+ if hasattr(self, 'interior'):
+ self.interior.requestDelete()
+ del self.interior
+
+ def getFrontDoorPoint(self):
+ return self.frontDoorPoint
+
+ def setFrontDoorPoint(self, point):
+ self.frontDoorPoint = point
+
+ def getBlock(self):
+ dummy, interiorZoneId = self.getExteriorAndInteriorZoneId()
+ return [
+ self.block, interiorZoneId]
+
+ def getSuitData(self):
+ return [
+ ord(self.track), self.difficulty, self.numFloors]
+
+ def getState(self):
+ return [
+ self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]
+
+ def setState(self, state, timestamp=0):
+ self.fsm.request(state)
+
+ def isSuitBuilding(self):
+ state = self.fsm.getCurrentState().getName()
+ return state == 'suit' or state == 'becomingSuit' or state == 'clearOutToonInterior'
+
+ def isCogdo(self):
+ state = self.fsm.getCurrentState().getName()
+ return state == 'cogdo' or state == 'becomingCogdo' or state == 'becomingCogdoFromCogdo' or state == 'clearOutToonInteriorForCogdo'
+
+ def isSuitBlock(self):
+ state = self.fsm.getCurrentState().getName()
+ return self.isSuitBuilding() or self.isCogdo()
+
+ def isEstablishedSuitBlock(self):
+ state = self.fsm.getCurrentState().getName()
+ return state == 'suit'
+
+ def isToonBlock(self):
+ state = self.fsm.getCurrentState().getName()
+ return state in ('toon', 'becomingToon', 'becomingToonFromCogdo')
+
+ def getExteriorAndInteriorZoneId(self):
+ blockNumber = self.block
+ dnaStore = self.air.dnaStoreMap[self.canonicalZoneId]
+ zoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
+ zoneId = ZoneUtil.getTrueZoneId(zoneId, self.zoneId)
+ interiorZoneId = zoneId - zoneId % 100 + 500 + blockNumber
+ return (
+ zoneId, interiorZoneId)
+
+ def d_setState(self, state):
+ self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
+
+ def b_setVictorList(self, victorList):
+ self.setVictorList(victorList)
+ self.d_setVictorList(victorList)
+
+ def d_setVictorList(self, victorList):
+ self.sendUpdate('setVictorList', [victorList])
+
+ def setVictorList(self, victorList):
+ self.victorList = victorList
+
+ def findVictorIndex(self, avId):
+ for i in range(len(self.victorList)):
+ if self.victorList[i] == avId:
+ return i
+
+ return None
+
+ def recordVictorResponse(self, avId):
+ index = self.findVictorIndex(avId)
+ if index == None:
+ self.air.writeServerEvent('suspicious', avId, 'DistributedBuildingAI.setVictorReady from toon not in %s.' % self.victorList)
+ return
+ self.victorResponses[index] = avId
+ return
+
+ def allVictorsResponded(self):
+ if self.victorResponses == self.victorList:
+ return 1
+ else:
+ return 0
+
+ def setVictorReady(self):
+ avId = self.air.getAvatarIdFromSender()
+ if self.victorResponses == None:
+ self.air.writeServerEvent('suspicious', avId, 'DistributedBuildingAI.setVictorReady in state %s.' % self.fsm.getCurrentState().getName())
+ return
+ event = self.air.getAvatarExitEvent(avId)
+ self.ignore(event)
+ if self.allVictorsResponded():
+ return
+ self.recordVictorResponse(avId)
+ if self.allVictorsResponded():
+ self.toonTakeOver()
+ return
+
+ def setVictorExited(self, avId):
+ print 'victor %d exited unexpectedly for bldg %d' % (avId, self.doId)
+ self.recordVictorResponse(avId)
+ if self.allVictorsResponded():
+ self.toonTakeOver()
+
+ def victorsTimedOutTask(self, task):
+ if self.allVictorsResponded():
+ return
+ if hasattr(self, 'interior'):
+ self.notify.info('victorsTimedOutTask: ejecting players by deleting interior.')
+ self.interior.requestDelete()
+ del self.interior
+ task.delayTime = 15.0
+ return task.again
+ self.notify.info('victorsTimedOutTask: suspicious players remaining, advancing state.')
+ for i in range(len(self.victorList)):
+ if self.victorList[i] and self.victorResponses[i] == 0:
+ self.air.writeServerEvent('suspicious', self.victorList[i], 'DistributedBuildingAI toon client refused to leave building.')
+ self.recordVictorResponse(self.victorList[i])
+ event = self.air.getAvatarExitEvent(self.victorList[i])
+ self.ignore(event)
+
+ self.toonTakeOver()
+ return Task.done
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def getToon(self, toonId):
+ if self.air.doId2do.has_key(toonId):
+ return self.air.doId2do[toonId]
+ else:
+ self.notify.warning('getToon() - toon: %d not in repository!' % toonId)
+ return None
+
+ def updateSavedBy(self, savedBy):
+ if self.savedBy:
+ for avId, name, dna in self.savedBy:
+ if not ZoneUtil.isWelcomeValley(self.zoneId):
+ self.trophyMgr.removeTrophy(avId, self.numFloors)
+
+ self.savedBy = savedBy
+ if self.savedBy:
+ for avId, name, dna in self.savedBy:
+ if not ZoneUtil.isWelcomeValley(self.zoneId):
+ self.trophyMgr.addTrophy(avId, name, self.numFloors)
+
+ def enterWaitForVictors(self, victorList, savedBy):
+ activeToons = []
+ for t in victorList:
+ toon = None
+ if t:
+ toon = self.getToon(t)
+ if toon != None:
+ activeToons.append(toon)
+
+ for t in victorList:
+ toon = None
+ if t:
+ toon = self.getToon(t)
+ self.air.writeServerEvent('buildingDefeated', t, '%s|%s|%s|%s' % (self.track, self.numFloors, self.zoneId, victorList))
+ if toon != None:
+ self.air.questManager.toonKilledBuilding(toon, self.track, self.difficulty, self.numFloors, self.zoneId, activeToons)
+
+ for i in range(0, 4):
+ victor = victorList[i]
+ if victor == None or not self.air.doId2do.has_key(victor):
+ victorList[i] = 0
+ else:
+ event = self.air.getAvatarExitEvent(victor)
+ self.accept(event, self.setVictorExited, extraArgs=[victor])
+
+ self.b_setVictorList(victorList)
+ self.updateSavedBy(savedBy)
+ self.victorResponses = [
+ 0, 0, 0, 0]
+ self.d_setState('waitForVictors')
+ return
+
+ def exitWaitForVictors(self):
+ self.victorResponses = None
+ for victor in self.victorList:
+ event = simbase.air.getAvatarExitEvent(victor)
+ self.ignore(event)
+
+ return
+
+ def enterWaitForVictorsFromCogdo(self, victorList, savedBy):
+ activeToons = []
+ for t in victorList:
+ toon = None
+ if t:
+ toon = self.getToon(t)
+ if toon != None:
+ activeToons.append(toon)
+
+ self.buildingDefeated = len(savedBy) > 0
+ if self.buildingDefeated:
+ for t in victorList:
+ toon = None
+ if t:
+ toon = self.getToon(t)
+ self.air.writeServerEvent('buildingDefeated', t, '%s|%s|%s|%s' % (self.track, self.numFloors, self.zoneId, victorList))
+ if toon != None:
+ self.air.questManager.toonKilledCogdo(toon, self.difficulty, self.numFloors, self.zoneId, activeToons)
+
+ for i in range(0, 4):
+ victor = victorList[i]
+ if victor == None or not self.air.doId2do.has_key(victor):
+ victorList[i] = 0
+ else:
+ event = self.air.getAvatarExitEvent(victor)
+ self.accept(event, self.setVictorExited, extraArgs=[victor])
+
+ self.b_setVictorList(victorList)
+ self.updateSavedBy(savedBy)
+ self.victorResponses = [
+ 0, 0, 0, 0]
+ taskMgr.doMethodLater(30, self.victorsTimedOutTask, self.taskName(str(self.block) + '_waitForVictors-timer'))
+ self.d_setState('waitForVictorsFromCogdo')
+ return
+
+ def exitWaitForVictorsFromCogdo(self):
+ taskMgr.remove(self.taskName(str(self.block) + '_waitForVictors-timer'))
+ self.victorResponses = None
+ for victor in self.victorList:
+ event = simbase.air.getAvatarExitEvent(victor)
+ self.ignore(event)
+
+ return
+
+ def enterBecomingToon(self):
+ self.d_setState('becomingToon')
+ name = self.taskName(str(self.block) + '_becomingToon-timer')
+ taskMgr.doMethodLater(SuitBuildingGlobals.VICTORY_SEQUENCE_TIME, self.becomingToonTask, name)
+
+ def exitBecomingToon(self):
+ name = self.taskName(str(self.block) + '_becomingToon-timer')
+ taskMgr.remove(name)
+
+ def enterBecomingToonFromCogdo(self):
+ self.d_setState('becomingToonFromCogdo')
+ name = self.taskName(str(self.block) + '_becomingToonFromCogdo-timer')
+ taskMgr.doMethodLater(SuitBuildingGlobals.VICTORY_SEQUENCE_TIME, self.becomingToonTask, name)
+
+ def exitBecomingToonFromCogdo(self):
+ name = self.taskName(str(self.block) + '_becomingToonFromCogdo-timer')
+ taskMgr.remove(name)
+
+ def becomingToonTask(self, task):
+ self.fsm.request('toon')
+ self.suitPlannerExt.buildingMgr.save()
+ return Task.done
+
+ def enterToon(self):
+ self.d_setState('toon')
+ exteriorZoneId, interiorZoneId = self.getExteriorAndInteriorZoneId()
+ if simbase.config.GetBool('want-new-toonhall', 1) and ZoneUtil.getCanonicalZoneId(interiorZoneId) == ToonHall:
+ self.interior = DistributedToonHallInteriorAI.DistributedToonHallInteriorAI(self.block, self.air, interiorZoneId, self)
+ else:
+ self.interior = DistributedToonInteriorAI.DistributedToonInteriorAI(self.block, self.air, interiorZoneId, self)
+ self.interior.generateWithRequired(interiorZoneId)
+ door = self.createExteriorDoor()
+ insideDoor = DistributedDoorAI.DistributedDoorAI(self.air, self.block, DoorTypes.INT_STANDARD)
+ door.setOtherDoor(insideDoor)
+ insideDoor.setOtherDoor(door)
+ door.zoneId = exteriorZoneId
+ insideDoor.zoneId = interiorZoneId
+ door.generateWithRequired(exteriorZoneId)
+ insideDoor.generateWithRequired(interiorZoneId)
+ self.door = door
+ self.insideDoor = insideDoor
+ self.becameSuitTime = 0
+ self.knockKnock = DistributedKnockKnockDoorAI.DistributedKnockKnockDoorAI(self.air, self.block)
+ self.knockKnock.generateWithRequired(exteriorZoneId)
+ self.air.writeServerEvent('building-toon', self.doId, '%s|%s' % (self.zoneId, self.block))
+
+ def createExteriorDoor(self):
+ result = DistributedDoorAI.DistributedDoorAI(self.air, self.block, DoorTypes.EXT_STANDARD)
+ return result
+
+ def exitToon(self):
+ self.door.setDoorLock(FADoorCodes.BUILDING_TAKEOVER)
+
+ def enterClearOutToonInterior(self):
+ self.d_setState('clearOutToonInterior')
+ if hasattr(self, 'interior'):
+ self.interior.setState('beingTakenOver')
+ name = self.taskName(str(self.block) + '_clearOutToonInterior-timer')
+ taskMgr.doMethodLater(SuitBuildingGlobals.CLEAR_OUT_TOON_BLDG_TIME, self.clearOutToonInteriorTask, name)
+
+ def exitClearOutToonInterior(self):
+ name = self.taskName(str(self.block) + '_clearOutToonInterior-timer')
+ taskMgr.remove(name)
+
+ def clearOutToonInteriorTask(self, task):
+ self.fsm.request('becomingSuit')
+ return Task.done
+
+ def enterBecomingSuit(self):
+ self.sendUpdate('setSuitData', [
+ ord(self.track), self.difficulty, self.numFloors])
+ self.d_setState('becomingSuit')
+ name = self.taskName(str(self.block) + '_becomingSuit-timer')
+ taskMgr.doMethodLater(SuitBuildingGlobals.TO_SUIT_BLDG_TIME, self.becomingSuitTask, name)
+
+ def exitBecomingSuit(self):
+ name = self.taskName(str(self.block) + '_becomingSuit-timer')
+ taskMgr.remove(name)
+ if hasattr(self, 'interior'):
+ self.interior.requestDelete()
+ del self.interior
+ self.door.requestDelete()
+ del self.door
+ self.insideDoor.requestDelete()
+ del self.insideDoor
+ self.knockKnock.requestDelete()
+ del self.knockKnock
+
+ def becomingSuitTask(self, task):
+ self.fsm.request('suit')
+ self.suitPlannerExt.buildingMgr.save()
+ return Task.done
+
+ def enterSuit(self):
+ self.sendUpdate('setSuitData', [
+ ord(self.track), self.difficulty, self.numFloors])
+ zoneId, interiorZoneId = self.getExteriorAndInteriorZoneId()
+ self.planner = SuitPlannerInteriorAI.SuitPlannerInteriorAI(self.numFloors, self.difficulty, self.track, interiorZoneId)
+ self.d_setState('suit')
+ exteriorZoneId, interiorZoneId = self.getExteriorAndInteriorZoneId()
+ self.elevator = DistributedElevatorExtAI.DistributedElevatorExtAI(self.air, self)
+ self.elevator.generateWithRequired(exteriorZoneId)
+ self.air.writeServerEvent('building-cog', self.doId, '%s|%s|%s|%s' % (self.zoneId, self.block, self.track, self.numFloors))
+
+ def exitSuit(self):
+ del self.planner
+ if hasattr(self, 'elevator'):
+ self.elevator.requestDelete()
+ del self.elevator
+
+ def enterClearOutToonInteriorForCogdo(self):
+ self.d_setState('clearOutToonInteriorForCogdo')
+ if hasattr(self, 'interior'):
+ self.interior.setState('beingTakenOver')
+ name = self.taskName(str(self.block) + '_clearOutToonInteriorForCogdo-timer')
+ taskMgr.doMethodLater(SuitBuildingGlobals.CLEAR_OUT_TOON_BLDG_TIME, self.clearOutToonInteriorForCogdoTask, name)
+
+ def exitClearOutToonInteriorForCogdo(self):
+ name = self.taskName(str(self.block) + '_clearOutToonInteriorForCogdo-timer')
+ taskMgr.remove(name)
+
+ def clearOutToonInteriorForCogdoTask(self, task):
+ self.fsm.request('becomingCogdo')
+ return Task.done
+
+ def enterBecomingCogdo(self):
+ self.sendUpdate('setSuitData', [
+ ord(self.track), self.difficulty, self.numFloors])
+ self.d_setState('becomingCogdo')
+ name = self.taskName(str(self.block) + '_becomingCogdo-timer')
+ taskMgr.doMethodLater(SuitBuildingGlobals.TO_SUIT_BLDG_TIME, self.becomingCogdoTask, name)
+
+ def exitBecomingCogdo(self):
+ name = self.taskName(str(self.block) + '_becomingCogdo-timer')
+ taskMgr.remove(name)
+ if hasattr(self, 'interior'):
+ self.interior.requestDelete()
+ del self.interior
+ self.door.requestDelete()
+ del self.door
+ self.insideDoor.requestDelete()
+ del self.insideDoor
+ self.knockKnock.requestDelete()
+ del self.knockKnock
+
+ def enterBecomingCogdoFromCogdo(self):
+ self.d_setState('becomingCogdoFromCogdo')
+ name = self.taskName(str(self.block) + '_becomingCogdoFromCogdo-timer')
+ taskMgr.doMethodLater(SuitBuildingGlobals.VICTORY_RUN_TIME, self.becomingCogdoTask, name)
+
+ def exitBecomingCogdoFromCogdo(self):
+ self.fSkipElevatorOpening = True
+ name = self.taskName(str(self.block) + '_becomingCogdoFromCogdo-timer')
+ taskMgr.remove(name)
+
+ def becomingCogdoTask(self, task):
+ self.fsm.request('cogdo')
+ self.suitPlannerExt.buildingMgr.save()
+ return Task.done
+
+ def enterCogdo(self):
+ self.sendUpdate('setSuitData', [
+ ord(self.track), self.difficulty, self.numFloors])
+ zoneId, interiorZoneId = self.getExteriorAndInteriorZoneId()
+ self._cogdoLayout = CogdoLayout(self.numFloors)
+ self.planner = SuitPlannerCogdoInteriorAI(self._cogdoLayout, self.difficulty, self.track, interiorZoneId)
+ self.d_setState('cogdo')
+ exteriorZoneId, interiorZoneId = self.getExteriorAndInteriorZoneId()
+ self.elevator = DistributedCogdoElevatorExtAI(self.air, self, fSkipOpening=self.fSkipElevatorOpening)
+ self.fSkipElevatorOpening = False
+ self.elevator.generateWithRequired(exteriorZoneId)
+ self.air.writeServerEvent('building-cogdo', self.doId, '%s|%s|%s' % (self.zoneId, self.block, self.numFloors))
+
+ def exitCogdo(self):
+ del self.planner
+ if hasattr(self, 'elevator'):
+ self.elevator.requestDelete()
+ del self.elevator
+
+ def setSuitPlannerExt(self, planner):
+ self.suitPlannerExt = planner
+
+ def _createSuitInterior(self):
+ return DistributedSuitInteriorAI.DistributedSuitInteriorAI(self.air, self.elevator)
+
+ def _createCogdoInterior(self):
+ return DistributedCogdoInteriorAI(self.air, self.elevator)
+
+ def createSuitInterior(self):
+ self.interior = self._createSuitInterior()
+ dummy, interiorZoneId = self.getExteriorAndInteriorZoneId()
+ self.interior.fsm.request('WaitForAllToonsInside')
+ self.interior.generateWithRequired(interiorZoneId)
+
+ def createCogdoInterior(self):
+ self.interior = self._createCogdoInterior()
+ dummy, interiorZoneId = self.getExteriorAndInteriorZoneId()
+ self.interior.fsm.request('WaitForAllToonsInside')
+ self.interior.generateWithRequired(interiorZoneId)
+
+ def deleteSuitInterior(self):
+ if hasattr(self, 'interior'):
+ self.interior.requestDelete()
+ del self.interior
+ if hasattr(self, 'elevator'):
+ self.elevator.d_setFloor(-1)
+ self.elevator.open()
+
+ def deleteCogdoInterior(self):
+ self.deleteSuitInterior()
diff --git a/toontown/building/DistributedBuildingMgrAI.py b/toontown/building/DistributedBuildingMgrAI.py
new file mode 100644
index 0000000..5a13499
--- /dev/null
+++ b/toontown/building/DistributedBuildingMgrAI.py
@@ -0,0 +1,306 @@
+import os
+from direct.task.Task import Task
+import cPickle
+from otp.ai.AIBaseGlobal import *
+import DistributedBuildingAI, HQBuildingAI, GagshopBuildingAI, PetshopBuildingAI
+from toontown.building.KartShopBuildingAI import KartShopBuildingAI
+from toontown.building import DistributedAnimBuildingAI
+from direct.directnotify import DirectNotifyGlobal
+from toontown.hood import ZoneUtil
+import time, random
+
+class DistributedBuildingMgrAI:
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBuildingMgrAI')
+ serverDatafolder = simbase.config.GetString('server-data-folder', '')
+
+ def __init__(self, air, branchID, dnaStore, trophyMgr):
+ self.branchID = branchID
+ self.canonicalBranchID = ZoneUtil.getCanonicalZoneId(branchID)
+ self.air = air
+ self.__buildings = {}
+ self.dnaStore = dnaStore
+ self.trophyMgr = trophyMgr
+ self.shard = str(air.districtId)
+ self.backupExtension = '.bu'
+ self.findAllLandmarkBuildings()
+ self.doLaterTask = None
+ return
+
+ def cleanup(self):
+ taskMgr.remove(str(self.branchID) + '_delayed_save-timer')
+ for building in self.__buildings.values():
+ building.cleanup()
+
+ self.__buildings = {}
+
+ def isValidBlockNumber(self, blockNumber):
+ return self.__buildings.has_key(blockNumber)
+
+ def delayedSaveTask(self, task):
+ self.save()
+ self.doLaterTask = None
+ return Task.done
+
+ def isSuitBlock(self, blockNumber):
+ return self.__buildings[blockNumber].isSuitBlock()
+
+ def getSuitBlocks(self):
+ blocks = []
+ for i in self.__buildings.values():
+ if i.isSuitBlock():
+ blocks.append(i.getBlock()[0])
+
+ return blocks
+
+ def isCogdoBlock(self, blockNumber):
+ return self.__buildings[blockNumber].isCogdo()
+
+ def getCogdoBlocks(self):
+ blocks = []
+ for i in self.__buildings.values():
+ if i.isCogdo():
+ blocks.append(i.getBlock()[0])
+
+ return blocks
+
+ def getEstablishedSuitBlocks(self):
+ blocks = []
+ for i in self.__buildings.values():
+ if i.isEstablishedSuitBlock():
+ blocks.append(i.getBlock()[0])
+
+ return blocks
+
+ def getToonBlocks(self):
+ blocks = []
+ for i in self.__buildings.values():
+ if isinstance(i, HQBuildingAI.HQBuildingAI):
+ continue
+ if not i.isSuitBlock():
+ blocks.append(i.getBlock()[0])
+
+ return blocks
+
+ def getBuildings(self):
+ return self.__buildings.values()
+
+ def getFrontDoorPoint(self, blockNumber):
+ return self.__buildings[blockNumber].getFrontDoorPoint()
+
+ def getBuildingTrack(self, blockNumber):
+ return self.__buildings[blockNumber].track
+
+ def getBuilding(self, blockNumber):
+ return self.__buildings[blockNumber]
+
+ def setFrontDoorPoint(self, blockNumber, point):
+ return self.__buildings[blockNumber].setFrontDoorPoint(point)
+
+ def getDNABlockLists(self):
+ blocks = []
+ hqBlocks = []
+ gagshopBlocks = []
+ petshopBlocks = []
+ kartshopBlocks = []
+ animBldgBlocks = []
+ for i in range(self.dnaStore.getNumBlockNumbers()):
+ blockNumber = self.dnaStore.getBlockNumberAt(i)
+ buildingType = self.dnaStore.getBlockBuildingType(blockNumber)
+ if buildingType == 'hq':
+ hqBlocks.append(blockNumber)
+ elif buildingType == 'gagshop':
+ gagshopBlocks.append(blockNumber)
+ elif buildingType == 'petshop':
+ petshopBlocks.append(blockNumber)
+ elif buildingType == 'kartshop':
+ kartshopBlocks.append(blockNumber)
+ elif buildingType == 'animbldg':
+ animBldgBlocks.append(blockNumber)
+ else:
+ blocks.append(blockNumber)
+
+ return (
+ blocks, hqBlocks, gagshopBlocks, petshopBlocks, kartshopBlocks, animBldgBlocks)
+
+ def findAllLandmarkBuildings(self):
+ buildings = self.load()
+ blocks, hqBlocks, gagshopBlocks, petshopBlocks, kartshopBlocks, animBldgBlocks = self.getDNABlockLists()
+ for block in blocks:
+ self.newBuilding(block, buildings.get(block, None))
+
+ for block in animBldgBlocks:
+ self.newAnimBuilding(block, buildings.get(block, None))
+
+ for block in hqBlocks:
+ self.newHQBuilding(block)
+
+ for block in gagshopBlocks:
+ self.newGagshopBuilding(block)
+
+ if simbase.wantPets:
+ for block in petshopBlocks:
+ self.newPetshopBuilding(block)
+
+ if simbase.wantKarts:
+ for block in kartshopBlocks:
+ self.newKartShopBuilding(block)
+
+ return
+
+ def newBuilding(self, blockNumber, blockData=None):
+ building = DistributedBuildingAI.DistributedBuildingAI(self.air, blockNumber, self.branchID, self.trophyMgr)
+ building.generateWithRequired(self.branchID)
+ if blockData:
+ building.track = blockData.get('track', 'c')
+ building.difficulty = int(blockData.get('difficulty', 1))
+ building.numFloors = int(blockData.get('numFloors', 1))
+ building.numFloors = max(1, min(5, building.numFloors))
+ if not ZoneUtil.isWelcomeValley(building.zoneId):
+ building.updateSavedBy(blockData.get('savedBy'))
+ else:
+ self.notify.warning('we had a cog building in welcome valley %d' % building.zoneId)
+ building.becameSuitTime = blockData.get('becameSuitTime', time.time())
+ if blockData['state'] == 'suit':
+ building.setState('suit')
+ elif blockData['state'] == 'cogdo':
+ if simbase.air.wantCogdominiums:
+ building.numFloors = DistributedBuildingAI.DistributedBuildingAI.FieldOfficeNumFloors
+ building.setState('cogdo')
+ else:
+ building.setState('toon')
+ else:
+ building.setState('toon')
+ self.__buildings[blockNumber] = building
+ return building
+
+ def newAnimBuilding(self, blockNumber, blockData=None):
+ building = DistributedAnimBuildingAI.DistributedAnimBuildingAI(self.air, blockNumber, self.branchID, self.trophyMgr)
+ building.generateWithRequired(self.branchID)
+ if blockData:
+ building.track = blockData.get('track', 'c')
+ building.difficulty = int(blockData.get('difficulty', 1))
+ building.numFloors = int(blockData.get('numFloors', 1))
+ if not ZoneUtil.isWelcomeValley(building.zoneId):
+ building.updateSavedBy(blockData.get('savedBy'))
+ else:
+ self.notify.warning('we had a cog building in welcome valley %d' % building.zoneId)
+ building.becameSuitTime = blockData.get('becameSuitTime', time.time())
+ if blockData['state'] == 'suit':
+ building.setState('suit')
+ else:
+ building.setState('toon')
+ else:
+ building.setState('toon')
+ self.__buildings[blockNumber] = building
+ return building
+
+ def newHQBuilding(self, blockNumber):
+ dnaStore = self.air.dnaStoreMap[self.canonicalBranchID]
+ exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
+ exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
+ interiorZoneId = self.branchID - self.branchID % 100 + 500 + blockNumber
+ building = HQBuildingAI.HQBuildingAI(self.air, exteriorZoneId, interiorZoneId, blockNumber)
+ self.__buildings[blockNumber] = building
+ return building
+
+ def newGagshopBuilding(self, blockNumber):
+ dnaStore = self.air.dnaStoreMap[self.canonicalBranchID]
+ exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
+ exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
+ interiorZoneId = self.branchID - self.branchID % 100 + 500 + blockNumber
+ building = GagshopBuildingAI.GagshopBuildingAI(self.air, exteriorZoneId, interiorZoneId, blockNumber)
+ self.__buildings[blockNumber] = building
+ return building
+
+ def newPetshopBuilding(self, blockNumber):
+ dnaStore = self.air.dnaStoreMap[self.canonicalBranchID]
+ exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
+ exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
+ interiorZoneId = self.branchID - self.branchID % 100 + 500 + blockNumber
+ building = PetshopBuildingAI.PetshopBuildingAI(self.air, exteriorZoneId, interiorZoneId, blockNumber)
+ self.__buildings[blockNumber] = building
+ return building
+
+ def newKartShopBuilding(self, blockNumber):
+ dnaStore = self.air.dnaStoreMap[self.canonicalBranchID]
+ exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
+ exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
+ interiorZoneId = self.branchID - self.branchID % 100 + 500 + blockNumber
+ building = KartShopBuildingAI(self.air, exteriorZoneId, interiorZoneId, blockNumber)
+ self.__buildings[blockNumber] = building
+ return building
+
+ def getFileName(self):
+ f = '%s%s_%d.buildings' % (self.serverDatafolder, self.shard, self.branchID)
+ return f
+
+ def saveTo(self, file, block=None):
+ if block:
+ pickleData = block.getPickleData()
+ cPickle.dump(pickleData, file)
+ else:
+ for i in self.__buildings.values():
+ if isinstance(i, HQBuildingAI.HQBuildingAI):
+ continue
+ pickleData = i.getPickleData()
+ cPickle.dump(pickleData, file)
+
+ def fastSave(self, block):
+ return
+ try:
+ fileName = self.getFileName() + '.delta'
+ working = fileName + '.temp'
+ if os.path.exists(working):
+ os.remove(working)
+ os.rename(fileName, working)
+ file = open(working, 'w')
+ file.seek(0, 2)
+ self.saveTo(file, block)
+ file.close()
+ os.rename(working, fileName)
+ except IOError:
+ self.notify.error(str(sys.exc_info()[1]))
+
+ def save(self):
+ try:
+ fileName = self.getFileName()
+ backup = fileName + self.backupExtension
+ if os.path.exists(fileName):
+ os.rename(fileName, backup)
+ file = open(fileName, 'w')
+ file.seek(0)
+ self.saveTo(file)
+ file.close()
+ if os.path.exists(backup):
+ os.remove(backup)
+ except EnvironmentError:
+ self.notify.warning(str(sys.exc_info()[1]))
+
+ def loadFrom(self, file):
+ blocks = {}
+ try:
+ while 1:
+ pickleData = cPickle.load(file)
+ blocks[int(pickleData['block'])] = pickleData
+
+ except EOFError:
+ pass
+
+ return blocks
+
+ def load(self):
+ fileName = self.getFileName()
+ try:
+ file = open(fileName + self.backupExtension, 'r')
+ if os.path.exists(fileName):
+ os.remove(fileName)
+ except IOError:
+ try:
+ file = open(fileName, 'r')
+ except IOError:
+ return {}
+
+ file.seek(0)
+ blocks = self.loadFrom(file)
+ file.close()
+ return blocks
diff --git a/toontown/building/DistributedCFOElevator.py b/toontown/building/DistributedCFOElevator.py
new file mode 100644
index 0000000..a0c1536
--- /dev/null
+++ b/toontown/building/DistributedCFOElevator.py
@@ -0,0 +1,24 @@
+import DistributedElevator
+import DistributedBossElevator
+from ElevatorConstants import *
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import TTLocalizer
+
+class DistributedCFOElevator(DistributedBossElevator.DistributedBossElevator):
+
+ def __init__(self, cr):
+ DistributedBossElevator.DistributedBossElevator.__init__(self, cr)
+ self.type = ELEVATOR_CFO
+ self.countdownTime = ElevatorData[self.type]['countdown']
+
+ def setupElevator(self):
+ self.elevatorModel = loader.loadModel('phase_10/models/cogHQ/CFOElevator')
+ self.leftDoor = self.elevatorModel.find('**/left_door')
+ self.rightDoor = self.elevatorModel.find('**/right_door')
+ geom = base.cr.playGame.hood.loader.geom
+ locator = geom.find('**/elevator_locator')
+ self.elevatorModel.reparentTo(locator)
+ DistributedElevator.DistributedElevator.setupElevator(self)
+
+ def getDestName(self):
+ return TTLocalizer.ElevatorCashBotBoss
diff --git a/toontown/building/DistributedCFOElevatorAI.py b/toontown/building/DistributedCFOElevatorAI.py
new file mode 100644
index 0000000..e4c706e
--- /dev/null
+++ b/toontown/building/DistributedCFOElevatorAI.py
@@ -0,0 +1,9 @@
+from ElevatorConstants import *
+import DistributedBossElevatorAI
+
+class DistributedCFOElevatorAI(DistributedBossElevatorAI.DistributedBossElevatorAI):
+
+ def __init__(self, air, bldg, zone, antiShuffle=0, minLaff=0):
+ DistributedBossElevatorAI.DistributedBossElevatorAI.__init__(self, air, bldg, zone, antiShuffle=antiShuffle, minLaff=minLaff)
+ self.type = ELEVATOR_CFO
+ self.countdownTime = ElevatorData[self.type]['countdown']
diff --git a/toontown/building/DistributedCJElevator.py b/toontown/building/DistributedCJElevator.py
new file mode 100644
index 0000000..26fb9b8
--- /dev/null
+++ b/toontown/building/DistributedCJElevator.py
@@ -0,0 +1,28 @@
+import DistributedElevator
+import DistributedBossElevator
+from ElevatorConstants import *
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import TTLocalizer
+
+class DistributedCJElevator(DistributedBossElevator.DistributedBossElevator):
+
+ def __init__(self, cr):
+ DistributedBossElevator.DistributedBossElevator.__init__(self, cr)
+ self.type = ELEVATOR_CJ
+ self.countdownTime = ElevatorData[self.type]['countdown']
+
+ def setupElevator(self):
+ self.elevatorModel = loader.loadModel('phase_11/models/lawbotHQ/LB_Elevator')
+ self.leftDoor = self.elevatorModel.find('**/left-door')
+ if self.leftDoor.isEmpty():
+ self.leftDoor = self.elevatorModel.find('**/left_door')
+ self.rightDoor = self.elevatorModel.find('**/right-door')
+ if self.rightDoor.isEmpty():
+ self.rightDoor = self.elevatorModel.find('**/right_door')
+ geom = base.cr.playGame.hood.loader.geom
+ locator = geom.find('**/elevator_locator')
+ self.elevatorModel.reparentTo(locator)
+ DistributedElevator.DistributedElevator.setupElevator(self)
+
+ def getDestName(self):
+ return TTLocalizer.ElevatorLawBotBoss
diff --git a/toontown/building/DistributedCJElevatorAI.py b/toontown/building/DistributedCJElevatorAI.py
new file mode 100644
index 0000000..98a9a12
--- /dev/null
+++ b/toontown/building/DistributedCJElevatorAI.py
@@ -0,0 +1,9 @@
+from ElevatorConstants import *
+import DistributedBossElevatorAI
+
+class DistributedCJElevatorAI(DistributedBossElevatorAI.DistributedBossElevatorAI):
+
+ def __init__(self, air, bldg, zone, antiShuffle=0, minLaff=0):
+ DistributedBossElevatorAI.DistributedBossElevatorAI.__init__(self, air, bldg, zone, antiShuffle=antiShuffle, minLaff=0)
+ self.type = ELEVATOR_CJ
+ self.countdownTime = ElevatorData[self.type]['countdown']
diff --git a/toontown/building/DistributedClubElevator.py b/toontown/building/DistributedClubElevator.py
new file mode 100644
index 0000000..6117c36
--- /dev/null
+++ b/toontown/building/DistributedClubElevator.py
@@ -0,0 +1,531 @@
+from pandac.PandaModules import *
+from direct.distributed.ClockDelta import *
+from direct.interval.IntervalGlobal import *
+from toontown.building import ElevatorConstants
+from toontown.building import ElevatorUtils
+from toontown.building import DistributedElevatorFSM
+from toontown.toonbase import ToontownGlobals
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from toontown.hood import ZoneUtil
+from toontown.toonbase import TTLocalizer
+from direct.fsm.FSM import FSM
+from direct.task import Task
+from toontown.distributed import DelayDelete
+from direct.showbase import PythonUtil
+
+class DistributedClubElevator(DistributedElevatorFSM.DistributedElevatorFSM):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedClubElevator')
+ JumpOutOffsets = ((3, 5, 0),
+ (1.5, 4, 0),
+ (-1.5, 4, 0),
+ (-3, 4, 0))
+ defaultTransitions = {'Off': ['Opening', 'Closed'],
+ 'Opening': ['WaitEmpty',
+ 'WaitCountdown',
+ 'Opening',
+ 'Closing'],
+ 'WaitEmpty': ['WaitCountdown', 'Closing', 'Off'],
+ 'WaitCountdown': ['WaitEmpty',
+ 'AllAboard',
+ 'Closing',
+ 'WaitCountdown'],
+ 'AllAboard': ['WaitEmpty', 'Closing'],
+ 'Closing': ['Closed',
+ 'WaitEmpty',
+ 'Closing',
+ 'Opening'],
+ 'Closed': ['Opening']}
+ id = 0
+
+ def __init__(self, cr):
+ DistributedElevatorFSM.DistributedElevatorFSM.__init__(self, cr)
+ FSM.__init__(self, 'ElevatorClub_%s_FSM' % self.id)
+ self.type = ElevatorConstants.ELEVATOR_COUNTRY_CLUB
+ self.countdownTime = ElevatorConstants.ElevatorData[self.type]['countdown']
+ self.nametag = None
+ self.currentFloor = -1
+ self.isLocked = 0
+ self.isEntering = 0
+ self.doorOpeningFlag = 0
+ self.doorsNeedToClose = 0
+ self.wantState = 0
+ self.latch = None
+ self.lastState = self.state
+ self.kartModelPath = 'phase_12/models/bossbotHQ/Coggolf_cart3.bam'
+ self.leftDoor = None
+ self.rightDoor = None
+ self.__toonTracks = {}
+ return
+
+ def setupElevator(self):
+ self.elevatorModel = loader.loadModel('phase_11/models/lawbotHQ/LB_ElevatorScaled')
+ if not self.elevatorModel:
+ self.notify.error('No Elevator Model in DistributedElevatorFloor.setupElevator. Please inform JML. Fool!')
+ self.leftDoor = self.elevatorModel.find('**/left-door')
+ if self.leftDoor.isEmpty():
+ self.leftDoor = self.elevatorModel.find('**/left_door')
+ self.rightDoor = self.elevatorModel.find('**/right-door')
+ if self.rightDoor.isEmpty():
+ self.rightDoor = self.elevatorModel.find('**/right_door')
+ DistributedElevatorFSM.DistributedElevatorFSM.setupElevator(self)
+
+ def generate(self):
+ DistributedElevatorFSM.DistributedElevatorFSM.generate(self)
+ self.loader = self.cr.playGame.hood.loader
+ self.golfKart = render.attachNewNode('golfKartNode')
+ self.kart = loader.loadModel(self.kartModelPath)
+ self.kart.setPos(0, 0, 0)
+ self.kart.setScale(1)
+ self.kart.reparentTo(self.golfKart)
+ self.wheels = self.kart.findAllMatches('**/wheelNode*')
+ self.numWheels = self.wheels.getNumPaths()
+ self.setPosHpr(0, 0, 0, 0, 0, 0)
+
+ def announceGenerate(self):
+ DistributedElevatorFSM.DistributedElevatorFSM.announceGenerate(self)
+ if self.latch:
+ self.notify.info('Setting latch in announce generate')
+ self.setLatch(self.latch)
+ angle = self.startingHpr[0]
+ angle -= 90
+ radAngle = deg2Rad(angle)
+ unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0)
+ unitVec *= 45.0
+ self.endPos = self.startingPos + unitVec
+ self.endPos.setZ(0.5)
+ dist = Vec3(self.endPos - self.enteringPos).length()
+ wheelAngle = dist / (4.8 * 1.4 * math.pi) * 360
+ self.kartEnterAnimateInterval = Parallel(LerpHprInterval(self.wheels[0], 5.0, Vec3(self.wheels[0].getH(), wheelAngle, self.wheels[0].getR())), LerpHprInterval(self.wheels[1], 5.0, Vec3(self.wheels[1].getH(), wheelAngle, self.wheels[1].getR())), LerpHprInterval(self.wheels[2], 5.0, Vec3(self.wheels[2].getH(), wheelAngle, self.wheels[2].getR())), LerpHprInterval(self.wheels[3], 5.0, Vec3(self.wheels[3].getH(), wheelAngle, self.wheels[3].getR())), name='CogKartAnimate')
+ trolleyExitTrack1 = Parallel(LerpPosInterval(self.golfKart, 5.0, self.endPos), self.kartEnterAnimateInterval, name='CogKartExitTrack')
+ self.trolleyExitTrack = Sequence(trolleyExitTrack1)
+ self.trolleyEnterTrack = Sequence(LerpPosInterval(self.golfKart, 5.0, self.startingPos, startPos=self.enteringPos))
+ self.closeDoors = Sequence(self.trolleyExitTrack, Func(self.onDoorCloseFinish))
+ self.openDoors = Sequence(self.trolleyEnterTrack)
+ self.setPos(0, 0, 0)
+
+ def __placeElevator(self):
+ self.notify.debug('PLACING ELEVATOR FOOL!!')
+ if self.isEntering:
+ elevatorNode = render.find('**/elevator_origin')
+ if not elevatorNode.isEmpty():
+ self.elevatorModel.setPos(0, 0, 0)
+ self.elevatorModel.reparentTo(elevatorNode)
+ else:
+ self.notify.debug('NO NODE elevator_origin!!')
+ else:
+ elevatorNode = render.find('**/SlidingDoor')
+ if not elevatorNode.isEmpty():
+ self.elevatorModel.setPos(0, 10, -15)
+ self.elevatorModel.setH(180)
+ self.elevatorModel.reparentTo(elevatorNode)
+ else:
+ self.notify.debug('NO NODE SlidingDoor!!')
+
+ def setLatch(self, markerId):
+ self.notify.info('Setting latch')
+ marker = self.cr.doId2do.get(markerId)
+ self.latchRequest = self.cr.relatedObjectMgr.requestObjects([markerId], allCallback=self.set2Latch, timeout=5)
+ self.latch = markerId
+
+ def set2Latch(self, taskMgrFooler = None):
+ self.latchRequest = None
+ if hasattr(self, 'cr'):
+ marker = self.cr.doId2do.get(self.latch)
+ if marker:
+ self.getElevatorModel().reparentTo(marker)
+ return
+ taskMgr.doMethodLater(10.0, self._repart2Marker, 'elevatorfloor-markerReparent')
+ self.notify.warning('Using backup, do method later version of latch')
+ return
+
+ def _repart2Marker(self, taskFoolio = 0):
+ if hasattr(self, 'cr') and self.cr:
+ marker = self.cr.doId2do.get(self.latch)
+ if marker:
+ self.getElevatorModel().reparentTo(marker)
+ else:
+ self.notify.error('could not find latch even in defered try')
+
+ def setPos(self, x, y, z):
+ self.getElevatorModel().setPos(x, y, z)
+
+ def setH(self, H):
+ self.getElevatorModel().setH(H)
+
+ def delete(self):
+ self.request('Off')
+ DistributedElevatorFSM.DistributedElevatorFSM.delete(self)
+ self.getElevatorModel().removeNode()
+ del self.golfKart
+ self.ignore('LawOffice_Spec_Loaded')
+ self.ignoreAll()
+
+ def disable(self):
+ self.request('Off')
+ self.clearToonTracks()
+ DistributedElevatorFSM.DistributedElevatorFSM.disable(self)
+
+ def setEntranceId(self, entranceId):
+ self.entranceId = entranceId
+ if self.entranceId == 0:
+ self.elevatorModel.setPosHpr(62.74, -85.31, 0.0, 2.0, 0.0, 0.0)
+ elif self.entranceId == 1:
+ self.elevatorModel.setPosHpr(-162.25, 26.43, 0.0, 269.0, 0.0, 0.0)
+ else:
+ self.notify.error('Invalid entranceId: %s' % entranceId)
+
+ def gotBldg(self, buildingList):
+ pass
+
+ def setFloor(self, floorNumber):
+ if self.currentFloor >= 0:
+ if self.bldg.floorIndicator[self.currentFloor]:
+ self.bldg.floorIndicator[self.currentFloor].setColor(LIGHT_OFF_COLOR)
+ if floorNumber >= 0:
+ if self.bldg.floorIndicator[floorNumber]:
+ self.bldg.floorIndicator[floorNumber].setColor(LIGHT_ON_COLOR)
+ self.currentFloor = floorNumber
+
+ def handleEnterSphere(self, collEntry):
+ self.cr.playGame.getPlace().detectedElevatorCollision(self)
+
+ def handleEnterElevator(self):
+ if base.localAvatar.hp > 0:
+ toon = base.localAvatar
+ self.sendUpdate('requestBoard', [])
+ else:
+ self.notify.warning('Tried to board elevator with hp: %d' % base.localAvatar.hp)
+
+ def enterWaitEmpty(self, ts):
+ self.lastState = self.state
+ self.elevatorSphereNodePath.unstash()
+ self.forceDoorsOpen()
+ self.accept(self.uniqueName('enterelevatorSphere'), self.handleEnterSphere)
+ self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator)
+ DistributedElevatorFSM.DistributedElevatorFSM.enterWaitEmpty(self, ts)
+
+ def exitWaitEmpty(self):
+ self.lastState = self.state
+ self.elevatorSphereNodePath.stash()
+ self.ignore(self.uniqueName('enterelevatorSphere'))
+ self.ignore(self.uniqueName('enterElevatorOK'))
+ DistributedElevatorFSM.DistributedElevatorFSM.exitWaitEmpty(self)
+
+ def enterWaitCountdown(self, ts):
+ self.lastState = self.state
+ DistributedElevatorFSM.DistributedElevatorFSM.enterWaitCountdown(self, ts)
+ self.forceDoorsOpen()
+ self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator)
+ self.startCountdownClock(self.countdownTime, ts)
+
+ def exitWaitCountdown(self):
+ self.lastState = self.state
+ self.ignore(self.uniqueName('enterElevatorOK'))
+ DistributedElevatorFSM.DistributedElevatorFSM.exitWaitCountdown(self)
+
+ def enterClosing(self, ts):
+ self.lastState = self.state
+ taskMgr.doMethodLater(1.0, self._delayIris, 'delayedIris')
+ DistributedElevatorFSM.DistributedElevatorFSM.enterClosing(self, ts)
+
+ def _delayIris(self, tskfooler = 0):
+ base.transitions.irisOut(1.0)
+ base.localAvatar.pauseGlitchKiller()
+ return Task.done
+
+ def kickToonsOut(self):
+ if not self.localToonOnBoard:
+ zoneId = self.cr.playGame.hood.hoodId
+ self.cr.playGame.getPlace().fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(zoneId),
+ 'where': ZoneUtil.getToonWhereName(zoneId),
+ 'how': 'teleportIn',
+ 'hoodId': zoneId,
+ 'zoneId': zoneId,
+ 'shardId': None,
+ 'avId': -1}])
+ return
+
+ def exitClosing(self):
+ self.lastState = self.state
+ DistributedElevatorFSM.DistributedElevatorFSM.exitClosing(self)
+
+ def enterClosed(self, ts):
+ self.lastState = self.state
+ self.forceDoorsClosed()
+ self.__doorsClosed(self.getZoneId())
+
+ def exitClosed(self):
+ self.lastState = self.state
+ DistributedElevatorFSM.DistributedElevatorFSM.exitClosed(self)
+
+ def enterOff(self):
+ self.lastState = self.state
+ if self.wantState == 'closed':
+ self.demand('Closing')
+ elif self.wantState == 'waitEmpty':
+ self.demand('WaitEmpty')
+ DistributedElevatorFSM.DistributedElevatorFSM.enterOff(self)
+
+ def exitOff(self):
+ self.lastState = self.state
+ DistributedElevatorFSM.DistributedElevatorFSM.exitOff(self)
+
+ def enterOpening(self, ts):
+ self.lastState = self.state
+ DistributedElevatorFSM.DistributedElevatorFSM.enterOpening(self, ts)
+
+ def exitOpening(self):
+ DistributedElevatorFSM.DistributedElevatorFSM.exitOpening(self)
+ self.kickEveryoneOut()
+
+ def getZoneId(self):
+ return 0
+
+ def setBldgDoId(self, bldgDoId):
+ self.bldg = None
+ self.setupElevatorKart()
+ return
+
+ def setupElevatorKart(self):
+ collisionRadius = ElevatorConstants.ElevatorData[self.type]['collRadius']
+ self.elevatorSphere = CollisionSphere(0, 0, 0, collisionRadius)
+ self.elevatorSphere.setTangible(1)
+ self.elevatorSphereNode = CollisionNode(self.uniqueName('elevatorSphere'))
+ self.elevatorSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
+ self.elevatorSphereNode.addSolid(self.elevatorSphere)
+ self.elevatorSphereNodePath = self.getElevatorModel().attachNewNode(self.elevatorSphereNode)
+ self.elevatorSphereNodePath.hide()
+ self.elevatorSphereNodePath.reparentTo(self.getElevatorModel())
+ self.elevatorSphereNodePath.stash()
+ self.boardedAvIds = {}
+ self.finishSetup()
+
+ def getElevatorModel(self):
+ return self.elevatorModel
+
+ def kickEveryoneOut(self):
+ bailFlag = 0
+ for avId, slot in self.boardedAvIds.items():
+ self.emptySlot(slot, avId, bailFlag, globalClockDelta.getRealNetworkTime())
+ if avId == base.localAvatar.doId:
+ pass
+
+ def __doorsClosed(self, zoneId):
+ pass
+
+ def onDoorCloseFinish(self):
+ pass
+
+ def setLocked(self, locked):
+ self.isLocked = locked
+ if locked:
+ if self.state == 'WaitEmpty':
+ self.request('Closing')
+ if self.countFullSeats() == 0:
+ self.wantState = 'closed'
+ else:
+ self.wantState = 'opening'
+ else:
+ self.wantState = 'waitEmpty'
+ if self.state == 'Closed':
+ self.request('Opening')
+
+ def getLocked(self):
+ return self.isLocked
+
+ def setEntering(self, entering):
+ self.isEntering = entering
+
+ def getEntering(self):
+ return self.isEntering
+
+ def forceDoorsOpen(self):
+ pass
+
+ def forceDoorsClosed(self):
+ pass
+
+ def enterOff(self):
+ self.lastState = self.state
+
+ def exitOff(self):
+ pass
+
+ def setLawOfficeInteriorZone(self, zoneId):
+ if self.localToonOnBoard:
+ hoodId = self.cr.playGame.hood.hoodId
+ doneStatus = {'loader': 'cogHQLoader',
+ 'where': 'factoryInterior',
+ 'how': 'teleportIn',
+ 'zoneId': zoneId,
+ 'hoodId': hoodId}
+ self.cr.playGame.getPlace().elevator.signalDone(doneStatus)
+
+ def getElevatorModel(self):
+ return self.golfKart
+
+ def setPosHpr(self, x, y, z, h, p, r):
+ self.startingPos = Vec3(x, y, z)
+ self.enteringPos = Vec3(x, y, z - 10)
+ self.startingHpr = Vec3(h, 0, 0)
+ self.golfKart.setPosHpr(x, y, z, h, 0, 0)
+
+ def fillSlot(self, index, avId):
+ self.notify.debug('%s.fillSlot(%s, %s, ...)' % (self.doId, index, avId))
+ request = self.toonRequests.get(index)
+ if request:
+ self.cr.relatedObjectMgr.abortRequest(request)
+ del self.toonRequests[index]
+ if avId == 0:
+ pass
+ elif not self.cr.doId2do.has_key(avId):
+ func = PythonUtil.Functor(self.gotToon, index, avId)
+ self.toonRequests[index] = self.cr.relatedObjectMgr.requestObjects([avId], allCallback=func)
+ elif not self.isSetup:
+ self.deferredSlots.append((index, avId))
+ else:
+ if avId == base.localAvatar.getDoId():
+ self.localToonOnBoard = 1
+ elevator = self.getPlaceElevator()
+ elevator.fsm.request('boarding', [self.getElevatorModel()])
+ elevator.fsm.request('boarded')
+ toon = self.cr.doId2do[avId]
+ toon.stopSmooth()
+ toon.wrtReparentTo(self.golfKart)
+ sitStartDuration = toon.getDuration('sit-start')
+ jumpTrack = self.generateToonJumpTrack(toon, index)
+ track = Sequence(jumpTrack, Func(toon.setAnimState, 'Sit', 1.0), Func(self.clearToonTrack, avId), name=toon.uniqueName('fillElevator'), autoPause=1)
+ track.delayDelete = DelayDelete.DelayDelete(toon, 'fillSlot')
+ self.storeToonTrack(avId, track)
+ track.start()
+ self.boardedAvIds[avId] = None
+ return
+
+ def generateToonJumpTrack(self, av, seatIndex):
+ av.pose('sit', 47)
+ hipOffset = av.getHipsParts()[2].getPos(av)
+
+ def getToonJumpTrack(av, seatIndex):
+
+ def getJumpDest(av = av, node = self.golfKart):
+ dest = Point3(0, 0, 0)
+ if hasattr(self, 'golfKart') and self.golfKart:
+ dest = Vec3(self.golfKart.getPos(av.getParent()))
+ seatNode = self.golfKart.find('**/seat' + str(seatIndex + 1))
+ dest += seatNode.getPos(self.golfKart)
+ dna = av.getStyle()
+ dest -= hipOffset
+ if seatIndex < 2:
+ dest.setY(dest.getY() + 2 * hipOffset.getY())
+ dest.setZ(dest.getZ() + 0.1)
+ else:
+ self.notify.warning('getJumpDestinvalid golfKart, returning (0,0,0)')
+ return dest
+
+ def getJumpHpr(av = av, node = self.golfKart):
+ hpr = Point3(0, 0, 0)
+ if hasattr(self, 'golfKart') and self.golfKart:
+ hpr = self.golfKart.getHpr(av.getParent())
+ if seatIndex < 2:
+ hpr.setX(hpr.getX() + 180)
+ else:
+ hpr.setX(hpr.getX())
+ angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
+ hpr.setX(angle)
+ else:
+ self.notify.warning('getJumpHpr invalid golfKart, returning (0,0,0)')
+ return hpr
+
+ toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.43), Parallel(LerpHprInterval(av, hpr=getJumpHpr, duration=0.9), ProjectileInterval(av, endPos=getJumpDest, duration=0.9))))
+ return toonJumpTrack
+
+ def getToonSitTrack(av):
+ toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit'))
+ return toonSitTrack
+
+ toonJumpTrack = getToonJumpTrack(av, seatIndex)
+ toonSitTrack = getToonSitTrack(av)
+ jumpTrack = Sequence(Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack)), Func(av.wrtReparentTo, self.golfKart))
+ return jumpTrack
+
+ def emptySlot(self, index, avId, bailFlag, timestamp):
+ if avId == 0:
+ pass
+ elif not self.isSetup:
+ newSlots = []
+ for slot in self.deferredSlots:
+ if slot[0] != index:
+ newSlots.append(slot)
+
+ self.deferredSlots = newSlots
+ elif self.cr.doId2do.has_key(avId):
+ if bailFlag == 1 and hasattr(self, 'clockNode'):
+ if timestamp < self.countdownTime and timestamp >= 0:
+ self.countdown(self.countdownTime - timestamp)
+ else:
+ self.countdown(self.countdownTime)
+ toon = self.cr.doId2do[avId]
+ toon.stopSmooth()
+ sitStartDuration = toon.getDuration('sit-start')
+ jumpOutTrack = self.generateToonReverseJumpTrack(toon, index)
+ track = Sequence(jumpOutTrack, Func(self.clearToonTrack, avId), Func(self.notifyToonOffElevator, toon), name=toon.uniqueName('emptyElevator'), autoPause=1)
+ track.delayDelete = DelayDelete.DelayDelete(toon, 'ClubElevator.emptySlot')
+ self.storeToonTrack(avId, track)
+ track.start()
+ if avId == base.localAvatar.getDoId():
+ messenger.send('exitElevator')
+ if avId in self.boardedAvIds:
+ del self.boardedAvIds[avId]
+ else:
+ self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the elevator!')
+
+ def generateToonReverseJumpTrack(self, av, seatIndex):
+ self.notify.debug('av.getH() = %s' % av.getH())
+
+ def getToonJumpTrack(av, destNode):
+
+ def getJumpDest(av = av, node = destNode):
+ dest = node.getPos(av.getParent())
+ dest += Vec3(*self.JumpOutOffsets[seatIndex])
+ return dest
+
+ def getJumpHpr(av = av, node = destNode):
+ hpr = node.getHpr(av.getParent())
+ hpr.setX(hpr.getX() + 180)
+ angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
+ hpr.setX(angle)
+ return hpr
+
+ toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.1), Parallel(ProjectileInterval(av, endPos=getJumpDest, duration=0.9))))
+ return toonJumpTrack
+
+ toonJumpTrack = getToonJumpTrack(av, self.golfKart)
+ jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render))
+ return jumpTrack
+
+ def startCountdownClock(self, countdownTime, ts):
+ DistributedElevatorFSM.DistributedElevatorFSM.startCountdownClock(self, countdownTime, ts)
+ self.clock.setH(self.clock.getH() + 180)
+
+ def storeToonTrack(self, avId, track):
+ self.clearToonTrack(avId)
+ self.__toonTracks[avId] = track
+
+ def clearToonTrack(self, avId):
+ oldTrack = self.__toonTracks.get(avId)
+ if oldTrack:
+ oldTrack.pause()
+ if self.__toonTracks.get(avId):
+ DelayDelete.cleanupDelayDeletes(self.__toonTracks[avId])
+ del self.__toonTracks[avId]
+
+ def clearToonTracks(self):
+ keyList = []
+ for key in self.__toonTracks:
+ keyList.append(key)
+
+ for key in keyList:
+ if self.__toonTracks.has_key(key):
+ self.clearToonTrack(key)
diff --git a/toontown/building/DistributedClubElevatorAI.py b/toontown/building/DistributedClubElevatorAI.py
new file mode 100644
index 0000000..ab21556
--- /dev/null
+++ b/toontown/building/DistributedClubElevatorAI.py
@@ -0,0 +1,283 @@
+from otp.ai.AIBase import *
+from toontown.toonbase import ToontownGlobals
+from direct.distributed.ClockDelta import *
+from toontown.building import ElevatorConstants
+from toontown.building import DistributedElevatorFSMAI
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm.FSM import FSM
+
+class DistributedClubElevatorAI(DistributedElevatorFSMAI.DistributedElevatorFSMAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorFloorAI')
+ defaultTransitions = {'Off': ['Opening', 'Closed'], 'Opening': ['WaitEmpty', 'WaitCountdown', 'Opening', 'Closing'], 'WaitEmpty': ['WaitCountdown', 'Closing', 'WaitEmpty'], 'WaitCountdown': ['WaitEmpty', 'AllAboard', 'Closing', 'WaitCountdown'], 'AllAboard': ['WaitEmpty', 'Closing'], 'Closing': ['Closed', 'WaitEmpty', 'Closing', 'Opening'], 'Closed': ['Opening']}
+ id = 0
+ DoBlockedRoomCheck = simbase.config.GetBool('elevator-blocked-rooms-check', 1)
+
+ def __init__(self, air, lawOfficeId, bldg, avIds, markerId=None, numSeats=4, antiShuffle=0, minLaff=0):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.__init__(self, air, bldg, numSeats, antiShuffle=antiShuffle, minLaff=minLaff)
+ FSM.__init__(self, 'ElevatorFloor_%s_FSM' % self.id)
+ self.type = ElevatorConstants.ELEVATOR_COUNTRY_CLUB
+ self.countdownTime = ElevatorConstants.ElevatorData[self.type]['countdown']
+ self.lawOfficeId = lawOfficeId
+ self.anyToonsBailed = 0
+ self.avIds = avIds
+ self.isEntering = 0
+ self.isLocked = 0
+ self.setLocked(0)
+ self.wantState = None
+ self.latchRoom = None
+ self.setLatch(markerId)
+ self.zoneId = bldg.zoneId
+ return
+
+ def generate(self):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.generate(self)
+
+ def generateWithRequired(self, zoneId):
+ self.zoneId = zoneId
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.generateWithRequired(self, self.zoneId)
+
+ def delete(self):
+ for seatIndex in range(len(self.seats)):
+ avId = self.seats[seatIndex]
+ if avId:
+ self.clearFullNow(seatIndex)
+ self.clearEmptyNow(seatIndex)
+
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.delete(self)
+
+ def getEntranceId(self):
+ return self.entranceId
+
+ def d_setFloor(self, floorNumber):
+ self.sendUpdate('setFloor', [floorNumber])
+
+ def avIsOKToBoard(self, av):
+ return av.hp > 0 and self.accepting and not self.isLocked
+
+ def acceptBoarder(self, avId, seatIndex):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.acceptBoarder(self, avId, seatIndex)
+ self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId])
+ if self.state == 'WaitEmpty' and self.countFullSeats() < self.countAvsInZone():
+ self.request('WaitCountdown')
+ self.bldg.elevatorAlert(avId)
+ elif self.state in ('WaitCountdown', 'WaitEmpty') and self.countFullSeats() >= self.countAvsInZone():
+ taskMgr.doMethodLater(ElevatorConstants.TOON_BOARD_ELEVATOR_TIME, self.goAllAboard, self.quickBoardTask)
+
+ def countAvsInZone(self):
+ matchingZones = 0
+ for avId in self.bldg.avIds:
+ av = self.air.doId2do.get(avId)
+ if av:
+ if av.zoneId == self.bldg.zoneId:
+ matchingZones += 1
+
+ return matchingZones
+
+ def goAllAboard(self, throwAway=1):
+ self.request('Closing')
+ return Task.done
+
+ def __handleUnexpectedExit(self, avId):
+ self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly')
+ seatIndex = self.findAvatar(avId)
+ if seatIndex == None:
+ pass
+ else:
+ self.clearFullNow(seatIndex)
+ self.clearEmptyNow(seatIndex)
+ if self.countFullSeats() == 0:
+ self.request('WaitEmpty')
+ return
+
+ def acceptExiter(self, avId):
+ seatIndex = self.findAvatar(avId)
+ if seatIndex == None:
+ pass
+ else:
+ self.clearFullNow(seatIndex)
+ bailFlag = 0
+ if self.anyToonsBailed == 0:
+ bailFlag = 1
+ self.resetCountdown()
+ self.anyToonsBailed = 1
+ self.sendUpdate('emptySlot' + str(seatIndex), [
+ avId, bailFlag, globalClockDelta.getRealNetworkTime()])
+ if self.countFullSeats() == 0:
+ self.request('WaitEmpty')
+ taskMgr.doMethodLater(ElevatorConstants.TOON_EXIT_ELEVATOR_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs=(seatIndex,))
+ return
+
+ def enterOpening(self):
+ self.d_setState('Opening')
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterOpening(self)
+ taskMgr.doMethodLater(ElevatorConstants.ElevatorData[ElevatorConstants.ELEVATOR_NORMAL]['openTime'], self.waitEmptyTask, self.uniqueName('opening-timer'))
+
+ def exitOpening(self):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.exitOpening(self)
+ if self.isLocked:
+ self.wantState = 'closed'
+ if self.wantState == 'closed':
+ self.demand('Closing')
+
+ def waitEmptyTask(self, task):
+ self.request('WaitEmpty')
+ return Task.done
+
+ def enterWaitEmpty(self):
+ self.lastState = self.state
+ for i in range(len(self.seats)):
+ self.seats[i] = None
+
+ print self.seats
+ if self.wantState == 'closed':
+ self.demand('Closing')
+ else:
+ self.d_setState('WaitEmpty')
+ self.accepting = 1
+ return
+
+ def enterWaitCountdown(self):
+ self.lastState = self.state
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterWaitCountdown(self)
+ taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer'))
+ if self.lastState == 'WaitCountdown':
+ pass
+
+ def timeToGoTask(self, task):
+ if self.countFullSeats() > 0:
+ self.request('AllAboard')
+ else:
+ self.request('WaitEmpty')
+ return Task.done
+
+ def resetCountdown(self):
+ taskMgr.remove(self.uniqueName('countdown-timer'))
+ taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer'))
+
+ def enterAllAboard(self):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterAllAboard(self)
+ currentTime = globalClock.getRealTime()
+ elapsedTime = currentTime - self.timeOfBoarding
+ self.notify.debug('elapsed time: ' + str(elapsedTime))
+ waitTime = max(ElevatorConstants.TOON_BOARD_ELEVATOR_TIME - elapsedTime, 0)
+ taskMgr.doMethodLater(waitTime, self.closeTask, self.uniqueName('waitForAllAboard'))
+
+ def closeTask(self, task):
+ if self.countFullSeats() >= 1:
+ self.request('Closing')
+ else:
+ self.request('WaitEmpty')
+ return Task.done
+
+ def enterClosing(self):
+ if self.countFullSeats() > 0:
+ self.sendUpdate('kickToonsOut')
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterClosing(self)
+ taskMgr.doMethodLater(ElevatorConstants.ElevatorData[ElevatorConstants.ELEVATOR_STAGE]['closeTime'], self.elevatorClosedTask, self.uniqueName('closing-timer'))
+ self.d_setState('Closing')
+
+ def elevatorClosedTask(self, task):
+ self.elevatorClosed()
+ return Task.done
+
+ def elevatorClosed(self):
+ if self.isLocked:
+ self.request('Closed')
+ return
+ numPlayers = self.countFullSeats()
+ if numPlayers > 0:
+ players = []
+ for i in self.seats:
+ if i not in [None, 0]:
+ players.append(i)
+
+ sittingAvIds = []
+ for seatIndex in range(len(self.seats)):
+ avId = self.seats[seatIndex]
+ if avId:
+ sittingAvIds.append(avId)
+
+ for avId in self.avIds:
+ if avId not in sittingAvIds:
+ pass
+
+ self.bldg.startNextFloor()
+ else:
+ self.notify.warning('The elevator left, but was empty.')
+ self.request('Closed')
+ return
+
+ def setLocked(self, locked):
+ self.isLocked = locked
+ if locked:
+ if self.state == 'WaitEmpty':
+ self.request('Closing')
+ if self.countFullSeats() == 0:
+ self.wantState = 'closed'
+ else:
+ self.wantState = 'opening'
+ else:
+ self.wantState = 'waitEmpty'
+ if self.state == 'Closed':
+ self.request('Opening')
+
+ def getLocked(self):
+ return self.isLocked
+
+ def unlock(self):
+ if self.isLocked:
+ self.setLocked(0)
+
+ def lock(self):
+ if not self.isLocked:
+ self.setLocked(1)
+
+ def start(self):
+ self.quickBoardTask = self.uniqueName('quickBoard')
+ self.request('Opening')
+
+ def beClosed(self):
+ pass
+
+ def setEntering(self, entering):
+ self.isEntering = entering
+
+ def getEntering(self):
+ return self.isEntering
+
+ def enterClosed(self):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterClosed(self)
+ if self.wantState == 'closed':
+ pass
+ else:
+ self.demand('Opening')
+
+ def enterOff(self):
+ self.lastState = self.state
+ if self.wantState == 'closed':
+ self.demand('Closing')
+ else:
+ if self.wantState == 'waitEmpty':
+ self.demand('WaitEmpty')
+
+ def setPos(self, pointPos):
+ self.sendUpdate('setPos', [pointPos[0], pointPos[1], pointPos[2]])
+
+ def setH(self, H):
+ self.sendUpdate('setH', [H])
+
+ def setLatch(self, markerId):
+ self.latch = markerId
+
+ def getLatch(self):
+ return self.latch
+
+ def checkBoard(self, av):
+ if av.hp < self.minLaff:
+ return ElevatorConstants.REJECT_MINLAFF
+ if self.DoBlockedRoomCheck and self.bldg:
+ if hasattr(self.bldg, 'blockedRooms'):
+ if self.bldg.blockedRooms:
+ return ElevatorConstants.REJECT_BLOCKED_ROOM
+ return 0
diff --git a/toontown/building/DistributedDoor.py b/toontown/building/DistributedDoor.py
new file mode 100644
index 0000000..a442ddf
--- /dev/null
+++ b/toontown/building/DistributedDoor.py
@@ -0,0 +1,654 @@
+from toontown.toonbase.ToonBaseGlobal import *
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from direct.distributed.ClockDelta import *
+from toontown.toonbase import ToontownGlobals
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObject
+from toontown.hood import ZoneUtil
+from toontown.suit import Suit
+from toontown.distributed import DelayDelete
+import FADoorCodes
+from direct.task.Task import Task
+import DoorTypes
+from toontown.toontowngui import TTDialog
+from toontown.toonbase import TTLocalizer
+from toontown.toontowngui import TeaserPanel
+from toontown.distributed.DelayDeletable import DelayDeletable
+if (__debug__):
+ import pdb
+
+class DistributedDoor(DistributedObject.DistributedObject, DelayDeletable):
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.openSfx = base.loadSfx('phase_3.5/audio/sfx/Door_Open_1.mp3')
+ self.closeSfx = base.loadSfx('phase_3.5/audio/sfx/Door_Close_1.mp3')
+ self.nametag = None
+ self.fsm = ClassicFSM.ClassicFSM('DistributedDoor_right', [State.State('off', self.enterOff, self.exitOff, ['closing',
+ 'closed',
+ 'opening',
+ 'open']),
+ State.State('closing', self.enterClosing, self.exitClosing, ['closed', 'opening']),
+ State.State('closed', self.enterClosed, self.exitClosed, ['opening']),
+ State.State('opening', self.enterOpening, self.exitOpening, ['open']),
+ State.State('open', self.enterOpen, self.exitOpen, ['closing', 'open'])], 'off', 'off')
+ self.fsm.enterInitialState()
+ self.exitDoorFSM = ClassicFSM.ClassicFSM('DistributedDoor_left', [State.State('off', self.exitDoorEnterOff, self.exitDoorExitOff, ['closing',
+ 'closed',
+ 'opening',
+ 'open']),
+ State.State('closing', self.exitDoorEnterClosing, self.exitDoorExitClosing, ['closed', 'opening']),
+ State.State('closed', self.exitDoorEnterClosed, self.exitDoorExitClosed, ['opening']),
+ State.State('opening', self.exitDoorEnterOpening, self.exitDoorExitOpening, ['open']),
+ State.State('open', self.exitDoorEnterOpen, self.exitDoorExitOpen, ['closing', 'open'])], 'off', 'off')
+ self.exitDoorFSM.enterInitialState()
+ self.specialDoorTypes = {DoorTypes.EXT_HQ: 0,
+ DoorTypes.EXT_COGHQ: 0,
+ DoorTypes.INT_COGHQ: 0,
+ DoorTypes.EXT_KS: 0,
+ DoorTypes.INT_KS: 0}
+ self.doorX = 1.5
+ return
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+ self.avatarTracks = []
+ self.avatarExitTracks = []
+ self.avatarIDList = []
+ self.avatarExitIDList = []
+ self.doorTrack = None
+ self.doorExitTrack = None
+ return
+
+ def disable(self):
+ self.clearNametag()
+ taskMgr.remove(self.checkIsDoorHitTaskName())
+ self.ignore(self.getEnterTriggerEvent())
+ self.ignore(self.getExitTriggerEvent())
+ self.ignore('clearOutToonInterior')
+ self.fsm.request('off')
+ self.exitDoorFSM.request('off')
+ if self.__dict__.has_key('building'):
+ del self.building
+ self.finishAllTracks()
+ self.avatarIDList = []
+ self.avatarExitIDList = []
+ if hasattr(self, 'tempDoorNodePath'):
+ self.tempDoorNodePath.removeNode()
+ del self.tempDoorNodePath
+ DistributedObject.DistributedObject.disable(self)
+
+ def delete(self):
+ del self.fsm
+ del self.exitDoorFSM
+ del self.openSfx
+ del self.closeSfx
+ DistributedObject.DistributedObject.delete(self)
+
+ def wantsNametag(self):
+ return not ZoneUtil.isInterior(self.zoneId)
+
+ def setupNametag(self):
+ if not self.wantsNametag():
+ return
+ if self.nametag == None:
+ self.nametag = NametagGroup()
+ self.nametag.setFont(ToontownGlobals.getBuildingNametagFont())
+ if TTLocalizer.BuildingNametagShadow:
+ self.nametag.setShadow(*TTLocalizer.BuildingNametagShadow)
+ self.nametag.setContents(Nametag.CName)
+ self.nametag.setColorCode(NametagGroup.CCToonBuilding)
+ self.nametag.setActive(0)
+ self.nametag.setAvatar(self.getDoorNodePath())
+ self.nametag.setObjectCode(self.block)
+ name = self.cr.playGame.dnaStore.getTitleFromBlockNumber(self.block)
+ self.nametag.setName(name)
+ self.nametag.manage(base.marginManager)
+ return
+
+ def clearNametag(self):
+ if self.nametag != None:
+ self.nametag.unmanage(base.marginManager)
+ self.nametag.setAvatar(NodePath())
+ self.nametag = None
+ return
+
+ def getTriggerName(self):
+ if self.doorType == DoorTypes.INT_HQ or self.specialDoorTypes.has_key(self.doorType):
+ return 'door_trigger_' + str(self.block) + '_' + str(self.doorIndex)
+ else:
+ return 'door_trigger_' + str(self.block)
+
+ def getTriggerName_wip(self):
+ name = 'door_trigger_%d' % (self.doId,)
+ return name
+
+ def getEnterTriggerEvent(self):
+ return 'enter' + self.getTriggerName()
+
+ def getExitTriggerEvent(self):
+ return 'exit' + self.getTriggerName()
+
+ def hideDoorParts(self):
+ if self.specialDoorTypes.has_key(self.doorType):
+ self.hideIfHasFlat(self.findDoorNode('rightDoor'))
+ self.hideIfHasFlat(self.findDoorNode('leftDoor'))
+ self.findDoorNode('doorFrameHoleRight').hide()
+ self.findDoorNode('doorFrameHoleLeft').hide()
+ else:
+ return
+
+ def setTriggerName(self):
+ if self.specialDoorTypes.has_key(self.doorType):
+ building = self.getBuilding()
+ doorTrigger = building.find('**/door_' + str(self.doorIndex) + '/**/door_trigger*')
+ doorTrigger.node().setName(self.getTriggerName())
+ else:
+ return
+
+ def setTriggerName_wip(self):
+ building = self.getBuilding()
+ doorTrigger = building.find('**/door_%d/**/door_trigger_%d' % (self.doorIndex, self.block))
+ if doorTrigger.isEmpty():
+ doorTrigger = building.find('**/door_trigger_%d' % (self.block,))
+ if doorTrigger.isEmpty():
+ doorTrigger = building.find('**/door_%d/**/door_trigger_*' % (self.doorIndex,))
+ if doorTrigger.isEmpty():
+ doorTrigger = building.find('**/door_trigger_*')
+ doorTrigger.node().setName(self.getTriggerName())
+
+ def setZoneIdAndBlock(self, zoneId, block):
+ self.zoneId = zoneId
+ self.block = block
+
+ def setDoorType(self, doorType):
+ self.notify.debug('Door type = ' + str(doorType) + ' on door #' + str(self.doId))
+ self.doorType = doorType
+
+ def setDoorIndex(self, doorIndex):
+ self.doorIndex = doorIndex
+
+ def setSwing(self, flags):
+ self.leftSwing = flags & 1 != 0
+ self.rightSwing = flags & 2 != 0
+
+ def setOtherZoneIdAndDoId(self, zoneId, distributedObjectID):
+ self.otherZoneId = zoneId
+ self.otherDoId = distributedObjectID
+
+ def setState(self, state, timestamp):
+ self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])
+
+ def setExitDoorState(self, state, timestamp):
+ self.exitDoorFSM.request(state, [globalClockDelta.localElapsedTime(timestamp)])
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ self.doPostAnnounceGenerate()
+
+ def doPostAnnounceGenerate(self):
+ if self.doorType == DoorTypes.INT_STANDARD:
+ self.bHasFlat = True
+ else:
+ self.bHasFlat = not self.findDoorNode('door*flat', True).isEmpty()
+ self.hideDoorParts()
+ self.setTriggerName()
+ self.accept(self.getEnterTriggerEvent(), self.doorTrigger)
+ self.acceptOnce('clearOutToonInterior', self.doorTrigger)
+ self.setupNametag()
+
+ def getBuilding(self):
+ if not self.__dict__.has_key('building'):
+ if self.doorType == DoorTypes.INT_STANDARD:
+ door = render.find('**/leftDoor;+s')
+ self.building = door.getParent()
+ elif self.doorType == DoorTypes.INT_HQ:
+ door = render.find('**/door_0')
+ self.building = door.getParent()
+ elif self.doorType == DoorTypes.INT_KS:
+ self.building = render.find('**/KartShop_Interior*')
+ elif self.doorType == DoorTypes.EXT_STANDARD or self.doorType == DoorTypes.EXT_HQ or self.doorType == DoorTypes.EXT_KS:
+ self.building = self.cr.playGame.hood.loader.geom.find('**/??' + str(self.block) + ':*_landmark_*_DNARoot;+s')
+ if self.building.isEmpty():
+ self.building = self.cr.playGame.hood.loader.geom.find('**/??' + str(self.block) + ':animated_building_*_DNARoot;+s')
+ elif self.doorType == DoorTypes.EXT_COGHQ or self.doorType == DoorTypes.INT_COGHQ:
+ self.building = self.cr.playGame.hood.loader.geom
+ else:
+ self.notify.error('No such door type as ' + str(self.doorType))
+ return self.building
+
+ def getBuilding_wip(self):
+ if not self.__dict__.has_key('building'):
+ if self.__dict__.has_key('block'):
+ self.building = self.cr.playGame.hood.loader.geom.find('**/??' + str(self.block) + ':*_landmark_*_DNARoot;+s')
+ else:
+ self.building = self.cr.playGame.hood.loader.geom
+ print '---------------- door is interior -------'
+ return self.building
+
+ def readyToExit(self):
+ base.transitions.fadeScreen(1.0)
+ self.sendUpdate('requestExit')
+
+ def avatarEnterDoorTrack(self, avatar, duration):
+ trackName = 'avatarEnterDoor-%d-%d' % (self.doId, avatar.doId)
+ track = Parallel(name=trackName)
+ otherNP = self.getDoorNodePath()
+ if hasattr(avatar, 'stopSmooth'):
+ avatar.stopSmooth()
+ if avatar.doId == base.localAvatar.doId:
+ track.append(LerpPosHprInterval(nodePath=camera, other=avatar, duration=duration, pos=Point3(0, -8, avatar.getHeight()), hpr=VBase3(0, 0, 0), blendType='easeInOut'))
+ finalPos = avatar.getParent().getRelativePoint(otherNP, Point3(self.doorX, 2, ToontownGlobals.FloorOffset))
+ moveHere = Sequence(self.getAnimStateInterval(avatar, 'walk'), LerpPosInterval(nodePath=avatar, duration=duration, pos=finalPos, blendType='easeIn'))
+ track.append(moveHere)
+ if avatar.doId == base.localAvatar.doId:
+ track.append(Sequence(Wait(duration * 0.5), Func(base.transitions.irisOut, duration * 0.5), Wait(duration * 0.5), Func(avatar.b_setParent, ToontownGlobals.SPHidden)))
+ track.delayDelete = DelayDelete.DelayDelete(avatar, 'avatarEnterDoorTrack')
+ return track
+
+ def avatarEnqueueTrack(self, avatar, duration):
+ if hasattr(avatar, 'stopSmooth'):
+ avatar.stopSmooth()
+ back = -5.0 - 2.0 * len(self.avatarIDList)
+ if back < -9.0:
+ back = -9.0
+ offset = Point3(self.doorX, back, ToontownGlobals.FloorOffset)
+ otherNP = self.getDoorNodePath()
+ walkLike = ActorInterval(avatar, 'walk', startTime=1, duration=duration, endTime=0.0001)
+ standHere = Sequence(LerpPosHprInterval(nodePath=avatar, other=otherNP, duration=duration, pos=offset, hpr=VBase3(0, 0, 0), blendType='easeInOut'), self.getAnimStateInterval(avatar, 'neutral'))
+ trackName = 'avatarEnqueueDoor-%d-%d' % (self.doId, avatar.doId)
+ track = Parallel(walkLike, standHere, name=trackName)
+ track.delayDelete = DelayDelete.DelayDelete(avatar, 'avatarEnqueueTrack')
+ return track
+
+ def getAnimStateInterval(self, avatar, animName):
+ isSuit = isinstance(avatar, Suit.Suit)
+ if isSuit:
+ return Func(avatar.loop, animName, 0)
+ else:
+ return Func(avatar.setAnimState, animName)
+
+ def isDoorHit(self):
+ vec = base.localAvatar.getRelativeVector(self.currentDoorNp, self.currentDoorVec)
+ netScale = self.currentDoorNp.getNetTransform().getScale()
+ yToTest = vec.getY() / netScale[1]
+ return yToTest < -0.5
+
+ def enterDoor(self):
+ if self.allowedToEnter():
+ messenger.send('DistributedDoor_doorTrigger')
+ self.sendUpdate('requestEnter')
+ else:
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.fsm.request('stopped')
+ self.dialog = TeaserPanel.TeaserPanel(pageName='otherHoods', doneFunc=self.handleOkTeaser)
+
+ def handleOkTeaser(self):
+ self.accept(self.getEnterTriggerEvent(), self.doorTrigger)
+ self.dialog.destroy()
+ del self.dialog
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.fsm.request('walk')
+
+ def allowedToEnter(self, zoneId = None):
+ allowed = False
+ if hasattr(base, 'ttAccess') and base.ttAccess:
+ if zoneId:
+ allowed = base.ttAccess.canAccess(zoneId)
+ else:
+ allowed = base.ttAccess.canAccess()
+ return allowed
+
+ def checkIsDoorHitTaskName(self):
+ return 'checkIsDoorHit' + self.getTriggerName()
+
+ def checkIsDoorHitTask(self, task):
+ if self.isDoorHit():
+ self.ignore(self.checkIsDoorHitTaskName())
+ self.ignore(self.getExitTriggerEvent())
+ self.enterDoor()
+ return Task.done
+ return Task.cont
+
+ def cancelCheckIsDoorHitTask(self, args):
+ taskMgr.remove(self.checkIsDoorHitTaskName())
+ del self.currentDoorNp
+ del self.currentDoorVec
+ self.ignore(self.getExitTriggerEvent())
+ self.accept(self.getEnterTriggerEvent(), self.doorTrigger)
+
+ def doorTrigger(self, args = None):
+ self.ignore(self.getEnterTriggerEvent())
+ if args == None:
+ self.enterDoor()
+ else:
+ self.currentDoorNp = NodePath(args.getIntoNodePath())
+ self.currentDoorVec = Vec3(args.getSurfaceNormal(self.currentDoorNp))
+ if self.isDoorHit():
+ self.enterDoor()
+ else:
+ self.accept(self.getExitTriggerEvent(), self.cancelCheckIsDoorHitTask)
+ taskMgr.add(self.checkIsDoorHitTask, self.checkIsDoorHitTaskName())
+ return
+
+ def avatarEnter(self, avatarID):
+ avatar = self.cr.doId2do.get(avatarID, None)
+ if avatar:
+ avatar.setAnimState('neutral')
+ track = self.avatarEnqueueTrack(avatar, 0.5)
+ track.start()
+ self.avatarTracks.append(track)
+ self.avatarIDList.append(avatarID)
+ return
+
+ def rejectEnter(self, reason):
+ message = FADoorCodes.reasonDict[reason]
+ if message:
+ self.__faRejectEnter(message)
+ else:
+ self.__basicRejectEnter()
+
+ def __basicRejectEnter(self):
+ self.accept(self.getEnterTriggerEvent(), self.doorTrigger)
+ if self.cr.playGame.getPlace():
+ self.cr.playGame.getPlace().setState('walk')
+
+ def __faRejectEnter(self, message):
+ self.rejectDialog = TTDialog.TTGlobalDialog(message=message, doneEvent='doorRejectAck', style=TTDialog.Acknowledge)
+ self.rejectDialog.show()
+ self.rejectDialog.delayDelete = DelayDelete.DelayDelete(self, '__faRejectEnter')
+ event = 'clientCleanup'
+ self.acceptOnce(event, self.__handleClientCleanup)
+ base.cr.playGame.getPlace().setState('stopped')
+ self.acceptOnce('doorRejectAck', self.__handleRejectAck)
+ self.acceptOnce('stoppedAsleep', self.__handleFallAsleepDoor)
+
+ def __handleClientCleanup(self):
+ if hasattr(self, 'rejectDialog') and self.rejectDialog:
+ self.rejectDialog.doneStatus = 'ok'
+ self.__handleRejectAck()
+
+ def __handleFallAsleepDoor(self):
+ self.rejectDialog.doneStatus = 'ok'
+ self.__handleRejectAck()
+
+ def __handleRejectAck(self):
+ self.ignore('doorRejectAck')
+ self.ignore('stoppedAsleep')
+ self.ignore('clientCleanup')
+ doneStatus = self.rejectDialog.doneStatus
+ if doneStatus != 'ok':
+ self.notify.error('Unrecognized doneStatus: ' + str(doneStatus))
+ self.__basicRejectEnter()
+ self.rejectDialog.delayDelete.destroy()
+ self.rejectDialog.cleanup()
+ del self.rejectDialog
+
+ def getDoorNodePath(self):
+ if self.doorType == DoorTypes.INT_STANDARD:
+ otherNP = render.find('**/door_origin')
+ elif self.doorType == DoorTypes.EXT_STANDARD:
+ if hasattr(self, 'tempDoorNodePath'):
+ return self.tempDoorNodePath
+ else:
+ posHpr = self.cr.playGame.dnaStore.getDoorPosHprFromBlockNumber(self.block)
+ otherNP = NodePath('doorOrigin')
+ otherNP.setPos(posHpr.getPos())
+ otherNP.setHpr(posHpr.getHpr())
+ self.tempDoorNodePath = otherNP
+ elif self.specialDoorTypes.has_key(self.doorType):
+ building = self.getBuilding()
+ otherNP = building.find('**/door_origin_' + str(self.doorIndex))
+ elif self.doorType == DoorTypes.INT_HQ:
+ otherNP = render.find('**/door_origin_' + str(self.doorIndex))
+ else:
+ self.notify.error('No such door type as ' + str(self.doorType))
+ return otherNP
+
+ def avatarExitTrack(self, avatar, duration):
+ if hasattr(avatar, 'stopSmooth'):
+ avatar.stopSmooth()
+ otherNP = self.getDoorNodePath()
+ trackName = 'avatarExitDoor-%d-%d' % (self.doId, avatar.doId)
+ track = Sequence(name=trackName)
+ track.append(self.getAnimStateInterval(avatar, 'walk'))
+ track.append(PosHprInterval(avatar, Point3(-self.doorX, 0, ToontownGlobals.FloorOffset), VBase3(179, 0, 0), other=otherNP))
+ track.append(Func(avatar.setParent, ToontownGlobals.SPRender))
+ if avatar.doId == base.localAvatar.doId:
+ track.append(PosHprInterval(camera, VBase3(-self.doorX, 5, avatar.getHeight()), VBase3(180, 0, 0), other=otherNP))
+ if avatar.doId == base.localAvatar.doId:
+ finalPos = render.getRelativePoint(otherNP, Point3(-self.doorX, -6, ToontownGlobals.FloorOffset))
+ else:
+ finalPos = render.getRelativePoint(otherNP, Point3(-self.doorX, -3, ToontownGlobals.FloorOffset))
+ track.append(LerpPosInterval(nodePath=avatar, duration=duration, pos=finalPos, blendType='easeInOut'))
+ if avatar.doId == base.localAvatar.doId:
+ track.append(Func(self.exitCompleted))
+ track.append(Func(base.transitions.irisIn))
+ if hasattr(avatar, 'startSmooth'):
+ track.append(Func(avatar.startSmooth))
+ track.delayDelete = DelayDelete.DelayDelete(avatar, 'DistributedDoor.avatarExitTrack')
+ return track
+
+ def exitCompleted(self):
+ base.localAvatar.setAnimState('neutral')
+ place = self.cr.playGame.getPlace()
+ if place:
+ place.setState('walk')
+ base.localAvatar.d_setParent(ToontownGlobals.SPRender)
+
+ def avatarExit(self, avatarID):
+ if avatarID in self.avatarIDList:
+ self.avatarIDList.remove(avatarID)
+ if avatarID == base.localAvatar.doId:
+ self.exitCompleted()
+ else:
+ self.avatarExitIDList.append(avatarID)
+
+ def finishDoorTrack(self):
+ if self.doorTrack:
+ self.doorTrack.finish()
+ self.doorTrack = None
+ return
+
+ def finishDoorExitTrack(self):
+ if self.doorExitTrack:
+ self.doorExitTrack.finish()
+ self.doorExitTrack = None
+ return
+
+ def finishAllTracks(self):
+ self.finishDoorTrack()
+ self.finishDoorExitTrack()
+ for t in self.avatarTracks:
+ t.finish()
+ DelayDelete.cleanupDelayDeletes(t)
+
+ self.avatarTracks = []
+ for t in self.avatarExitTracks:
+ t.finish()
+ DelayDelete.cleanupDelayDeletes(t)
+
+ self.avatarExitTracks = []
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def getRequestStatus(self):
+ zoneId = self.otherZoneId
+ request = {'loader': ZoneUtil.getBranchLoaderName(zoneId),
+ 'where': ZoneUtil.getToonWhereName(zoneId),
+ 'how': 'doorIn',
+ 'hoodId': ZoneUtil.getHoodId(zoneId),
+ 'zoneId': zoneId,
+ 'shardId': None,
+ 'avId': -1,
+ 'allowRedirect': 0,
+ 'doorDoId': self.otherDoId}
+ return request
+
+ def enterClosing(self, ts):
+ doorFrameHoleRight = self.findDoorNode('doorFrameHoleRight')
+ if doorFrameHoleRight.isEmpty():
+ self.notify.warning('enterClosing(): did not find doorFrameHoleRight')
+ return
+ rightDoor = self.findDoorNode('rightDoor')
+ if rightDoor.isEmpty():
+ self.notify.warning('enterClosing(): did not find rightDoor')
+ return
+ otherNP = self.getDoorNodePath()
+ trackName = 'doorClose-%d' % self.doId
+ if self.rightSwing:
+ h = 100
+ else:
+ h = -100
+ self.finishDoorTrack()
+ self.doorTrack = Sequence(LerpHprInterval(nodePath=rightDoor, duration=1.0, hpr=VBase3(0, 0, 0), startHpr=VBase3(h, 0, 0), other=otherNP, blendType='easeInOut'), Func(doorFrameHoleRight.hide), Func(self.hideIfHasFlat, rightDoor), SoundInterval(self.closeSfx, node=rightDoor), name=trackName)
+ self.doorTrack.start(ts)
+ if hasattr(self, 'done'):
+ request = self.getRequestStatus()
+ messenger.send('doorDoneEvent', [request])
+
+ def exitClosing(self):
+ pass
+
+ def enterClosed(self, ts):
+ pass
+
+ def exitClosed(self):
+ pass
+
+ def enterOpening(self, ts):
+ doorFrameHoleRight = self.findDoorNode('doorFrameHoleRight')
+ if doorFrameHoleRight.isEmpty():
+ self.notify.warning('enterOpening(): did not find doorFrameHoleRight')
+ return
+ rightDoor = self.findDoorNode('rightDoor')
+ if rightDoor.isEmpty():
+ self.notify.warning('enterOpening(): did not find rightDoor')
+ return
+ otherNP = self.getDoorNodePath()
+ trackName = 'doorOpen-%d' % self.doId
+ if self.rightSwing:
+ h = 100
+ else:
+ h = -100
+ self.finishDoorTrack()
+ self.doorTrack = Parallel(SoundInterval(self.openSfx, node=rightDoor), Sequence(HprInterval(rightDoor, VBase3(0, 0, 0), other=otherNP), Wait(0.4), Func(rightDoor.show), Func(doorFrameHoleRight.show), LerpHprInterval(nodePath=rightDoor, duration=0.6, hpr=VBase3(h, 0, 0), startHpr=VBase3(0, 0, 0), other=otherNP, blendType='easeInOut')), name=trackName)
+ self.doorTrack.start(ts)
+
+ def exitOpening(self):
+ pass
+
+ def enterOpen(self, ts):
+ for avatarID in self.avatarIDList:
+ avatar = self.cr.doId2do.get(avatarID)
+ if avatar:
+ track = self.avatarEnterDoorTrack(avatar, 1.0)
+ track.start(ts)
+ self.avatarTracks.append(track)
+ if avatarID == base.localAvatar.doId:
+ self.done = 1
+
+ self.avatarIDList = []
+
+ def exitOpen(self):
+ for track in self.avatarTracks:
+ track.finish()
+ DelayDelete.cleanupDelayDeletes(track)
+
+ self.avatarTracks = []
+
+ def exitDoorEnterOff(self):
+ pass
+
+ def exitDoorExitOff(self):
+ pass
+
+ def exitDoorEnterClosing(self, ts):
+ doorFrameHoleLeft = self.findDoorNode('doorFrameHoleLeft')
+ if doorFrameHoleLeft.isEmpty():
+ self.notify.warning('enterOpening(): did not find flatDoors')
+ return
+ if self.leftSwing:
+ h = -100
+ else:
+ h = 100
+ leftDoor = self.findDoorNode('leftDoor')
+ if not leftDoor.isEmpty():
+ otherNP = self.getDoorNodePath()
+ trackName = 'doorExitTrack-%d' % self.doId
+ self.finishDoorExitTrack()
+ self.doorExitTrack = Sequence(LerpHprInterval(nodePath=leftDoor, duration=1.0, hpr=VBase3(0, 0, 0), startHpr=VBase3(h, 0, 0), other=otherNP, blendType='easeInOut'), Func(doorFrameHoleLeft.hide), Func(self.hideIfHasFlat, leftDoor), SoundInterval(self.closeSfx, node=leftDoor), name=trackName)
+ self.doorExitTrack.start(ts)
+
+ def exitDoorExitClosing(self):
+ pass
+
+ def exitDoorEnterClosed(self, ts):
+ pass
+
+ def exitDoorExitClosed(self):
+ pass
+
+ def exitDoorEnterOpening(self, ts):
+ doorFrameHoleLeft = self.findDoorNode('doorFrameHoleLeft')
+ if doorFrameHoleLeft.isEmpty():
+ self.notify.warning('enterOpening(): did not find flatDoors')
+ return
+ leftDoor = self.findDoorNode('leftDoor')
+ if self.leftSwing:
+ h = -100
+ else:
+ h = 100
+ if not leftDoor.isEmpty():
+ otherNP = self.getDoorNodePath()
+ trackName = 'doorDoorExitTrack-%d' % self.doId
+ self.finishDoorExitTrack()
+ self.doorExitTrack = Parallel(SoundInterval(self.openSfx, node=leftDoor), Sequence(Func(leftDoor.show), Func(doorFrameHoleLeft.show), LerpHprInterval(nodePath=leftDoor, duration=0.6, hpr=VBase3(h, 0, 0), startHpr=VBase3(0, 0, 0), other=otherNP, blendType='easeInOut')), name=trackName)
+ self.doorExitTrack.start(ts)
+ else:
+ self.notify.warning('exitDoorEnterOpening(): did not find leftDoor')
+
+ def exitDoorExitOpening(self):
+ pass
+
+ def exitDoorEnterOpen(self, ts):
+ for avatarID in self.avatarExitIDList:
+ avatar = self.cr.doId2do.get(avatarID)
+ if avatar:
+ track = self.avatarExitTrack(avatar, 0.2)
+ track.start()
+ self.avatarExitTracks.append(track)
+
+ self.avatarExitIDList = []
+
+ def exitDoorExitOpen(self):
+ for track in self.avatarExitTracks:
+ track.finish()
+ DelayDelete.cleanupDelayDeletes(track)
+
+ self.avatarExitTracks = []
+
+ def findDoorNode(self, string, allowEmpty = False):
+ building = self.getBuilding()
+ if not building:
+ self.notify.warning('getBuilding() returned None, avoiding crash, remark 896029')
+ foundNode = None
+ else:
+ foundNode = building.find('**/door_' + str(self.doorIndex) + '/**/' + string + '*;+s+i')
+ if foundNode.isEmpty():
+ foundNode = building.find('**/' + string + '*;+s+i')
+ if allowEmpty:
+ return foundNode
+ return foundNode
+
+ def hideIfHasFlat(self, node):
+ if self.bHasFlat:
+ node.hide()
diff --git a/toontown/building/DistributedDoorAI.py b/toontown/building/DistributedDoorAI.py
new file mode 100644
index 0000000..b3ce1a2
--- /dev/null
+++ b/toontown/building/DistributedDoorAI.py
@@ -0,0 +1,276 @@
+from otp.ai.AIBaseGlobal import *
+from direct.task.Task import Task
+from direct.distributed.ClockDelta import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObjectAI
+from direct.fsm import State
+from toontown.toonbase import ToontownAccessAI
+
+class DistributedDoorAI(DistributedObjectAI.DistributedObjectAI):
+
+ def __init__(self, air, blockNumber, doorType, doorIndex=0, lockValue=0, swing=3):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ self.block = blockNumber
+ self.swing = swing
+ self.otherDoor = None
+ self.doorType = doorType
+ self.doorIndex = doorIndex
+ self.setDoorLock(lockValue)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedDoorAI_right', [
+ State.State('off', self.enterOff, self.exitOff, [
+ 'closing', 'closed', 'opening', 'open']),
+ State.State('closing', self.enterClosing, self.exitClosing, [
+ 'closed', 'opening']),
+ State.State('closed', self.enterClosed, self.exitClosed, [
+ 'opening']),
+ State.State('opening', self.enterOpening, self.exitOpening, [
+ 'open']),
+ State.State('open', self.enterOpen, self.exitOpen, [
+ 'closing', 'open'])], 'off', 'off')
+ self.fsm.enterInitialState()
+ self.exitDoorFSM = ClassicFSM.ClassicFSM('DistributedDoorAI_left', [
+ State.State('off', self.exitDoorEnterOff, self.exitDoorExitOff, [
+ 'closing', 'closed', 'opening', 'open']),
+ State.State('closing', self.exitDoorEnterClosing, self.exitDoorExitClosing, [
+ 'closed', 'opening']),
+ State.State('closed', self.exitDoorEnterClosed, self.exitDoorExitClosed, [
+ 'opening']),
+ State.State('opening', self.exitDoorEnterOpening, self.exitDoorExitOpening, [
+ 'open']),
+ State.State('open', self.exitDoorEnterOpen, self.exitDoorExitOpen, [
+ 'closing', 'open'])], 'off', 'off')
+ self.exitDoorFSM.enterInitialState()
+ self.doLaterTask = None
+ self.exitDoorDoLaterTask = None
+ self.avatarsWhoAreEntering = {}
+ self.avatarsWhoAreExiting = {}
+ return
+
+ def delete(self):
+ taskMgr.remove(self.uniqueName('door_opening-timer'))
+ taskMgr.remove(self.uniqueName('door_open-timer'))
+ taskMgr.remove(self.uniqueName('door_closing-timer'))
+ taskMgr.remove(self.uniqueName('exit_door_open-timer'))
+ taskMgr.remove(self.uniqueName('exit_door_closing-timer'))
+ taskMgr.remove(self.uniqueName('exit_door_opening-timer'))
+ self.ignoreAll()
+ del self.fsm
+ del self.exitDoorFSM
+ del self.otherDoor
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def getDoorIndex(self):
+ return self.doorIndex
+
+ def setSwing(self, swing):
+ self.swing = swing
+
+ def getSwing(self):
+ return self.swing
+
+ def getDoorType(self):
+ return self.doorType
+
+ def getZoneIdAndBlock(self):
+ r = [
+ self.zoneId, self.block]
+ return r
+
+ def setOtherDoor(self, door):
+ self.otherDoor = door
+
+ def getZoneId(self):
+ return self.zoneId
+
+ def getState(self):
+ return [
+ self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]
+
+ def getExitDoorState(self):
+ return [
+ self.exitDoorFSM.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]
+
+ def isOpen(self):
+ state = self.fsm.getCurrentState().getName()
+ return state == 'open' or state == 'opening'
+
+ def isClosed(self):
+ return not self.isOpen()
+
+ def setDoorLock(self, locked):
+ self.lockedDoor = locked
+
+ def isLockedDoor(self):
+ if simbase.config.GetBool('no-locked-doors', 0):
+ return 0
+ else:
+ return self.lockedDoor
+
+ def sendReject(self, avatarID, lockedVal):
+ self.sendUpdateToAvatarId(avatarID, 'rejectEnter', [lockedVal])
+
+ def requestEnter(self):
+ avatarID = self.air.getAvatarIdFromSender()
+ lockedVal = self.isLockedDoor()
+ if not ToontownAccessAI.canAccess(avatarID, self.zoneId, 'DistributedDoorAI.requestEnter'):
+ lockedVal = True
+ if lockedVal:
+ self.sendReject(avatarID, lockedVal)
+ else:
+ self.enqueueAvatarIdEnter(avatarID)
+ self.sendUpdateToAvatarId(avatarID, 'setOtherZoneIdAndDoId', [
+ self.otherDoor.getZoneId(), self.otherDoor.getDoId()])
+
+ def enqueueAvatarIdEnter(self, avatarID):
+ if not self.avatarsWhoAreEntering.has_key(avatarID):
+ self.avatarsWhoAreEntering[avatarID] = 1
+ self.sendUpdate('avatarEnter', [avatarID])
+ self.openDoor(self.fsm)
+
+ def openDoor(self, doorFsm):
+ stateName = doorFsm.getCurrentState().getName()
+ if stateName == 'open':
+ doorFsm.request('open')
+ else:
+ if stateName != 'opening':
+ doorFsm.request('opening')
+
+ def requestExit(self):
+ avatarID = self.air.getAvatarIdFromSender()
+ self.sendUpdate('avatarExit', [avatarID])
+ self.enqueueAvatarIdExit(avatarID)
+
+ def enqueueAvatarIdExit(self, avatarID):
+ if self.avatarsWhoAreEntering.has_key(avatarID):
+ del self.avatarsWhoAreEntering[avatarID]
+ else:
+ if not self.avatarsWhoAreExiting.has_key(avatarID):
+ self.avatarsWhoAreExiting[avatarID] = 1
+ self.openDoor(self.exitDoorFSM)
+
+ def requestSuitEnter(self, avatarID):
+ self.enqueueAvatarIdEnter(avatarID)
+
+ def requestSuitExit(self, avatarID):
+ self.sendUpdate('avatarExit', [avatarID])
+ self.enqueueAvatarIdExit(avatarID)
+
+ def d_setState(self, state):
+ self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
+
+ def d_setExitDoorState(self, state):
+ self.sendUpdate('setExitDoorState', [state, globalClockDelta.getRealNetworkTime()])
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def openTask(self, task):
+ self.fsm.request('closing')
+ return Task.done
+
+ def enterClosing(self):
+ self.d_setState('closing')
+ self.doLaterTask = taskMgr.doMethodLater(1, self.closingTask, self.uniqueName('door_closing-timer'))
+
+ def exitClosing(self):
+ if self.doLaterTask:
+ taskMgr.remove(self.doLaterTask)
+ self.doLaterTask = None
+ return
+
+ def closingTask(self, task):
+ self.fsm.request('closed')
+ return Task.done
+
+ def enterClosed(self):
+ self.d_setState('closed')
+
+ def exitClosed(self):
+ pass
+
+ def enterOpening(self):
+ self.d_setState('opening')
+ self.doLaterTask = taskMgr.doMethodLater(1, self.openingTask, self.uniqueName('door_opening-timer'))
+
+ def exitOpening(self):
+ if self.doLaterTask:
+ taskMgr.remove(self.doLaterTask)
+ self.doLaterTask = None
+ return
+
+ def openingTask(self, task):
+ self.fsm.request('open')
+ return Task.done
+
+ def enterOpen(self):
+ self.d_setState('open')
+ self.avatarsWhoAreEntering = {}
+ self.doLaterTask = taskMgr.doMethodLater(1, self.openTask, self.uniqueName('door_open-timer'))
+
+ def exitOpen(self):
+ if self.doLaterTask:
+ taskMgr.remove(self.doLaterTask)
+ self.doLaterTask = None
+ return
+
+ def exitDoorEnterOff(self):
+ pass
+
+ def exitDoorExitOff(self):
+ pass
+
+ def exitDoorOpenTask(self, task):
+ self.exitDoorFSM.request('closing')
+ return Task.done
+
+ def exitDoorEnterClosing(self):
+ self.d_setExitDoorState('closing')
+ self.exitDoorDoLaterTask = taskMgr.doMethodLater(1, self.exitDoorClosingTask, self.uniqueName('exit_door_closing-timer'))
+
+ def exitDoorExitClosing(self):
+ if self.exitDoorDoLaterTask:
+ taskMgr.remove(self.exitDoorDoLaterTask)
+ self.exitDoorDoLaterTask = None
+ return
+
+ def exitDoorClosingTask(self, task):
+ self.exitDoorFSM.request('closed')
+ return Task.done
+
+ def exitDoorEnterClosed(self):
+ self.d_setExitDoorState('closed')
+
+ def exitDoorExitClosed(self):
+ if self.exitDoorDoLaterTask:
+ taskMgr.remove(self.exitDoorDoLaterTask)
+ self.exitDoorDoLaterTask = None
+ return
+
+ def exitDoorEnterOpening(self):
+ self.d_setExitDoorState('opening')
+ self.exitDoorDoLaterTask = taskMgr.doMethodLater(1, self.exitDoorOpeningTask, self.uniqueName('exit_door_opening-timer'))
+
+ def exitDoorExitOpening(self):
+ if self.exitDoorDoLaterTask:
+ taskMgr.remove(self.exitDoorDoLaterTask)
+ self.exitDoorDoLaterTask = None
+ return
+
+ def exitDoorOpeningTask(self, task):
+ self.exitDoorFSM.request('open')
+ return Task.done
+
+ def exitDoorEnterOpen(self):
+ self.d_setExitDoorState('open')
+ self.avatarsWhoAreExiting = {}
+ self.exitDoorDoLaterTask = taskMgr.doMethodLater(1, self.exitDoorOpenTask, self.uniqueName('exit_door_open-timer'))
+
+ def exitDoorExitOpen(self):
+ if self.exitDoorDoLaterTask:
+ taskMgr.remove(self.exitDoorDoLaterTask)
+ self.exitDoorDoLaterTask = None
+ return
diff --git a/toontown/building/DistributedElevator.py b/toontown/building/DistributedElevator.py
new file mode 100644
index 0000000..2781964
--- /dev/null
+++ b/toontown/building/DistributedElevator.py
@@ -0,0 +1,572 @@
+from pandac.PandaModules import *
+from direct.distributed.ClockDelta import *
+from direct.interval.IntervalGlobal import *
+from ElevatorConstants import *
+from ElevatorUtils import *
+from direct.showbase import PythonUtil
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObject
+from direct.fsm import State
+from toontown.toonbase import TTLocalizer, ToontownGlobals
+from direct.task.Task import Task
+from toontown.distributed import DelayDelete
+from toontown.hood import ZoneUtil
+from toontown.toontowngui import TeaserPanel
+from toontown.building import BoardingGroupShow
+
+class DistributedElevator(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevator')
+ JumpOutOffsets = JumpOutOffsets
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.bldgRequest = None
+ self.toonRequests = {}
+ self.deferredSlots = []
+ self.localToonOnBoard = 0
+ self.boardedAvIds = {}
+ self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.mp3')
+ self.finalOpenSfx = None
+ self.closeSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_close.mp3')
+ self.elevatorFSM = None
+ self.finalCloseSfx = None
+ self.elevatorPoints = ElevatorPoints
+ self.fillSlotTrack = None
+ self.type = ELEVATOR_NORMAL
+ self.countdownTime = ElevatorData[self.type]['countdown']
+ self.__toonTracks = {}
+ self.fsm = ClassicFSM.ClassicFSM('DistributedElevator', [State.State('off', self.enterOff, self.exitOff, ['opening',
+ 'waitEmpty',
+ 'waitCountdown',
+ 'closing',
+ 'closed']),
+ State.State('opening', self.enterOpening, self.exitOpening, ['waitEmpty', 'waitCountdown']),
+ State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']),
+ State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty', 'closing']),
+ State.State('closing', self.enterClosing, self.exitClosing, ['closed', 'waitEmpty']),
+ State.State('closed', self.enterClosed, self.exitClosed, ['opening'])], 'off', 'off')
+ self.fsm.enterInitialState()
+ self.isSetup = 0
+ self.__preSetupState = None
+ self.bigElevator = 0
+ return
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+
+ def setupElevator(self):
+ collisionRadius = ElevatorData[self.type]['collRadius']
+ self.elevatorSphere = CollisionSphere(0, 5, 0, collisionRadius)
+ self.elevatorSphere.setTangible(0)
+ self.elevatorSphereNode = CollisionNode(self.uniqueName('elevatorSphere'))
+ self.elevatorSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
+ self.elevatorSphereNode.addSolid(self.elevatorSphere)
+ self.elevatorSphereNodePath = self.getElevatorModel().attachNewNode(self.elevatorSphereNode)
+ self.elevatorSphereNodePath.hide()
+ self.elevatorSphereNodePath.reparentTo(self.getElevatorModel())
+ self.elevatorSphereNodePath.stash()
+ self.boardedAvIds = {}
+ self.openDoors = getOpenInterval(self, self.leftDoor, self.rightDoor, self.openSfx, self.finalOpenSfx, self.type)
+ self.closeDoors = getCloseInterval(self, self.leftDoor, self.rightDoor, self.closeSfx, self.finalCloseSfx, self.type)
+ self.closeDoors = Sequence(self.closeDoors, Func(self.onDoorCloseFinish))
+ self.finishSetup()
+
+ def finishSetup(self):
+ self.isSetup = 1
+ self.offsetNP = self.getElevatorModel().attachNewNode('dummyNP')
+ if self.__preSetupState:
+ self.fsm.request(self.__preSetupState, [0])
+ self.__preSetupState = None
+ for slot in self.deferredSlots:
+ self.fillSlot(*slot)
+
+ self.deferredSlots = []
+ return
+
+ def disable(self):
+ if self.bldgRequest:
+ self.cr.relatedObjectMgr.abortRequest(self.bldgRequest)
+ self.bldgRequest = None
+ for request in self.toonRequests.values():
+ self.cr.relatedObjectMgr.abortRequest(request)
+
+ self.toonRequests = {}
+ if hasattr(self, 'openDoors'):
+ self.openDoors.pause()
+ if hasattr(self, 'closeDoors'):
+ self.closeDoors.pause()
+ self.clearToonTracks()
+ self.fsm.request('off')
+ DistributedObject.DistributedObject.disable(self)
+ return
+
+ def delete(self):
+ if self.isSetup:
+ self.elevatorSphereNodePath.removeNode()
+ del self.elevatorSphereNodePath
+ del self.elevatorSphereNode
+ del self.elevatorSphere
+ del self.bldg
+ if self.leftDoor:
+ del self.leftDoor
+ if self.rightDoor:
+ del self.rightDoor
+ if hasattr(self, 'openDoors'):
+ del self.openDoors
+ if hasattr(self, 'closeDoors'):
+ del self.closeDoors
+ del self.fsm
+ del self.openSfx
+ del self.closeSfx
+ self.isSetup = 0
+ self.fillSlotTrack = None
+ self.offsetNP.removeNode()
+ if hasattr(base.localAvatar, 'elevatorNotifier'):
+ base.localAvatar.elevatorNotifier.cleanup()
+ DistributedObject.DistributedObject.delete(self)
+ return
+
+ def setBldgDoId(self, bldgDoId):
+ self.bldgDoId = bldgDoId
+ self.bldgRequest = self.cr.relatedObjectMgr.requestObjects([bldgDoId], allCallback=self.gotBldg, timeout=2)
+
+ def gotBldg(self, buildingList):
+ self.bldgRequest = None
+ self.bldg = buildingList[0]
+ if not self.bldg:
+ self.notify.error('setBldgDoId: elevator %d cannot find bldg %d!' % (self.doId, self.bldgDoId))
+ return
+ self.setupElevator()
+ return
+
+ def gotToon(self, index, avId, toonList):
+ request = self.toonRequests.get(index)
+ if request:
+ del self.toonRequests[index]
+ self.fillSlot(index, avId)
+ else:
+ self.notify.error('gotToon: already had got toon in slot %s.' % index)
+
+ def setState(self, state, timestamp):
+ if self.isSetup:
+ self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])
+ else:
+ self.__preSetupState = state
+
+ def fillSlot0(self, avId, wantBoardingShow):
+ self.fillSlot(0, avId, wantBoardingShow)
+
+ def fillSlot1(self, avId, wantBoardingShow):
+ self.fillSlot(1, avId, wantBoardingShow)
+
+ def fillSlot2(self, avId, wantBoardingShow):
+ self.fillSlot(2, avId, wantBoardingShow)
+
+ def fillSlot3(self, avId, wantBoardingShow):
+ self.fillSlot(3, avId, wantBoardingShow)
+
+ def fillSlot4(self, avId, wantBoardingShow):
+ self.fillSlot(4, avId, wantBoardingShow)
+
+ def fillSlot5(self, avId, wantBoardingShow):
+ self.fillSlot(5, avId, wantBoardingShow)
+
+ def fillSlot6(self, avId, wantBoardingShow):
+ self.fillSlot(6, avId, wantBoardingShow)
+
+ def fillSlot7(self, avId, wantBoardingShow):
+ self.fillSlot(7, avId, wantBoardingShow)
+
+ def fillSlot(self, index, avId, wantBoardingShow = 0):
+ self.notify.debug('%s.fillSlot(%s, %s, ...)' % (self.doId, index, avId))
+ request = self.toonRequests.get(index)
+ if request:
+ self.cr.relatedObjectMgr.abortRequest(request)
+ del self.toonRequests[index]
+ if avId == 0:
+ pass
+ elif not self.cr.doId2do.has_key(avId):
+ func = PythonUtil.Functor(self.gotToon, index, avId)
+ self.toonRequests[index] = self.cr.relatedObjectMgr.requestObjects([avId], allCallback=func)
+ elif not self.isSetup:
+ self.deferredSlots.append((index, avId, wantBoardingShow))
+ else:
+ if avId == base.localAvatar.getDoId():
+ place = base.cr.playGame.getPlace()
+ if not place:
+ return
+ place.detectedElevatorCollision(self)
+ elevator = self.getPlaceElevator()
+ if elevator == None:
+ if place.fsm.hasStateNamed('elevator'):
+ place.fsm.request('elevator')
+ elif place.fsm.hasStateNamed('Elevator'):
+ place.fsm.request('Elevator')
+ elevator = self.getPlaceElevator()
+ if not elevator:
+ return
+ self.localToonOnBoard = 1
+ if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty:
+ localAvatar.boardingParty.forceCleanupInviteePanel()
+ localAvatar.boardingParty.forceCleanupInviterPanels()
+ if hasattr(base.localAvatar, 'elevatorNotifier'):
+ base.localAvatar.elevatorNotifier.cleanup()
+ cameraTrack = Sequence()
+ cameraTrack.append(Func(elevator.fsm.request, 'boarding', [self.getElevatorModel()]))
+ cameraTrack.append(Func(elevator.fsm.request, 'boarded'))
+ toon = self.cr.doId2do[avId]
+ toon.stopSmooth()
+ if not wantBoardingShow:
+ toon.setZ(self.getElevatorModel(), self.elevatorPoints[index][2])
+ toon.setShadowHeight(0)
+ if toon.isDisguised:
+ animInFunc = Sequence(Func(toon.suit.loop, 'walk'))
+ animFunc = Sequence(Func(toon.setAnimState, 'neutral', 1.0), Func(toon.suit.loop, 'neutral'))
+ else:
+ animInFunc = Sequence(Func(toon.setAnimState, 'run', 1.0))
+ animFunc = Func(toon.setAnimState, 'neutral', 1.0)
+ toon.headsUp(self.getElevatorModel(), apply(Point3, self.elevatorPoints[index]))
+ track = Sequence(animInFunc, LerpPosInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.75, apply(Point3, self.elevatorPoints[index]), other=self.getElevatorModel()), LerpHprInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.25, Point3(180, 0, 0), other=self.getElevatorModel()), Func(self.clearToonTrack, avId), animFunc, name=toon.uniqueName('fillElevator'), autoPause=1)
+ if wantBoardingShow:
+ boardingTrack, boardingTrackType = self.getBoardingTrack(toon, index, False)
+ track = Sequence(boardingTrack, track)
+ if avId == base.localAvatar.getDoId():
+ cameraWaitTime = 2.5
+ if boardingTrackType == BoardingGroupShow.TRACK_TYPE_RUN:
+ cameraWaitTime = 0.5
+ elif boardingTrackType == BoardingGroupShow.TRACK_TYPE_POOF:
+ cameraWaitTime = 1
+ cameraTrack = Sequence(Wait(cameraWaitTime), cameraTrack)
+ if self.canHideBoardingQuitBtn(avId):
+ track = Sequence(Func(localAvatar.boardingParty.groupPanel.disableQuitButton), track)
+ if avId == base.localAvatar.getDoId():
+ track = Parallel(cameraTrack, track)
+ track.delayDelete = DelayDelete.DelayDelete(toon, 'Elevator.fillSlot')
+ self.storeToonTrack(avId, track)
+ track.start()
+ self.fillSlotTrack = track
+ self.boardedAvIds[avId] = None
+ return
+
+ def emptySlot0(self, avId, bailFlag, timestamp, time):
+ self.emptySlot(0, avId, bailFlag, timestamp, time)
+
+ def emptySlot1(self, avId, bailFlag, timestamp, time):
+ self.emptySlot(1, avId, bailFlag, timestamp, time)
+
+ def emptySlot2(self, avId, bailFlag, timestamp, time):
+ self.emptySlot(2, avId, bailFlag, timestamp, time)
+
+ def emptySlot3(self, avId, bailFlag, timestamp, time):
+ self.emptySlot(3, avId, bailFlag, timestamp, time)
+
+ def emptySlot4(self, avId, bailFlag, timestamp, time):
+ self.emptySlot(4, avId, bailFlag, timestamp, time)
+
+ def emptySlot5(self, avId, bailFlag, timestamp, time):
+ self.emptySlot(5, avId, bailFlag, timestamp)
+
+ def emptySlot6(self, avId, bailFlag, timestamp, time):
+ self.emptySlot(6, avId, bailFlag, timestamp, time)
+
+ def emptySlot7(self, avId, bailFlag, timestamp, time):
+ self.emptySlot(7, avId, bailFlag, timestamp, time)
+
+ def notifyToonOffElevator(self, toon):
+ toon.setAnimState('neutral', 1.0)
+ if toon == base.localAvatar:
+ doneStatus = {'where': 'exit'}
+ elevator = self.getPlaceElevator()
+ if elevator:
+ elevator.signalDone(doneStatus)
+ self.localToonOnBoard = 0
+ else:
+ toon.startSmooth()
+
+ def emptySlot(self, index, avId, bailFlag, timestamp, timeSent = 0):
+ if self.fillSlotTrack:
+ self.fillSlotTrack.finish()
+ self.fillSlotTrack = None
+ if avId == 0:
+ pass
+ elif not self.isSetup:
+ newSlots = []
+ for slot in self.deferredSlots:
+ if slot[0] != index:
+ newSlots.append(slot)
+
+ self.deferredSlots = newSlots
+ else:
+ timeToSet = self.countdownTime
+ if timeSent > 0:
+ timeToSet = timeSent
+ if self.cr.doId2do.has_key(avId):
+ if bailFlag == 1 and hasattr(self, 'clockNode'):
+ if timestamp < timeToSet and timestamp >= 0:
+ self.countdown(timeToSet - timestamp)
+ else:
+ self.countdown(timeToSet)
+ toon = self.cr.doId2do[avId]
+ toon.stopSmooth()
+ if toon.isDisguised:
+ toon.suit.loop('walk')
+ animFunc = Func(toon.suit.loop, 'neutral')
+ else:
+ toon.setAnimState('run', 1.0)
+ animFunc = Func(toon.setAnimState, 'neutral', 1.0)
+ track = Sequence(LerpPosInterval(toon, TOON_EXIT_ELEVATOR_TIME, Point3(*self.JumpOutOffsets[index]), other=self.getElevatorModel()), animFunc, Func(self.notifyToonOffElevator, toon), Func(self.clearToonTrack, avId), name=toon.uniqueName('emptyElevator'), autoPause=1)
+ if self.canHideBoardingQuitBtn(avId):
+ track.append(Func(localAvatar.boardingParty.groupPanel.enableQuitButton))
+ track.append(Func(localAvatar.boardingParty.enableGoButton))
+ track.delayDelete = DelayDelete.DelayDelete(toon, 'Elevator.emptySlot')
+ self.storeToonTrack(avId, track)
+ track.start()
+ if avId == base.localAvatar.getDoId():
+ messenger.send('exitElevator')
+ if avId in self.boardedAvIds:
+ del self.boardedAvIds[avId]
+ else:
+ self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the elevator!')
+ return
+
+ def allowedToEnter(self, zoneId = None):
+ allowed = False
+ if hasattr(base, 'ttAccess') and base.ttAccess:
+ if zoneId:
+ allowed = base.ttAccess.canAccess(zoneId)
+ else:
+ allowed = base.ttAccess.canAccess()
+ return allowed
+
+ def handleEnterSphere(self, collEntry):
+ self.notify.debug('Entering Elevator Sphere....')
+ if self.allowedToEnter(self.zoneId):
+ if self.elevatorTripId and localAvatar.lastElevatorLeft == self.elevatorTripId:
+ self.rejectBoard(base.localAvatar.doId, REJECT_SHUFFLE)
+ elif base.localAvatar.hp > 0:
+ self.cr.playGame.getPlace().detectedElevatorCollision(self)
+ toon = base.localAvatar
+ self.sendUpdate('requestBoard', [])
+ else:
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.fsm.request('stopped')
+ self.dialog = TeaserPanel.TeaserPanel(pageName='cogHQ', doneFunc=self.handleOkTeaser)
+
+ def handleOkTeaser(self):
+ self.dialog.destroy()
+ del self.dialog
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.fsm.request('walk')
+
+ def rejectBoard(self, avId, reason = 0):
+ print 'rejectBoard %s' % reason
+ if hasattr(base.localAvatar, 'elevatorNotifier'):
+ if reason == REJECT_SHUFFLE:
+ base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorHoppedOff)
+ elif reason == REJECT_MINLAFF:
+ base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorMinLaff % self.minLaff)
+ elif reason == REJECT_PROMOTION:
+ base.localAvatar.elevatorNotifier.showMe(TTLocalizer.BossElevatorRejectMessage)
+ elif reason == REJECT_NOT_YET_AVAILABLE:
+ base.localAvatar.elevatorNotifier.showMe(TTLocalizer.NotYetAvailable)
+ doneStatus = {'where': 'reject'}
+ elevator = self.getPlaceElevator()
+ if elevator:
+ elevator.signalDone(doneStatus)
+
+ def timerTask(self, task):
+ countdownTime = int(task.duration - task.time)
+ timeStr = str(countdownTime)
+ if self.clockNode.getText() != timeStr:
+ self.clockNode.setText(timeStr)
+ if task.time >= task.duration:
+ return Task.done
+ else:
+ return Task.cont
+
+ def countdown(self, duration):
+ countdownTask = Task(self.timerTask)
+ countdownTask.duration = duration
+ taskMgr.remove(self.uniqueName('elevatorTimerTask'))
+ return taskMgr.add(countdownTask, self.uniqueName('elevatorTimerTask'))
+
+ def handleExitButton(self):
+ localAvatar.lastElevatorLeft = self.elevatorTripId
+ self.sendUpdate('requestExit')
+
+ def enterWaitCountdown(self, ts):
+ self.elevatorSphereNodePath.unstash()
+ self.accept(self.uniqueName('enterelevatorSphere'), self.handleEnterSphere)
+ self.accept('elevatorExitButton', self.handleExitButton)
+
+ def exitWaitCountdown(self):
+ self.elevatorSphereNodePath.stash()
+ self.ignore(self.uniqueName('enterelevatorSphere'))
+ self.ignore('elevatorExitButton')
+ self.ignore('localToonLeft')
+ taskMgr.remove(self.uniqueName('elevatorTimerTask'))
+ self.clock.removeNode()
+ del self.clock
+ del self.clockNode
+
+ def enterClosing(self, ts):
+ if self.localToonOnBoard:
+ elevator = self.getPlaceElevator()
+ if elevator:
+ elevator.fsm.request('elevatorClosing')
+ self.closeDoors.start(ts)
+
+ def exitClosing(self):
+ pass
+
+ def onDoorCloseFinish(self):
+ for avId in self.boardedAvIds.keys():
+ av = self.cr.doId2do.get(avId)
+ if av is not None:
+ if av.getParent().compareTo(self.getElevatorModel()) == 0:
+ av.detachNode()
+
+ self.boardedAvIds = {}
+ return
+
+ def enterClosed(self, ts):
+ self.forceDoorsClosed()
+ self.__doorsClosed(self.getZoneId())
+
+ def exitClosed(self):
+ pass
+
+ def forceDoorsOpen(self):
+ openDoors(self.leftDoor, self.rightDoor)
+
+ def forceDoorsClosed(self):
+ self.closeDoors.finish()
+ closeDoors(self.leftDoor, self.rightDoor)
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterWaitEmpty(self, ts):
+ pass
+
+ def exitWaitEmpty(self):
+ pass
+
+ def enterOpening(self, ts):
+ self.openDoors.start(ts)
+
+ def exitOpening(self):
+ pass
+
+ def startCountdownClock(self, countdownTime, ts):
+ self.clockNode = TextNode('elevatorClock')
+ self.clockNode.setFont(ToontownGlobals.getSignFont())
+ self.clockNode.setAlign(TextNode.ACenter)
+ self.clockNode.setTextColor(0.5, 0.5, 0.5, 1)
+ self.clockNode.setText(str(int(countdownTime)))
+ self.clock = self.getElevatorModel().attachNewNode(self.clockNode)
+ self.clock.setPosHprScale(0, 2.0, 7.5, 0, 0, 0, 2.0, 2.0, 2.0)
+ if ts < countdownTime:
+ self.countdown(countdownTime - ts)
+
+ def _getDoorsClosedInfo(self):
+ return ('suitInterior', 'suitInterior')
+
+ def __doorsClosed(self, zoneId):
+ if self.localToonOnBoard:
+ hoodId = ZoneUtil.getHoodId(zoneId)
+ loader, where = self._getDoorsClosedInfo()
+ doneStatus = {'loader': loader,
+ 'where': where,
+ 'hoodId': hoodId,
+ 'zoneId': zoneId,
+ 'shardId': None}
+ elevator = self.elevatorFSM
+ del self.elevatorFSM
+ elevator.signalDone(doneStatus)
+ return
+
+ def getElevatorModel(self):
+ self.notify.error('getElevatorModel: pure virtual -- inheritors must override')
+
+ def getPlaceElevator(self):
+ place = self.cr.playGame.getPlace()
+ if place:
+ if hasattr(place, 'elevator'):
+ return place.elevator
+ else:
+ self.notify.warning("Place was in state '%s' instead of Elevator." % place.fsm.getCurrentState().getName())
+ place.detectedElevatorCollision(self)
+ else:
+ self.notify.warning("Place didn't exist")
+ return None
+
+ def setElevatorTripId(self, id):
+ self.elevatorTripId = id
+
+ def getElevatorTripId(self):
+ return self.elevatorTripId
+
+ def setAntiShuffle(self, antiShuffle):
+ self.antiShuffle = antiShuffle
+
+ def getAntiShuffle(self):
+ return self.antiShuffle
+
+ def setMinLaff(self, minLaff):
+ self.minLaff = minLaff
+
+ def getMinLaff(self):
+ return self.minLaff
+
+ def storeToonTrack(self, avId, track):
+ self.clearToonTrack(avId)
+ self.__toonTracks[avId] = track
+
+ def clearToonTrack(self, avId):
+ oldTrack = self.__toonTracks.get(avId)
+ if oldTrack:
+ oldTrack.pause()
+ if self.__toonTracks.get(avId):
+ DelayDelete.cleanupDelayDeletes(self.__toonTracks[avId])
+ del self.__toonTracks[avId]
+
+ def clearToonTracks(self):
+ keyList = []
+ for key in self.__toonTracks:
+ keyList.append(key)
+
+ for key in keyList:
+ if self.__toonTracks.has_key(key):
+ self.clearToonTrack(key)
+
+ def getDestName(self):
+ return None
+
+ def getOffsetPos(self, seatIndex = 0):
+ return self.JumpOutOffsets[seatIndex]
+
+ def getOffsetPosWrtToonParent(self, toon, seatIndex = 0):
+ self.offsetNP.setPos(apply(Point3, self.getOffsetPos(seatIndex)))
+ return self.offsetNP.getPos(toon.getParent())
+
+ def getOffsetPosWrtRender(self, seatIndex = 0):
+ self.offsetNP.setPos(apply(Point3, self.getOffsetPos(seatIndex)))
+ return self.offsetNP.getPos(render)
+
+ def canHideBoardingQuitBtn(self, avId):
+ if avId == localAvatar.doId and hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty and localAvatar.boardingParty.groupPanel:
+ return True
+ else:
+ return False
+
+ def getBoardingTrack(self, toon, seatIndex, wantToonRotation):
+ self.boardingGroupShow = BoardingGroupShow.BoardingGroupShow(toon)
+ track, trackType = self.boardingGroupShow.getBoardingTrack(self.getElevatorModel(), self.getOffsetPosWrtToonParent(toon, seatIndex), self.getOffsetPosWrtRender(seatIndex), wantToonRotation)
+ return (track, trackType)
diff --git a/toontown/building/DistributedElevatorAI.py b/toontown/building/DistributedElevatorAI.py
new file mode 100644
index 0000000..19b46dd
--- /dev/null
+++ b/toontown/building/DistributedElevatorAI.py
@@ -0,0 +1,318 @@
+from otp.ai.AIBase import *
+from toontown.toonbase import ToontownGlobals
+from direct.distributed.ClockDelta import *
+from ElevatorConstants import *
+from direct.distributed import DistributedObjectAI
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownAccessAI
+
+class DistributedElevatorAI(DistributedObjectAI.DistributedObjectAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorAI')
+
+ def __init__(self, air, bldg, numSeats=4, antiShuffle=0, minLaff=0, fSkipOpening=False):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ self.fSkipOpening = fSkipOpening
+ self.type = ELEVATOR_NORMAL
+ self.countdownTime = ElevatorData[self.type]['countdown']
+ self.bldg = bldg
+ self.bldgDoId = bldg.getDoId()
+ self.seats = []
+ self.setAntiShuffle(antiShuffle)
+ self.setMinLaff(minLaff)
+ if self.antiShuffle:
+ if not hasattr(simbase.air, 'elevatorTripId'):
+ simbase.air.elevatorTripId = 1
+ self.elevatorTripId = simbase.air.elevatorTripId
+ simbase.air.elevatorTripId += 1
+ else:
+ self.elevatorTripId = 0
+ for seat in range(numSeats):
+ self.seats.append(None)
+
+ self.accepting = 0
+ self.fsm = ClassicFSM.ClassicFSM('DistributedElevatorAI', [
+ State.State('off', self.enterOff, self.exitOff, [
+ 'opening', 'closed', 'waitEmpty']),
+ State.State('opening', self.enterOpening, self.exitOpening, [
+ 'waitEmpty', 'waitCountdown']),
+ State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, [
+ 'waitCountdown']),
+ State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, [
+ 'waitEmpty', 'allAboard']),
+ State.State('allAboard', self.enterAllAboard, self.exitAllAboard, [
+ 'closing', 'waitEmpty']),
+ State.State('closing', self.enterClosing, self.exitClosing, [
+ 'closed', 'waitEmpty']),
+ State.State('closed', self.enterClosed, self.exitClosed, [
+ 'opening'])], 'off', 'off')
+ self.fsm.enterInitialState()
+ self.boardingParty = None
+ return
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ del self.fsm
+ del self.bldg
+ self.ignoreAll()
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def setBoardingParty(self, party):
+ self.boardingParty = party
+
+ def generate(self):
+ if not self.fSkipOpening:
+ self.start()
+ else:
+ self.fsm.request('waitEmpty')
+ DistributedObjectAI.DistributedObjectAI.generate(self)
+
+ def getBldgDoId(self):
+ return self.bldgDoId
+
+ def findAvailableSeat(self):
+ for i in range(len(self.seats)):
+ if self.seats[i] == None:
+ return i
+
+ return
+
+ def findAvatar(self, avId):
+ for i in range(len(self.seats)):
+ if self.seats[i] == avId:
+ return i
+
+ return None
+
+ def countFullSeats(self):
+ avCounter = 0
+ for i in self.seats:
+ if i:
+ avCounter += 1
+
+ return avCounter
+
+ def countOpenSeats(self):
+ openSeats = 0
+ for i in range(len(self.seats)):
+ if self.seats[i] == None:
+ openSeats += 1
+
+ return openSeats
+
+ def rejectingBoardersHandler(self, avId, reason=0, wantBoardingShow=0):
+ self.rejectBoarder(avId, reason)
+
+ def rejectBoarder(self, avId, reason=0):
+ self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId, reason])
+
+ def acceptingBoardersHandler(self, avId, reason=0, wantBoardingShow=0):
+ self.notify.debug('acceptingBoardersHandler')
+ seatIndex = self.findAvailableSeat()
+ if seatIndex == None:
+ self.rejectBoarder(avId, REJECT_NOSEAT)
+ else:
+ self.acceptBoarder(avId, seatIndex, wantBoardingShow)
+ return
+
+ def acceptBoarder(self, avId, seatIndex, wantBoardingShow=0):
+ self.notify.debug('acceptBoarder')
+ if self.findAvatar(avId) != None:
+ return
+ self.seats[seatIndex] = avId
+ self.timeOfBoarding = globalClock.getRealTime()
+ if wantBoardingShow:
+ self.timeOfGroupBoarding = globalClock.getRealTime()
+ self.sendUpdate('fillSlot' + str(seatIndex), [avId, wantBoardingShow])
+ return
+
+ def rejectingExitersHandler(self, avId):
+ self.rejectExiter(avId)
+
+ def rejectExiter(self, avId):
+ pass
+
+ def acceptingExitersHandler(self, avId):
+ self.acceptExiter(avId)
+
+ def clearEmptyNow(self, seatIndex):
+ self.sendUpdate('emptySlot' + str(seatIndex), [
+ 0, 0, globalClockDelta.getRealNetworkTime(), 0])
+
+ def clearFullNow(self, seatIndex):
+ avId = self.seats[seatIndex]
+ if avId == None:
+ self.notify.warning('Clearing an empty seat index: ' + str(seatIndex) + ' ... Strange...')
+ else:
+ self.seats[seatIndex] = None
+ self.sendUpdate('fillSlot' + str(seatIndex), [0, 0])
+ self.ignore(self.air.getAvatarExitEvent(avId))
+ return
+
+ def d_setState(self, state):
+ self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
+
+ def getState(self):
+ return self.fsm.getCurrentState().getName()
+
+ def avIsOKToBoard(self, av):
+ return av.hp > self.minLaff and self.accepting
+
+ def checkBoard(self, av):
+ if av.hp < self.minLaff:
+ return REJECT_MINLAFF
+ return 0
+
+ def requestBoard(self, *args):
+ self.notify.debug('requestBoard')
+ avId = self.air.getAvatarIdFromSender()
+ if self.findAvatar(avId) != None:
+ self.notify.warning('Ignoring multiple requests from %s to board.' % avId)
+ return
+ av = self.air.doId2do.get(avId)
+ if av:
+ boardResponse = self.checkBoard(av)
+ newArgs = (avId,) + args + (boardResponse,)
+ if not ToontownAccessAI.canAccess(avId, self.zoneId, 'DistributedElevatorAI.requestBoard'):
+ self.notify.warning('Toon %s does not have access to the eleavtor.' % avId)
+ self.rejectingBoardersHandler(*newArgs)
+ return
+ if self.boardingParty and self.boardingParty.hasActiveGroup(avId) and self.boardingParty.getGroupLeader(avId) != avId:
+ self.notify.warning('Rejecting %s from boarding the elevator because he is already part of a Boarding Group.' % avId)
+ self.rejectingBoardersHandler(*newArgs)
+ return
+ if boardResponse == 0:
+ self.acceptingBoardersHandler(*newArgs)
+ else:
+ self.rejectingBoardersHandler(*newArgs)
+ else:
+ self.notify.warning('avid: %s does not exist, but tried to board an elevator' % avId)
+ return
+
+ def partyAvatarBoard(self, avatar, wantBoardingShow=0):
+ av = avatar
+ avId = avatar.doId
+ if self.findAvatar(avId) != None:
+ self.notify.warning('Ignoring multiple requests from %s to board.' % avId)
+ return
+ if av:
+ boardResponse = self.checkBoard(av)
+ newArgs = (avId,) + (boardResponse,) + (wantBoardingShow,)
+ if boardResponse == 0:
+ self.acceptingBoardersHandler(*newArgs)
+ else:
+ self.rejectingBoardersHandler(*newArgs)
+ else:
+ self.notify.warning('avid: %s does not exist, but tried to board an elevator' % avId)
+ return
+
+ def requestExit(self, *args):
+ self.notify.debug('requestExit')
+ avId = self.air.getAvatarIdFromSender()
+ av = self.air.doId2do.get(avId)
+ if av:
+ newArgs = (
+ avId,) + args
+ if self.accepting:
+ self.acceptingExitersHandler(*newArgs)
+ else:
+ self.rejectingExitersHandler(*newArgs)
+ else:
+ self.notify.warning('avId: %s does not exist, but tried to exit an elevator' % avId)
+
+ def start(self):
+ self.open()
+
+ def enterOff(self):
+ self.accepting = 0
+ self.timeOfBoarding = None
+ self.timeOfGroupBoarding = None
+ if hasattr(self, 'doId'):
+ for seatIndex in range(len(self.seats)):
+ taskMgr.remove(self.uniqueName('clearEmpty-' + str(seatIndex)))
+
+ return
+
+ def exitOff(self):
+ self.accepting = 0
+
+ def open(self):
+ self.fsm.request('opening')
+
+ def enterOpening(self):
+ self.d_setState('opening')
+ self.accepting = 0
+ for seat in self.seats:
+ seat = None
+
+ return
+
+ def exitOpening(self):
+ self.accepting = 0
+ taskMgr.remove(self.uniqueName('opening-timer'))
+
+ def enterWaitCountdown(self):
+ self.d_setState('waitCountdown')
+ self.accepting = 1
+
+ def exitWaitCountdown(self):
+ print 'exit wait countdown'
+ self.accepting = 0
+ taskMgr.remove(self.uniqueName('countdown-timer'))
+ self.newTrip()
+
+ def enterAllAboard(self):
+ self.accepting = 0
+
+ def exitAllAboard(self):
+ self.accepting = 0
+ taskMgr.remove(self.uniqueName('waitForAllAboard'))
+
+ def enterClosing(self):
+ self.d_setState('closing')
+ self.accepting = 0
+
+ def exitClosing(self):
+ self.accepting = 0
+ taskMgr.remove(self.uniqueName('closing-timer'))
+
+ def enterClosed(self):
+ self.d_setState('closed')
+
+ def exitClosed(self):
+ pass
+
+ def enterWaitEmpty(self):
+ self.d_setState('waitEmpty')
+ self.accepting = 1
+
+ def exitWaitEmpty(self):
+ self.accepting = 0
+
+ def setElevatorTripId(self, id):
+ self.elevatorTripId = id
+
+ def getElevatorTripId(self):
+ return self.elevatorTripId
+
+ def newTrip(self):
+ if self.antiShuffle:
+ self.elevatorTripId = simbase.air.elevatorTripId
+ if simbase.air.elevatorTripId > 2100000000:
+ simbase.air.elevatorTripId = 1
+ simbase.air.elevatorTripId += 1
+ self.sendUpdate('setElevatorTripId', [self.elevatorTripId])
+
+ def setAntiShuffle(self, antiShuffle):
+ self.antiShuffle = antiShuffle
+
+ def getAntiShuffle(self):
+ return self.antiShuffle
+
+ def setMinLaff(self, minLaff):
+ self.minLaff = minLaff
+
+ def getMinLaff(self):
+ return self.minLaff
diff --git a/toontown/building/DistributedElevatorExt.py b/toontown/building/DistributedElevatorExt.py
new file mode 100644
index 0000000..9638f7a
--- /dev/null
+++ b/toontown/building/DistributedElevatorExt.py
@@ -0,0 +1,137 @@
+from pandac.PandaModules import *
+from direct.distributed.ClockDelta import *
+from direct.interval.IntervalGlobal import *
+from ElevatorConstants import *
+from ElevatorUtils import *
+import DistributedElevator
+from toontown.toonbase import ToontownGlobals
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from toontown.hood import ZoneUtil
+from toontown.toonbase import TTLocalizer
+from toontown.toontowngui import TeaserPanel
+
+class DistributedElevatorExt(DistributedElevator.DistributedElevator):
+
+ def __init__(self, cr):
+ DistributedElevator.DistributedElevator.__init__(self, cr)
+ self.nametag = None
+ self.currentFloor = -1
+ return
+
+ def setupElevator(self):
+ if self.isSetup:
+ self.elevatorSphereNodePath.removeNode()
+ self.leftDoor = self.bldg.leftDoor
+ self.rightDoor = self.bldg.rightDoor
+ DistributedElevator.DistributedElevator.setupElevator(self)
+ self.setupNametag()
+
+ def disable(self):
+ self.clearNametag()
+ DistributedElevator.DistributedElevator.disable(self)
+
+ def setupNametag(self):
+ if self.nametag == None:
+ self.nametag = NametagGroup()
+ self.nametag.setFont(ToontownGlobals.getBuildingNametagFont())
+ if TTLocalizer.BuildingNametagShadow:
+ self.nametag.setShadow(*TTLocalizer.BuildingNametagShadow)
+ self.nametag.setContents(Nametag.CName)
+ self.nametag.setColorCode(NametagGroup.CCSuitBuilding)
+ self.nametag.setActive(0)
+ self.nametag.setAvatar(self.getElevatorModel())
+ name = self.cr.playGame.dnaStore.getTitleFromBlockNumber(self.bldg.block)
+ if not name:
+ name = TTLocalizer.CogsInc
+ else:
+ name += TTLocalizer.CogsIncExt
+ self.nametag.setName(name)
+ self.nametag.manage(base.marginManager)
+ return
+
+ def clearNametag(self):
+ if self.nametag != None:
+ self.nametag.unmanage(base.marginManager)
+ self.nametag.setAvatar(NodePath())
+ self.nametag = None
+ return
+
+ def getBldgDoorOrigin(self):
+ return self.bldg.getSuitDoorOrigin()
+
+ def gotBldg(self, buildingList):
+ self.bldgRequest = None
+ self.bldg = buildingList[0]
+ if not self.bldg:
+ self.notify.error('setBldgDoId: elevator %d cannot find bldg %d!' % (self.doId, self.bldgDoId))
+ return
+ if self.getBldgDoorOrigin():
+ self.bossLevel = self.bldg.getBossLevel()
+ self.setupElevator()
+ else:
+ self.notify.warning('setBldgDoId: elevator %d cannot find suitDoorOrigin for bldg %d!' % (self.doId, bldgDoId))
+ return
+
+ def setFloor(self, floorNumber):
+ if self.currentFloor >= 0:
+ if self.bldg.floorIndicator[self.currentFloor]:
+ self.bldg.floorIndicator[self.currentFloor].setColor(LIGHT_OFF_COLOR)
+ if floorNumber >= 0:
+ if self.bldg.floorIndicator[floorNumber]:
+ self.bldg.floorIndicator[floorNumber].setColor(LIGHT_ON_COLOR)
+ self.currentFloor = floorNumber
+
+ def handleEnterSphere(self, collEntry):
+ self.notify.debug('Entering Elevator Sphere....')
+ if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty and localAvatar.boardingParty.getGroupLeader(localAvatar.doId) and localAvatar.boardingParty.getGroupLeader(localAvatar.doId) != localAvatar.doId:
+ base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorGroupMember)
+ elif self.allowedToEnter(self.zoneId):
+ self.cr.playGame.getPlace().detectedElevatorCollision(self)
+ else:
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.fsm.request('stopped')
+ self.dialog = TeaserPanel.TeaserPanel(pageName='cogHQ', doneFunc=self.handleOkTeaser)
+
+ def handleEnterElevator(self):
+ if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty and localAvatar.boardingParty.getGroupLeader(localAvatar.doId):
+ if localAvatar.boardingParty.getGroupLeader(localAvatar.doId) == localAvatar.doId:
+ localAvatar.boardingParty.handleEnterElevator(self)
+ elif self.elevatorTripId and localAvatar.lastElevatorLeft == self.elevatorTripId:
+ self.rejectBoard(base.localAvatar.doId, REJECT_SHUFFLE)
+ elif base.localAvatar.hp > 0:
+ toon = base.localAvatar
+ self.sendUpdate('requestBoard', [])
+ else:
+ self.notify.warning('Tried to board elevator with hp: %d' % base.localAvatar.hp)
+
+ def enterWaitEmpty(self, ts):
+ self.elevatorSphereNodePath.unstash()
+ self.forceDoorsOpen()
+ self.accept(self.uniqueName('enterelevatorSphere'), self.handleEnterSphere)
+ self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator)
+ DistributedElevator.DistributedElevator.enterWaitEmpty(self, ts)
+
+ def exitWaitEmpty(self):
+ self.elevatorSphereNodePath.stash()
+ self.ignore(self.uniqueName('enterelevatorSphere'))
+ self.ignore(self.uniqueName('enterElevatorOK'))
+ DistributedElevator.DistributedElevator.exitWaitEmpty(self)
+
+ def enterWaitCountdown(self, ts):
+ DistributedElevator.DistributedElevator.enterWaitCountdown(self, ts)
+ self.forceDoorsOpen()
+ self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator)
+ self.startCountdownClock(self.countdownTime, ts)
+
+ def exitWaitCountdown(self):
+ self.ignore(self.uniqueName('enterElevatorOK'))
+ DistributedElevator.DistributedElevator.exitWaitCountdown(self)
+
+ def getZoneId(self):
+ return self.bldg.interiorZoneId
+
+ def getElevatorModel(self):
+ return self.bldg.getSuitElevatorNodePath()
diff --git a/toontown/building/DistributedElevatorExtAI.py b/toontown/building/DistributedElevatorExtAI.py
new file mode 100644
index 0000000..f2c608b
--- /dev/null
+++ b/toontown/building/DistributedElevatorExtAI.py
@@ -0,0 +1,190 @@
+from otp.ai.AIBase import *
+from toontown.toonbase import ToontownGlobals
+from direct.distributed.ClockDelta import *
+from ElevatorConstants import *
+import DistributedElevatorAI
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+
+class DistributedElevatorExtAI(DistributedElevatorAI.DistributedElevatorAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorExtAI')
+
+ def __init__(self, air, bldg, numSeats=4, antiShuffle=0, minLaff=0, fSkipOpening=False):
+ DistributedElevatorAI.DistributedElevatorAI.__init__(self, air, bldg, numSeats, antiShuffle=antiShuffle, minLaff=minLaff, fSkipOpening=fSkipOpening)
+ self.anyToonsBailed = 0
+ self.boardingParty = None
+ return
+
+ def delete(self):
+ for seatIndex in range(len(self.seats)):
+ avId = self.seats[seatIndex]
+ if avId:
+ self.clearFullNow(seatIndex)
+ self.clearEmptyNow(seatIndex)
+
+ DistributedElevatorAI.DistributedElevatorAI.delete(self)
+
+ def d_setFloor(self, floorNumber):
+ self.sendUpdate('setFloor', [floorNumber])
+
+ def acceptBoarder(self, avId, seatIndex, wantBoardingShow=0):
+ DistributedElevatorAI.DistributedElevatorAI.acceptBoarder(self, avId, seatIndex, wantBoardingShow)
+ self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId])
+ self.fsm.request('waitCountdown')
+
+ def __handleUnexpectedExit(self, avId):
+ self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly')
+ seatIndex = self.findAvatar(avId)
+ if seatIndex == None:
+ pass
+ else:
+ self.clearFullNow(seatIndex)
+ self.clearEmptyNow(seatIndex)
+ if self.countFullSeats() == 0:
+ self.fsm.request('waitEmpty')
+ return
+
+ def acceptExiter(self, avId):
+ seatIndex = self.findAvatar(avId)
+ if seatIndex == None:
+ pass
+ else:
+ self.clearFullNow(seatIndex)
+ bailFlag = 0
+ timeToSend = self.countdownTime
+ if self.antiShuffle:
+ myTask = taskMgr.getTasksNamed(self.uniqueName('countdown-timer'))[0]
+ timeLeft = myTask.wakeTime - globalClock.getFrameTime()
+ timeLeft = max(0, timeLeft)
+ timeToSet = timeLeft + 10.0
+ timeToSet = min(timeLeft + 10.0, self.countdownTime)
+ self.setCountdown(timeToSet)
+ timeToSend = timeToSet
+ self.sendUpdate('emptySlot' + str(seatIndex), [
+ avId, 1, globalClockDelta.getRealNetworkTime(), timeToSend])
+ else:
+ if self.anyToonsBailed == 0:
+ bailFlag = 1
+ self.resetCountdown()
+ self.anyToonsBailed = 1
+ self.sendUpdate('emptySlot' + str(seatIndex), [
+ avId, bailFlag, globalClockDelta.getRealNetworkTime(), timeToSend])
+ else:
+ self.sendUpdate('emptySlot' + str(seatIndex), [
+ avId, bailFlag, globalClockDelta.getRealNetworkTime(), timeToSend])
+ if self.countFullSeats() == 0:
+ self.fsm.request('waitEmpty')
+ taskMgr.doMethodLater(TOON_EXIT_ELEVATOR_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs=(seatIndex,))
+ return
+
+ def enterOpening(self):
+ DistributedElevatorAI.DistributedElevatorAI.enterOpening(self)
+ taskMgr.doMethodLater(ElevatorData[ELEVATOR_NORMAL]['openTime'], self.waitEmptyTask, self.uniqueName('opening-timer'))
+
+ def waitEmptyTask(self, task):
+ self.fsm.request('waitEmpty')
+ return Task.done
+
+ def enterWaitEmpty(self):
+ DistributedElevatorAI.DistributedElevatorAI.enterWaitEmpty(self)
+ self.anyToonsBailed = 0
+
+ def enterWaitCountdown(self):
+ DistributedElevatorAI.DistributedElevatorAI.enterWaitCountdown(self)
+ taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer'))
+
+ def timeToGoTask(self, task):
+ if self.countFullSeats() > 0:
+ self.fsm.request('allAboard')
+ else:
+ self.fsm.request('waitEmpty')
+ return Task.done
+
+ def resetCountdown(self):
+ taskMgr.remove(self.uniqueName('countdown-timer'))
+ taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer'))
+
+ def setCountdown(self, timeToSet):
+ taskMgr.remove(self.uniqueName('countdown-timer'))
+ taskMgr.doMethodLater(timeToSet, self.timeToGoTask, self.uniqueName('countdown-timer'))
+
+ def enterAllAboard(self):
+ DistributedElevatorAI.DistributedElevatorAI.enterAllAboard(self)
+ currentTime = globalClock.getRealTime()
+ elapsedTime = currentTime - self.timeOfBoarding
+ self.notify.debug('elapsed time: ' + str(elapsedTime))
+ waitTime = max(TOON_BOARD_ELEVATOR_TIME - elapsedTime, 0)
+ waitTime += self.getBoardingShowTimeLeft()
+ taskMgr.doMethodLater(waitTime, self.closeTask, self.uniqueName('waitForAllAboard'))
+
+ def getBoardingShowTimeLeft(self):
+ currentTime = globalClock.getRealTime()
+ timeLeft = 0.0
+ if hasattr(self, 'timeOfGroupBoarding') and self.timeOfGroupBoarding:
+ elapsedTime = currentTime - self.timeOfGroupBoarding
+ timeLeft = max(MAX_GROUP_BOARDING_TIME - elapsedTime, 0)
+ if timeLeft > MAX_GROUP_BOARDING_TIME:
+ timeLeft = 0.0
+ return timeLeft
+
+ def closeTask(self, task):
+ if self.countFullSeats() > 0:
+ self.fsm.request('closing')
+ else:
+ self.fsm.request('waitEmpty')
+ return Task.done
+
+ def enterClosing(self):
+ DistributedElevatorAI.DistributedElevatorAI.enterClosing(self)
+ taskMgr.doMethodLater(ElevatorData[ELEVATOR_NORMAL]['closeTime'], self.elevatorClosedTask, self.uniqueName('closing-timer'))
+
+ def elevatorClosedTask(self, task):
+ self.elevatorClosed()
+ return Task.done
+
+ def _createInterior(self):
+ self.bldg.createSuitInterior()
+
+ def elevatorClosed(self):
+ numPlayers = self.countFullSeats()
+ if numPlayers > 0:
+ self._createInterior()
+ for seatIndex in range(len(self.seats)):
+ avId = self.seats[seatIndex]
+ if avId:
+ self.clearFullNow(seatIndex)
+
+ else:
+ self.notify.warning('The elevator left, but was empty.')
+ self.fsm.request('closed')
+
+ def requestExit(self, *args):
+ self.notify.debug('requestExit')
+ avId = self.air.getAvatarIdFromSender()
+ av = self.air.doId2do.get(avId)
+ if self.boardingParty and self.boardingParty.getGroupLeader(avId) and avId:
+ if avId == self.boardingParty.getGroupLeader(avId):
+ memberIds = self.boardingParty.getGroupMemberList(avId)
+ for memberId in memberIds:
+ member = simbase.air.doId2do.get(memberId)
+ if member:
+ if self.accepting:
+ self.acceptingExitersHandler(memberId)
+ else:
+ self.rejectingExitersHandler(memberId)
+
+ else:
+ self.rejectingExitersHandler(avId)
+ else:
+ if av:
+ newArgs = (
+ avId,) + args
+ if self.accepting:
+ self.acceptingExitersHandler(*newArgs)
+ else:
+ self.rejectingExitersHandler(*newArgs)
+ else:
+ self.notify.warning('avId: %s does not exist, but tried to exit an elevator' % avId)
+ return
diff --git a/toontown/building/DistributedElevatorFSM.py b/toontown/building/DistributedElevatorFSM.py
new file mode 100644
index 0000000..348bd38
--- /dev/null
+++ b/toontown/building/DistributedElevatorFSM.py
@@ -0,0 +1,485 @@
+from pandac.PandaModules import *
+from direct.distributed.ClockDelta import *
+from direct.interval.IntervalGlobal import *
+from ElevatorConstants import *
+from ElevatorUtils import *
+from direct.showbase import PythonUtil
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.distributed import DistributedObject
+from direct.fsm import State
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase import ToontownGlobals
+from direct.task.Task import Task
+from toontown.hood import ZoneUtil
+from direct.fsm.FSM import FSM
+
+class DistributedElevatorFSM(DistributedObject.DistributedObject, FSM):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevator')
+ defaultTransitions = {'Off': ['Opening', 'Closed', 'Off'],
+ 'Opening': ['WaitEmpty',
+ 'WaitCountdown',
+ 'Opening',
+ 'Closing'],
+ 'WaitEmpty': ['WaitCountdown', 'Closing', 'Off'],
+ 'WaitCountdown': ['WaitEmpty', 'AllAboard', 'Closing'],
+ 'AllAboard': ['WaitEmpty', 'Closing'],
+ 'Closing': ['Closed',
+ 'WaitEmpty',
+ 'Closing',
+ 'Opening'],
+ 'Closed': ['Opening']}
+ id = 0
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ FSM.__init__(self, 'Elevator_%s_FSM' % self.id)
+ self.bldgRequest = None
+ self.toonRequests = {}
+ self.deferredSlots = []
+ self.localToonOnBoard = 0
+ self.boardedAvIds = {}
+ self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.mp3')
+ self.finalOpenSfx = None
+ self.closeSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_close.mp3')
+ self.elevatorFSM = None
+ self.finalCloseSfx = None
+ self.elevatorPoints = ElevatorPoints
+ self.type = ELEVATOR_NORMAL
+ self.countdownTime = ElevatorData[self.type]['countdown']
+ self.isSetup = 0
+ self.__preSetupState = None
+ self.bigElevator = 0
+ self.offTrack = [None,
+ None,
+ None,
+ None]
+ self.boardingParty = None
+ return
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+
+ def setBoardingParty(self, party):
+ self.boardingParty = party
+
+ def setupElevator(self):
+ collisionRadius = ElevatorData[self.type]['collRadius']
+ self.elevatorSphere = CollisionSphere(0, 5, 0, collisionRadius)
+ self.elevatorSphere.setTangible(0)
+ self.elevatorSphereNode = CollisionNode(self.uniqueName('elevatorSphere'))
+ self.elevatorSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
+ self.elevatorSphereNode.addSolid(self.elevatorSphere)
+ self.elevatorSphereNodePath = self.getElevatorModel().attachNewNode(self.elevatorSphereNode)
+ self.elevatorSphereNodePath.hide()
+ self.elevatorSphereNodePath.reparentTo(self.getElevatorModel())
+ self.elevatorSphereNodePath.stash()
+ self.boardedAvIds = {}
+ self.openDoors = getOpenInterval(self, self.leftDoor, self.rightDoor, self.openSfx, self.finalOpenSfx, self.type)
+ self.closeDoors = getCloseInterval(self, self.leftDoor, self.rightDoor, self.closeSfx, self.finalCloseSfx, self.type)
+ self.openDoors = Sequence(self.openDoors, Func(self.onDoorOpenFinish))
+ self.closeDoors = Sequence(self.closeDoors, Func(self.onDoorCloseFinish))
+ self.finishSetup()
+
+ def finishSetup(self):
+ self.isSetup = 1
+ if self.__preSetupState:
+ self.request(self.__preSetupState, 0)
+ self.__preSetupState = None
+ for slot in self.deferredSlots:
+ self.fillSlot(*slot)
+
+ self.deferredSlots = []
+ return
+
+ def disable(self):
+ for track in self.offTrack:
+ if track:
+ if track.isPlaying():
+ track.pause()
+ track = None
+
+ if self.bldgRequest:
+ self.cr.relatedObjectMgr.abortRequest(self.bldgRequest)
+ self.bldgRequest = None
+ for request in self.toonRequests.values():
+ self.cr.relatedObjectMgr.abortRequest(request)
+
+ self.toonRequests = {}
+ if hasattr(self, 'openDoors'):
+ self.openDoors.pause()
+ if hasattr(self, 'closeDoors'):
+ self.closeDoors.pause()
+ self.request('off')
+ DistributedObject.DistributedObject.disable(self)
+ return
+
+ def delete(self):
+ for track in self.offTrack:
+ if track:
+ if track.isPlaying():
+ track.pause()
+ track = None
+
+ self.ignoreAll()
+ if self.isSetup:
+ self.elevatorSphereNodePath.removeNode()
+ del self.elevatorSphereNodePath
+ del self.elevatorSphereNode
+ del self.elevatorSphere
+ del self.bldg
+ del self.leftDoor
+ del self.rightDoor
+ del self.openDoors
+ del self.closeDoors
+ del self.openSfx
+ del self.closeSfx
+ self.isSetup = 0
+ DistributedObject.DistributedObject.delete(self)
+ return
+
+ def setBldgDoId(self, bldgDoId):
+ self.bldgDoId = bldgDoId
+ self.bldgRequest = self.cr.relatedObjectMgr.requestObjects([bldgDoId], allCallback=self.gotBldg, timeout=2)
+
+ def gotBldg(self, buildingList):
+ self.bldgRequest = None
+ self.bldg = buildingList[0]
+ if not self.bldg:
+ self.notify.error('setBldgDoId: elevator %d cannot find bldg %d!' % (self.doId, self.bldgDoId))
+ return
+ self.setupElevator()
+ return
+
+ def gotToon(self, index, avId, toonList):
+ request = self.toonRequests.get(index)
+ if request:
+ del self.toonRequests[index]
+ self.fillSlot(index, avId)
+ else:
+ self.notify.error('gotToon: already had got toon in slot %s.' % index)
+
+ def setState(self, state, timestamp):
+ if self.isSetup:
+ self.request(state, globalClockDelta.localElapsedTime(timestamp))
+ else:
+ self.__preSetupState = state
+
+ def fillSlot0(self, avId):
+ self.fillSlot(0, avId)
+
+ def fillSlot1(self, avId):
+ self.fillSlot(1, avId)
+
+ def fillSlot2(self, avId):
+ self.fillSlot(2, avId)
+
+ def fillSlot3(self, avId):
+ self.fillSlot(3, avId)
+
+ def fillSlot4(self, avId):
+ self.fillSlot(4, avId)
+
+ def fillSlot5(self, avId):
+ self.fillSlot(5, avId)
+
+ def fillSlot6(self, avId):
+ self.fillSlot(6, avId)
+
+ def fillSlot7(self, avId):
+ self.fillSlot(7, avId)
+
+ def fillSlot(self, index, avId):
+ self.notify.debug('%s.fillSlot(%s, %s, ...)' % (self.doId, index, avId))
+ request = self.toonRequests.get(index)
+ if request:
+ self.cr.relatedObjectMgr.abortRequest(request)
+ del self.toonRequests[index]
+ if avId == 0:
+ pass
+ elif not self.cr.doId2do.has_key(avId):
+ func = PythonUtil.Functor(self.gotToon, index, avId)
+ self.toonRequests[index] = self.cr.relatedObjectMgr.requestObjects([avId], allCallback=func)
+ elif not self.isSetup:
+ self.deferredSlots.append((index, avId))
+ else:
+ if avId == base.localAvatar.getDoId():
+ self.localToonOnBoard = 1
+ elevator = self.getPlaceElevator()
+ elevator.fsm.request('boarding', [self.getElevatorModel()])
+ elevator.fsm.request('boarded')
+ toon = self.cr.doId2do[avId]
+ toon.stopSmooth()
+ toon.setZ(self.getElevatorModel(), self.getScaledPoint(index)[2])
+ toon.setShadowHeight(0)
+ if toon.isDisguised:
+ toon.suit.loop('walk')
+ animFunc = Func(toon.suit.loop, 'neutral')
+ else:
+ toon.setAnimState('run', 1.0)
+ animFunc = Func(toon.setAnimState, 'neutral', 1.0)
+ toon.headsUp(self.getElevatorModel(), apply(Point3, self.getScaledPoint(index)))
+ track = Sequence(LerpPosInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.75, apply(Point3, self.getScaledPoint(index)), other=self.getElevatorModel()), LerpHprInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.25, Point3(180, 0, 0), other=self.getElevatorModel()), animFunc, name=toon.uniqueName('fillElevator'), autoPause=1)
+ track.start()
+ self.boardedAvIds[avId] = index
+
+ def emptySlot0(self, avId, bailFlag, timestamp):
+ self.emptySlot(0, avId, bailFlag, timestamp)
+
+ def emptySlot1(self, avId, bailFlag, timestamp):
+ self.emptySlot(1, avId, bailFlag, timestamp)
+
+ def emptySlot2(self, avId, bailFlag, timestamp):
+ self.emptySlot(2, avId, bailFlag, timestamp)
+
+ def emptySlot3(self, avId, bailFlag, timestamp):
+ self.emptySlot(3, avId, bailFlag, timestamp)
+
+ def emptySlot4(self, avId, bailFlag, timestamp):
+ self.emptySlot(4, avId, bailFlag, timestamp)
+
+ def emptySlot5(self, avId, bailFlag, timestamp):
+ self.emptySlot(5, avId, bailFlag, timestamp)
+
+ def emptySlot6(self, avId, bailFlag, timestamp):
+ self.emptySlot(6, avId, bailFlag, timestamp)
+
+ def emptySlot7(self, avId, bailFlag, timestamp):
+ self.emptySlot(7, avId, bailFlag, timestamp)
+
+ def notifyToonOffElevator(self, toon):
+ if self.cr:
+ toon.setAnimState('neutral', 1.0)
+ if toon == base.localAvatar:
+ print 'moving the local toon off the elevator'
+ doneStatus = {'where': 'exit'}
+ elevator = self.getPlaceElevator()
+ elevator.signalDone(doneStatus)
+ self.localToonOnBoard = 0
+ else:
+ toon.startSmooth()
+ return
+
+ def emptySlot(self, index, avId, bailFlag, timestamp):
+ print 'Emptying slot: %d for %d' % (index, avId)
+ if avId == 0:
+ pass
+ elif not self.isSetup:
+ newSlots = []
+ for slot in self.deferredSlots:
+ if slot[0] != index:
+ newSlots.append(slot)
+
+ self.deferredSlots = newSlots
+ elif self.cr.doId2do.has_key(avId):
+ if bailFlag == 1 and hasattr(self, 'clockNode'):
+ if timestamp < self.countdownTime and timestamp >= 0:
+ self.countdown(self.countdownTime - timestamp)
+ else:
+ self.countdown(self.countdownTime)
+ toon = self.cr.doId2do[avId]
+ toon.stopSmooth()
+ if toon.isDisguised:
+ toon.suit.loop('walk')
+ animFunc = Func(toon.suit.loop, 'neutral')
+ else:
+ toon.setAnimState('run', 1.0)
+ animFunc = Func(toon.setAnimState, 'neutral', 1.0)
+ if self.offTrack[index]:
+ if self.offTrack[index].isPlaying():
+ self.offTrack[index].finish()
+ self.offTrack[index] = None
+ self.offTrack[index] = Sequence(LerpPosInterval(toon, TOON_EXIT_ELEVATOR_TIME, Point3(0, -ElevatorData[self.type]['collRadius'], 0), startPos=apply(Point3, self.getScaledPoint(index)), other=self.getElevatorModel()), animFunc, Func(self.notifyToonOffElevator, toon), name=toon.uniqueName('emptyElevator'), autoPause=1)
+ if avId == base.localAvatar.getDoId():
+ messenger.send('exitElevator')
+ scale = base.localAvatar.getScale()
+ self.offTrack[index].append(Func(base.camera.setScale, scale))
+ self.offTrack[index].start()
+ if avId in self.boardedAvIds:
+ del self.boardedAvIds[avId]
+ else:
+ self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the elevator!')
+ return
+
+ def handleEnterSphere(self, collEntry):
+ self.notify.debug('Entering Elevator Sphere....')
+ print 'FSMhandleEnterSphere elevator%s avatar%s' % (self.elevatorTripId, localAvatar.lastElevatorLeft)
+ if self.elevatorTripId and localAvatar.lastElevatorLeft == self.elevatorTripId:
+ self.rejectBoard(base.localAvatar.doId, REJECT_SHUFFLE)
+ elif base.localAvatar.hp > 0:
+ self.cr.playGame.getPlace().detectedElevatorCollision(self)
+ toon = base.localAvatar
+ self.sendUpdate('requestBoard', [])
+
+ def rejectBoard(self, avId, reason = 0):
+ print 'rejectBoard %s' % reason
+ if hasattr(base.localAvatar, 'elevatorNotifier'):
+ if reason == REJECT_SHUFFLE:
+ base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorHoppedOff)
+ elif reason == REJECT_MINLAFF:
+ base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorMinLaff % self.minLaff)
+ elif reason == REJECT_PROMOTION:
+ base.localAvatar.elevatorNotifier.showMe(TTLocalizer.BossElevatorRejectMessage)
+ elif reason == REJECT_BLOCKED_ROOM:
+ base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorBlockedRoom)
+ doneStatus = {'where': 'reject'}
+ elevator = self.getPlaceElevator()
+ if elevator:
+ elevator.signalDone(doneStatus)
+
+ def timerTask(self, task):
+ countdownTime = int(task.duration - task.time)
+ timeStr = str(countdownTime)
+ if self.clockNode.getText() != timeStr:
+ self.clockNode.setText(timeStr)
+ if task.time >= task.duration:
+ return Task.done
+ else:
+ return Task.cont
+
+ def countdown(self, duration):
+ countdownTask = Task(self.timerTask)
+ countdownTask.duration = duration
+ taskMgr.remove(self.uniqueName('elevatorTimerTask'))
+ return taskMgr.add(countdownTask, self.uniqueName('elevatorTimerTask'))
+
+ def handleExitButton(self):
+ self.sendUpdate('requestExit')
+
+ def enterWaitCountdown(self, ts):
+ self.elevatorSphereNodePath.unstash()
+ self.accept(self.uniqueName('enterelevatorSphere'), self.handleEnterSphere)
+ self.accept('elevatorExitButton', self.handleExitButton)
+ self.lastState = self.state
+
+ def exitWaitCountdown(self):
+ self.elevatorSphereNodePath.stash()
+ self.ignore(self.uniqueName('enterelevatorSphere'))
+ self.ignore('elevatorExitButton')
+ self.ignore('localToonLeft')
+ taskMgr.remove(self.uniqueName('elevatorTimerTask'))
+ self.clock.removeNode()
+ del self.clock
+ del self.clockNode
+
+ def enterClosing(self, ts):
+ if self.localToonOnBoard:
+ elevator = self.getPlaceElevator()
+ if self.closeDoors.isPlaying() or self.lastState == 'closed' or self.openDoors.isPlaying():
+ self.doorsNeedToClose = 1
+ else:
+ self.doorsNeedToClose = 0
+ self.closeDoors.start(ts)
+
+ def exitClosing(self):
+ pass
+
+ def onDoorOpenFinish(self):
+ pass
+
+ def onDoorCloseFinish(self):
+ for avId in self.boardedAvIds.keys():
+ av = self.cr.doId2do.get(avId)
+ if av is not None:
+ if av.getParent().compareTo(self.getElevatorModel()) == 0:
+ av.detachNode()
+
+ return
+
+ def enterClosed(self, ts):
+ self.__doorsClosed(self.getZoneId())
+
+ def exitClosed(self):
+ pass
+
+ def forceDoorsOpen(self):
+ openDoors(self.leftDoor, self.rightDoor)
+
+ def forceDoorsClosed(self):
+ self.closeDoors.finish()
+ closeDoors(self.leftDoor, self.rightDoor)
+
+ def enterOff(self):
+ self.lastState = self.state
+
+ def exitOff(self):
+ pass
+
+ def enterWaitEmpty(self, ts):
+ self.lastState = self.state
+
+ def exitWaitEmpty(self):
+ pass
+
+ def enterOpening(self, ts):
+ self.openDoors.start(ts)
+ self.lastState = self.state
+
+ def exitOpening(self):
+ pass
+
+ def startCountdownClock(self, countdownTime, ts):
+ self.clockNode = TextNode('elevatorClock')
+ self.clockNode.setFont(ToontownGlobals.getSignFont())
+ self.clockNode.setAlign(TextNode.ACenter)
+ self.clockNode.setTextColor(0.5, 0.5, 0.5, 1)
+ self.clockNode.setText(str(int(countdownTime)))
+ self.clock = self.getElevatorModel().attachNewNode(self.clockNode)
+ self.clock.setPosHprScale(0, 4.4, 6.0, 0, 0, 0, 2.0, 2.0, 2.0)
+ if ts < countdownTime:
+ self.countdown(countdownTime - ts)
+
+ def __doorsClosed(self, zoneId):
+ if self.localToonOnBoard:
+ self.localAvatar.stopGlitchKiller()
+ hoodId = ZoneUtil.getHoodId(zoneId)
+ loader = 'suitInterior'
+ where = 'suitInterior'
+ if base.cr.wantCogdominiums:
+ loader = 'cogdoInterior'
+ where = 'cogdoInterior'
+ doneStatus = {'loader': loader,
+ 'where': where,
+ 'hoodId': hoodId,
+ 'zoneId': zoneId,
+ 'shardId': None}
+ elevator = self.elevatorFSM
+ del self.elevatorFSM
+ elevator.signalDone(doneStatus)
+ return
+
+ def getElevatorModel(self):
+ self.notify.error('getElevatorModel: pure virtual -- inheritors must override')
+
+ def getPlaceElevator(self):
+ place = self.cr.playGame.getPlace()
+ if not hasattr(place, 'elevator'):
+ self.notify.warning("Place was in state '%s' instead of Elevator." % place.state)
+ place.detectedElevatorCollision(self)
+ return None
+ return place.elevator
+
+ def getScaledPoint(self, index):
+ point = self.elevatorPoints[index]
+ return point
+
+ def setElevatorTripId(self, id):
+ self.elevatorTripId = id
+
+ def getElevatorTripId(self):
+ return self.elevatorTripId
+
+ def setAntiShuffle(self, antiShuffle):
+ self.antiShuffle = antiShuffle
+
+ def getAntiShuffle(self):
+ return self.antiShuffle
+
+ def setMinLaff(self, minLaff):
+ self.minLaff = minLaff
+
+ def getMinLaff(self):
+ return self.minLaff
+
+ def getDestName(self):
+ return None
diff --git a/toontown/building/DistributedElevatorFSMAI.py b/toontown/building/DistributedElevatorFSMAI.py
new file mode 100644
index 0000000..d23c541
--- /dev/null
+++ b/toontown/building/DistributedElevatorFSMAI.py
@@ -0,0 +1,277 @@
+from otp.ai.AIBase import *
+from toontown.toonbase import ToontownGlobals
+from direct.distributed.ClockDelta import *
+from ElevatorConstants import *
+from direct.distributed import DistributedObjectAI
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm.FSM import FSM
+
+class DistributedElevatorFSMAI(DistributedObjectAI.DistributedObjectAI, FSM):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorFSMAI')
+ defaultTransitions = {'Off': ['Opening', 'Closed'], 'Opening': ['WaitEmpty', 'WaitCountdown', 'Opening', 'Closing'], 'WaitEmpty': ['WaitCountdown', 'Closing'], 'WaitCountdown': ['WaitEmpty', 'AllAboard', 'Closing'], 'AllAboard': ['WaitEmpty', 'Closing'], 'Closing': ['Closed', 'WaitEmpty', 'Closing', 'Opening'], 'Closed': ['Opening']}
+ id = 0
+
+ def __init__(self, air, bldg, numSeats=4, antiShuffle=0, minLaff=0):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ FSM.__init__(self, 'Elevator_%s_FSM' % self.id)
+ self.type = ELEVATOR_NORMAL
+ self.countdownTime = ElevatorData[self.type]['countdown']
+ self.bldg = bldg
+ self.bldgDoId = bldg.getDoId()
+ self.seats = []
+ for seat in range(numSeats):
+ self.seats.append(None)
+
+ self.accepting = 0
+ self.setAntiShuffle(antiShuffle)
+ self.setMinLaff(minLaff)
+ if self.antiShuffle:
+ if not hasattr(simbase.air, 'elevatorTripId'):
+ simbase.air.elevatorTripId = 1
+ self.elevatorTripId = simbase.air.elevatorTripId
+ simbase.air.elevatorTripId += 1
+ else:
+ self.elevatorTripId = 0
+ return
+
+ def delete(self):
+ del self.bldg
+ self.ignoreAll()
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def generate(self):
+ DistributedObjectAI.DistributedObjectAI.generate(self)
+ self.start()
+
+ def getBldgDoId(self):
+ return self.bldgDoId
+
+ def findAvailableSeat(self):
+ for i in range(len(self.seats)):
+ if self.seats[i] == None:
+ return i
+
+ return
+
+ def findAvatar(self, avId):
+ for i in range(len(self.seats)):
+ if self.seats[i] == avId:
+ return i
+
+ return None
+
+ def countFullSeats(self):
+ avCounter = 0
+ for i in self.seats:
+ if i:
+ avCounter += 1
+
+ return avCounter
+
+ def countOpenSeats(self):
+ openSeats = 0
+ for i in range(len(self.seats)):
+ if self.seats[i] == None:
+ openSeats += 1
+
+ return openSeats
+
+ def rejectingBoardersHandler(self, avId, reason=0):
+ self.rejectBoarder(avId, reason)
+
+ def rejectBoarder(self, avId, reason=0):
+ self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId, reason])
+
+ def acceptingBoardersHandler(self, avId, reason=0):
+ self.notify.debug('acceptingBoardersHandler')
+ seatIndex = self.findAvailableSeat()
+ if seatIndex == None:
+ self.rejectBoarder(avId, REJECT_NOSEAT)
+ else:
+ self.acceptBoarder(avId, seatIndex)
+ return
+
+ def acceptBoarder(self, avId, seatIndex):
+ self.notify.debug('acceptBoarder')
+ if self.findAvatar(avId) != None:
+ return
+ self.seats[seatIndex] = avId
+ self.timeOfBoarding = globalClock.getRealTime()
+ self.sendUpdate('fillSlot' + str(seatIndex), [
+ avId])
+ return
+
+ def rejectingExitersHandler(self, avId):
+ self.rejectExiter(avId)
+
+ def rejectExiter(self, avId):
+ pass
+
+ def acceptingExitersHandler(self, avId):
+ self.acceptExiter(avId)
+
+ def clearEmptyNow(self, seatIndex):
+ self.sendUpdate('emptySlot' + str(seatIndex), [
+ 0, 0, globalClockDelta.getRealNetworkTime()])
+
+ def clearFullNow(self, seatIndex):
+ avId = self.seats[seatIndex]
+ if avId == None:
+ self.notify.warning('Clearing an empty seat index: ' + str(seatIndex) + ' ... Strange...')
+ else:
+ self.seats[seatIndex] = None
+ self.sendUpdate('fillSlot' + str(seatIndex), [
+ 0])
+ self.ignore(self.air.getAvatarExitEvent(avId))
+ return
+
+ def d_setState(self, state):
+ self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
+
+ def getState(self):
+ return self.state
+
+ def avIsOKToBoard(self, av):
+ return av.hp > self.minLaff and self.accepting
+
+ def checkBoard(self, av):
+ if av.hp < self.minLaff:
+ return REJECT_MINLAFF
+ return 0
+
+ def requestBoard(self, *args):
+ self.notify.debug('requestBoard')
+ avId = self.air.getAvatarIdFromSender()
+ if self.findAvatar(avId) != None:
+ self.notify.warning('Ignoring multiple requests from %s to board.' % avId)
+ return
+ av = self.air.doId2do.get(avId)
+ if av:
+ newArgs = (
+ avId,) + args
+ boardResponse = self.checkBoard(av)
+ newArgs = (avId,) + args + (boardResponse,)
+ if boardResponse == 0:
+ self.acceptingBoardersHandler(*newArgs)
+ else:
+ self.rejectingBoardersHandler(*newArgs)
+ else:
+ self.notify.warning('avid: %s does not exist, but tried to board an elevator' % avId)
+ return
+
+ def requestExit(self, *args):
+ if hasattr(self, 'air'):
+ self.notify.debug('requestExit')
+ avId = self.air.getAvatarIdFromSender()
+ av = self.air.doId2do.get(avId)
+ if av:
+ newArgs = (
+ avId,) + args
+ if self.accepting:
+ self.acceptingExitersHandler(*newArgs)
+ else:
+ self.rejectingExitersHandler(*newArgs)
+ else:
+ self.notify.warning('avId: %s does not exist, but tried to exit an elevator' % avId)
+
+ def start(self):
+ self.open()
+
+ def enterOff(self):
+ self.accepting = 0
+ self.timeOfBoarding = None
+ if hasattr(self, 'doId'):
+ for seatIndex in range(len(self.seats)):
+ taskMgr.remove(self.uniqueName('clearEmpty-' + str(seatIndex)))
+
+ return
+
+ def exitOff(self):
+ self.accepting = 0
+
+ def open(self):
+ self.request('Opening')
+
+ def enterOpening(self):
+ self.d_setState('Opening')
+ self.accepting = 0
+ for seat in self.seats:
+ seat = None
+
+ return
+
+ def exitOpening(self):
+ self.accepting = 0
+ taskMgr.remove(self.uniqueName('opening-timer'))
+
+ def enterWaitCountdown(self):
+ self.d_setState('WaitCountdown')
+ self.accepting = 1
+
+ def exitWaitCountdown(self):
+ print 'exit wait countdown'
+ self.accepting = 0
+ taskMgr.remove(self.uniqueName('countdown-timer'))
+ self.newTrip()
+
+ def enterAllAboard(self):
+ self.accepting = 0
+
+ def exitAllAboard(self):
+ self.accepting = 0
+ taskMgr.remove(self.uniqueName('waitForAllAboard'))
+
+ def enterClosing(self):
+ self.d_setState('Closing')
+ self.accepting = 0
+
+ def exitClosing(self):
+ self.accepting = 0
+ taskMgr.remove(self.uniqueName('closing-timer'))
+
+ def enterClosed(self):
+ if hasattr(self, 'doId'):
+ print self.doId
+ self.d_setState('Closed')
+
+ def exitClosed(self):
+ pass
+
+ def enterWaitEmpty(self):
+ for i in range(len(self.seats)):
+ self.seats[i] = None
+
+ print self.seats
+ self.d_setState('WaitEmpty')
+ self.accepting = 1
+ return
+
+ def exitWaitEmpty(self):
+ self.accepting = 0
+
+ def setElevatorTripId(self, id):
+ self.elevatorTripId = id
+
+ def getElevatorTripId(self):
+ return self.elevatorTripId
+
+ def newTrip(self):
+ if self.antiShuffle:
+ self.elevatorTripId = simbase.air.elevatorTripId
+ if simbase.air.elevatorTripId > 2100000000:
+ simbase.air.elevatorTripId = 1
+ simbase.air.elevatorTripId += 1
+ self.sendUpdate('setElevatorTripId', [self.elevatorTripId])
+
+ def setAntiShuffle(self, antiShuffle):
+ self.antiShuffle = antiShuffle
+
+ def getAntiShuffle(self):
+ return self.antiShuffle
+
+ def setMinLaff(self, minLaff):
+ self.minLaff = minLaff
+
+ def getMinLaff(self):
+ return self.minLaff
diff --git a/toontown/building/DistributedElevatorFloor.py b/toontown/building/DistributedElevatorFloor.py
new file mode 100644
index 0000000..5dc2be2
--- /dev/null
+++ b/toontown/building/DistributedElevatorFloor.py
@@ -0,0 +1,326 @@
+from pandac.PandaModules import *
+from direct.distributed.ClockDelta import *
+from direct.interval.IntervalGlobal import *
+from ElevatorConstants import *
+from ElevatorUtils import *
+import DistributedElevatorFSM
+from toontown.toonbase import ToontownGlobals
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from toontown.hood import ZoneUtil
+from toontown.toonbase import TTLocalizer
+from direct.fsm.FSM import FSM
+from direct.task import Task
+
+class DistributedElevatorFloor(DistributedElevatorFSM.DistributedElevatorFSM):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorFloor')
+ defaultTransitions = {'Off': ['Opening', 'Closed'],
+ 'Opening': ['WaitEmpty',
+ 'WaitCountdown',
+ 'Opening',
+ 'Closing'],
+ 'WaitEmpty': ['WaitCountdown', 'Closing'],
+ 'WaitCountdown': ['WaitEmpty',
+ 'AllAboard',
+ 'Closing',
+ 'WaitCountdown'],
+ 'AllAboard': ['WaitEmpty', 'Closing'],
+ 'Closing': ['Closed',
+ 'WaitEmpty',
+ 'Closing',
+ 'Opening'],
+ 'Closed': ['Opening']}
+ id = 0
+
+ def __init__(self, cr):
+ DistributedElevatorFSM.DistributedElevatorFSM.__init__(self, cr)
+ FSM.__init__(self, 'ElevatorFloor_%s_FSM' % self.id)
+ self.type = ELEVATOR_STAGE
+ self.countdownTime = ElevatorData[self.type]['countdown']
+ self.nametag = None
+ self.currentFloor = -1
+ self.isLocked = 0
+ self.isEntering = 0
+ self.doorOpeningFlag = 0
+ self.doorsNeedToClose = 0
+ self.wantState = 0
+ self.latch = None
+ self.lastState = self.state
+ return
+
+ def setupElevator2(self):
+ self.elevatorModel = loader.loadModel('phase_4/models/modules/elevator')
+ self.elevatorModel.reparentTo(hidden)
+ self.elevatorModel.setScale(1.05)
+ self.leftDoor = self.elevatorModel.find('**/left-door')
+ self.rightDoor = self.elevatorModel.find('**/right-door')
+ self.elevatorModel.find('**/light_panel').removeNode()
+ self.elevatorModel.find('**/light_panel_frame').removeNode()
+ if self.isSetup:
+ self.elevatorSphereNodePath.removeNode()
+ DistributedElevatorFSM.DistributedElevatorFSM.setupElevator(self)
+
+ def setupElevator(self):
+ self.elevatorModel = loader.loadModel('phase_11/models/lawbotHQ/LB_ElevatorScaled')
+ if not self.elevatorModel:
+ self.notify.error('No Elevator Model in DistributedElevatorFloor.setupElevator. Please inform JML. Fool!')
+ self.leftDoor = self.elevatorModel.find('**/left-door')
+ if self.leftDoor.isEmpty():
+ self.leftDoor = self.elevatorModel.find('**/left_door')
+ self.rightDoor = self.elevatorModel.find('**/right-door')
+ if self.rightDoor.isEmpty():
+ self.rightDoor = self.elevatorModel.find('**/right_door')
+ DistributedElevatorFSM.DistributedElevatorFSM.setupElevator(self)
+
+ def generate(self):
+ DistributedElevatorFSM.DistributedElevatorFSM.generate(self)
+
+ def announceGenerate(self):
+ DistributedElevatorFSM.DistributedElevatorFSM.announceGenerate(self)
+ if self.latch:
+ self.notify.info('Setting latch in announce generate')
+ self.setLatch(self.latch)
+
+ def __placeElevator(self):
+ self.notify.debug('PLACING ELEVATOR FOOL!!')
+ if self.isEntering:
+ elevatorNode = render.find('**/elevator_origin')
+ if not elevatorNode.isEmpty():
+ self.elevatorModel.setPos(0, 0, 0)
+ self.elevatorModel.reparentTo(elevatorNode)
+ else:
+ self.notify.debug('NO NODE elevator_origin!!')
+ else:
+ elevatorNode = render.find('**/SlidingDoor')
+ if not elevatorNode.isEmpty():
+ self.elevatorModel.setPos(0, 10, -15)
+ self.elevatorModel.setH(180)
+ self.elevatorModel.reparentTo(elevatorNode)
+ else:
+ self.notify.debug('NO NODE SlidingDoor!!')
+
+ def setLatch(self, markerId):
+ self.notify.info('Setting latch')
+ marker = self.cr.doId2do.get(markerId)
+ self.latchRequest = self.cr.relatedObjectMgr.requestObjects([markerId], allCallback=self.set2Latch, timeout=5)
+ self.latch = markerId
+
+ def set2Latch(self, taskMgrFooler = None):
+ if hasattr(self, 'cr'):
+ marker = self.cr.doId2do.get(self.latch)
+ if marker:
+ self.elevatorModel.reparentTo(marker)
+ return
+ taskMgr.doMethodLater(10.0, self._repart2Marker, 'elevatorfloor-markerReparent')
+ self.notify.warning('Using backup, do method later version of latch')
+
+ def _repart2Marker(self, taskFoolio = 0):
+ if hasattr(self, 'cr'):
+ marker = self.cr.doId2do.get(self.latch)
+ if marker:
+ self.elevatorModel.reparentTo(marker)
+ else:
+ self.notify.error('could not find latch even in defered try')
+
+ def setPos(self, x, y, z):
+ self.elevatorModel.setPos(x, y, z)
+
+ def setH(self, H):
+ self.elevatorModel.setH(H)
+
+ def delete(self):
+ DistributedElevatorFSM.DistributedElevatorFSM.delete(self)
+ self.elevatorModel.removeNode()
+ del self.elevatorModel
+ self.ignore('LawOffice_Spec_Loaded')
+ self.ignoreAll()
+
+ def disable(self):
+ DistributedElevatorFSM.DistributedElevatorFSM.disable(self)
+
+ def setEntranceId(self, entranceId):
+ self.entranceId = entranceId
+ if self.entranceId == 0:
+ self.elevatorModel.setPosHpr(62.74, -85.31, 0.0, 2.0, 0.0, 0.0)
+ elif self.entranceId == 1:
+ self.elevatorModel.setPosHpr(-162.25, 26.43, 0.0, 269.0, 0.0, 0.0)
+ else:
+ self.notify.error('Invalid entranceId: %s' % entranceId)
+
+ def gotBldg(self, buildingList):
+ pass
+
+ def setFloor(self, floorNumber):
+ if self.currentFloor >= 0:
+ if self.bldg.floorIndicator[self.currentFloor]:
+ self.bldg.floorIndicator[self.currentFloor].setColor(LIGHT_OFF_COLOR)
+ if floorNumber >= 0:
+ if self.bldg.floorIndicator[floorNumber]:
+ self.bldg.floorIndicator[floorNumber].setColor(LIGHT_ON_COLOR)
+ self.currentFloor = floorNumber
+
+ def handleEnterSphere(self, collEntry):
+ self.cr.playGame.getPlace().detectedElevatorCollision(self)
+
+ def handleEnterElevator(self):
+ if base.localAvatar.hp > 0:
+ toon = base.localAvatar
+ self.sendUpdate('requestBoard', [])
+ else:
+ self.notify.warning('Tried to board elevator with hp: %d' % base.localAvatar.hp)
+
+ def enterWaitEmpty(self, ts):
+ self.lastState = self.state
+ self.elevatorSphereNodePath.unstash()
+ self.forceDoorsOpen()
+ self.accept(self.uniqueName('enterelevatorSphere'), self.handleEnterSphere)
+ self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator)
+ DistributedElevatorFSM.DistributedElevatorFSM.enterWaitEmpty(self, ts)
+
+ def exitWaitEmpty(self):
+ self.lastState = self.state
+ self.elevatorSphereNodePath.stash()
+ self.ignore(self.uniqueName('enterelevatorSphere'))
+ self.ignore(self.uniqueName('enterElevatorOK'))
+ DistributedElevatorFSM.DistributedElevatorFSM.exitWaitEmpty(self)
+
+ def enterWaitCountdown(self, ts):
+ self.lastState = self.state
+ DistributedElevatorFSM.DistributedElevatorFSM.enterWaitCountdown(self, ts)
+ self.forceDoorsOpen()
+ self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator)
+ self.startCountdownClock(self.countdownTime, ts)
+
+ def exitWaitCountdown(self):
+ self.lastState = self.state
+ self.ignore(self.uniqueName('enterElevatorOK'))
+ DistributedElevatorFSM.DistributedElevatorFSM.exitWaitCountdown(self)
+
+ def enterClosing(self, ts):
+ self.lastState = self.state
+ taskMgr.doMethodLater(1.0, self._delayIris, 'delayedIris')
+ DistributedElevatorFSM.DistributedElevatorFSM.enterClosing(self, ts)
+
+ def _delayIris(self, tskfooler = 0):
+ base.transitions.irisOut(1.0)
+ base.localAvatar.pauseGlitchKiller()
+ return Task.done
+
+ def kickToonsOut(self):
+ if not self.localToonOnBoard:
+ zoneId = self.cr.playGame.hood.hoodId
+ self.cr.playGame.getPlace().fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(zoneId),
+ 'where': ZoneUtil.getToonWhereName(zoneId),
+ 'how': 'teleportIn',
+ 'hoodId': zoneId,
+ 'zoneId': zoneId,
+ 'shardId': None,
+ 'avId': -1}])
+ return
+
+ def exitClosing(self):
+ self.lastState = self.state
+ DistributedElevatorFSM.DistributedElevatorFSM.exitClosing(self)
+
+ def enterClosed(self, ts):
+ self.lastState = self.state
+ self.forceDoorsClosed()
+ self.__doorsClosed(self.getZoneId())
+
+ def exitClosed(self):
+ self.lastState = self.state
+ DistributedElevatorFSM.DistributedElevatorFSM.exitClosed(self)
+
+ def enterOff(self):
+ self.lastState = self.state
+ if self.wantState == 'closed':
+ self.demand('Closing')
+ elif self.wantState == 'waitEmpty':
+ self.demand('WaitEmpty')
+ DistributedElevatorFSM.DistributedElevatorFSM.enterOff(self)
+
+ def exitOff(self):
+ self.lastState = self.state
+ DistributedElevatorFSM.DistributedElevatorFSM.exitOff(self)
+
+ def enterOpening(self, ts):
+ self.lastState = self.state
+ DistributedElevatorFSM.DistributedElevatorFSM.enterOpening(self, ts)
+
+ def exitOpening(self):
+ DistributedElevatorFSM.DistributedElevatorFSM.exitOpening(self)
+ self.kickEveryoneOut()
+
+ def getZoneId(self):
+ return 0
+
+ def setBldgDoId(self, bldgDoId):
+ self.bldg = None
+ self.setupElevator()
+ return
+
+ def getElevatorModel(self):
+ return self.elevatorModel
+
+ def kickEveryoneOut(self):
+ bailFlag = 0
+ for avId, slot in self.boardedAvIds.items():
+ self.emptySlot(slot, avId, bailFlag, globalClockDelta.getRealNetworkTime())
+ if avId == base.localAvatar.doId:
+ pass
+
+ def __doorsClosed(self, zoneId):
+ pass
+
+ def onDoorCloseFinish(self):
+ pass
+
+ def setLocked(self, locked):
+ self.isLocked = locked
+ if locked:
+ if self.state == 'WaitEmpty':
+ self.request('Closing')
+ if self.countFullSeats() == 0:
+ self.wantState = 'closed'
+ else:
+ self.wantState = 'opening'
+ else:
+ self.wantState = 'waitEmpty'
+ if self.state == 'Closed':
+ self.request('Opening')
+
+ def getLocked(self):
+ return self.isLocked
+
+ def setEntering(self, entering):
+ self.isEntering = entering
+
+ def getEntering(self):
+ return self.isEntering
+
+ def forceDoorsOpen(self):
+ openDoors(self.leftDoor, self.rightDoor)
+
+ def forceDoorsClosed(self):
+ if self.openDoors.isPlaying():
+ self.doorsNeedToClose = 1
+ else:
+ self.closeDoors.finish()
+ closeDoors(self.leftDoor, self.rightDoor)
+
+ def enterOff(self):
+ self.lastState = self.state
+
+ def exitOff(self):
+ pass
+
+ def setLawOfficeInteriorZone(self, zoneId):
+ if self.localToonOnBoard:
+ hoodId = self.cr.playGame.hood.hoodId
+ doneStatus = {'loader': 'cogHQLoader',
+ 'where': 'factoryInterior',
+ 'how': 'teleportIn',
+ 'zoneId': zoneId,
+ 'hoodId': hoodId}
+ self.cr.playGame.getPlace().elevator.signalDone(doneStatus)
diff --git a/toontown/building/DistributedElevatorFloorAI.py b/toontown/building/DistributedElevatorFloorAI.py
new file mode 100644
index 0000000..6bcf429
--- /dev/null
+++ b/toontown/building/DistributedElevatorFloorAI.py
@@ -0,0 +1,273 @@
+from otp.ai.AIBase import *
+from toontown.toonbase import ToontownGlobals
+from direct.distributed.ClockDelta import *
+from ElevatorConstants import *
+import DistributedElevatorFSMAI
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm.FSM import FSM
+
+class DistributedElevatorFloorAI(DistributedElevatorFSMAI.DistributedElevatorFSMAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorFloorAI')
+ defaultTransitions = {'Off': ['Opening', 'Closed'], 'Opening': ['WaitEmpty', 'WaitCountdown', 'Opening', 'Closing'], 'WaitEmpty': ['WaitCountdown', 'Closing', 'WaitEmpty'], 'WaitCountdown': ['WaitEmpty', 'AllAboard', 'Closing', 'WaitCountdown'], 'AllAboard': ['WaitEmpty', 'Closing'], 'Closing': ['Closed', 'WaitEmpty', 'Closing', 'Opening'], 'Closed': ['Opening']}
+ id = 0
+
+ def __init__(self, air, lawOfficeId, bldg, avIds, markerId=None, numSeats=4, antiShuffle=0, minLaff=0):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.__init__(self, air, bldg, numSeats, antiShuffle=antiShuffle, minLaff=minLaff)
+ FSM.__init__(self, 'ElevatorFloor_%s_FSM' % self.id)
+ self.type = ELEVATOR_STAGE
+ self.countdownTime = ElevatorData[self.type]['countdown']
+ self.lawOfficeId = lawOfficeId
+ self.anyToonsBailed = 0
+ self.avIds = avIds
+ self.isEntering = 0
+ self.isLocked = 0
+ self.setLocked(0)
+ self.wantState = None
+ self.latchRoom = None
+ self.setLatch(markerId)
+ self.zoneId = bldg.zoneId
+ return
+
+ def generate(self):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.generate(self)
+
+ def generateWithRequired(self, zoneId):
+ self.zoneId = zoneId
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.generateWithRequired(self, self.zoneId)
+
+ def delete(self):
+ for seatIndex in range(len(self.seats)):
+ avId = self.seats[seatIndex]
+ if avId:
+ self.clearFullNow(seatIndex)
+ self.clearEmptyNow(seatIndex)
+
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.delete(self)
+
+ def getEntranceId(self):
+ return self.entranceId
+
+ def d_setFloor(self, floorNumber):
+ self.sendUpdate('setFloor', [floorNumber])
+
+ def avIsOKToBoard(self, av):
+ return av.hp > 0 and self.accepting and not self.isLocked
+
+ def acceptBoarder(self, avId, seatIndex):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.acceptBoarder(self, avId, seatIndex)
+ self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId])
+ if self.state == 'WaitEmpty' and self.countFullSeats() < self.countAvsInZone():
+ self.request('WaitCountdown')
+ self.bldg.elevatorAlert(avId)
+ elif self.state in ('WaitCountdown', 'WaitEmpty') and self.countFullSeats() >= self.countAvsInZone():
+ taskMgr.doMethodLater(TOON_BOARD_ELEVATOR_TIME, self.goAllAboard, self.quickBoardTask)
+
+ def countAvsInZone(self):
+ matchingZones = 0
+ for avId in self.bldg.avIds:
+ av = self.air.doId2do.get(avId)
+ if av:
+ if av.zoneId == self.bldg.zoneId:
+ matchingZones += 1
+
+ return matchingZones
+
+ def goAllAboard(self, throwAway=1):
+ self.request('Closing')
+ return Task.done
+
+ def __handleUnexpectedExit(self, avId):
+ self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly')
+ seatIndex = self.findAvatar(avId)
+ if seatIndex == None:
+ pass
+ else:
+ self.clearFullNow(seatIndex)
+ self.clearEmptyNow(seatIndex)
+ if self.countFullSeats() == 0:
+ self.request('WaitEmpty')
+ return
+
+ def acceptExiter(self, avId):
+ seatIndex = self.findAvatar(avId)
+ if seatIndex == None:
+ pass
+ else:
+ self.clearFullNow(seatIndex)
+ bailFlag = 0
+ if self.anyToonsBailed == 0:
+ bailFlag = 1
+ self.resetCountdown()
+ self.anyToonsBailed = 1
+ self.sendUpdate('emptySlot' + str(seatIndex), [
+ avId, bailFlag, globalClockDelta.getRealNetworkTime()])
+ if self.countFullSeats() == 0:
+ self.request('WaitEmpty')
+ taskMgr.doMethodLater(TOON_EXIT_ELEVATOR_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs=(seatIndex,))
+ return
+
+ def enterOpening(self):
+ self.d_setState('Opening')
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterOpening(self)
+ taskMgr.doMethodLater(ElevatorData[ELEVATOR_NORMAL]['openTime'], self.waitEmptyTask, self.uniqueName('opening-timer'))
+
+ def exitOpening(self):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.exitOpening(self)
+ if self.isLocked:
+ self.wantState = 'closed'
+ if self.wantState == 'closed':
+ self.demand('Closing')
+
+ def waitEmptyTask(self, task):
+ self.request('WaitEmpty')
+ return Task.done
+
+ def enterWaitEmpty(self):
+ self.lastState = self.state
+ for i in range(len(self.seats)):
+ self.seats[i] = None
+
+ print self.seats
+ if self.wantState == 'closed':
+ self.demand('Closing')
+ else:
+ self.d_setState('WaitEmpty')
+ self.accepting = 1
+ return
+
+ def enterWaitCountdown(self):
+ self.lastState = self.state
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterWaitCountdown(self)
+ taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer'))
+ if self.lastState == 'WaitCountdown':
+ pass
+
+ def timeToGoTask(self, task):
+ if self.countFullSeats() > 0:
+ self.request('AllAboard')
+ else:
+ self.request('WaitEmpty')
+ return Task.done
+
+ def resetCountdown(self):
+ taskMgr.remove(self.uniqueName('countdown-timer'))
+ taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer'))
+
+ def enterAllAboard(self):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterAllAboard(self)
+ currentTime = globalClock.getRealTime()
+ elapsedTime = currentTime - self.timeOfBoarding
+ self.notify.debug('elapsed time: ' + str(elapsedTime))
+ waitTime = max(TOON_BOARD_ELEVATOR_TIME - elapsedTime, 0)
+ taskMgr.doMethodLater(waitTime, self.closeTask, self.uniqueName('waitForAllAboard'))
+
+ def closeTask(self, task):
+ if self.countFullSeats() >= 1:
+ self.request('Closing')
+ else:
+ self.request('WaitEmpty')
+ return Task.done
+
+ def enterClosing(self):
+ if self.countFullSeats() > 0:
+ self.sendUpdate('kickToonsOut')
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterClosing(self)
+ taskMgr.doMethodLater(ElevatorData[ELEVATOR_STAGE]['closeTime'], self.elevatorClosedTask, self.uniqueName('closing-timer'))
+ self.d_setState('Closing')
+
+ def elevatorClosedTask(self, task):
+ self.elevatorClosed()
+ return Task.done
+
+ def elevatorClosed(self):
+ if self.isLocked:
+ self.request('Closed')
+ return
+ numPlayers = self.countFullSeats()
+ if numPlayers > 0:
+ players = []
+ for i in self.seats:
+ if i not in [None, 0]:
+ players.append(i)
+
+ sittingAvIds = []
+ for seatIndex in range(len(self.seats)):
+ avId = self.seats[seatIndex]
+ if avId:
+ sittingAvIds.append(avId)
+
+ for avId in self.avIds:
+ if avId not in sittingAvIds:
+ pass
+
+ self.bldg.startNextFloor()
+ else:
+ self.notify.warning('The elevator left, but was empty.')
+ self.request('Closed')
+ return
+
+ def setLocked(self, locked):
+ self.isLocked = locked
+ if locked:
+ if self.state == 'WaitEmpty':
+ self.request('Closing')
+ if self.countFullSeats() == 0:
+ self.wantState = 'closed'
+ else:
+ self.wantState = 'opening'
+ else:
+ self.wantState = 'waitEmpty'
+ if self.state == 'Closed':
+ self.request('Opening')
+
+ def getLocked(self):
+ return self.isLocked
+
+ def unlock(self):
+ if self.isLocked:
+ self.setLocked(0)
+
+ def lock(self):
+ if not self.isLocked:
+ self.setLocked(1)
+
+ def start(self):
+ self.quickBoardTask = self.uniqueName('quickBoard')
+ self.request('Opening')
+
+ def beClosed(self):
+ pass
+
+ def setEntering(self, entering):
+ self.isEntering = entering
+
+ def getEntering(self):
+ return self.isEntering
+
+ def enterClosed(self):
+ DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterClosed(self)
+ if self.wantState == 'closed':
+ pass
+ else:
+ self.demand('Opening')
+
+ def enterOff(self):
+ self.lastState = self.state
+ if self.wantState == 'closed':
+ self.demand('Closing')
+ else:
+ if self.wantState == 'waitEmpty':
+ self.demand('WaitEmpty')
+
+ def setPos(self, pointPos):
+ self.sendUpdate('setPos', [pointPos[0], pointPos[1], pointPos[2]])
+
+ def setH(self, H):
+ self.sendUpdate('setH', [H])
+
+ def setLatch(self, markerId):
+ self.latch = markerId
+
+ def getLatch(self):
+ return self.latch
diff --git a/toontown/building/DistributedElevatorInt.py b/toontown/building/DistributedElevatorInt.py
new file mode 100644
index 0000000..b36b686
--- /dev/null
+++ b/toontown/building/DistributedElevatorInt.py
@@ -0,0 +1,52 @@
+from pandac.PandaModules import *
+from direct.distributed.ClockDelta import *
+from direct.interval.IntervalGlobal import *
+from ElevatorConstants import *
+from ElevatorUtils import *
+import DistributedElevator
+from toontown.toonbase import ToontownGlobals
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from toontown.hood import ZoneUtil
+from toontown.toonbase import TTLocalizer
+
+class DistributedElevatorInt(DistributedElevator.DistributedElevator):
+
+ def __init__(self, cr):
+ DistributedElevator.DistributedElevator.__init__(self, cr)
+ self.countdownTime = base.config.GetFloat('int-elevator-timeout', INTERIOR_ELEVATOR_COUNTDOWN_TIME)
+
+ def setupElevator(self):
+ self.leftDoor = self.bldg.leftDoorOut
+ self.rightDoor = self.bldg.rightDoorOut
+ DistributedElevator.DistributedElevator.setupElevator(self)
+
+ def forcedExit(self, avId):
+ target_sz = base.localAvatar.defaultZone
+ base.cr.playGame.getPlace().fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(target_sz),
+ 'where': ZoneUtil.getWhereName(target_sz, 1),
+ 'how': 'teleportIn',
+ 'hoodId': target_sz,
+ 'zoneId': target_sz,
+ 'shardId': None,
+ 'avId': -1}], force=1)
+ return
+
+ def enterWaitCountdown(self, ts):
+ DistributedElevator.DistributedElevator.enterWaitCountdown(self, ts)
+ self.acceptOnce('localToonLeft', self.__handleTeleportOut)
+ self.startCountdownClock(self.countdownTime, ts)
+
+ def __handleTeleportOut(self):
+ self.sendUpdate('requestBuildingExit', [])
+
+ def exitWaitCountdown(self):
+ self.ignore('localToonLeft')
+ DistributedElevator.DistributedElevator.exitWaitCountdown(self)
+
+ def getZoneId(self):
+ return self.bldg.getZoneId()
+
+ def getElevatorModel(self):
+ return self.bldg.elevatorModelOut
diff --git a/toontown/building/DistributedElevatorIntAI.py b/toontown/building/DistributedElevatorIntAI.py
new file mode 100644
index 0000000..d535457
--- /dev/null
+++ b/toontown/building/DistributedElevatorIntAI.py
@@ -0,0 +1,140 @@
+from otp.ai.AIBase import *
+from toontown.toonbase import ToontownGlobals
+from direct.distributed.ClockDelta import *
+from ElevatorConstants import *
+import copy, DistributedElevatorAI
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from direct.task import Task
+from direct.directnotify import DirectNotifyGlobal
+from toontown.battle import BattleBase
+
+class DistributedElevatorIntAI(DistributedElevatorAI.DistributedElevatorAI):
+
+ def __init__(self, air, bldg, avIds):
+ DistributedElevatorAI.DistributedElevatorAI.__init__(self, air, bldg)
+ self.countdownTime = simbase.config.GetFloat('int-elevator-timeout', INTERIOR_ELEVATOR_COUNTDOWN_TIME)
+ self.avIds = copy.copy(avIds)
+ for avId in avIds:
+ self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleAllAvsUnexpectedExit, extraArgs=[avId])
+
+ def checkBoard(self, av):
+ result = 0
+ if av.doId not in self.avIds:
+ result = REJECT_NOSEAT
+ else:
+ result = DistributedElevatorAI.DistributedElevatorAI.checkBoard(self, av)
+ return result
+
+ def acceptBoarder(self, avId, seatIndex, wantBoardingShow=0):
+ DistributedElevatorAI.DistributedElevatorAI.acceptBoarder(self, avId, seatIndex, wantBoardingShow)
+ self.__closeIfNecessary()
+
+ def __closeIfNecessary(self):
+ numFullSeats = self.countFullSeats()
+ if not numFullSeats <= len(self.avIds):
+ self.notify.warning('we are about to crash. self.seats=%s self.avIds=%s' % (self.seats, self.avIds))
+ if numFullSeats == len(self.avIds):
+ self.fsm.request('allAboard')
+
+ def __handleAllAvsUnexpectedExit(self, avId):
+ self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly')
+ seatIndex = self.findAvatar(avId)
+ avIdCount = self.avIds.count(avId)
+ if avIdCount == 1:
+ self.avIds.remove(avId)
+ else:
+ if avIdCount == 0:
+ self.notify.warning("Strange... %d exited unexpectedly, but I don't have them on my list." % avId)
+ else:
+ self.notify.error('This list is screwed up! %s' % self.avIds)
+ if seatIndex == None:
+ self.notify.debug('%d is not boarded, but exited' % avId)
+ else:
+ self.clearFullNow(seatIndex)
+ self.clearEmptyNow(seatIndex)
+ self.__closeIfNecessary()
+ return
+
+ def acceptExiter(self, avId):
+ seatIndex = self.findAvatar(avId)
+ if seatIndex == None:
+ pass
+ else:
+ self.clearFullNow(seatIndex)
+ self.sendUpdate('emptySlot' + str(seatIndex), [
+ avId, 0, globalClockDelta.getRealNetworkTime(), self.countdownTime])
+ taskMgr.doMethodLater(TOON_EXIT_ELEVATOR_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs=(seatIndex,))
+ return
+
+ def d_forcedExit(self, avId):
+ self.sendUpdateToAvatarId(avId, 'forcedExit', [avId])
+
+ def requestBuildingExit(self):
+ avId = self.air.getAvatarIdFromSender()
+ self.notify.debug('requestBuildingExit from %d' % avId)
+ if self.accepting:
+ if avId in self.avIds:
+ self.avIds.remove(avId)
+ self.__closeIfNecessary()
+ else:
+ self.notify.warning('avId: %s not known, but tried to exit the building' % avId)
+
+ def enterOpening(self):
+ DistributedElevatorAI.DistributedElevatorAI.enterOpening(self)
+ taskMgr.doMethodLater(ElevatorData[ELEVATOR_NORMAL]['openTime'], self.waitCountdownTask, self.uniqueName('opening-timer'))
+
+ def waitCountdownTask(self, task):
+ self.fsm.request('waitCountdown')
+ return Task.done
+
+ def enterWaitCountdown(self):
+ DistributedElevatorAI.DistributedElevatorAI.enterWaitCountdown(self)
+ taskMgr.doMethodLater(self.countdownTime + BattleBase.SERVER_BUFFER_TIME, self.timeToGoTask, self.uniqueName('countdown-timer'))
+
+ def timeToGoTask(self, task):
+ self.allAboard()
+ return Task.done
+
+ def allAboard(self):
+ if len(self.avIds) == 0:
+ self.bldg.handleAllAboard([None, None, None, None])
+ else:
+ self.fsm.request('allAboard')
+ return
+
+ def enterAllAboard(self):
+ for avId in self.avIds:
+ if self.findAvatar(avId) == None:
+ self.d_forcedExit(avId)
+
+ DistributedElevatorAI.DistributedElevatorAI.enterAllAboard(self)
+ if self.timeOfBoarding != None:
+ currentTime = globalClock.getRealTime()
+ elapsedTime = currentTime - self.timeOfBoarding
+ self.notify.debug('elapsed time: ' + str(elapsedTime))
+ waitTime = max(TOON_BOARD_ELEVATOR_TIME - elapsedTime, 0)
+ taskMgr.doMethodLater(waitTime, self.closeTask, self.uniqueName('waitForAllAboard'))
+ else:
+ self.fsm.request('closing')
+ return
+
+ def closeTask(self, task):
+ self.fsm.request('closing')
+ return Task.done
+
+ def enterClosing(self):
+ DistributedElevatorAI.DistributedElevatorAI.enterClosing(self)
+ taskMgr.doMethodLater(ElevatorData[ELEVATOR_NORMAL]['closeTime'] + BattleBase.SERVER_BUFFER_TIME, self.elevatorClosedTask, self.uniqueName('closing-timer'))
+
+ def elevatorClosedTask(self, task):
+ self.fsm.request('closed')
+ return Task.done
+
+ def __doorsClosed(self):
+ self.bldg.handleAllAboard(self.seats)
+ self.fsm.request('closed')
+
+ def enterClosed(self):
+ DistributedElevatorAI.DistributedElevatorAI.enterClosed(self)
+ self.__doorsClosed()
diff --git a/toontown/building/DistributedGagshopInterior.py b/toontown/building/DistributedGagshopInterior.py
new file mode 100644
index 0000000..adc1ec2
--- /dev/null
+++ b/toontown/building/DistributedGagshopInterior.py
@@ -0,0 +1,95 @@
+from toontown.toonbase.ToonBaseGlobal import *
+from pandac.PandaModules import *
+from toontown.toonbase.ToontownGlobals import *
+import random
+from direct.distributed import DistributedObject
+from direct.directnotify import DirectNotifyGlobal
+import ToonInteriorColors
+from toontown.hood import ZoneUtil
+
+class DistributedGagshopInterior(DistributedObject.DistributedObject):
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.dnaStore = cr.playGame.dnaStore
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ self.setup()
+
+ def randomDNAItem(self, category, findFunc):
+ codeCount = self.dnaStore.getNumCatalogCodes(category)
+ index = self.randomGenerator.randint(0, codeCount - 1)
+ code = self.dnaStore.getCatalogCode(category, index)
+ return findFunc(code)
+
+ def replaceRandomInModel(self, model):
+ baseTag = 'random_'
+ npc = model.findAllMatches('**/' + baseTag + '???_*')
+ for i in range(npc.getNumPaths()):
+ np = npc.getPath(i)
+ name = np.getName()
+ b = len(baseTag)
+ category = name[b + 4:]
+ key1 = name[b]
+ key2 = name[b + 1]
+ if key1 == 'm':
+ model = self.randomDNAItem(category, self.dnaStore.findNode)
+ newNP = model.copyTo(np)
+ if key2 == 'r':
+ self.replaceRandomInModel(newNP)
+ elif key1 == 't':
+ texture = self.randomDNAItem(category, self.dnaStore.findTexture)
+ np.setTexture(texture, 100)
+ newNP = np
+ if key2 == 'c':
+ if category == 'TI_wallpaper' or category == 'TI_wallpaper_border':
+ self.randomGenerator.seed(self.zoneId)
+ newNP.setColorScale(self.randomGenerator.choice(self.colors[category]))
+ else:
+ newNP.setColorScale(self.randomGenerator.choice(self.colors[category]))
+
+ def setZoneIdAndBlock(self, zoneId, block):
+ self.zoneId = zoneId
+ self.block = block
+
+ def chooseDoor(self):
+ doorModelName = 'door_double_round_ul'
+ if doorModelName[-1:] == 'r':
+ doorModelName = doorModelName[:-1] + 'l'
+ else:
+ doorModelName = doorModelName[:-1] + 'r'
+ door = self.dnaStore.findNode(doorModelName)
+ return door
+
+ def setup(self):
+ self.dnaStore = base.cr.playGame.dnaStore
+ self.randomGenerator = random.Random()
+ self.randomGenerator.seed(self.zoneId)
+ self.interior = loader.loadModel('phase_4/models/modules/gagShop_interior')
+ self.interior.reparentTo(render)
+ hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId)
+ self.colors = ToonInteriorColors.colors[hoodId]
+ self.replaceRandomInModel(self.interior)
+ door = self.chooseDoor()
+ doorOrigin = render.find('**/door_origin;+s')
+ doorNP = door.copyTo(doorOrigin)
+ doorOrigin.setScale(0.8, 0.8, 0.8)
+ doorOrigin.setPos(doorOrigin, 0, -0.025, 0)
+ doorColor = self.randomGenerator.choice(self.colors['TI_door'])
+ DNADoor.setupDoor(doorNP, self.interior, doorOrigin, self.dnaStore, str(self.block), doorColor)
+ doorFrame = doorNP.find('door_*_flat')
+ doorFrame.wrtReparentTo(self.interior)
+ doorFrame.setColor(doorColor)
+ del self.colors
+ del self.dnaStore
+ del self.randomGenerator
+ self.interior.flattenMedium()
+
+ def disable(self):
+ self.interior.removeNode()
+ del self.interior
+ DistributedObject.DistributedObject.disable(self)
diff --git a/toontown/building/DistributedGagshopInteriorAI.py b/toontown/building/DistributedGagshopInteriorAI.py
new file mode 100644
index 0000000..4f7b6b7
--- /dev/null
+++ b/toontown/building/DistributedGagshopInteriorAI.py
@@ -0,0 +1,14 @@
+from direct.distributed import DistributedObjectAI
+from direct.directnotify import DirectNotifyGlobal
+
+class DistributedGagshopInteriorAI(DistributedObjectAI.DistributedObjectAI):
+
+ def __init__(self, block, air, zoneId):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ self.block = block
+ self.zoneId = zoneId
+
+ def getZoneIdAndBlock(self):
+ r = [
+ self.zoneId, self.block]
+ return r
diff --git a/toontown/building/DistributedHQInterior.py b/toontown/building/DistributedHQInterior.py
new file mode 100644
index 0000000..c0f0316
--- /dev/null
+++ b/toontown/building/DistributedHQInterior.py
@@ -0,0 +1,216 @@
+from toontown.toonbase.ToonBaseGlobal import *
+from pandac.PandaModules import *
+from toontown.toonbase.ToontownGlobals import *
+import random
+from direct.task.Task import Task
+from direct.distributed import DistributedObject
+from direct.directnotify import DirectNotifyGlobal
+import ToonInteriorColors
+import cPickle
+from toontown.toonbase import TTLocalizer
+
+class DistributedHQInterior(DistributedObject.DistributedObject):
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.dnaStore = cr.playGame.dnaStore
+ self.leaderAvIds = []
+ self.leaderNames = []
+ self.leaderScores = []
+ self.numLeaders = 10
+ self.tutorial = 0
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+ self.interior = loader.loadModel('phase_3.5/models/modules/HQ_interior')
+ self.interior.reparentTo(render)
+ self.interior.find('**/cream').hide()
+ self.interior.find('**/crashed_piano').hide()
+ self.buildLeaderBoard()
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ self.setupDoors()
+ self.interior.flattenMedium()
+ emptyBoard = self.interior.find('**/empty_board')
+ self.leaderBoard.reparentTo(emptyBoard.getChild(0))
+
+ def setTutorial(self, flag):
+ if self.tutorial == flag:
+ return
+ else:
+ self.tutorial = flag
+ if self.tutorial:
+ self.interior.find('**/periscope').hide()
+ self.interior.find('**/speakers').hide()
+ else:
+ self.interior.find('**/periscope').show()
+ self.interior.find('**/speakers').show()
+
+ def setZoneIdAndBlock(self, zoneId, block):
+ self.zoneId = zoneId
+ self.block = block
+
+ def buildLeaderBoard(self):
+ self.leaderBoard = hidden.attachNewNode('leaderBoard')
+ self.leaderBoard.setPosHprScale(0.1, 0, 4.5, 90, 0, 0, 0.9, 0.9, 0.9)
+ z = 0
+ row = self.buildTitleRow()
+ row.reparentTo(self.leaderBoard)
+ row.setPos(0, 0, z)
+ z -= 1
+ self.nameTextNodes = []
+ self.scoreTextNodes = []
+ self.trophyStars = []
+ for i in range(self.numLeaders):
+ row, nameText, scoreText, trophyStar = self.buildLeaderRow()
+ self.nameTextNodes.append(nameText)
+ self.scoreTextNodes.append(scoreText)
+ self.trophyStars.append(trophyStar)
+ row.reparentTo(self.leaderBoard)
+ row.setPos(0, 0, z)
+ z -= 1
+
+ def updateLeaderBoard(self):
+ taskMgr.remove(self.uniqueName('starSpinHQ'))
+ for i in range(len(self.leaderNames)):
+ name = self.leaderNames[i]
+ score = self.leaderScores[i]
+ self.nameTextNodes[i].setText(name)
+ self.scoreTextNodes[i].setText(str(score))
+ self.updateTrophyStar(self.trophyStars[i], score)
+
+ for i in range(len(self.leaderNames), self.numLeaders):
+ self.nameTextNodes[i].setText('-')
+ self.scoreTextNodes[i].setText('-')
+ self.trophyStars[i].hide()
+
+ def buildTitleRow(self):
+ row = hidden.attachNewNode('leaderRow')
+ nameText = TextNode('titleRow')
+ nameText.setFont(ToontownGlobals.getSignFont())
+ nameText.setAlign(TextNode.ACenter)
+ nameText.setTextColor(0.5, 0.75, 0.7, 1)
+ nameText.setText(TTLocalizer.LeaderboardTitle)
+ namePath = row.attachNewNode(nameText)
+ namePath.setPos(0, 0, 0)
+ return row
+
+ def buildLeaderRow(self):
+ row = hidden.attachNewNode('leaderRow')
+ nameText = TextNode('nameText')
+ nameText.setFont(ToontownGlobals.getToonFont())
+ nameText.setAlign(TextNode.ALeft)
+ nameText.setTextColor(1, 1, 1, 0.7)
+ nameText.setText('-')
+ namePath = row.attachNewNode(nameText)
+ namePath.setPos(*TTLocalizer.DHQInamePathPos)
+ namePath.setScale(TTLocalizer.DHQInamePath)
+ scoreText = TextNode('scoreText')
+ scoreText.setFont(ToontownGlobals.getToonFont())
+ scoreText.setAlign(TextNode.ARight)
+ scoreText.setTextColor(1, 1, 0.1, 0.7)
+ scoreText.setText('-')
+ scorePath = row.attachNewNode(scoreText)
+ scorePath.setPos(*TTLocalizer.DHQIscorePathPos)
+ trophyStar = self.buildTrophyStar()
+ trophyStar.reparentTo(row)
+ return (row,
+ nameText,
+ scoreText,
+ trophyStar)
+
+ def setLeaderBoard(self, leaderData):
+ avIds, names, scores = cPickle.loads(leaderData)
+ self.notify.debug('setLeaderBoard: avIds: %s, names: %s, scores: %s' % (avIds, names, scores))
+ self.leaderAvIds = avIds
+ self.leaderNames = names
+ self.leaderScores = scores
+ self.updateLeaderBoard()
+
+ def chooseDoor(self):
+ doorModelName = 'door_double_round_ul'
+ if doorModelName[-1:] == 'r':
+ doorModelName = doorModelName[:-1] + 'l'
+ else:
+ doorModelName = doorModelName[:-1] + 'r'
+ door = self.dnaStore.findNode(doorModelName)
+ return door
+
+ def setupDoors(self):
+ self.randomGenerator = random.Random()
+ self.randomGenerator.seed(self.zoneId)
+ self.colors = ToonInteriorColors.colors[ToontownCentral]
+ door = self.chooseDoor()
+ doorOrigins = render.findAllMatches('**/door_origin*')
+ numDoorOrigins = doorOrigins.getNumPaths()
+ for npIndex in range(numDoorOrigins):
+ doorOrigin = doorOrigins[npIndex]
+ doorOriginNPName = doorOrigin.getName()
+ doorOriginIndexStr = doorOriginNPName[len('door_origin_'):]
+ newNode = ModelNode('door_' + doorOriginIndexStr)
+ newNodePath = NodePath(newNode)
+ newNodePath.reparentTo(self.interior)
+ doorNP = door.copyTo(newNodePath)
+ doorOrigin.setScale(0.8, 0.8, 0.8)
+ doorOrigin.setPos(doorOrigin, 0, -0.025, 0)
+ doorColor = self.randomGenerator.choice(self.colors['TI_door'])
+ triggerId = str(self.block) + '_' + doorOriginIndexStr
+ DNADoor.setupDoor(doorNP, newNodePath, doorOrigin, self.dnaStore, triggerId, doorColor)
+ doorFrame = doorNP.find('door_*_flat')
+ doorFrame.setColor(doorColor)
+
+ del self.dnaStore
+ del self.randomGenerator
+
+ def disable(self):
+ self.leaderBoard.removeNode()
+ del self.leaderBoard
+ self.interior.removeNode()
+ del self.interior
+ del self.nameTextNodes
+ del self.scoreTextNodes
+ del self.trophyStars
+ taskMgr.remove(self.uniqueName('starSpinHQ'))
+ DistributedObject.DistributedObject.disable(self)
+
+ def buildTrophyStar(self):
+ trophyStar = loader.loadModel('phase_3.5/models/gui/name_star')
+ trophyStar.hide()
+ trophyStar.setPos(*TTLocalizer.DHQItrophyStarPos)
+ return trophyStar
+
+ def updateTrophyStar(self, trophyStar, score):
+ scale = 0.8
+ if score >= ToontownGlobals.TrophyStarLevels[4]:
+ trophyStar.show()
+ trophyStar.setScale(scale)
+ trophyStar.setColor(ToontownGlobals.TrophyStarColors[4])
+ if score >= ToontownGlobals.TrophyStarLevels[5]:
+ task = taskMgr.add(self.__starSpin, self.uniqueName('starSpinHQ'))
+ task.trophyStarSpeed = 15
+ task.trophyStar = trophyStar
+ elif score >= ToontownGlobals.TrophyStarLevels[2]:
+ trophyStar.show()
+ trophyStar.setScale(0.75 * scale)
+ trophyStar.setColor(ToontownGlobals.TrophyStarColors[2])
+ if score >= ToontownGlobals.TrophyStarLevels[3]:
+ task = taskMgr.add(self.__starSpin, self.uniqueName('starSpinHQ'))
+ task.trophyStarSpeed = 10
+ task.trophyStar = trophyStar
+ elif score >= ToontownGlobals.TrophyStarLevels[0]:
+ trophyStar.show()
+ trophyStar.setScale(0.75 * scale)
+ trophyStar.setColor(ToontownGlobals.TrophyStarColors[0])
+ if score >= ToontownGlobals.TrophyStarLevels[1]:
+ task = taskMgr.add(self.__starSpin, self.uniqueName('starSpinHQ'))
+ task.trophyStarSpeed = 8
+ task.trophyStar = trophyStar
+ else:
+ trophyStar.hide()
+
+ def __starSpin(self, task):
+ now = globalClock.getFrameTime()
+ r = now * task.trophyStarSpeed % 360.0
+ task.trophyStar.setR(r)
+ return Task.cont
diff --git a/toontown/building/DistributedHQInteriorAI.py b/toontown/building/DistributedHQInteriorAI.py
new file mode 100644
index 0000000..3863f05
--- /dev/null
+++ b/toontown/building/DistributedHQInteriorAI.py
@@ -0,0 +1,49 @@
+from direct.distributed import DistributedObjectAI
+from direct.directnotify import DirectNotifyGlobal
+import cPickle
+
+class DistributedHQInteriorAI(DistributedObjectAI.DistributedObjectAI):
+
+ def __init__(self, block, air, zoneId):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ self.block = block
+ self.zoneId = zoneId
+ self.tutorial = 0
+ self.isDirty = False
+ self.accept('leaderboardChanged', self.leaderboardChanged)
+ self.accept('leaderboardFlush', self.leaderboardFlush)
+
+ def delete(self):
+ self.ignore('leaderboardChanged')
+ self.ignore('leaderboardFlush')
+ self.ignore('setLeaderBoard')
+ self.ignore('AIStarted')
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def getZoneIdAndBlock(self):
+ r = [
+ self.zoneId, self.block]
+ return r
+
+ def leaderboardChanged(self):
+ self.isDirty = True
+
+ def leaderboardFlush(self):
+ if self.isDirty:
+ self.sendNewLeaderBoard()
+
+ def sendNewLeaderBoard(self):
+ if self.air:
+ self.isDirty = False
+ self.sendUpdate('setLeaderBoard', [cPickle.dumps(self.air.trophyMgr.getLeaderInfo(), 1)])
+
+ def getLeaderBoard(self):
+ return cPickle.dumps(self.air.trophyMgr.getLeaderInfo(), 1)
+
+ def getTutorial(self):
+ return self.tutorial
+
+ def setTutorial(self, flag):
+ if self.tutorial != flag:
+ self.tutorial = flag
+ self.sendUpdate('setTutorial', [self.tutorial])
diff --git a/toontown/building/DistributedKartShopInterior.py b/toontown/building/DistributedKartShopInterior.py
new file mode 100644
index 0000000..4c9c519
--- /dev/null
+++ b/toontown/building/DistributedKartShopInterior.py
@@ -0,0 +1,37 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed.DistributedObject import DistributedObject
+from pandac.PandaModules import *
+from toontown.building import ToonInteriorColors
+from toontown.hood import ZoneUtil
+from toontown.toonbase.ToonBaseGlobal import *
+from toontown.toonbase.ToontownGlobals import *
+if (__debug__):
+ import pdb
+
+class DistributedKartShopInterior(DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedKartShopInterior')
+
+ def __init__(self, cr):
+ DistributedObject.__init__(self, cr)
+ self.dnaStore = cr.playGame.dnaStore
+
+ def generate(self):
+ DistributedObject.generate(self)
+
+ def announceGenerate(self):
+ DistributedObject.announceGenerate(self)
+ self.__handleInteriorSetup()
+
+ def disable(self):
+ self.interior.removeNode()
+ del self.interior
+ DistributedObject.disable(self)
+
+ def setZoneIdAndBlock(self, zoneId, block):
+ self.zoneId = zoneId
+ self.block = block
+
+ def __handleInteriorSetup(self):
+ self.interior = loader.loadModel('phase_6/models/karting/KartShop_Interior')
+ self.interior.reparentTo(render)
+ self.interior.flattenMedium()
diff --git a/toontown/building/DistributedKartShopInteriorAI.py b/toontown/building/DistributedKartShopInteriorAI.py
new file mode 100644
index 0000000..a91f517
--- /dev/null
+++ b/toontown/building/DistributedKartShopInteriorAI.py
@@ -0,0 +1,17 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed.DistributedObjectAI import DistributedObjectAI
+
+class DistributedKartShopInteriorAI(DistributedObjectAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedKartShopInteriorAI')
+
+ def __init__(self, block, air, zoneId):
+ DistributedObjectAI.__init__(self, air)
+ self.block = block
+ self.zoneId = zoneId
+
+ def generate(self):
+ DistributedObjectAI.generate(self)
+
+ def getZoneIdAndBlock(self):
+ return [
+ self.zoneId, self.block]
diff --git a/toontown/building/DistributedKnockKnockDoor.py b/toontown/building/DistributedKnockKnockDoor.py
new file mode 100644
index 0000000..b8c57a7
--- /dev/null
+++ b/toontown/building/DistributedKnockKnockDoor.py
@@ -0,0 +1,145 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from direct.distributed.ClockDelta import *
+from KnockKnockJokes import *
+from toontown.toonbase import ToontownGlobals
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+import DistributedAnimatedProp
+from toontown.distributed import DelayDelete
+from toontown.toonbase import TTLocalizer
+from toontown.hood import ZoneUtil
+
+class DistributedKnockKnockDoor(DistributedAnimatedProp.DistributedAnimatedProp):
+
+ def __init__(self, cr):
+ DistributedAnimatedProp.DistributedAnimatedProp.__init__(self, cr)
+ self.fsm.setName('DistributedKnockKnockDoor')
+ self.rimshot = None
+ self.knockSfx = None
+ return
+
+ def generate(self):
+ DistributedAnimatedProp.DistributedAnimatedProp.generate(self)
+ self.avatarTracks = []
+ self.avatarId = 0
+
+ def announceGenerate(self):
+ DistributedAnimatedProp.DistributedAnimatedProp.announceGenerate(self)
+ self.accept('exitKnockKnockDoorSphere_' + str(self.propId), self.exitTrigger)
+ self.acceptAvatar()
+
+ def disable(self):
+ self.ignore('exitKnockKnockDoorSphere_' + str(self.propId))
+ self.ignore('enterKnockKnockDoorSphere_' + str(self.propId))
+ DistributedAnimatedProp.DistributedAnimatedProp.disable(self)
+
+ def delete(self):
+ DistributedAnimatedProp.DistributedAnimatedProp.delete(self)
+ if self.rimshot:
+ self.rimshot = None
+ if self.knockSfx:
+ self.knockSfx = None
+ return
+
+ def acceptAvatar(self):
+ self.acceptOnce('enterKnockKnockDoorSphere_' + str(self.propId), self.enterTrigger)
+
+ def setAvatarInteract(self, avatarId):
+ DistributedAnimatedProp.DistributedAnimatedProp.setAvatarInteract(self, avatarId)
+
+ def avatarExit(self, avatarId):
+ if avatarId == self.avatarId:
+ for track in self.avatarTracks:
+ track.finish()
+ DelayDelete.cleanupDelayDeletes(track)
+
+ self.avatarTracks = []
+
+ def knockKnockTrack(self, avatar, duration):
+ if avatar == None:
+ return
+ self.rimshot = base.loadSfx('phase_5/audio/sfx/AA_heal_telljoke.mp3')
+ self.knockSfx = base.loadSfx('phase_5/audio/sfx/GUI_knock_3.mp3')
+ joke = KnockKnockJokes[self.propId % len(KnockKnockJokes)]
+ place = base.cr.playGame.getPlace()
+ if place:
+ zone = place.getZoneId()
+ branch = ZoneUtil.getBranchZone(zone)
+ if branch == ToontownGlobals.SillyStreet:
+ if self.propId == 44:
+ joke = KnockKnockContestJokes[ToontownGlobals.SillyStreet]
+ elif branch == ToontownGlobals.LoopyLane:
+ if self.propId in KnockKnockContestJokes[ToontownGlobals.LoopyLane].keys():
+ joke = KnockKnockContestJokes[ToontownGlobals.LoopyLane][self.propId]
+ elif branch == ToontownGlobals.PunchlinePlace:
+ if self.propId == 1:
+ joke = KnockKnockContestJokes[ToontownGlobals.PunchlinePlace]
+ elif branch == ToontownGlobals.PolarPlace:
+ if self.propId in KnockKnockContestJokes[ToontownGlobals.PolarPlace].keys():
+ joke = KnockKnockContestJokes[ToontownGlobals.PolarPlace][self.propId]
+ self.nametag = None
+ self.nametagNP = None
+ doorNP = render.find('**/KnockKnockDoorSphere_' + str(self.propId) + ';+s')
+ if doorNP.isEmpty():
+ self.notify.warning('Could not find KnockKnockDoorSphere_%s' % self.propId)
+ return
+ self.nametag = NametagGroup()
+ self.nametag.setAvatar(doorNP)
+ self.nametag.setFont(ToontownGlobals.getToonFont())
+ self.nametag.setName(TTLocalizer.DoorNametag)
+ self.nametag.setActive(0)
+ self.nametag.manage(base.marginManager)
+ self.nametag.getNametag3d().setBillboardOffset(4)
+ nametagNode = self.nametag.getNametag3d().upcastToPandaNode()
+ self.nametagNP = render.attachNewNode(nametagNode)
+ self.nametagNP.setName('knockKnockDoor_nt_' + str(self.propId))
+ pos = doorNP.node().getSolid(0).getCenter()
+ self.nametagNP.setPos(pos + Vec3(0, 0, avatar.getHeight() + 2))
+ d = duration * 0.125
+ track = Sequence(Parallel(Sequence(Wait(d * 0.5), SoundInterval(self.knockSfx)), Func(self.nametag.setChat, TTLocalizer.DoorKnockKnock, CFSpeech), Wait(d)), Func(avatar.setChatAbsolute, TTLocalizer.DoorWhosThere, CFSpeech | CFTimeout, openEnded=0), Wait(d), Func(self.nametag.setChat, joke[0], CFSpeech), Wait(d), Func(avatar.setChatAbsolute, joke[0] + TTLocalizer.DoorWhoAppendix, CFSpeech | CFTimeout, openEnded=0), Wait(d), Func(self.nametag.setChat, joke[1], CFSpeech), Parallel(SoundInterval(self.rimshot, startTime=2.0), Wait(d * 4)), Func(self.cleanupTrack))
+ track.delayDelete = DelayDelete.DelayDelete(avatar, 'knockKnockTrack')
+ return track
+
+ def cleanupTrack(self):
+ avatar = self.cr.doId2do.get(self.avatarId, None)
+ if avatar:
+ avatar.clearChat()
+ if self.nametag:
+ self.nametag.unmanage(base.marginManager)
+ self.nametagNP.removeNode()
+ self.nametag = None
+ self.nametagNP = None
+ return
+
+ def enterOff(self):
+ DistributedAnimatedProp.DistributedAnimatedProp.enterOff(self)
+
+ def exitOff(self):
+ DistributedAnimatedProp.DistributedAnimatedProp.exitOff(self)
+
+ def enterAttract(self, ts):
+ DistributedAnimatedProp.DistributedAnimatedProp.enterAttract(self, ts)
+ self.acceptAvatar()
+
+ def exitAttract(self):
+ DistributedAnimatedProp.DistributedAnimatedProp.exitAttract(self)
+
+ def enterPlaying(self, ts):
+ DistributedAnimatedProp.DistributedAnimatedProp.enterPlaying(self, ts)
+ if self.avatarId:
+ avatar = self.cr.doId2do.get(self.avatarId, None)
+ track = self.knockKnockTrack(avatar, 8)
+ if track != None:
+ track.start(ts)
+ self.avatarTracks.append(track)
+ return
+
+ def exitPlaying(self):
+ DistributedAnimatedProp.DistributedAnimatedProp.exitPlaying(self)
+ for track in self.avatarTracks:
+ track.finish()
+ DelayDelete.cleanupDelayDeletes(track)
+
+ self.avatarTracks = []
+ self.avatarId = 0
diff --git a/toontown/building/DistributedKnockKnockDoorAI.py b/toontown/building/DistributedKnockKnockDoorAI.py
new file mode 100644
index 0000000..27727f3
--- /dev/null
+++ b/toontown/building/DistributedKnockKnockDoorAI.py
@@ -0,0 +1,42 @@
+from otp.ai.AIBaseGlobal import *
+from direct.distributed.ClockDelta import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+import DistributedAnimatedPropAI
+from direct.task.Task import Task
+from direct.fsm import State
+
+class DistributedKnockKnockDoorAI(DistributedAnimatedPropAI.DistributedAnimatedPropAI):
+
+ def __init__(self, air, propId):
+ DistributedAnimatedPropAI.DistributedAnimatedPropAI.__init__(self, air, propId)
+ self.fsm.setName('DistributedKnockKnockDoor')
+ self.propId = propId
+ self.doLaterTask = None
+ return
+
+ def enterOff(self):
+ DistributedAnimatedPropAI.DistributedAnimatedPropAI.enterOff(self)
+
+ def exitOff(self):
+ DistributedAnimatedPropAI.DistributedAnimatedPropAI.exitOff(self)
+
+ def attractTask(self, task):
+ self.fsm.request('attract')
+ return Task.done
+
+ def enterAttract(self):
+ DistributedAnimatedPropAI.DistributedAnimatedPropAI.enterAttract(self)
+
+ def exitAttract(self):
+ DistributedAnimatedPropAI.DistributedAnimatedPropAI.exitAttract(self)
+
+ def enterPlaying(self):
+ DistributedAnimatedPropAI.DistributedAnimatedPropAI.enterPlaying(self)
+ self.doLaterTask = taskMgr.doMethodLater(9, self.attractTask, self.uniqueName('knockKnock-timer'))
+
+ def exitPlaying(self):
+ DistributedAnimatedPropAI.DistributedAnimatedPropAI.exitPlaying(self)
+ taskMgr.remove(self.doLaterTask)
+ self.doLaterTask = None
+ return
diff --git a/toontown/building/DistributedPetshopInterior.py b/toontown/building/DistributedPetshopInterior.py
new file mode 100644
index 0000000..3e3a73b
--- /dev/null
+++ b/toontown/building/DistributedPetshopInterior.py
@@ -0,0 +1,106 @@
+from toontown.toonbase.ToonBaseGlobal import *
+from pandac.PandaModules import *
+from toontown.toonbase.ToontownGlobals import *
+import random
+from direct.distributed import DistributedObject
+from direct.directnotify import DirectNotifyGlobal
+from direct.actor import Actor
+import ToonInteriorColors
+from toontown.hood import ZoneUtil
+
+class DistributedPetshopInterior(DistributedObject.DistributedObject):
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.dnaStore = cr.playGame.dnaStore
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ self.setup()
+
+ def randomDNAItem(self, category, findFunc):
+ codeCount = self.dnaStore.getNumCatalogCodes(category)
+ index = self.randomGenerator.randint(0, codeCount - 1)
+ code = self.dnaStore.getCatalogCode(category, index)
+ return findFunc(code)
+
+ def replaceRandomInModel(self, model):
+ baseTag = 'random_'
+ npc = model.findAllMatches('**/' + baseTag + '???_*')
+ for i in range(npc.getNumPaths()):
+ np = npc.getPath(i)
+ name = np.getName()
+ b = len(baseTag)
+ category = name[b + 4:]
+ key1 = name[b]
+ key2 = name[b + 1]
+ if key1 == 'm':
+ model = self.randomDNAItem(category, self.dnaStore.findNode)
+ newNP = model.copyTo(np)
+ if key2 == 'r':
+ self.replaceRandomInModel(newNP)
+ elif key1 == 't':
+ texture = self.randomDNAItem(category, self.dnaStore.findTexture)
+ np.setTexture(texture, 100)
+ newNP = np
+ if key2 == 'c':
+ if category == 'TI_wallpaper' or category == 'TI_wallpaper_border':
+ self.randomGenerator.seed(self.zoneId)
+ newNP.setColorScale(self.randomGenerator.choice(self.colors[category]))
+ else:
+ newNP.setColorScale(self.randomGenerator.choice(self.colors[category]))
+
+ def setZoneIdAndBlock(self, zoneId, block):
+ self.zoneId = zoneId
+ self.block = block
+
+ def chooseDoor(self):
+ doorModelName = 'door_double_round_ul'
+ if doorModelName[-1:] == 'r':
+ doorModelName = doorModelName[:-1] + 'l'
+ else:
+ doorModelName = doorModelName[:-1] + 'r'
+ door = self.dnaStore.findNode(doorModelName)
+ return door
+
+ def setup(self):
+ self.dnaStore = base.cr.playGame.dnaStore
+ self.randomGenerator = random.Random()
+ self.randomGenerator.seed(self.zoneId)
+ self.interior = loader.loadModel('phase_4/models/modules/PetShopInterior')
+ self.interior.reparentTo(render)
+ self.fish = Actor.Actor('phase_4/models/props/interiorfish-zero', {'swim': 'phase_4/models/props/interiorfish-swim'})
+ self.fish.reparentTo(self.interior)
+ self.fish.setColorScale(0.8, 0.9, 1, 0.8)
+ self.fish.setScale(0.8)
+ self.fish.setPos(0, 6, -4)
+ self.fish.setPlayRate(0.7, 'swim')
+ self.fish.loop('swim')
+ hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId)
+ self.colors = ToonInteriorColors.colors[hoodId]
+ self.replaceRandomInModel(self.interior)
+ door = self.chooseDoor()
+ doorOrigin = render.find('**/door_origin;+s')
+ doorNP = door.copyTo(doorOrigin)
+ doorOrigin.setScale(0.8, 0.8, 0.8)
+ doorOrigin.setPos(doorOrigin, 0, -0.25, 0)
+ doorColor = self.randomGenerator.choice(self.colors['TI_door'])
+ DNADoor.setupDoor(doorNP, self.interior, doorOrigin, self.dnaStore, str(self.block), doorColor)
+ doorFrame = doorNP.find('door_*_flat')
+ doorFrame.wrtReparentTo(self.interior)
+ doorFrame.setColor(doorColor)
+ del self.colors
+ del self.dnaStore
+ del self.randomGenerator
+ self.interior.flattenMedium()
+
+ def disable(self):
+ self.fish.stop()
+ self.fish.cleanup()
+ del self.fish
+ self.interior.removeNode()
+ del self.interior
+ DistributedObject.DistributedObject.disable(self)
diff --git a/toontown/building/DistributedPetshopInteriorAI.py b/toontown/building/DistributedPetshopInteriorAI.py
new file mode 100644
index 0000000..baf628e
--- /dev/null
+++ b/toontown/building/DistributedPetshopInteriorAI.py
@@ -0,0 +1,17 @@
+from direct.distributed import DistributedObjectAI
+from direct.directnotify import DirectNotifyGlobal
+
+class DistributedPetshopInteriorAI(DistributedObjectAI.DistributedObjectAI):
+
+ def __init__(self, block, air, zoneId):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ self.block = block
+ self.zoneId = zoneId
+
+ def generate(self):
+ DistributedObjectAI.DistributedObjectAI.generate(self)
+
+ def getZoneIdAndBlock(self):
+ r = [
+ self.zoneId, self.block]
+ return r
diff --git a/toontown/building/DistributedSuitInterior.py b/toontown/building/DistributedSuitInterior.py
new file mode 100644
index 0000000..78bbf5e
--- /dev/null
+++ b/toontown/building/DistributedSuitInterior.py
@@ -0,0 +1,385 @@
+from direct.interval.IntervalGlobal import *
+from direct.distributed.ClockDelta import *
+from ElevatorConstants import *
+import ElevatorUtils
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import ToontownBattleGlobals
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObject
+from direct.fsm import State
+from toontown.battle import BattleBase
+from toontown.hood import ZoneUtil
+
+class DistributedSuitInterior(DistributedObject.DistributedObject):
+ id = 0
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.toons = []
+ self.activeIntervals = {}
+ self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.mp3')
+ self.closeSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_close.mp3')
+ self.suits = []
+ self.reserveSuits = []
+ self.joiningReserves = []
+ self.distBldgDoId = None
+ self.currentFloor = -1
+ self.numFloors = None
+ self.elevatorName = self.__uniqueName('elevator')
+ self.floorModel = None
+ self.elevatorOutOpen = 0
+ self.BottomFloor_SuitPositions = [Point3(0, 15, 0),
+ Point3(10, 20, 0),
+ Point3(-7, 24, 0),
+ Point3(-10, 0, 0)]
+ self.BottomFloor_SuitHs = [75,
+ 170,
+ -91,
+ -44]
+ self.Cubicle_SuitPositions = [Point3(0, 18, 0),
+ Point3(10, 12, 0),
+ Point3(-9, 11, 0),
+ Point3(-3, 13, 0)]
+ self.Cubicle_SuitHs = [170,
+ 56,
+ -52,
+ 10]
+ self.BossOffice_SuitPositions = [Point3(0, 15, 0),
+ Point3(10, 20, 0),
+ Point3(-10, 6, 0),
+ Point3(-17, 34, 11)]
+ self.BossOffice_SuitHs = [170,
+ 120,
+ 12,
+ 38]
+ self.waitMusic = base.loadMusic('phase_7/audio/bgm/encntr_toon_winning_indoor.mid')
+ self.elevatorMusic = base.loadMusic('phase_7/audio/bgm/tt_elevator.mid')
+ self.fsm = ClassicFSM.ClassicFSM('DistributedSuitInterior', [State.State('WaitForAllToonsInside', self.enterWaitForAllToonsInside, self.exitWaitForAllToonsInside, ['Elevator']),
+ State.State('Elevator', self.enterElevator, self.exitElevator, ['Battle']),
+ State.State('Battle', self.enterBattle, self.exitBattle, ['Resting', 'Reward', 'ReservesJoining']),
+ State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, ['Battle']),
+ State.State('Resting', self.enterResting, self.exitResting, ['Elevator']),
+ State.State('Reward', self.enterReward, self.exitReward, ['Off']),
+ State.State('Off', self.enterOff, self.exitOff, ['Elevator', 'WaitForAllToonsInside', 'Battle'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ return
+
+ def __uniqueName(self, name):
+ DistributedSuitInterior.id += 1
+ return name + '%d' % DistributedSuitInterior.id
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+ self.announceGenerateName = self.uniqueName('generate')
+ self.accept(self.announceGenerateName, self.handleAnnounceGenerate)
+ self.elevatorModelIn = loader.loadModel('phase_4/models/modules/elevator')
+ self.leftDoorIn = self.elevatorModelIn.find('**/left-door')
+ self.rightDoorIn = self.elevatorModelIn.find('**/right-door')
+ self.elevatorModelOut = loader.loadModel('phase_4/models/modules/elevator')
+ self.leftDoorOut = self.elevatorModelOut.find('**/left-door')
+ self.rightDoorOut = self.elevatorModelOut.find('**/right-door')
+
+ def setElevatorLights(self, elevatorModel):
+ npc = elevatorModel.findAllMatches('**/floor_light_?;+s')
+ for i in range(npc.getNumPaths()):
+ np = npc.getPath(i)
+ floor = int(np.getName()[-1:]) - 1
+ if floor == self.currentFloor:
+ np.setColor(LIGHT_ON_COLOR)
+ elif floor < self.numFloors:
+ np.setColor(LIGHT_OFF_COLOR)
+ else:
+ np.hide()
+
+ def handleAnnounceGenerate(self, obj):
+ self.ignore(self.announceGenerateName)
+ self.sendUpdate('setAvatarJoined', [])
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ self.__cleanupIntervals()
+ self.ignoreAll()
+ self.__cleanup()
+ DistributedObject.DistributedObject.disable(self)
+
+ def delete(self):
+ del self.waitMusic
+ del self.elevatorMusic
+ del self.openSfx
+ del self.closeSfx
+ del self.fsm
+ base.localAvatar.inventory.setBattleCreditMultiplier(1)
+ DistributedObject.DistributedObject.delete(self)
+
+ def __cleanup(self):
+ self.toons = []
+ self.suits = []
+ self.reserveSuits = []
+ self.joiningReserves = []
+ if self.elevatorModelIn != None:
+ self.elevatorModelIn.removeNode()
+ if self.elevatorModelOut != None:
+ self.elevatorModelOut.removeNode()
+ if self.floorModel != None:
+ self.floorModel.removeNode()
+ self.leftDoorIn = None
+ self.rightDoorIn = None
+ self.leftDoorOut = None
+ self.rightDoorOut = None
+ return
+
+ def __addToon(self, toon):
+ self.accept(toon.uniqueName('disable'), self.__handleUnexpectedExit, extraArgs=[toon])
+
+ def __handleUnexpectedExit(self, toon):
+ self.notify.warning('handleUnexpectedExit() - toon: %d' % toon.doId)
+ self.__removeToon(toon, unexpected=1)
+
+ def __removeToon(self, toon, unexpected = 0):
+ if self.toons.count(toon) == 1:
+ self.toons.remove(toon)
+ self.ignore(toon.uniqueName('disable'))
+
+ def __finishInterval(self, name):
+ if self.activeIntervals.has_key(name):
+ interval = self.activeIntervals[name]
+ if interval.isPlaying():
+ interval.finish()
+
+ def __cleanupIntervals(self):
+ for interval in self.activeIntervals.values():
+ interval.finish()
+
+ self.activeIntervals = {}
+
+ def __closeInElevator(self):
+ self.leftDoorIn.setPos(3.5, 0, 0)
+ self.rightDoorIn.setPos(-3.5, 0, 0)
+
+ def getZoneId(self):
+ return self.zoneId
+
+ def setZoneId(self, zoneId):
+ self.zoneId = zoneId
+
+ def getExtZoneId(self):
+ return self.extZoneId
+
+ def setExtZoneId(self, extZoneId):
+ self.extZoneId = extZoneId
+
+ def getDistBldgDoId(self):
+ return self.distBldgDoId
+
+ def setDistBldgDoId(self, distBldgDoId):
+ self.distBldgDoId = distBldgDoId
+
+ def setNumFloors(self, numFloors):
+ self.numFloors = numFloors
+
+ def setToons(self, toonIds, hack):
+ self.toonIds = toonIds
+ oldtoons = self.toons
+ self.toons = []
+ for toonId in toonIds:
+ if toonId != 0:
+ if self.cr.doId2do.has_key(toonId):
+ toon = self.cr.doId2do[toonId]
+ toon.stopSmooth()
+ self.toons.append(toon)
+ if oldtoons.count(toon) == 0:
+ self.__addToon(toon)
+ else:
+ self.notify.warning('setToons() - no toon: %d' % toonId)
+
+ for toon in oldtoons:
+ if self.toons.count(toon) == 0:
+ self.__removeToon(toon)
+
+ def setSuits(self, suitIds, reserveIds, values):
+ oldsuits = self.suits
+ self.suits = []
+ self.joiningReserves = []
+ for suitId in suitIds:
+ if self.cr.doId2do.has_key(suitId):
+ suit = self.cr.doId2do[suitId]
+ self.suits.append(suit)
+ suit.fsm.request('Battle')
+ suit.buildingSuit = 1
+ suit.reparentTo(render)
+ if oldsuits.count(suit) == 0:
+ self.joiningReserves.append(suit)
+ else:
+ self.notify.warning('setSuits() - no suit: %d' % suitId)
+
+ self.reserveSuits = []
+ for index in range(len(reserveIds)):
+ suitId = reserveIds[index]
+ if self.cr.doId2do.has_key(suitId):
+ suit = self.cr.doId2do[suitId]
+ self.reserveSuits.append((suit, values[index]))
+ else:
+ self.notify.warning('setSuits() - no suit: %d' % suitId)
+
+ if len(self.joiningReserves) > 0:
+ self.fsm.request('ReservesJoining')
+
+ def setState(self, state, timestamp):
+ self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])
+
+ def d_elevatorDone(self):
+ self.sendUpdate('elevatorDone', [])
+
+ def d_reserveJoinDone(self):
+ self.sendUpdate('reserveJoinDone', [])
+
+ def enterOff(self, ts = 0):
+ return None
+
+ def exitOff(self):
+ return None
+
+ def enterWaitForAllToonsInside(self, ts = 0):
+ return None
+
+ def exitWaitForAllToonsInside(self):
+ return None
+
+ def __playElevator(self, ts, name, callback):
+ SuitHs = []
+ SuitPositions = []
+ if self.floorModel:
+ self.floorModel.removeNode()
+ if self.currentFloor == 0:
+ self.floorModel = loader.loadModel('phase_7/models/modules/suit_interior')
+ SuitHs = self.BottomFloor_SuitHs
+ SuitPositions = self.BottomFloor_SuitPositions
+ elif self.currentFloor == self.numFloors - 1:
+ self.floorModel = loader.loadModel('phase_7/models/modules/boss_suit_office')
+ SuitHs = self.BossOffice_SuitHs
+ SuitPositions = self.BossOffice_SuitPositions
+ else:
+ self.floorModel = loader.loadModel('phase_7/models/modules/cubicle_room')
+ SuitHs = self.Cubicle_SuitHs
+ SuitPositions = self.Cubicle_SuitPositions
+ self.floorModel.reparentTo(render)
+ elevIn = self.floorModel.find('**/elevator-in')
+ elevOut = self.floorModel.find('**/elevator-out')
+ for index in range(len(self.suits)):
+ self.suits[index].setPos(SuitPositions[index])
+ if len(self.suits) > 2:
+ self.suits[index].setH(SuitHs[index])
+ else:
+ self.suits[index].setH(170)
+ self.suits[index].loop('neutral')
+
+ for toon in self.toons:
+ toon.reparentTo(self.elevatorModelIn)
+ index = self.toonIds.index(toon.doId)
+ toon.setPos(ElevatorPoints[index][0], ElevatorPoints[index][1], ElevatorPoints[index][2])
+ toon.setHpr(180, 0, 0)
+ toon.loop('neutral')
+
+ self.elevatorModelIn.reparentTo(elevIn)
+ self.leftDoorIn.setPos(3.5, 0, 0)
+ self.rightDoorIn.setPos(-3.5, 0, 0)
+ self.elevatorModelOut.reparentTo(elevOut)
+ self.leftDoorOut.setPos(3.5, 0, 0)
+ self.rightDoorOut.setPos(-3.5, 0, 0)
+ camera.reparentTo(self.elevatorModelIn)
+ camera.setH(180)
+ camera.setPos(0, 14, 4)
+ base.playMusic(self.elevatorMusic, looping=1, volume=0.8)
+ track = Sequence(ElevatorUtils.getRideElevatorInterval(ELEVATOR_NORMAL), ElevatorUtils.getOpenInterval(self, self.leftDoorIn, self.rightDoorIn, self.openSfx, None, type=ELEVATOR_NORMAL), Func(camera.wrtReparentTo, render))
+ for toon in self.toons:
+ track.append(Func(toon.wrtReparentTo, render))
+
+ track.append(Func(callback))
+ track.start(ts)
+ self.activeIntervals[name] = track
+ return
+
+ def enterElevator(self, ts = 0):
+ self.currentFloor += 1
+ self.cr.playGame.getPlace().currentFloor = self.currentFloor
+ self.setElevatorLights(self.elevatorModelIn)
+ self.setElevatorLights(self.elevatorModelOut)
+ self.__playElevator(ts, self.elevatorName, self.__handleElevatorDone)
+ mult = ToontownBattleGlobals.getCreditMultiplier(self.currentFloor)
+ base.localAvatar.inventory.setBattleCreditMultiplier(mult)
+
+ def __handleElevatorDone(self):
+ self.d_elevatorDone()
+
+ def exitElevator(self):
+ self.elevatorMusic.stop()
+ self.__finishInterval(self.elevatorName)
+ return None
+
+ def __playCloseElevatorOut(self, name):
+ track = Sequence(Wait(SUIT_LEAVE_ELEVATOR_TIME), Parallel(SoundInterval(self.closeSfx), LerpPosInterval(self.leftDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL), startPos=Point3(0, 0, 0), blendType='easeOut'), LerpPosInterval(self.rightDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL), startPos=Point3(0, 0, 0), blendType='easeOut')))
+ track.start()
+ self.activeIntervals[name] = track
+
+ def enterBattle(self, ts = 0):
+ if self.elevatorOutOpen == 1:
+ self.__playCloseElevatorOut(self.uniqueName('close-out-elevator'))
+ camera.setPos(0, -15, 6)
+ camera.headsUp(self.elevatorModelOut)
+ return None
+
+ def exitBattle(self):
+ if self.elevatorOutOpen == 1:
+ self.__finishInterval(self.uniqueName('close-out-elevator'))
+ self.elevatorOutOpen = 0
+ return None
+
+ def __playReservesJoining(self, ts, name, callback):
+ index = 0
+ for suit in self.joiningReserves:
+ suit.reparentTo(render)
+ suit.setPos(self.elevatorModelOut, Point3(ElevatorPoints[index][0], ElevatorPoints[index][1], ElevatorPoints[index][2]))
+ index += 1
+ suit.setH(180)
+ suit.loop('neutral')
+
+ track = Sequence(Func(camera.wrtReparentTo, self.elevatorModelOut), Func(camera.setPos, Point3(0, -8, 2)), Func(camera.setHpr, Vec3(0, 10, 0)), Parallel(SoundInterval(self.openSfx), LerpPosInterval(self.leftDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], Point3(0, 0, 0), startPos=ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL), blendType='easeOut'), LerpPosInterval(self.rightDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], Point3(0, 0, 0), startPos=ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL), blendType='easeOut')), Wait(SUIT_HOLD_ELEVATOR_TIME), Func(camera.wrtReparentTo, render), Func(callback))
+ track.start(ts)
+ self.activeIntervals[name] = track
+
+ def enterReservesJoining(self, ts = 0):
+ self.__playReservesJoining(ts, self.uniqueName('reserves-joining'), self.__handleReserveJoinDone)
+ return None
+
+ def __handleReserveJoinDone(self):
+ self.joiningReserves = []
+ self.elevatorOutOpen = 1
+ self.d_reserveJoinDone()
+
+ def exitReservesJoining(self):
+ self.__finishInterval(self.uniqueName('reserves-joining'))
+ return None
+
+ def enterResting(self, ts = 0):
+ base.playMusic(self.waitMusic, looping=1, volume=0.7)
+ self.__closeInElevator()
+
+ def exitResting(self):
+ self.waitMusic.stop()
+
+ def enterReward(self, ts = 0):
+ base.localAvatar.b_setParent(ToontownGlobals.SPHidden)
+ request = {'loader': ZoneUtil.getBranchLoaderName(self.extZoneId),
+ 'where': ZoneUtil.getToonWhereName(self.extZoneId),
+ 'how': 'elevatorIn',
+ 'hoodId': ZoneUtil.getHoodId(self.extZoneId),
+ 'zoneId': self.extZoneId,
+ 'shardId': None,
+ 'avId': -1,
+ 'bldgDoId': self.distBldgDoId}
+ messenger.send('DSIDoneEvent', [request])
+ return
+
+ def exitReward(self):
+ return None
diff --git a/toontown/building/DistributedSuitInteriorAI.py b/toontown/building/DistributedSuitInteriorAI.py
new file mode 100644
index 0000000..f1014e0
--- /dev/null
+++ b/toontown/building/DistributedSuitInteriorAI.py
@@ -0,0 +1,473 @@
+from toontown.toonbase.ToontownBattleGlobals import *
+from otp.ai.AIBaseGlobal import *
+from direct.distributed.ClockDelta import *
+from ElevatorConstants import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObjectAI
+from direct.fsm import State
+from toontown.battle import DistributedBattleBldgAI
+from toontown.battle import BattleBase
+from direct.task import Timer
+import DistributedElevatorIntAI, copy
+
+class DistributedSuitInteriorAI(DistributedObjectAI.DistributedObjectAI):
+
+ def __init__(self, air, elevator):
+ self.air = air
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ self.extZoneId, self.zoneId = elevator.bldg.getExteriorAndInteriorZoneId()
+ self.numFloors = elevator.bldg.planner.numFloors
+ self.avatarExitEvents = []
+ self.toons = []
+ self.toonSkillPtsGained = {}
+ self.toonExp = {}
+ self.toonOrigQuests = {}
+ self.toonItems = {}
+ self.toonOrigMerits = {}
+ self.toonMerits = {}
+ self.toonParts = {}
+ self.helpfulToons = []
+ self.currentFloor = 0
+ self.topFloor = self.numFloors - 1
+ self.bldg = elevator.bldg
+ self.elevator = elevator
+ self.suits = []
+ self.activeSuits = []
+ self.reserveSuits = []
+ self.joinedReserves = []
+ self.suitsKilled = []
+ self.suitsKilledPerFloor = []
+ self.battle = None
+ self.timer = Timer.Timer()
+ self.responses = {}
+ self.ignoreResponses = 0
+ self.ignoreElevatorDone = 0
+ self.ignoreReserveJoinDone = 0
+ self.toonIds = copy.copy(elevator.seats)
+ for toonId in self.toonIds:
+ if toonId != None:
+ self.__addToon(toonId)
+
+ self.savedByMap = {}
+ self.fsm = ClassicFSM.ClassicFSM('DistributedSuitInteriorAI', [
+ State.State('WaitForAllToonsInside', self.enterWaitForAllToonsInside, self.exitWaitForAllToonsInside, [
+ 'Elevator']),
+ State.State('Elevator', self.enterElevator, self.exitElevator, [
+ 'Battle']),
+ State.State('Battle', self.enterBattle, self.exitBattle, [
+ 'ReservesJoining', 'BattleDone']),
+ State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, [
+ 'Battle']),
+ State.State('BattleDone', self.enterBattleDone, self.exitBattleDone, [
+ 'Resting', 'Reward']),
+ State.State('Resting', self.enterResting, self.exitResting, [
+ 'Elevator']),
+ State.State('Reward', self.enterReward, self.exitReward, [
+ 'Off']),
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'WaitForAllToonsInside'])], 'Off', 'Off', onUndefTransition=ClassicFSM.ClassicFSM.ALLOW)
+ self.fsm.enterInitialState()
+ return
+
+ def delete(self):
+ self.ignoreAll()
+ self.toons = []
+ self.toonIds = []
+ self.fsm.requestFinalState()
+ del self.fsm
+ del self.bldg
+ del self.elevator
+ self.timer.stop()
+ del self.timer
+ self.__cleanupFloorBattle()
+ taskName = self.taskName('deleteInterior')
+ taskMgr.remove(taskName)
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def __handleUnexpectedExit(self, toonId):
+ self.notify.warning('toon: %d exited unexpectedly' % toonId)
+ self.__removeToon(toonId)
+ if len(self.toons) == 0:
+ self.timer.stop()
+ if self.fsm.getCurrentState().getName() == 'Resting':
+ pass
+ elif self.battle == None:
+ self.bldg.deleteSuitInterior()
+ return
+
+ def __addToon(self, toonId):
+ if not self.air.doId2do.has_key(toonId):
+ self.notify.warning('addToon() - no toon for doId: %d' % toonId)
+ return
+ event = self.air.getAvatarExitEvent(toonId)
+ self.avatarExitEvents.append(event)
+ self.accept(event, self.__handleUnexpectedExit, extraArgs=[toonId])
+ self.toons.append(toonId)
+ self.responses[toonId] = 0
+
+ def __removeToon(self, toonId):
+ if self.toons.count(toonId):
+ self.toons.remove(toonId)
+ if self.toonIds.count(toonId):
+ self.toonIds[self.toonIds.index(toonId)] = None
+ if self.responses.has_key(toonId):
+ del self.responses[toonId]
+ event = self.air.getAvatarExitEvent(toonId)
+ if self.avatarExitEvents.count(event):
+ self.avatarExitEvents.remove(event)
+ self.ignore(event)
+ return
+
+ def __resetResponses(self):
+ self.responses = {}
+ for toon in self.toons:
+ self.responses[toon] = 0
+
+ self.ignoreResponses = 0
+
+ def __allToonsResponded(self):
+ for toon in self.toons:
+ if self.responses[toon] == 0:
+ return 0
+
+ self.ignoreResponses = 1
+ return 1
+
+ def getZoneId(self):
+ return self.zoneId
+
+ def getExtZoneId(self):
+ return self.extZoneId
+
+ def getDistBldgDoId(self):
+ return self.bldg.getDoId()
+
+ def getNumFloors(self):
+ return self.numFloors
+
+ def d_setToons(self):
+ self.sendUpdate('setToons', self.getToons())
+
+ def getToons(self):
+ sendIds = []
+ for toonId in self.toonIds:
+ if toonId == None:
+ sendIds.append(0)
+ else:
+ sendIds.append(toonId)
+
+ return [sendIds, 0]
+
+ def d_setSuits(self):
+ self.sendUpdate('setSuits', self.getSuits())
+
+ def getSuits(self):
+ suitIds = []
+ for suit in self.activeSuits:
+ suitIds.append(suit.doId)
+
+ reserveIds = []
+ values = []
+ for info in self.reserveSuits:
+ reserveIds.append(info[0].doId)
+ values.append(info[1])
+
+ return [suitIds, reserveIds, values]
+
+ def b_setState(self, state):
+ self.d_setState(state)
+ self.setState(state)
+
+ def d_setState(self, state):
+ stime = globalClock.getRealTime() + BattleBase.SERVER_BUFFER_TIME
+ self.sendUpdate('setState', [state, globalClockDelta.localToNetworkTime(stime)])
+
+ def setState(self, state):
+ self.fsm.request(state)
+
+ def getState(self):
+ return [
+ self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]
+
+ def setAvatarJoined(self):
+ avId = self.air.getAvatarIdFromSender()
+ if self.toons.count(avId) == 0:
+ self.air.writeServerEvent('suspicious', avId, 'DistributedSuitInteriorAI.setAvatarJoined from toon not in %s.' % self.toons)
+ self.notify.warning('setAvatarJoined() - av: %d not in list' % avId)
+ return
+ avatar = self.air.doId2do.get(avId)
+ if avatar != None:
+ self.savedByMap[avId] = (
+ avatar.getName(), avatar.dna.asTuple())
+ self.responses[avId] += 1
+ if self.__allToonsResponded():
+ self.fsm.request('Elevator')
+ return
+
+ def elevatorDone(self):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreResponses == 1:
+ return
+ else:
+ if self.fsm.getCurrentState().getName() != 'Elevator':
+ self.notify.warning('elevatorDone() - in state: %s' % self.fsm.getCurrentState().getName())
+ return
+ else:
+ if self.toons.count(toonId) == 0:
+ self.notify.warning('elevatorDone() - toon not in toon list: %d' % toonId)
+ return
+ self.responses[toonId] += 1
+ if self.__allToonsResponded() and self.ignoreElevatorDone == 0:
+ self.b_setState('Battle')
+
+ def reserveJoinDone(self):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreResponses == 1:
+ return
+ else:
+ if self.fsm.getCurrentState().getName() != 'ReservesJoining':
+ self.notify.warning('reserveJoinDone() - in state: %s' % self.fsm.getCurrentState().getName())
+ return
+ else:
+ if self.toons.count(toonId) == 0:
+ self.notify.warning('reserveJoinDone() - toon not in list: %d' % toonId)
+ return
+ self.responses[toonId] += 1
+ if self.__allToonsResponded() and self.ignoreReserveJoinDone == 0:
+ self.b_setState('Battle')
+
+ def enterOff(self):
+ return None
+
+ def exitOff(self):
+ return None
+
+ def enterWaitForAllToonsInside(self):
+ self.__resetResponses()
+ return None
+
+ def exitWaitForAllToonsInside(self):
+ self.__resetResponses()
+ return None
+
+ def enterElevator(self):
+ suitHandles = self.bldg.planner.genFloorSuits(self.currentFloor)
+ self.suits = suitHandles['activeSuits']
+ self.activeSuits = []
+ for suit in self.suits:
+ self.activeSuits.append(suit)
+
+ self.reserveSuits = suitHandles['reserveSuits']
+ self.d_setToons()
+ self.d_setSuits()
+ self.__resetResponses()
+ self.d_setState('Elevator')
+ self.timer.startCallback(BattleBase.ELEVATOR_T + ElevatorData[ELEVATOR_NORMAL]['openTime'] + BattleBase.SERVER_BUFFER_TIME, self.__serverElevatorDone)
+ return None
+
+ def __serverElevatorDone(self):
+ self.ignoreElevatorDone = 1
+ self.b_setState('Battle')
+
+ def exitElevator(self):
+ self.timer.stop()
+ self.__resetResponses()
+ return None
+
+ def __createFloorBattle(self):
+ if self.currentFloor == self.topFloor:
+ bossBattle = 1
+ else:
+ bossBattle = 0
+ self.battle = DistributedBattleBldgAI.DistributedBattleBldgAI(self.air, self.zoneId, self.__handleRoundDone, self.__handleBattleDone, bossBattle=bossBattle)
+ self.battle.suitsKilled = self.suitsKilled
+ self.battle.suitsKilledPerFloor = self.suitsKilledPerFloor
+ self.battle.battleCalc.toonSkillPtsGained = self.toonSkillPtsGained
+ self.battle.toonExp = self.toonExp
+ self.battle.toonOrigQuests = self.toonOrigQuests
+ self.battle.toonItems = self.toonItems
+ self.battle.toonOrigMerits = self.toonOrigMerits
+ self.battle.toonMerits = self.toonMerits
+ self.battle.toonParts = self.toonParts
+ self.battle.helpfulToons = self.helpfulToons
+ self.battle.setInitialMembers(self.toons, self.suits)
+ self.battle.generateWithRequired(self.zoneId)
+ mult = getCreditMultiplier(self.currentFloor)
+ if self.air.suitInvasionManager.getInvading():
+ mult *= getInvasionMultiplier()
+ self.battle.battleCalc.setSkillCreditMultiplier(mult)
+
+ def __cleanupFloorBattle(self):
+ for suit in self.suits:
+ self.notify.debug('cleaning up floor suit: %d' % suit.doId)
+ if suit.isDeleted():
+ self.notify.debug('whoops, suit %d is deleted.' % suit.doId)
+ else:
+ suit.requestDelete()
+
+ self.suits = []
+ self.reserveSuits = []
+ self.activeSuits = []
+ if self.battle != None:
+ self.battle.requestDelete()
+ self.battle = None
+ return
+
+ def __handleRoundDone(self, toonIds, totalHp, deadSuits):
+ totalMaxHp = 0
+ for suit in self.suits:
+ totalMaxHp += suit.maxHP
+
+ for suit in deadSuits:
+ self.activeSuits.remove(suit)
+
+ if len(self.reserveSuits) > 0 and len(self.activeSuits) < 4:
+ self.joinedReserves = []
+ hpPercent = 100 - totalHp / totalMaxHp * 100.0
+ for info in self.reserveSuits:
+ if info[1] <= hpPercent and len(self.activeSuits) < 4:
+ self.suits.append(info[0])
+ self.activeSuits.append(info[0])
+ self.joinedReserves.append(info)
+
+ for info in self.joinedReserves:
+ self.reserveSuits.remove(info)
+
+ if len(self.joinedReserves) > 0:
+ self.fsm.request('ReservesJoining')
+ self.d_setSuits()
+ return
+ if len(self.activeSuits) == 0:
+ self.fsm.request('BattleDone', [toonIds])
+ else:
+ self.battle.resume()
+
+ def __handleBattleDone(self, zoneId, toonIds):
+ if len(toonIds) == 0:
+ taskName = self.taskName('deleteInterior')
+ taskMgr.doMethodLater(10, self.__doDeleteInterior, taskName)
+ else:
+ if self.currentFloor == self.topFloor:
+ self.setState('Reward')
+ else:
+ self.b_setState('Resting')
+
+ def __doDeleteInterior(self, task):
+ self.bldg.deleteSuitInterior()
+
+ def enterBattle(self):
+ if self.battle == None:
+ self.__createFloorBattle()
+ self.elevator.d_setFloor(self.currentFloor)
+ return None
+
+ def exitBattle(self):
+ return None
+
+ def enterReservesJoining(self):
+ self.__resetResponses()
+ self.timer.startCallback(ElevatorData[ELEVATOR_NORMAL]['openTime'] + SUIT_HOLD_ELEVATOR_TIME + BattleBase.SERVER_BUFFER_TIME, self.__serverReserveJoinDone)
+ return None
+
+ def __serverReserveJoinDone(self):
+ self.ignoreReserveJoinDone = 1
+ self.b_setState('Battle')
+
+ def exitReservesJoining(self):
+ self.timer.stop()
+ self.__resetResponses()
+ for info in self.joinedReserves:
+ self.battle.suitRequestJoin(info[0])
+
+ self.battle.resume()
+ self.joinedReserves = []
+ return None
+
+ def enterBattleDone(self, toonIds):
+ if len(toonIds) != len(self.toons):
+ deadToons = []
+ for toon in self.toons:
+ if toonIds.count(toon) == 0:
+ deadToons.append(toon)
+
+ for toon in deadToons:
+ self.__removeToon(toon)
+
+ self.d_setToons()
+ if len(self.toons) == 0:
+ self.bldg.deleteSuitInterior()
+ else:
+ if self.currentFloor == self.topFloor:
+ self.battle.resume(self.currentFloor, topFloor=1)
+ else:
+ self.battle.resume(self.currentFloor, topFloor=0)
+ return None
+
+ def exitBattleDone(self):
+ self.__cleanupFloorBattle()
+ return None
+
+ def __handleEnterElevator(self):
+ self.fsm.request('Elevator')
+
+ def enterResting(self):
+ self.intElevator = DistributedElevatorIntAI.DistributedElevatorIntAI(self.air, self, self.toons)
+ self.intElevator.generateWithRequired(self.zoneId)
+ return None
+
+ def handleAllAboard(self, seats):
+ if not hasattr(self, 'fsm'):
+ return
+ if __dev__:
+ pass
+ else:
+ currState = self.fsm.getCurrentState().getName()
+ if not currState == 'Resting':
+ avId = self.air.getAvatarIdFromSender()
+ self.air.writeServerEvent('suspicious', avId, 'unexpected state for DistributedSuitInteriorAI.handleAllAboard(). Current State = %s.' % currState)
+ self.notify.warning('unexpected state for DistributedSuitInteriorAI.handleAllAboard(). Current State = %s' % currState)
+ return
+ numOfEmptySeats = seats.count(None)
+ if numOfEmptySeats == 4:
+ self.bldg.deleteSuitInterior()
+ return
+ else:
+ if numOfEmptySeats >= 0 and numOfEmptySeats <= 3:
+ pass
+ else:
+ self.error('Bad number of empty seats: %s' % numOfEmptySeats)
+ for toon in self.toons:
+ if seats.count(toon) == 0:
+ self.__removeToon(toon)
+
+ self.toonIds = copy.copy(seats)
+ self.toons = []
+ for toonId in self.toonIds:
+ if toonId != None:
+ self.toons.append(toonId)
+
+ self.d_setToons()
+ self.currentFloor += 1
+ self.fsm.request('Elevator')
+ return None
+
+ def exitResting(self):
+ self.intElevator.requestDelete()
+ del self.intElevator
+ return None
+
+ def enterReward(self):
+ victors = self.toonIds[:]
+ savedBy = []
+ for v in victors:
+ tuple = self.savedByMap.get(v)
+ if tuple:
+ savedBy.append([v, tuple[0], tuple[1]])
+
+ self.bldg.fsm.request('waitForVictors', [victors, savedBy])
+ self.d_setState('Reward')
+ return None
+
+ def exitReward(self):
+ return None
diff --git a/toontown/building/DistributedToonHallInterior.py b/toontown/building/DistributedToonHallInterior.py
new file mode 100644
index 0000000..0332b0e
--- /dev/null
+++ b/toontown/building/DistributedToonHallInterior.py
@@ -0,0 +1,713 @@
+from toontown.toonbase.ToonBaseGlobal import *
+from direct.interval.IntervalGlobal import *
+from direct.distributed.ClockDelta import *
+from direct.showbase import Audio3DManager
+from toontown.toonbase import ToontownGlobals
+import cPickle
+from DistributedToonInterior import DistributedToonInterior
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObject
+from direct.fsm import State
+from direct.actor import Actor
+import random
+import time
+import ToonInteriorColors
+from toontown.hood import ZoneUtil
+from toontown.toon import ToonDNA
+from toontown.toon import ToonHead
+
+class DistributedToonHallInterior(DistributedToonInterior):
+
+ def __init__(self, cr):
+ DistributedToonInterior.__init__(self, cr)
+ self.sillyFSM = ClassicFSM.ClassicFSM('SillyOMeter', [State.State('Setup', self.enterSetup, self.exitSetup, ['Phase0',
+ 'Phase1',
+ 'Phase2',
+ 'Phase3',
+ 'Phase4',
+ 'Phase5',
+ 'Phase6',
+ 'Phase7',
+ 'Phase8',
+ 'Phase9',
+ 'Phase10',
+ 'Phase11',
+ 'Phase12',
+ 'Phase13',
+ 'Phase14',
+ 'Phase15',
+ 'Flat',
+ 'Off']),
+ State.State('Phase0', self.enterPhase0, self.exitPhase0, ['Phase1', 'Flat', 'Off']),
+ State.State('Phase1', self.enterPhase1, self.exitPhase1, ['Phase2', 'Flat', 'Off']),
+ State.State('Phase2', self.enterPhase2, self.exitPhase2, ['Phase3', 'Flat', 'Off']),
+ State.State('Phase3', self.enterPhase3, self.exitPhase3, ['Phase4', 'Flat', 'Off']),
+ State.State('Phase4', self.enterPhase4, self.exitPhase4, ['Phase5', 'Flat', 'Off']),
+ State.State('Phase5', self.enterPhase5, self.exitPhase5, ['Phase6', 'Flat', 'Off']),
+ State.State('Phase6', self.enterPhase6, self.exitPhase6, ['Phase7', 'Flat', 'Off']),
+ State.State('Phase7', self.enterPhase7, self.exitPhase7, ['Phase8', 'Flat', 'Off']),
+ State.State('Phase8', self.enterPhase8, self.exitPhase8, ['Phase9', 'Flat', 'Off']),
+ State.State('Phase9', self.enterPhase9, self.exitPhase9, ['Phase10', 'Flat', 'Off']),
+ State.State('Phase10', self.enterPhase10, self.exitPhase10, ['Phase11', 'Flat', 'Off']),
+ State.State('Phase11', self.enterPhase11, self.exitPhase11, ['Phase12', 'Flat', 'Off']),
+ State.State('Phase12', self.enterPhase12, self.exitPhase12, ['Phase13', 'Flat', 'Off']),
+ State.State('Phase13', self.enterPhase13, self.exitPhase13, ['Phase14', 'Flat', 'Off']),
+ State.State('Phase14', self.enterPhase14, self.exitPhase14, ['Phase15', 'Flat', 'Off']),
+ State.State('Phase15', self.enterPhase15, self.exitPhase15, ['Flat', 'Off']),
+ State.State('Flat', self.enterFlat, self.exitFlat, ['Off', 'Phase0']),
+ State.State('Off', self.enterOff, self.exitOff, [])], 'Setup', 'Off')
+
+ def setup(self):
+ self.dnaStore = base.cr.playGame.dnaStore
+ self.randomGenerator = random.Random()
+ self.randomGenerator.seed(self.zoneId)
+ interior = self.randomDNAItem('TI_hall', self.dnaStore.findNode)
+ self.interior = interior.copyTo(render)
+ hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId)
+ self.colors = ToonInteriorColors.colors[hoodId]
+ self.replaceRandomInModel(self.interior)
+ doorModelName = 'door_double_round_ul'
+ if doorModelName[-1:] == 'r':
+ doorModelName = doorModelName[:-1] + 'l'
+ else:
+ doorModelName = doorModelName[:-1] + 'r'
+ door = self.dnaStore.findNode(doorModelName)
+ door_origin = render.find('**/door_origin;+s')
+ doorNP = door.copyTo(door_origin)
+ door_origin.setScale(0.8, 0.8, 0.8)
+ door_origin.setPos(door_origin, 0, -0.025, 0)
+ color = self.randomGenerator.choice(self.colors['TI_door'])
+ DNADoor.setupDoor(doorNP, self.interior, door_origin, self.dnaStore, str(self.block), color)
+ doorFrame = doorNP.find('door_*_flat')
+ doorFrame.wrtReparentTo(self.interior)
+ doorFrame.setColor(color)
+ del self.colors
+ del self.dnaStore
+ del self.randomGenerator
+ self.interior.flattenMedium()
+ self.sillyFSM.enterInitialState()
+
+ def sillyMeterIsRunning(self, isRunning):
+ if isRunning:
+ self.sillyFSM.request('Phase0')
+ else:
+ self.sillyFSM.request('Flat')
+
+ def selectPhase(self, newPhase):
+ try:
+ gotoPhase = 'Phase' + str(newPhase)
+ if self.sillyFSM.hasStateNamed(gotoPhase) and not self.sillyFSM.getCurrentState() == self.sillyFSM.getStateNamed(gotoPhase):
+ self.sillyFSM.request(gotoPhase)
+ except:
+ self.notify.warning('Illegal phase transition requested %s' % newPhase)
+
+ def startIfNeeded(self):
+ try:
+ self.curPhase = self.getPhaseToRun()
+ if self.curPhase >= 0:
+ self.sillyFSM.request('Phase' + str(self.curPhase))
+ else:
+ self.sillyFSM.request('Flat')
+ except:
+ pass
+
+ def getPhaseToRun(self):
+ result = -1
+ enoughInfoToRun = False
+ if base.cr.newsManager.isHolidayRunning(ToontownGlobals.SILLYMETER_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY):
+ if hasattr(base.cr, 'SillyMeterMgr') and not base.cr.SillyMeterMgr.isDisabled():
+ enoughInfoToRun = True
+ elif hasattr(base.cr, 'SillyMeterMgr'):
+ self.notify.debug('isDisabled = %s' % base.cr.SillyMeterMgr.isDisabled())
+ else:
+ self.notify.debug('base.cr does not have SillyMeterMgr')
+ else:
+ self.notify.debug('holiday is not running')
+ self.notify.debug('enoughInfoToRun = %s' % enoughInfoToRun)
+ if enoughInfoToRun and base.cr.SillyMeterMgr.getIsRunning():
+ result = base.cr.SillyMeterMgr.getCurPhase()
+ return result
+
+ def calculatePhaseDuration(self):
+ result = -1
+ valid = False
+ if base.cr.newsManager.isHolidayRunning(ToontownGlobals.SILLYMETER_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY):
+ if hasattr(base.cr, 'SillyMeterMgr') and not base.cr.SillyMeterMgr.isDisabled():
+ valid = True
+ elif hasattr(base.cr, 'SillyMeterMgr'):
+ self.notify.debug('isDisabled = %s' % base.cr.SillyMeterMgr.isDisabled())
+ else:
+ self.notify.debug('base.cr does not have SillyMeterMgr')
+ else:
+ self.notify.debug('holiday is not running')
+ self.notify.debug('valid = %s' % valid)
+ if valid and base.cr.SillyMeterMgr.getIsRunning():
+ result = base.cr.SillyMeterMgr.getCurPhaseDuration()
+ return result
+
+ def calculateFrameOffset(self, phaseDuration, numFrames):
+ result = -1
+ valid = False
+ if base.cr.newsManager.isHolidayRunning(ToontownGlobals.SILLYMETER_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY):
+ if hasattr(base.cr, 'SillyMeterMgr') and not base.cr.SillyMeterMgr.isDisabled():
+ valid = True
+ elif hasattr(base.cr, 'SillyMeterMgr'):
+ self.notify.debug('isDisabled = %s' % base.cr.SillyMeterMgr.isDisabled())
+ else:
+ self.notify.debug('base.cr does not have SillyMeterMgr')
+ else:
+ self.notify.debug('holiday is not running')
+ self.notify.debug('valid = %s' % valid)
+ if valid and base.cr.SillyMeterMgr.getIsRunning():
+ startTime = time.mktime(base.cr.SillyMeterMgr.getCurPhaseStartDate().timetuple())
+ serverTime = time.mktime(base.cr.toontownTimeManager.getCurServerDateTime().timetuple())
+ offset = (serverTime - startTime) / phaseDuration
+ if offset < 0:
+ result = -1
+ else:
+ result = offset * numFrames
+ return result
+
+ def calculateFrameRange(self, frameNo):
+ pass
+
+ def enterSetup(self):
+ ropes = loader.loadModel('phase_4/models/modules/tt_m_ara_int_ropes')
+ ropes.reparentTo(self.interior)
+ self.sillyMeter = Actor.Actor('phase_4/models/props/tt_a_ara_ttc_sillyMeter_default', {'arrowTube': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_arrowFluid',
+ 'phaseOne': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseOne',
+ 'phaseTwo': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseTwo',
+ 'phaseThree': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseThree',
+ 'phaseFour': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseFour',
+ 'phaseFourToFive': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseFourToFive',
+ 'phaseFive': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseFive'})
+ self.sillyMeter.reparentTo(self.interior)
+ self.flatSillyMeter = loader.loadModel('phase_3.5/models/modules/tt_m_ara_int_sillyMeterFlat')
+ self.flatSillyMeter.reparentTo(self.interior)
+ self.flatSillyMeter.hide()
+ self.flatDuck = loader.loadModel('phase_3.5/models/modules/tt_m_ara_int_scientistDuckFlat')
+ loc1 = self.interior.find('**/npc_origin_1')
+ if loc1:
+ self.flatDuck.reparentTo(loc1)
+ self.flatDuck.hide()
+ self.flatMonkey = loader.loadModel('phase_3.5/models/modules/tt_m_ara_int_scientistMonkeyFlat')
+ loc1 = self.interior.find('**/npc_origin_2')
+ if loc1:
+ self.flatMonkey.reparentTo(loc1)
+ self.flatMonkey.hide()
+ self.flatHorse = loader.loadModel('phase_3.5/models/modules/tt_m_ara_int_scientistHorseFlat')
+ loc1 = self.interior.find('**/npc_origin_3')
+ if loc1:
+ self.flatHorse.reparentTo(loc1)
+ self.flatHorse.hide()
+ self.smPhase1 = self.sillyMeter.find('**/stage1')
+ self.smPhase2 = self.sillyMeter.find('**/stage2')
+ self.smPhase3 = self.sillyMeter.find('**/stage3')
+ self.smPhase4 = self.sillyMeter.find('**/stage4')
+ self.smPhase2.hide()
+ self.smPhase3.hide()
+ self.smPhase4.hide()
+ thermometerLocator = self.sillyMeter.findAllMatches('**/uvj_progressBar')[1]
+ thermometerMesh = self.sillyMeter.find('**/tube')
+ thermometerMesh.setTexProjector(thermometerMesh.findTextureStage('default'), thermometerLocator, self.sillyMeter)
+ self.sillyMeter.flattenMedium()
+ self.sillyMeter.makeSubpart('arrow', ['uvj_progressBar*', 'def_springA'])
+ self.sillyMeter.makeSubpart('meter', ['def_pivot'], ['uvj_progressBar*', 'def_springA'])
+ self.audio3d = Audio3DManager.Audio3DManager(base.sfxManagerList[0], camera)
+ self.phase1Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseOne.mp3')
+ self.phase1Sfx.setLoop(True)
+ self.phase2Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseTwo.mp3')
+ self.phase2Sfx.setLoop(True)
+ self.phase3Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseThree.mp3')
+ self.phase3Sfx.setLoop(True)
+ self.phase4Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseFour.mp3')
+ self.phase4Sfx.setLoop(True)
+ self.phase4To5Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseFourToFive.mp3')
+ self.phase4To5Sfx.setLoop(False)
+ self.phase5Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseFive.mp3')
+ self.phase5Sfx.setLoop(True)
+ self.arrowSfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterArrow.mp3')
+ self.arrowSfx.setLoop(False)
+ self.audio3d.setDropOffFactor(0.1)
+ self.accept('SillyMeterPhase', self.selectPhase)
+ self.accept('SillyMeterIsRunning', self.sillyMeterIsRunning)
+ self.startIfNeeded()
+
+ def exitSetup(self):
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase0(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=1, endFrame=30), Sequence(Func(self.phase1Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase1Sfx, self.sillyMeter)))
+ self.animSeq.start()
+ self.sillyMeter.loop('phaseOne', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase0(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.audio3d.detachSound(self.phase1Sfx)
+ self.phase1Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase1(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.audio3d.attachSoundToObject(self.phase1Sfx, self.sillyMeter)
+ self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=31, endFrame=42), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=42, endFrame=71), Sequence(Func(self.phase1Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase1Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.sillyMeter.loop('phaseOne', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase1(self):
+ self.audio3d.detachSound(self.phase1Sfx)
+ self.phase1Sfx.stop()
+ self.animSeq.finish()
+ del self.animSeq
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase2(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=42, endFrame=71), Sequence(Func(self.phase1Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase1Sfx, self.sillyMeter)))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.sillyMeter.loop('phaseOne', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase2(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.audio3d.detachSound(self.phase1Sfx)
+ self.phase1Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase3(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=72, endFrame=83), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=83, endFrame=112), Sequence(Func(self.phase1Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase1Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.sillyMeter.loop('phaseOne', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase3(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.audio3d.detachSound(self.phase1Sfx)
+ self.phase1Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase4(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=113, endFrame=124), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=124, endFrame=153), Sequence(Func(self.phase1Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase1Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.sillyMeter.loop('phaseOne', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase4(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.audio3d.detachSound(self.phase1Sfx)
+ self.phase1Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase5(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=154, endFrame=165), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=165, endFrame=194), Sequence(Func(self.phase2Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase2Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.sillyMeter.loop('phaseTwo', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase5(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.audio3d.detachSound(self.phase2Sfx)
+ self.phase2Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase6(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=195, endFrame=206), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=206, endFrame=235), Sequence(Func(self.phase2Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase2Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.sillyMeter.loop('phaseTwo', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase6(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.audio3d.detachSound(self.phase2Sfx)
+ self.phase2Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase7(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=236, endFrame=247), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=247, endFrame=276), Sequence(Func(self.phase3Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase3Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.smPhase3.show()
+ self.sillyMeter.loop('phaseThree', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase7(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.smPhase3.hide()
+ self.audio3d.detachSound(self.phase3Sfx)
+ self.phase3Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase8(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=277, endFrame=288), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=288, endFrame=317), Sequence(Func(self.phase3Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase3Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.smPhase3.show()
+ self.sillyMeter.loop('phaseThree', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase8(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.smPhase3.hide()
+ self.audio3d.detachSound(self.phase3Sfx)
+ self.phase3Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase9(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=318, endFrame=329), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=329, endFrame=358), Sequence(Func(self.phase3Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase3Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.smPhase3.show()
+ self.sillyMeter.loop('phaseThree', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase9(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.smPhase3.hide()
+ self.audio3d.detachSound(self.phase3Sfx)
+ self.phase3Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase10(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=359, endFrame=370), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=370, endFrame=399), Sequence(Func(self.phase4Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase4Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.smPhase3.show()
+ self.smPhase4.show()
+ self.sillyMeter.loop('phaseFour', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase10(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.smPhase3.hide()
+ self.smPhase4.hide()
+ self.audio3d.detachSound(self.phase4Sfx)
+ self.phase4Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase11(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=400, endFrame=411), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=411, endFrame=440), Sequence(Func(self.phase4Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase4Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.smPhase3.show()
+ self.smPhase4.show()
+ self.sillyMeter.loop('phaseFour', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase11(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.smPhase3.hide()
+ self.smPhase4.hide()
+ self.audio3d.detachSound(self.phase4Sfx)
+ self.phase4Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase12(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ elif phaseDuration > 2000000:
+ phaseDuration = 2000000
+ self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=441, endFrame=452), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=phaseDuration, constrainedLoop=1, startFrame=452, endFrame=481), Sequence(Func(self.phase4Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase4Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.smPhase3.show()
+ self.smPhase4.show()
+ self.sillyMeter.loop('phaseFour', partName='meter')
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase12(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.smPhase3.hide()
+ self.smPhase4.hide()
+ self.audio3d.detachSound(self.phase4Sfx)
+ self.phase4Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase13(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Sequence(Parallel(Func(self.phase4To5Sfx.play), ActorInterval(self.sillyMeter, 'phaseFourToFive', constrainedLoop=0, startFrame=1, endFrame=120)), Parallel(ActorInterval(self.sillyMeter, 'phaseFive', duration=phaseDuration, constrainedLoop=1, startFrame=1, endFrame=48), Sequence(Func(self.phase5Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase5Sfx, self.sillyMeter))))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.smPhase3.show()
+ self.smPhase4.show()
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase13(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.smPhase3.hide()
+ self.smPhase4.hide()
+ self.audio3d.detachSound(self.phase5Sfx)
+ self.phase5Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase14(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Parallel(ActorInterval(self.sillyMeter, 'phaseFive', duration=phaseDuration, constrainedLoop=1, startFrame=1, endFrame=48), Sequence(Func(self.phase5Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase5Sfx, self.sillyMeter)))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.smPhase3.show()
+ self.smPhase4.show()
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase14(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.smPhase3.hide()
+ self.smPhase4.hide()
+ self.audio3d.detachSound(self.phase5Sfx)
+ self.phase5Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterPhase15(self):
+ phaseDuration = self.calculatePhaseDuration()
+ if phaseDuration < 0:
+ phaseDuration = 604800
+ self.animSeq = Parallel(ActorInterval(self.sillyMeter, 'phaseFive', duration=phaseDuration, constrainedLoop=1, startFrame=1, endFrame=48), Sequence(Func(self.phase5Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase5Sfx, self.sillyMeter)))
+ self.animSeq.start()
+ self.smPhase2.show()
+ self.smPhase3.show()
+ self.smPhase4.show()
+ self.accept('SillyMeterPhase', self.selectPhase)
+
+ def exitPhase15(self):
+ self.animSeq.finish()
+ del self.animSeq
+ self.smPhase2.hide()
+ self.smPhase3.hide()
+ self.smPhase4.hide()
+ self.audio3d.detachSound(self.phase5Sfx)
+ self.phase5Sfx.stop()
+ self.sillyMeter.stop()
+ self.ignore('SillyMeterPhase')
+
+ def enterFlat(self):
+ self.sillyMeter.hide()
+ self.flatSillyMeter.show()
+ self.flatDuck.show()
+ self.flatMonkey.show()
+ self.flatHorse.show()
+
+ def exitFlat(self):
+ self.sillyMeter.show()
+ self.flatSillyMeter.hide()
+ self.flatDuck.hide()
+ self.flatMonkey.hide()
+ self.flatHorse.hide()
+
+ def enterOff(self):
+ if hasattr(self, 'animSeq') and self.animSeq:
+ self.animSeq.finish()
+ self.ignore('SillyMeterPhase')
+ if hasattr(self, 'sillyMeter'):
+ del self.sillyMeter
+ if hasattr(self, 'smPhase1'):
+ del self.smPhase1
+ if hasattr(self, 'smPhase2'):
+ del self.smPhase2
+ if hasattr(self, 'smPhase3'):
+ del self.smPhase3
+ if hasattr(self, 'smPhase4'):
+ del self.smPhase4
+ self.cleanUpSounds()
+
+ def exitOff(self):
+ pass
+
+ def enterToon(self):
+ self.toonhallView = (Point3(0, -5, 3),
+ Point3(0, 12.0, 7.0),
+ Point3(0.0, 10.0, 5.0),
+ Point3(0.0, 10.0, 5.0),
+ 1)
+ self.setupCollisions(2.5)
+ self.firstEnter = 1
+ self.accept('CamChangeColl' + '-into', self.handleCloseToWall)
+
+ def exitToon(self):
+ pass
+
+ def handleCloseToWall(self, collEntry):
+ if self.firstEnter == 0:
+ return
+ interiorRopes = self.interior.find('**/*interior_ropes')
+ if interiorRopes == collEntry.getIntoNodePath().getParent():
+ return
+ self.restoreCam()
+ self.accept('CamChangeColl' + '-exit', self.handleAwayFromWall)
+
+ def handleAwayFromWall(self, collEntry):
+ if self.firstEnter == 1:
+ self.cleanUpCollisions()
+ self.setupCollisions(0.75)
+ self.oldView = base.localAvatar.cameraIndex
+ base.localAvatar.addCameraPosition(self.toonhallView)
+ self.firstEnter = 0
+ self.setUpToonHallCam()
+ return
+ flippy = self.interior.find('**/*Flippy*/*NPCToon*')
+ if flippy == collEntry.getIntoNodePath():
+ self.setUpToonHallCam()
+
+ def setupCollisions(self, radius):
+ r = base.localAvatar.getClampedAvatarHeight() * radius
+ cs = CollisionSphere(0, 0, 0, r)
+ cn = CollisionNode('CamChangeColl')
+ cn.addSolid(cs)
+ cn.setFromCollideMask(ToontownGlobals.WallBitmask)
+ cn.setIntoCollideMask(BitMask32.allOff())
+ self.camChangeNP = base.localAvatar.getPart('torso', '1000').attachNewNode(cn)
+ self.cHandlerEvent = CollisionHandlerEvent()
+ self.cHandlerEvent.addInPattern('%fn-into')
+ self.cHandlerEvent.addOutPattern('%fn-exit')
+ base.cTrav.addCollider(self.camChangeNP, self.cHandlerEvent)
+
+ def cleanUpCollisions(self):
+ base.cTrav.removeCollider(self.camChangeNP)
+ self.camChangeNP.detachNode()
+ if hasattr(self, 'camChangeNP'):
+ del self.camChangeNP
+ if hasattr(self, 'cHandlerEvent'):
+ del self.cHandlerEvent
+
+ def cleanUpSounds(self):
+
+ def __cleanUpSound__(soundFile):
+ if soundFile.status() == soundFile.PLAYING:
+ soundFile.setLoop(False)
+ soundFile.stop()
+
+ if hasattr(self, 'audio3d'):
+ self.audio3d.disable()
+ del self.audio3d
+ if hasattr(self, 'phase1Sfx'):
+ __cleanUpSound__(self.phase1Sfx)
+ del self.phase1Sfx
+ if hasattr(self, 'phase2Sfx'):
+ __cleanUpSound__(self.phase2Sfx)
+ del self.phase2Sfx
+ if hasattr(self, 'phase3Sfx'):
+ __cleanUpSound__(self.phase3Sfx)
+ del self.phase3Sfx
+ if hasattr(self, 'phase4Sfx'):
+ __cleanUpSound__(self.phase4Sfx)
+ del self.phase4Sfx
+ if hasattr(self, 'phase4To5Sfx'):
+ __cleanUpSound__(self.phase4To5Sfx)
+ del self.phase4To5Sfx
+ if hasattr(self, 'phase5Sfx'):
+ __cleanUpSound__(self.phase5Sfx)
+ del self.phase5Sfx
+ if hasattr(self, 'arrowSfx'):
+ __cleanUpSound__(self.arrowSfx)
+ del self.arrowSfx
+
+ def setUpToonHallCam(self):
+ base.localAvatar.setCameraFov(75)
+ base.localAvatar.setCameraSettings(self.toonhallView)
+
+ def restoreCam(self):
+ base.localAvatar.setCameraFov(ToontownGlobals.DefaultCameraFov)
+ if hasattr(self, 'oldView'):
+ base.localAvatar.setCameraPositionByIndex(self.oldView)
+
+ def disable(self):
+ self.setUpToonHallCam()
+ base.localAvatar.removeCameraPosition()
+ base.localAvatar.resetCameraPosition()
+ self.restoreCam()
+ self.ignoreAll()
+ self.cleanUpCollisions()
+ if hasattr(self, 'sillyFSM'):
+ self.sillyFSM.requestFinalState()
+ del self.sillyFSM
+ DistributedToonInterior.disable(self)
+
+ def delete(self):
+ DistributedToonInterior.delete(self)
diff --git a/toontown/building/DistributedToonHallInteriorAI.py b/toontown/building/DistributedToonHallInteriorAI.py
new file mode 100644
index 0000000..de16dc7
--- /dev/null
+++ b/toontown/building/DistributedToonHallInteriorAI.py
@@ -0,0 +1,44 @@
+from DistributedToonInteriorAI import *
+from toontown.toonbase import ToontownGlobals
+
+class DistributedToonHallInteriorAI(DistributedToonInteriorAI):
+
+ def __init__(self, block, air, zoneId, building):
+ DistributedToonInteriorAI.__init__(self, block, air, zoneId, building)
+ self.accept('ToonEnteredZone', self.logToonEntered)
+ self.accept('ToonLeftZone', self.logToonLeft)
+
+ def logToonEntered(self, avId, zoneId):
+ result = self.getCurPhase()
+ if result == -1:
+ phase = 'not available'
+ else:
+ phase = str(result)
+ self.air.writeServerEvent('sillyMeter', avId, 'enter|%s' % phase)
+
+ def logToonLeft(self, avId, zoneId):
+ result = self.getCurPhase()
+ if result == -1:
+ phase = 'not available'
+ else:
+ phase = str(result)
+ self.air.writeServerEvent('sillyMeter', avId, 'exit|%s' % phase)
+
+ def getCurPhase(self):
+ result = -1
+ enoughInfoToRun = False
+ if ToontownGlobals.SILLYMETER_HOLIDAY in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.SILLYMETER_HOLIDAY] != None and simbase.air.holidayManager.currentHolidays[ToontownGlobals.SILLYMETER_HOLIDAY].getRunningState():
+ if hasattr(simbase.air, 'SillyMeterMgr'):
+ enoughInfoToRun = True
+ else:
+ self.notify.debug('simbase.air does not have SillyMeterMgr')
+ else:
+ self.notify.debug('holiday is not running')
+ self.notify.debug('enoughInfoToRun = %s' % enoughInfoToRun)
+ if enoughInfoToRun and simbase.air.SillyMeterMgr.getIsRunning():
+ result = simbase.air.SillyMeterMgr.getCurPhase()
+ return result
+
+ def delete(self):
+ self.ignoreAll()
+ DistributedToonInteriorAI.delete(self)
diff --git a/toontown/building/DistributedToonInterior.py b/toontown/building/DistributedToonInterior.py
new file mode 100644
index 0000000..f7d6abd
--- /dev/null
+++ b/toontown/building/DistributedToonInterior.py
@@ -0,0 +1,202 @@
+from toontown.toonbase.ToonBaseGlobal import *
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from direct.distributed.ClockDelta import *
+from toontown.toonbase import ToontownGlobals
+import cPickle
+import ToonInterior
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObject
+from direct.fsm import State
+import random
+import ToonInteriorColors
+from toontown.hood import ZoneUtil
+from toontown.toon import ToonDNA
+from toontown.toon import ToonHead
+SIGN_LEFT = -4
+SIGN_RIGHT = 4
+SIGN_BOTTOM = -3.5
+SIGN_TOP = 1.5
+FrameScale = 1.4
+
+class DistributedToonInterior(DistributedObject.DistributedObject):
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedToonInterior', [State.State('toon', self.enterToon, self.exitToon, ['beingTakenOver']), State.State('beingTakenOver', self.enterBeingTakenOver, self.exitBeingTakenOver, []), State.State('off', self.enterOff, self.exitOff, [])], 'toon', 'off')
+ self.fsm.enterInitialState()
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ self.setup()
+
+ def disable(self):
+ self.interior.removeNode()
+ del self.interior
+ DistributedObject.DistributedObject.disable(self)
+
+ def delete(self):
+ del self.fsm
+ DistributedObject.DistributedObject.delete(self)
+
+ def randomDNAItem(self, category, findFunc):
+ codeCount = self.dnaStore.getNumCatalogCodes(category)
+ index = self.randomGenerator.randint(0, codeCount - 1)
+ code = self.dnaStore.getCatalogCode(category, index)
+ return findFunc(code)
+
+ def replaceRandomInModel(self, model):
+ baseTag = 'random_'
+ npc = model.findAllMatches('**/' + baseTag + '???_*')
+ for i in range(npc.getNumPaths()):
+ np = npc.getPath(i)
+ name = np.getName()
+ b = len(baseTag)
+ category = name[b + 4:]
+ key1 = name[b]
+ key2 = name[b + 1]
+ if key1 == 'm':
+ model = self.randomDNAItem(category, self.dnaStore.findNode)
+ newNP = model.copyTo(np)
+ c = render.findAllMatches('**/collision')
+ c.stash()
+ if key2 == 'r':
+ self.replaceRandomInModel(newNP)
+ elif key1 == 't':
+ texture = self.randomDNAItem(category, self.dnaStore.findTexture)
+ np.setTexture(texture, 100)
+ newNP = np
+ if key2 == 'c':
+ if category == 'TI_wallpaper' or category == 'TI_wallpaper_border':
+ self.randomGenerator.seed(self.zoneId)
+ newNP.setColorScale(self.randomGenerator.choice(self.colors[category]))
+ else:
+ newNP.setColorScale(self.randomGenerator.choice(self.colors[category]))
+
+ def setup(self):
+ self.dnaStore = base.cr.playGame.dnaStore
+ self.randomGenerator = random.Random()
+ self.randomGenerator.seed(self.zoneId)
+ interior = self.randomDNAItem('TI_room', self.dnaStore.findNode)
+ self.interior = interior.copyTo(render)
+ hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId)
+ self.colors = ToonInteriorColors.colors[hoodId]
+ self.replaceRandomInModel(self.interior)
+ doorModelName = 'door_double_round_ul'
+ if doorModelName[-1:] == 'r':
+ doorModelName = doorModelName[:-1] + 'l'
+ else:
+ doorModelName = doorModelName[:-1] + 'r'
+ door = self.dnaStore.findNode(doorModelName)
+ door_origin = render.find('**/door_origin;+s')
+ doorNP = door.copyTo(door_origin)
+ door_origin.setScale(0.8, 0.8, 0.8)
+ door_origin.setPos(door_origin, 0, -0.025, 0)
+ color = self.randomGenerator.choice(self.colors['TI_door'])
+ DNADoor.setupDoor(doorNP, self.interior, door_origin, self.dnaStore, str(self.block), color)
+ doorFrame = doorNP.find('door_*_flat')
+ doorFrame.wrtReparentTo(self.interior)
+ doorFrame.setColor(color)
+ sign = hidden.find('**/tb%s:*_landmark_*_DNARoot/**/sign;+s' % (self.block,))
+ if not sign.isEmpty():
+ signOrigin = self.interior.find('**/sign_origin;+s')
+ newSignNP = sign.copyTo(signOrigin)
+ newSignNP.setDepthWrite(1, 1)
+ mat = self.dnaStore.getSignTransformFromBlockNumber(int(self.block))
+ inv = Mat4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ inv.invertFrom(mat)
+ newSignNP.setMat(inv)
+ newSignNP.flattenLight()
+ ll = Point3()
+ ur = Point3()
+ newSignNP.calcTightBounds(ll, ur)
+ width = ur[0] - ll[0]
+ height = ur[2] - ll[2]
+ if width != 0 and height != 0:
+ xScale = (SIGN_RIGHT - SIGN_LEFT) / width
+ zScale = (SIGN_TOP - SIGN_BOTTOM) / height
+ scale = min(xScale, zScale)
+ xCenter = (ur[0] + ll[0]) / 2.0
+ zCenter = (ur[2] + ll[2]) / 2.0
+ newSignNP.setPosHprScale((SIGN_RIGHT + SIGN_LEFT) / 2.0 - xCenter * scale, -0.1, (SIGN_TOP + SIGN_BOTTOM) / 2.0 - zCenter * scale, 0.0, 0.0, 0.0, scale, scale, scale)
+ trophyOrigin = self.interior.find('**/trophy_origin')
+ trophy = self.buildTrophy()
+ if trophy:
+ trophy.reparentTo(trophyOrigin)
+ del self.colors
+ del self.dnaStore
+ del self.randomGenerator
+ self.interior.flattenMedium()
+
+ def setZoneIdAndBlock(self, zoneId, block):
+ self.zoneId = zoneId
+ self.block = block
+
+ def setToonData(self, toonData):
+ savedBy = cPickle.loads(toonData)
+ self.savedBy = savedBy
+
+ def buildTrophy(self):
+ if self.savedBy == None:
+ return
+ numToons = len(self.savedBy)
+ pos = 1.25 - 1.25 * numToons
+ trophy = hidden.attachNewNode('trophy')
+ for avId, name, dnaTuple, isGM in self.savedBy:
+ frame = self.buildFrame(name, dnaTuple)
+ frame.reparentTo(trophy)
+ frame.setPos(pos, 0, 0)
+ pos += 2.5
+
+ return trophy
+
+ def buildFrame(self, name, dnaTuple):
+ frame = loader.loadModel('phase_3.5/models/modules/trophy_frame')
+ dna = ToonDNA.ToonDNA()
+ apply(dna.newToonFromProperties, dnaTuple)
+ head = ToonHead.ToonHead()
+ head.setupHead(dna)
+ head.setPosHprScale(0, -0.05, -0.05, 180, 0, 0, 0.55, 0.02, 0.55)
+ if dna.head[0] == 'r':
+ head.setZ(-0.15)
+ elif dna.head[0] == 'h':
+ head.setZ(0.05)
+ elif dna.head[0] == 'm':
+ head.setScale(0.45, 0.02, 0.45)
+ head.reparentTo(frame)
+ nameText = TextNode('trophy')
+ nameText.setFont(ToontownGlobals.getToonFont())
+ nameText.setAlign(TextNode.ACenter)
+ nameText.setTextColor(0, 0, 0, 1)
+ nameText.setWordwrap(5.36 * FrameScale)
+ nameText.setText(name)
+ namePath = frame.attachNewNode(nameText.generate())
+ namePath.setPos(0, -0.03, -.6)
+ namePath.setScale(0.186 / FrameScale)
+ frame.setScale(FrameScale, 1.0, FrameScale)
+ return frame
+
+ def setState(self, state, timestamp):
+ self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterToon(self):
+ pass
+
+ def exitToon(self):
+ pass
+
+ def enterBeingTakenOver(self, ts):
+ messenger.send('clearOutToonInterior')
+
+ def exitBeingTakenOver(self):
+ pass
diff --git a/toontown/building/DistributedToonInteriorAI.py b/toontown/building/DistributedToonInteriorAI.py
new file mode 100644
index 0000000..07e502c
--- /dev/null
+++ b/toontown/building/DistributedToonInteriorAI.py
@@ -0,0 +1,69 @@
+from toontown.toonbase.ToontownGlobals import *
+from otp.ai.AIBaseGlobal import *
+from direct.distributed.ClockDelta import *
+import cPickle
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObjectAI
+from direct.fsm import State
+from toontown.toon import NPCToons
+
+class DistributedToonInteriorAI(DistributedObjectAI.DistributedObjectAI):
+
+ def __init__(self, block, air, zoneId, building):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ self.block = block
+ self.zoneId = zoneId
+ self.building = building
+ self.npcs = NPCToons.createNpcsInZone(air, zoneId)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedToonInteriorAI', [
+ State.State('toon', self.enterToon, self.exitToon, [
+ 'beingTakenOver']),
+ State.State('beingTakenOver', self.enterBeingTakenOver, self.exitBeingTakenOver, []),
+ State.State('off', self.enterOff, self.exitOff, [])], 'toon', 'off')
+ self.fsm.enterInitialState()
+
+ def delete(self):
+ self.ignoreAll()
+ for npc in self.npcs:
+ npc.requestDelete()
+
+ del self.npcs
+ del self.fsm
+ del self.building
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def getZoneIdAndBlock(self):
+ r = [
+ self.zoneId, self.block]
+ return r
+
+ def getToonData(self):
+ return cPickle.dumps(self.building.savedBy, 1)
+
+ def getState(self):
+ r = [
+ self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]
+ return r
+
+ def setState(self, state):
+ self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
+ self.fsm.request(state)
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterToon(self):
+ pass
+
+ def exitToon(self):
+ pass
+
+ def enterBeingTakenOver(self):
+ pass
+
+ def exitBeingTakenOver(self):
+ pass
diff --git a/toontown/building/DistributedTrophyMgr.py b/toontown/building/DistributedTrophyMgr.py
new file mode 100644
index 0000000..ef334a7
--- /dev/null
+++ b/toontown/building/DistributedTrophyMgr.py
@@ -0,0 +1,30 @@
+from direct.distributed import DistributedObject
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import TTLocalizer
+
+class DistributedTrophyMgr(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTrophyMgr')
+ neverDisable = 1
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+
+ def generate(self):
+ if base.cr.trophyManager != None:
+ base.cr.trophyManager.delete()
+ base.cr.trophyManager = self
+ DistributedObject.DistributedObject.generate(self)
+ return
+
+ def disable(self):
+ base.cr.trophyManager = None
+ DistributedObject.DistributedObject.disable(self)
+ return
+
+ def delete(self):
+ base.cr.trophyManager = None
+ DistributedObject.DistributedObject.delete(self)
+ return
+
+ def d_requestTrophyScore(self):
+ self.sendUpdate('requestTrophyScore', [])
diff --git a/toontown/building/DistributedTutorialInterior.py b/toontown/building/DistributedTutorialInterior.py
new file mode 100644
index 0000000..81cfde5
--- /dev/null
+++ b/toontown/building/DistributedTutorialInterior.py
@@ -0,0 +1,163 @@
+from toontown.toonbase.ToonBaseGlobal import *
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from direct.distributed.ClockDelta import *
+from toontown.toonbase import ToontownGlobals
+import ToonInterior
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+import random
+import ToonInteriorColors
+from toontown.hood import ZoneUtil
+from toontown.char import Char
+from toontown.suit import SuitDNA
+from toontown.suit import Suit
+from toontown.quest import QuestParser
+
+class DistributedTutorialInterior(DistributedObject.DistributedObject):
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ self.setup()
+
+ def disable(self):
+ self.interior.removeNode()
+ del self.interior
+ self.street.removeNode()
+ del self.street
+ self.sky.removeNode()
+ del self.sky
+ self.mickeyMovie.cleanup()
+ del self.mickeyMovie
+ self.suitWalkTrack.finish()
+ del self.suitWalkTrack
+ self.suit.delete()
+ del self.suit
+ self.ignore('enterTutotialInterior')
+ DistributedObject.DistributedObject.disable(self)
+
+ def delete(self):
+ DistributedObject.DistributedObject.delete(self)
+
+ def randomDNAItem(self, category, findFunc):
+ codeCount = self.dnaStore.getNumCatalogCodes(category)
+ index = self.randomGenerator.randint(0, codeCount - 1)
+ code = self.dnaStore.getCatalogCode(category, index)
+ return findFunc(code)
+
+ def replaceRandomInModel(self, model):
+ baseTag = 'random_'
+ npc = model.findAllMatches('**/' + baseTag + '???_*')
+ for i in range(npc.getNumPaths()):
+ np = npc.getPath(i)
+ name = np.getName()
+ b = len(baseTag)
+ category = name[b + 4:]
+ key1 = name[b]
+ key2 = name[b + 1]
+ if key1 == 'm':
+ model = self.randomDNAItem(category, self.dnaStore.findNode)
+ newNP = model.copyTo(np)
+ c = render.findAllMatches('**/collision')
+ c.stash()
+ if key2 == 'r':
+ self.replaceRandomInModel(newNP)
+ elif key1 == 't':
+ texture = self.randomDNAItem(category, self.dnaStore.findTexture)
+ np.setTexture(texture, 100)
+ newNP = np
+ if key2 == 'c':
+ if category == 'TI_wallpaper' or category == 'TI_wallpaper_border':
+ self.randomGenerator.seed(self.zoneId)
+ newNP.setColorScale(self.randomGenerator.choice(self.colors[category]))
+ else:
+ newNP.setColorScale(self.randomGenerator.choice(self.colors[category]))
+
+ def setup(self):
+ self.dnaStore = base.cr.playGame.dnaStore
+ self.randomGenerator = random.Random()
+ self.randomGenerator.seed(self.zoneId)
+ self.interior = loader.loadModel('phase_3.5/models/modules/toon_interior_tutorial')
+ self.interior.reparentTo(render)
+ dnaStore = DNAStorage()
+ node = loader.loadDNAFile(self.cr.playGame.hood.dnaStore, 'phase_3.5/dna/tutorial_street.dna')
+ self.street = render.attachNewNode(node)
+ self.street.flattenMedium()
+ self.street.setPosHpr(-17, 42, -0.5, 180, 0, 0)
+ self.street.find('**/tb2:toon_landmark_TT_A1_DNARoot').stash()
+ self.street.find('**/tb1:toon_landmark_hqTT_DNARoot/**/door_flat_0').stash()
+ self.street.findAllMatches('**/+CollisionNode').stash()
+ self.skyFile = 'phase_3.5/models/props/TT_sky'
+ self.sky = loader.loadModel(self.skyFile)
+ self.sky.setScale(0.8)
+ self.sky.reparentTo(render)
+ self.sky.setDepthTest(0)
+ self.sky.setDepthWrite(0)
+ self.sky.setBin('background', 100)
+ self.sky.find('**/Sky').reparentTo(self.sky, -1)
+ hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId)
+ self.colors = ToonInteriorColors.colors[hoodId]
+ self.replaceRandomInModel(self.interior)
+ doorModelName = 'door_double_round_ul'
+ if doorModelName[-1:] == 'r':
+ doorModelName = doorModelName[:-1] + 'l'
+ else:
+ doorModelName = doorModelName[:-1] + 'r'
+ door = self.dnaStore.findNode(doorModelName)
+ door_origin = render.find('**/door_origin;+s')
+ doorNP = door.copyTo(door_origin)
+ door_origin.setScale(0.8, 0.8, 0.8)
+ door_origin.setPos(door_origin, 0, -0.025, 0)
+ color = self.randomGenerator.choice(self.colors['TI_door'])
+ DNADoor.setupDoor(doorNP, self.interior, door_origin, self.dnaStore, str(self.block), color)
+ doorFrame = doorNP.find('door_*_flat')
+ doorFrame.wrtReparentTo(self.interior)
+ doorFrame.setColor(color)
+ del self.colors
+ del self.dnaStore
+ del self.randomGenerator
+ self.interior.flattenMedium()
+ npcOrigin = self.interior.find('**/npc_origin_' + `(self.npc.posIndex)`)
+ if not npcOrigin.isEmpty():
+ self.npc.reparentTo(npcOrigin)
+ self.npc.clearMat()
+ self.createSuit()
+ self.mickeyMovie = QuestParser.NPCMoviePlayer('tutorial_mickey', base.localAvatar, self.npc)
+ place = base.cr.playGame.getPlace()
+ if place and hasattr(place, 'fsm') and place.fsm.getCurrentState().getName():
+ self.notify.info('Tutorial movie: Place ready.')
+ self.playMovie()
+ else:
+ self.notify.info('Tutorial movie: Waiting for place=%s, has fsm=%s' % (place, hasattr(place, 'fsm')))
+ if hasattr(place, 'fsm'):
+ self.notify.info('Tutorial movie: place state=%s' % place.fsm.getCurrentState().getName())
+ self.acceptOnce('enterTutorialInterior', self.playMovie)
+
+ def playMovie(self):
+ self.notify.info('Tutorial movie: Play.')
+ self.mickeyMovie.play()
+
+ def createSuit(self):
+ self.suit = Suit.Suit()
+ suitDNA = SuitDNA.SuitDNA()
+ suitDNA.newSuit('f')
+ self.suit.setDNA(suitDNA)
+ self.suit.loop('neutral')
+ self.suit.setPosHpr(-20, 8, 0, 0, 0, 0)
+ self.suit.reparentTo(self.interior)
+ self.suitWalkTrack = Sequence(self.suit.hprInterval(0.1, Vec3(0, 0, 0)), Func(self.suit.loop, 'walk'), self.suit.posInterval(2, Point3(-20, 20, 0)), Func(self.suit.loop, 'neutral'), Wait(1.0), self.suit.hprInterval(0.1, Vec3(180, 0, 0)), Func(self.suit.loop, 'walk'), self.suit.posInterval(2, Point3(-20, 10, 0)), Func(self.suit.loop, 'neutral'), Wait(1.0))
+ self.suitWalkTrack.loop()
+
+ def setZoneIdAndBlock(self, zoneId, block):
+ self.zoneId = zoneId
+ self.block = block
+
+ def setTutorialNpcId(self, npcId):
+ self.npcId = npcId
+ self.npc = self.cr.doId2do[npcId]
diff --git a/toontown/building/DistributedVPElevator.py b/toontown/building/DistributedVPElevator.py
new file mode 100644
index 0000000..afcbf51
--- /dev/null
+++ b/toontown/building/DistributedVPElevator.py
@@ -0,0 +1,28 @@
+import DistributedElevator
+import DistributedBossElevator
+from ElevatorConstants import *
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import TTLocalizer
+
+class DistributedVPElevator(DistributedBossElevator.DistributedBossElevator):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVPElevator')
+
+ def __init__(self, cr):
+ DistributedBossElevator.DistributedBossElevator.__init__(self, cr)
+ self.type = ELEVATOR_VP
+ self.countdownTime = ElevatorData[self.type]['countdown']
+
+ def setupElevator(self):
+ self.elevatorModel = loader.loadModel('phase_9/models/cogHQ/cogHQ_elevator')
+ icon = self.elevatorModel.find('**/big_frame/')
+ icon.hide()
+ self.leftDoor = self.elevatorModel.find('**/left-door')
+ self.rightDoor = self.elevatorModel.find('**/right-door')
+ geom = base.cr.playGame.hood.loader.geom
+ locator = geom.find('**/elevator_locator')
+ self.elevatorModel.reparentTo(locator)
+ self.elevatorModel.setH(180)
+ DistributedElevator.DistributedElevator.setupElevator(self)
+
+ def getDestName(self):
+ return TTLocalizer.ElevatorSellBotBoss
diff --git a/toontown/building/DistributedVPElevatorAI.py b/toontown/building/DistributedVPElevatorAI.py
new file mode 100644
index 0000000..d63dbad
--- /dev/null
+++ b/toontown/building/DistributedVPElevatorAI.py
@@ -0,0 +1,23 @@
+from ElevatorConstants import *
+import DistributedBossElevatorAI
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownGlobals
+
+class DistributedVPElevatorAI(DistributedBossElevatorAI.DistributedBossElevatorAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVPElevatorAI')
+
+ def __init__(self, air, bldg, zone, antiShuffle=0, minLaff=0):
+ DistributedBossElevatorAI.DistributedBossElevatorAI.__init__(self, air, bldg, zone, antiShuffle=antiShuffle, minLaff=minLaff)
+ self.type = ELEVATOR_VP
+ self.countdownTime = ElevatorData[self.type]['countdown']
+
+ def checkBoard(self, av):
+ dept = ToontownGlobals.cogHQZoneId2deptIndex(self.zone)
+ boardingResult = 0
+ if av.hp < self.minLaff:
+ boardingResult = REJECT_MINLAFF
+ if not av.readyForPromotion(dept):
+ boardingResult = REJECT_PROMOTION
+ if ToontownGlobals.SELLBOT_NERF_HOLIDAY in self.air.holidayManager.currentHolidays:
+ boardingResult = 0
+ return boardingResult
diff --git a/toontown/building/DoorTypes.py b/toontown/building/DoorTypes.py
new file mode 100644
index 0000000..9541a0f
--- /dev/null
+++ b/toontown/building/DoorTypes.py
@@ -0,0 +1,11 @@
+EXT_STANDARD = 1
+INT_STANDARD = 2
+EXT_HQ = 3
+INT_HQ = 4
+EXT_HOUSE = 5
+INT_HOUSE = 6
+EXT_COGHQ = 7
+INT_COGHQ = 8
+EXT_KS = 9
+INT_KS = 10
+EXT_ANIM_STANDARD = 11
diff --git a/toontown/building/Elevator.py b/toontown/building/Elevator.py
new file mode 100644
index 0000000..8aae96c
--- /dev/null
+++ b/toontown/building/Elevator.py
@@ -0,0 +1,159 @@
+from pandac.PandaModules import *
+from toontown.toonbase.ToonBaseGlobal import *
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.fsm import StateData
+from toontown.launcher import DownloadForceAcknowledge
+from toontown.toonbase import TTLocalizer
+from direct.showbase import PythonUtil
+
+class Elevator(StateData.StateData):
+
+ def __init__(self, elevatorState, doneEvent, distElevator):
+ StateData.StateData.__init__(self, doneEvent)
+ self.fsm = ClassicFSM.ClassicFSM('Elevator', [State.State('start', self.enterStart, self.exitStart, ['elevatorDFA']),
+ State.State('elevatorDFA', self.enterElevatorDFA, self.exitElevatorDFA, ['requestBoard', 'final']),
+ State.State('requestBoard', self.enterRequestBoard, self.exitRequestBoard, ['boarding']),
+ State.State('boarding', self.enterBoarding, self.exitBoarding, ['boarded']),
+ State.State('boarded', self.enterBoarded, self.exitBoarded, ['requestExit', 'elevatorClosing', 'final']),
+ State.State('requestExit', self.enterRequestExit, self.exitRequestExit, ['exiting', 'elevatorClosing']),
+ State.State('elevatorClosing', self.enterElevatorClosing, self.exitElevatorClosing, ['final']),
+ State.State('exiting', self.enterExiting, self.exitExiting, ['final']),
+ State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final')
+ self.dfaDoneEvent = 'elevatorDfaDoneEvent'
+ self.elevatorState = elevatorState
+ self.distElevator = distElevator
+ distElevator.elevatorFSM = self
+ self.reverseBoardingCamera = False
+ self.skipDFABoard = 0
+
+ def load(self):
+ self.elevatorState.addChild(self.fsm)
+ self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui')
+ self.upButton = self.buttonModels.find('**//InventoryButtonUp')
+ self.downButton = self.buttonModels.find('**/InventoryButtonDown')
+ self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover')
+
+ def unload(self):
+ self.elevatorState.removeChild(self.fsm)
+ self.distElevator.elevatorFSM = None
+ del self.distElevator
+ del self.fsm
+ del self.elevatorState
+ self.buttonModels.removeNode()
+ del self.buttonModels
+ del self.upButton
+ del self.downButton
+ del self.rolloverButton
+ return
+
+ def enter(self):
+ self.fsm.enterInitialState()
+ self.fsm.request('elevatorDFA')
+
+ def exit(self):
+ self.ignoreAll()
+
+ def signalDone(self, doneStatus):
+ messenger.send(self.doneEvent, [doneStatus])
+
+ def enterStart(self):
+ pass
+
+ def exitStart(self):
+ pass
+
+ def enterElevatorDFA(self):
+ self.acceptOnce(self.dfaDoneEvent, self.enterDFACallback)
+ self.dfa = DownloadForceAcknowledge.DownloadForceAcknowledge(self.dfaDoneEvent)
+ self.dfa.enter(7)
+
+ def enterDFACallback(self, DFAdoneStatus):
+ self.dfa.exit()
+ del self.dfa
+ if DFAdoneStatus['mode'] == 'complete':
+ if self.skipDFABoard:
+ self.skipDFABoard = 0
+ else:
+ self.fsm.request('requestBoard')
+ elif DFAdoneStatus['mode'] == 'incomplete':
+ elevatorDoneStatus = {}
+ elevatorDoneStatus['where'] = 'reject'
+ messenger.send(self.doneEvent, [elevatorDoneStatus])
+ else:
+ self.notify.error('Unrecognized doneStatus: ' + str(DFAdoneStatus))
+
+ def exitElevatorDFA(self):
+ self.ignore(self.dfaDoneEvent)
+
+ def enterRequestBoard(self):
+ messenger.send(self.distElevator.uniqueName('enterElevatorOK'))
+
+ def exitRequestBoard(self):
+ pass
+
+ def enterBoarding(self, nodePath):
+ camera.wrtReparentTo(nodePath)
+ if self.reverseBoardingCamera:
+ heading = PythonUtil.fitDestAngle2Src(camera.getH(nodePath), 180)
+ self.cameraBoardTrack = LerpPosHprInterval(camera, 1.5, Point3(0, 18, 8), Point3(heading, -10, 0))
+ else:
+ self.cameraBoardTrack = LerpPosHprInterval(camera, 1.5, Point3(0, -16, 5.5), Point3(0, 0, 0))
+ self.cameraBoardTrack.start()
+
+ def exitBoarding(self):
+ self.ignore('boardedElevator')
+
+ def enterBoarded(self):
+ self.enableExitButton()
+
+ def exitBoarded(self):
+ self.cameraBoardTrack.finish()
+ self.disableExitButton()
+
+ def enableExitButton(self):
+ self.exitButton = DirectButton(relief=None, text=TTLocalizer.ElevatorHopOff, text_fg=(0.9, 0.9, 0.9, 1), text_pos=(0, -0.23), text_scale=TTLocalizer.EexitButton, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(0.5, 0.5, 0.5, 1), image_scale=(20, 1, 11), pos=(0, 0, 0.8), scale=0.15, command=lambda self = self: self.fsm.request('requestExit'))
+ if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty and localAvatar.boardingParty.getGroupLeader(localAvatar.doId) and localAvatar.boardingParty.getGroupLeader(localAvatar.doId) != localAvatar.doId:
+ self.exitButton['command'] = None
+ self.exitButton.hide()
+ if self.distElevator.antiShuffle:
+ self.hopWarning = DirectLabel(parent=self.exitButton, relief=None, pos=Vec3(0, 0, 0.0), text=TTLocalizer.ElevatorStayOff, text_fg=(0.9, 0.9, 0.9, 1), text_pos=(0, -1.1), text_scale=0.6)
+ self.hopWarning.reparentTo(self.exitButton.stateNodePath[2])
+ else:
+ self.hopWarning = None
+ return
+
+ def disableExitButton(self):
+ if self.hopWarning:
+ self.hopWarning.destroy()
+ self.exitButton.destroy()
+
+ def enterRequestExit(self):
+ messenger.send('elevatorExitButton')
+
+ def exitRequestExit(self):
+ pass
+
+ def enterElevatorClosing(self):
+ pass
+
+ def exitElevatorClosing(self):
+ pass
+
+ def enterExiting(self):
+ pass
+
+ def exitExiting(self):
+ pass
+
+ def enterFinal(self):
+ pass
+
+ def exitFinal(self):
+ pass
+
+ def setReverseBoardingCamera(self, newVal):
+ self.reverseBoardingCamera = newVal
diff --git a/toontown/building/ElevatorConstants.py b/toontown/building/ElevatorConstants.py
new file mode 100644
index 0000000..a8bbcb2
--- /dev/null
+++ b/toontown/building/ElevatorConstants.py
@@ -0,0 +1,131 @@
+from pandac.PandaModules import *
+ELEVATOR_NORMAL = 0
+ELEVATOR_VP = 1
+ELEVATOR_MINT = 2
+ELEVATOR_CFO = 3
+ELEVATOR_CJ = 4
+ELEVATOR_OFFICE = 5
+ELEVATOR_STAGE = 6
+ELEVATOR_BB = 7
+ELEVATOR_COUNTRY_CLUB = 8
+REJECT_NOREASON = 0
+REJECT_SHUFFLE = 1
+REJECT_MINLAFF = 2
+REJECT_NOSEAT = 3
+REJECT_PROMOTION = 4
+REJECT_BLOCKED_ROOM = 5
+REJECT_NOT_YET_AVAILABLE = 6
+REJECT_BOARDINGPARTY = 7
+REJECT_NOTPAID = 8
+MAX_GROUP_BOARDING_TIME = 6.0
+if __dev__:
+ try:
+ config = simbase.config
+ except:
+ config = base.config
+
+ elevatorCountdown = config.GetFloat('elevator-countdown', -1)
+ if elevatorCountdown != -1:
+ bboard.post('elevatorCountdown', elevatorCountdown)
+ElevatorData = {ELEVATOR_NORMAL: {'openTime': 2.0,
+ 'closeTime': 2.0,
+ 'width': 3.5,
+ 'countdown': bboard.get('elevatorCountdown', 15.0),
+ 'sfxVolume': 1.0,
+ 'collRadius': 5},
+ ELEVATOR_VP: {'openTime': 4.0,
+ 'closeTime': 4.0,
+ 'width': 11.5,
+ 'countdown': bboard.get('elevatorCountdown', 30.0),
+ 'sfxVolume': 0.7,
+ 'collRadius': 7.5},
+ ELEVATOR_MINT: {'openTime': 2.0,
+ 'closeTime': 2.0,
+ 'width': 5.875,
+ 'countdown': bboard.get('elevatorCountdown', 15.0),
+ 'sfxVolume': 1.0,
+ 'collRadius': 5},
+ ELEVATOR_OFFICE: {'openTime': 2.0,
+ 'closeTime': 2.0,
+ 'width': 5.875,
+ 'countdown': bboard.get('elevatorCountdown', 15.0),
+ 'sfxVolume': 1.0,
+ 'collRadius': 5},
+ ELEVATOR_CFO: {'openTime': 3.0,
+ 'closeTime': 3.0,
+ 'width': 8.166,
+ 'countdown': bboard.get('elevatorCountdown', 30.0),
+ 'sfxVolume': 0.7,
+ 'collRadius': 7.5},
+ ELEVATOR_CJ: {'openTime': 4.0,
+ 'closeTime': 4.0,
+ 'width': 15.8,
+ 'countdown': bboard.get('elevatorCountdown', 30.0),
+ 'sfxVolume': 0.7,
+ 'collRadius': 7.5},
+ ELEVATOR_STAGE: {'openTime': 4.0,
+ 'closeTime': 4.0,
+ 'width': 6.5,
+ 'countdown': bboard.get('elevatorCountdown', 42.0),
+ 'sfxVolume': 1.0,
+ 'collRadius': 9.5},
+ ELEVATOR_BB: {'openTime': 4.0,
+ 'closeTime': 4.0,
+ 'width': 6.3,
+ 'countdown': bboard.get('elevatorCountdown', 30.0),
+ 'sfxVolume': 0.7,
+ 'collRadius': 7.5},
+ ELEVATOR_COUNTRY_CLUB: {'openTime': 2.0,
+ 'closeTime': 2.0,
+ 'width': 5.875,
+ 'countdown': bboard.get('elevatorCountdown', 15.0),
+ 'sfxVolume': 1.0,
+ 'collRadius': 4}}
+TOON_BOARD_ELEVATOR_TIME = 1.0
+TOON_EXIT_ELEVATOR_TIME = 1.0
+TOON_VICTORY_EXIT_TIME = 1.0
+SUIT_HOLD_ELEVATOR_TIME = 1.0
+SUIT_LEAVE_ELEVATOR_TIME = 2.0
+INTERIOR_ELEVATOR_COUNTDOWN_TIME = 90
+LIGHT_OFF_COLOR = Vec4(0.5, 0.5, 0.5, 1.0)
+LIGHT_ON_COLOR = Vec4(1.0, 1.0, 1.0, 1.0)
+ElevatorPoints = [[-1.5, 5, 0.1],
+ [1.5, 5, 0.1],
+ [-2.5, 3, 0.1],
+ [2.5, 3, 0.1],
+ [-3.5, 5, 0.1],
+ [3.5, 5, 0.1],
+ [-4, 3, 0.1],
+ [4, 3, 0.1]]
+JumpOutOffsets = [[-1.5, -5, -0],
+ [1.5, -5, -0],
+ [-2.5, -7, -0],
+ [2.5, -7, -0],
+ [-3.5, -5, -0],
+ [3.5, -5, -0],
+ [-4, -7, -0],
+ [4, -7, -0]]
+BigElevatorPoints = [[-2.5, 9, 0.1],
+ [2.5, 9, 0.1],
+ [-8.0, 9, 0.1],
+ [8.0, 9, 0.1],
+ [-2.5, 4, 0.1],
+ [2.5, 4, 0.1],
+ [-8.0, 4, 0.1],
+ [8.0, 4, 0.1]]
+BossbotElevatorPoints = [[-2.5, 7.5, 0.1],
+ [2.5, 7.5, 0.1],
+ [-5.5, 7.5, 0.1],
+ [5.5, 7.5, 0.1],
+ [-2.5, 3.5, 0.1],
+ [2.5, 3.5, 0.1],
+ [-5.5, 3.5, 0.1],
+ [5.5, 3.5, 0.1]]
+ElevatorOutPoints = [[-4.6, -5.2, 0.1],
+ [4.6, -5.2, 0.1],
+ [-1.6, -6.2, 0.1],
+ [1.6, -6.2, 0.1]]
+ElevatorOutPointsFar = [[-4.6, -12.2, 0.1],
+ [4.6, -12.2, 0.1],
+ [-1.6, -13.2, 0.1],
+ [1.6, -13.2, 0.1]]
diff --git a/toontown/building/ElevatorUtils.py b/toontown/building/ElevatorUtils.py
new file mode 100644
index 0000000..c11def8
--- /dev/null
+++ b/toontown/building/ElevatorUtils.py
@@ -0,0 +1,111 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from ElevatorConstants import *
+
+def getLeftClosePoint(type):
+ width = ElevatorData[type]['width']
+ return Point3(width, 0, 0)
+
+
+def getRightClosePoint(type):
+ width = ElevatorData[type]['width']
+ return Point3(-width, 0, 0)
+
+
+def getLeftOpenPoint(type):
+ return Point3(0, 0, 0)
+
+
+def getRightOpenPoint(type):
+ return Point3(0, 0, 0)
+
+
+def closeDoors(leftDoor, rightDoor, type = ELEVATOR_NORMAL):
+ closedPosLeft = getLeftClosePoint(type)
+ closedPosRight = getRightClosePoint(type)
+ leftDoor.setPos(closedPosLeft)
+ rightDoor.setPos(closedPosRight)
+
+
+def openDoors(leftDoor, rightDoor, type = ELEVATOR_NORMAL):
+ openPosLeft = getLeftOpenPoint(type)
+ openPosRight = getRightOpenPoint(type)
+ leftDoor.setPos(openPosLeft)
+ rightDoor.setPos(openPosRight)
+
+
+def getLeftOpenInterval(distObj, leftDoor, type):
+ openTime = ElevatorData[type]['openTime']
+ closedPos = getLeftClosePoint(type)
+ openPos = getLeftOpenPoint(type)
+ leftOpenInterval = LerpPosInterval(leftDoor, openTime, openPos, startPos=closedPos, blendType='easeOut', name=distObj.uniqueName('leftDoorOpen'))
+ return leftOpenInterval
+
+
+def getRightOpenInterval(distObj, rightDoor, type):
+ openTime = ElevatorData[type]['openTime']
+ closedPos = getRightClosePoint(type)
+ openPos = getRightOpenPoint(type)
+ rightOpenInterval = LerpPosInterval(rightDoor, openTime, openPos, startPos=closedPos, blendType='easeOut', name=distObj.uniqueName('rightDoorOpen'))
+ return rightOpenInterval
+
+
+def getOpenInterval(distObj, leftDoor, rightDoor, openSfx, finalOpenSfx, type = ELEVATOR_NORMAL):
+ left = getLeftOpenInterval(distObj, leftDoor, type)
+ right = getRightOpenInterval(distObj, rightDoor, type)
+ openDuration = left.getDuration()
+ sfxVolume = ElevatorData[type]['sfxVolume']
+ if finalOpenSfx:
+ sound = Sequence(SoundInterval(openSfx, duration=openDuration, volume=sfxVolume, node=leftDoor), SoundInterval(finalOpenSfx, volume=sfxVolume, node=leftDoor))
+ else:
+ sound = SoundInterval(openSfx, volume=sfxVolume, node=leftDoor)
+ return Parallel(sound, left, right)
+
+
+def getLeftCloseInterval(distObj, leftDoor, type):
+ closeTime = ElevatorData[type]['closeTime']
+ closedPos = getLeftClosePoint(type)
+ openPos = getLeftOpenPoint(type)
+ leftCloseInterval = LerpPosInterval(leftDoor, closeTime, closedPos, startPos=openPos, blendType='easeOut', name=distObj.uniqueName('leftDoorClose'))
+ return leftCloseInterval
+
+
+def getRightCloseInterval(distObj, rightDoor, type):
+ closeTime = ElevatorData[type]['closeTime']
+ closedPos = getRightClosePoint(type)
+ openPos = getRightOpenPoint(type)
+ rightCloseInterval = LerpPosInterval(rightDoor, closeTime, closedPos, startPos=openPos, blendType='easeOut', name=distObj.uniqueName('rightDoorClose'))
+ return rightCloseInterval
+
+
+def getCloseInterval(distObj, leftDoor, rightDoor, closeSfx, finalCloseSfx, type = ELEVATOR_NORMAL):
+ left = getLeftCloseInterval(distObj, leftDoor, type)
+ right = getRightCloseInterval(distObj, rightDoor, type)
+ closeDuration = left.getDuration()
+ sfxVolume = ElevatorData[type]['sfxVolume']
+ if finalCloseSfx:
+ sound = Sequence(SoundInterval(closeSfx, duration=closeDuration, volume=sfxVolume, node=leftDoor), SoundInterval(finalCloseSfx, volume=sfxVolume, node=leftDoor))
+ else:
+ sound = SoundInterval(closeSfx, volume=sfxVolume, node=leftDoor)
+ return Parallel(sound, left, right)
+
+
+def getRideElevatorInterval(type = ELEVATOR_NORMAL):
+ if type == ELEVATOR_VP or type == ELEVATOR_CFO or type == ELEVATOR_CJ:
+ yValue = 30
+ zMin = 7.8
+ zMid = 8
+ zMax = 8.2
+ elif type == ELEVATOR_BB:
+ yValue = 21
+ zMin = 7
+ zMid = 7.2
+ zMax = 7.4
+ if type in (ELEVATOR_VP,
+ ELEVATOR_CFO,
+ ELEVATOR_CJ,
+ ELEVATOR_BB):
+ ival = Sequence(Wait(0.5), LerpPosInterval(camera, 0.5, Point3(0, yValue, zMin), startPos=Point3(0, yValue, zMid), blendType='easeOut'), LerpPosInterval(camera, 0.5, Point3(0, yValue, zMid), startPos=Point3(0, yValue, zMin)), Wait(1.0), LerpPosInterval(camera, 0.5, Point3(0, yValue, zMax), startPos=Point3(0, yValue, zMid), blendType='easeOut'), LerpPosInterval(camera, 1.0, Point3(0, yValue, zMid), startPos=Point3(0, yValue, zMax)))
+ else:
+ ival = Sequence(Wait(0.5), LerpPosInterval(camera, 0.5, Point3(0, 14, 3.8), startPos=Point3(0, 14, 4), blendType='easeOut'), LerpPosInterval(camera, 0.5, Point3(0, 14, 4), startPos=Point3(0, 14, 3.8)), Wait(1.0), LerpPosInterval(camera, 0.5, Point3(0, 14, 4.2), startPos=Point3(0, 14, 4), blendType='easeOut'), LerpPosInterval(camera, 1.0, Point3(0, 14, 4), startPos=Point3(0, 14, 4.2)))
+ return ival
diff --git a/toontown/building/FADoorCodes.py b/toontown/building/FADoorCodes.py
new file mode 100644
index 0000000..c483d03
--- /dev/null
+++ b/toontown/building/FADoorCodes.py
@@ -0,0 +1,29 @@
+from toontown.toonbase import TTLocalizer
+UNLOCKED = 0
+TALK_TO_TOM = 1
+DEFEAT_FLUNKY_HQ = 2
+TALK_TO_HQ = 3
+WRONG_DOOR_HQ = 4
+GO_TO_PLAYGROUND = 5
+DEFEAT_FLUNKY_TOM = 6
+TALK_TO_HQ_TOM = 7
+SUIT_APPROACHING = 8
+BUILDING_TAKEOVER = 9
+SB_DISGUISE_INCOMPLETE = 10
+CB_DISGUISE_INCOMPLETE = 11
+LB_DISGUISE_INCOMPLETE = 12
+BB_DISGUISE_INCOMPLETE = 13
+reasonDict = {UNLOCKED: TTLocalizer.FADoorCodes_UNLOCKED,
+ TALK_TO_TOM: TTLocalizer.FADoorCodes_TALK_TO_TOM,
+ DEFEAT_FLUNKY_HQ: TTLocalizer.FADoorCodes_DEFEAT_FLUNKY_HQ,
+ TALK_TO_HQ: TTLocalizer.FADoorCodes_TALK_TO_HQ,
+ WRONG_DOOR_HQ: TTLocalizer.FADoorCodes_WRONG_DOOR_HQ,
+ GO_TO_PLAYGROUND: TTLocalizer.FADoorCodes_GO_TO_PLAYGROUND,
+ DEFEAT_FLUNKY_TOM: TTLocalizer.FADoorCodes_DEFEAT_FLUNKY_TOM,
+ TALK_TO_HQ_TOM: TTLocalizer.FADoorCodes_TALK_TO_HQ_TOM,
+ SUIT_APPROACHING: TTLocalizer.FADoorCodes_SUIT_APPROACHING,
+ BUILDING_TAKEOVER: TTLocalizer.FADoorCodes_BUILDING_TAKEOVER,
+ SB_DISGUISE_INCOMPLETE: TTLocalizer.FADoorCodes_SB_DISGUISE_INCOMPLETE,
+ CB_DISGUISE_INCOMPLETE: TTLocalizer.FADoorCodes_CB_DISGUISE_INCOMPLETE,
+ LB_DISGUISE_INCOMPLETE: TTLocalizer.FADoorCodes_LB_DISGUISE_INCOMPLETE,
+ BB_DISGUISE_INCOMPLETE: TTLocalizer.FADoorCodes_BB_DISGUISE_INCOMPLETE}
diff --git a/toontown/building/GagshopBuildingAI.py b/toontown/building/GagshopBuildingAI.py
new file mode 100644
index 0000000..8f09a39
--- /dev/null
+++ b/toontown/building/GagshopBuildingAI.py
@@ -0,0 +1,40 @@
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+import DistributedDoorAI, DistributedGagshopInteriorAI, FADoorCodes, DoorTypes
+from toontown.toon import NPCToons
+from toontown.quest import Quests
+
+class GagshopBuildingAI:
+
+ def __init__(self, air, exteriorZone, interiorZone, blockNumber):
+ self.air = air
+ self.exteriorZone = exteriorZone
+ self.interiorZone = interiorZone
+ self.setup(blockNumber)
+
+ def cleanup(self):
+ for npc in self.npcs:
+ npc.requestDelete()
+
+ del self.npcs
+ self.door.requestDelete()
+ del self.door
+ self.insideDoor.requestDelete()
+ del self.insideDoor
+ self.interior.requestDelete()
+ del self.interior
+
+ def setup(self, blockNumber):
+ self.interior = DistributedGagshopInteriorAI.DistributedGagshopInteriorAI(blockNumber, self.air, self.interiorZone)
+ self.npcs = NPCToons.createNpcsInZone(self.air, self.interiorZone)
+ self.interior.generateWithRequired(self.interiorZone)
+ door = DistributedDoorAI.DistributedDoorAI(self.air, blockNumber, DoorTypes.EXT_STANDARD)
+ insideDoor = DistributedDoorAI.DistributedDoorAI(self.air, blockNumber, DoorTypes.INT_STANDARD)
+ door.setOtherDoor(insideDoor)
+ insideDoor.setOtherDoor(door)
+ door.zoneId = self.exteriorZone
+ insideDoor.zoneId = self.interiorZone
+ door.generateWithRequired(self.exteriorZone)
+ insideDoor.generateWithRequired(self.interiorZone)
+ self.door = door
+ self.insideDoor = insideDoor
diff --git a/toontown/building/HQBuildingAI.py b/toontown/building/HQBuildingAI.py
new file mode 100644
index 0000000..2874184
--- /dev/null
+++ b/toontown/building/HQBuildingAI.py
@@ -0,0 +1,70 @@
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+import DistributedDoorAI, DistributedHQInteriorAI, FADoorCodes, DoorTypes
+from toontown.toon import NPCToons
+from toontown.quest import Quests
+
+class HQBuildingAI:
+
+ def __init__(self, air, exteriorZone, interiorZone, blockNumber):
+ self.air = air
+ self.exteriorZone = exteriorZone
+ self.interiorZone = interiorZone
+ self.setup(blockNumber)
+
+ def cleanup(self):
+ for npc in self.npcs:
+ npc.requestDelete()
+
+ del self.npcs
+ self.door0.requestDelete()
+ del self.door0
+ self.door1.requestDelete()
+ del self.door1
+ self.insideDoor0.requestDelete()
+ del self.insideDoor0
+ self.insideDoor1.requestDelete()
+ del self.insideDoor1
+ self.interior.requestDelete()
+ del self.interior
+
+ def setup(self, blockNumber):
+ self.interior = DistributedHQInteriorAI.DistributedHQInteriorAI(blockNumber, self.air, self.interiorZone)
+ self.npcs = NPCToons.createNpcsInZone(self.air, self.interiorZone)
+ self.interior.generateWithRequired(self.interiorZone)
+ door0 = DistributedDoorAI.DistributedDoorAI(self.air, blockNumber, DoorTypes.EXT_HQ, doorIndex=0)
+ door1 = DistributedDoorAI.DistributedDoorAI(self.air, blockNumber, DoorTypes.EXT_HQ, doorIndex=1)
+ insideDoor0 = DistributedDoorAI.DistributedDoorAI(self.air, blockNumber, DoorTypes.INT_HQ, doorIndex=0)
+ insideDoor1 = DistributedDoorAI.DistributedDoorAI(self.air, blockNumber, DoorTypes.INT_HQ, doorIndex=1)
+ door0.setOtherDoor(insideDoor0)
+ insideDoor0.setOtherDoor(door0)
+ door1.setOtherDoor(insideDoor1)
+ insideDoor1.setOtherDoor(door1)
+ door0.zoneId = self.exteriorZone
+ door1.zoneId = self.exteriorZone
+ insideDoor0.zoneId = self.interiorZone
+ insideDoor1.zoneId = self.interiorZone
+ door0.generateWithRequired(self.exteriorZone)
+ door1.generateWithRequired(self.exteriorZone)
+ door0.sendUpdate('setDoorIndex', [door0.getDoorIndex()])
+ door1.sendUpdate('setDoorIndex', [door1.getDoorIndex()])
+ insideDoor0.generateWithRequired(self.interiorZone)
+ insideDoor1.generateWithRequired(self.interiorZone)
+ insideDoor0.sendUpdate('setDoorIndex', [insideDoor0.getDoorIndex()])
+ insideDoor1.sendUpdate('setDoorIndex', [insideDoor1.getDoorIndex()])
+ self.door0 = door0
+ self.door1 = door1
+ self.insideDoor0 = insideDoor0
+ self.insideDoor1 = insideDoor1
+
+ def isSuitBlock(self):
+ return 0
+
+ def isSuitBuilding(self):
+ return 0
+
+ def isCogdo(self):
+ return 0
+
+ def isEstablishedSuitBlock(self):
+ return 0
diff --git a/toontown/building/KartShopBuildingAI.py b/toontown/building/KartShopBuildingAI.py
new file mode 100644
index 0000000..fdcfd82
--- /dev/null
+++ b/toontown/building/KartShopBuildingAI.py
@@ -0,0 +1,60 @@
+from direct.directnotify import DirectNotifyGlobal
+from pandac.PandaModules import *
+from toontown.building import FADoorCodes, DoorTypes
+from toontown.building.DistributedDoorAI import DistributedDoorAI
+from toontown.building.DistributedKartShopInteriorAI import DistributedKartShopInteriorAI
+from toontown.hood import ZoneUtil
+from toontown.toon import NPCToons
+from toontown.toonbase import ToontownGlobals
+if __debug__:
+ import pdb
+
+class KartShopBuildingAI:
+ notify = DirectNotifyGlobal.directNotify.newCategory('KartShopBuildingAI')
+
+ def __init__(self, air, exteriorZone, interiorZone, blockNumber):
+ self.air = air
+ self.exteriorZone = exteriorZone
+ self.interiorZone = interiorZone
+ self.setup(blockNumber)
+
+ def cleanup(self):
+ for npc in self.npcs:
+ npc.requestDelete()
+
+ del self.npcs
+ self.outsideDoor0.requestDelete()
+ self.outsideDoor1.requestDelete()
+ self.insideDoor0.requestDelete()
+ self.insideDoor1.requestDelete()
+ del self.outsideDoor0
+ del self.insideDoor0
+ del self.outsideDoor1
+ del self.insideDoor1
+ self.kartShopInterior.requestDelete()
+ del self.kartShopInterior
+
+ def setup(self, blockNumber):
+ self.kartShopInterior = DistributedKartShopInteriorAI(blockNumber, self.air, self.interiorZone)
+ self.npcs = NPCToons.createNpcsInZone(self.air, self.interiorZone)
+ self.kartShopInterior.generateWithRequired(self.interiorZone)
+ self.outsideDoor0 = DistributedDoorAI(self.air, blockNumber, DoorTypes.EXT_KS, doorIndex=1)
+ self.outsideDoor1 = DistributedDoorAI(self.air, blockNumber, DoorTypes.EXT_KS, doorIndex=2)
+ self.insideDoor0 = DistributedDoorAI(self.air, blockNumber, DoorTypes.INT_KS, doorIndex=1)
+ self.insideDoor1 = DistributedDoorAI(self.air, blockNumber, DoorTypes.INT_KS, doorIndex=2)
+ self.outsideDoor0.setOtherDoor(self.insideDoor0)
+ self.outsideDoor1.setOtherDoor(self.insideDoor1)
+ self.insideDoor0.setOtherDoor(self.outsideDoor0)
+ self.insideDoor1.setOtherDoor(self.outsideDoor1)
+ self.outsideDoor0.zoneId = self.exteriorZone
+ self.outsideDoor1.zoneId = self.exteriorZone
+ self.insideDoor0.zoneId = self.interiorZone
+ self.insideDoor1.zoneId = self.interiorZone
+ self.outsideDoor0.generateWithRequired(self.exteriorZone)
+ self.outsideDoor1.generateWithRequired(self.exteriorZone)
+ self.insideDoor0.generateWithRequired(self.interiorZone)
+ self.insideDoor1.generateWithRequired(self.interiorZone)
+ self.outsideDoor0.sendUpdate('setDoorIndex', [self.outsideDoor0.getDoorIndex()])
+ self.outsideDoor1.sendUpdate('setDoorIndex', [self.outsideDoor1.getDoorIndex()])
+ self.insideDoor0.sendUpdate('setDoorIndex', [self.insideDoor0.getDoorIndex()])
+ self.insideDoor1.sendUpdate('setDoorIndex', [self.insideDoor1.getDoorIndex()])
diff --git a/toontown/building/KnockKnockJokes.py b/toontown/building/KnockKnockJokes.py
new file mode 100644
index 0000000..506552f
--- /dev/null
+++ b/toontown/building/KnockKnockJokes.py
@@ -0,0 +1,3 @@
+from toontown.toonbase import TTLocalizer
+KnockKnockJokes = TTLocalizer.KnockKnockJokes
+KnockKnockContestJokes = TTLocalizer.KnockKnockContestJokes
diff --git a/toontown/building/PetshopBuildingAI.py b/toontown/building/PetshopBuildingAI.py
new file mode 100644
index 0000000..f63b349
--- /dev/null
+++ b/toontown/building/PetshopBuildingAI.py
@@ -0,0 +1,56 @@
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+import DistributedDoorAI, DistributedPetshopInteriorAI, FADoorCodes, DoorTypes
+from toontown.toon import NPCToons
+from toontown.toonbase import ToontownGlobals
+from toontown.quest import Quests
+from toontown.pets import DistributedPetAI, PetTraits, PetUtil
+from toontown.hood import ZoneUtil
+
+class PetshopBuildingAI:
+
+ def __init__(self, air, exteriorZone, interiorZone, blockNumber):
+ self.air = air
+ self.exteriorZone = exteriorZone
+ self.interiorZone = interiorZone
+ self.setup(blockNumber)
+
+ def cleanup(self):
+ for npc in self.npcs:
+ npc.requestDelete()
+
+ del self.npcs
+ self.door.requestDelete()
+ del self.door
+ self.insideDoor.requestDelete()
+ del self.insideDoor
+ self.interior.requestDelete()
+ del self.interior
+
+ def setup(self, blockNumber):
+ self.interior = DistributedPetshopInteriorAI.DistributedPetshopInteriorAI(blockNumber, self.air, self.interiorZone)
+ self.npcs = NPCToons.createNpcsInZone(self.air, self.interiorZone)
+ seeds = self.air.petMgr.getAvailablePets(1, len(self.npcs))
+ self.interior.generateWithRequired(self.interiorZone)
+ door = DistributedDoorAI.DistributedDoorAI(self.air, blockNumber, DoorTypes.EXT_STANDARD)
+ insideDoor = DistributedDoorAI.DistributedDoorAI(self.air, blockNumber, DoorTypes.INT_STANDARD)
+ door.setOtherDoor(insideDoor)
+ insideDoor.setOtherDoor(door)
+ door.zoneId = self.exteriorZone
+ insideDoor.zoneId = self.interiorZone
+ door.generateWithRequired(self.exteriorZone)
+ insideDoor.generateWithRequired(self.interiorZone)
+ self.door = door
+ self.insideDoor = insideDoor
+
+ def createPet(self, ownerId, seed):
+ zoneId = self.interiorZone
+ safeZoneId = ZoneUtil.getCanonicalSafeZoneId(zoneId)
+ name, dna, traitSeed = PetUtil.getPetInfoFromSeed(seed, safeZoneId)
+ pet = DistributedPetAI.DistributedPetAI(self.air, dna=dna)
+ pet.setOwnerId(ownerId)
+ pet.setPetName(name)
+ pet.traits = PetTraits.PetTraits(traitSeed=traitSeed, safeZoneId=safeZoneId)
+ pet.generateWithRequired(zoneId)
+ pet.setPos(0, 0, 0)
+ pet.b_setParent(ToontownGlobals.SPRender)
diff --git a/toontown/building/SuitBuildingGlobals.py b/toontown/building/SuitBuildingGlobals.py
new file mode 100644
index 0000000..cafa5cd
--- /dev/null
+++ b/toontown/building/SuitBuildingGlobals.py
@@ -0,0 +1,148 @@
+from ElevatorConstants import *
+SuitBuildingInfo = (((1, 1),
+ (1, 3),
+ (4, 4),
+ (8, 10),
+ (1,)),
+ ((1, 2),
+ (2, 4),
+ (5, 5),
+ (8, 10),
+ (1, 1.2)),
+ ((1, 3),
+ (3, 5),
+ (6, 6),
+ (8, 10),
+ (1, 1.3, 1.6)),
+ ((2, 3),
+ (4, 6),
+ (7, 7),
+ (8, 10),
+ (1, 1.4, 1.8)),
+ ((2, 4),
+ (5, 7),
+ (8, 8),
+ (8, 10),
+ (1,
+ 1.6,
+ 1.8,
+ 2)),
+ ((3, 4),
+ (6, 8),
+ (9, 9),
+ (10, 12),
+ (1,
+ 1.6,
+ 2,
+ 2.4)),
+ ((3, 5),
+ (7, 9),
+ (10, 10),
+ (10, 14),
+ (1,
+ 1.6,
+ 1.8,
+ 2.2,
+ 2.4)),
+ ((4, 5),
+ (8, 10),
+ (11, 11),
+ (12, 16),
+ (1,
+ 1.8,
+ 2.4,
+ 3,
+ 3.2)),
+ ((5, 5),
+ (9, 11),
+ (12, 12),
+ (14, 20),
+ (1.4,
+ 1.8,
+ 2.6,
+ 3.4,
+ 4)),
+ ((1, 1),
+ (1, 12),
+ (12, 12),
+ (67, 67),
+ (1,
+ 1,
+ 1,
+ 1,
+ 1)),
+ ((1, 1),
+ (8, 12),
+ (12, 12),
+ (100, 100),
+ (1,
+ 1,
+ 1,
+ 1,
+ 1)),
+ ((1, 1),
+ (1, 12),
+ (12, 12),
+ (100, 100),
+ (1,
+ 1,
+ 1,
+ 1,
+ 1)),
+ ((1, 1),
+ (8, 12),
+ (12, 12),
+ (150, 150),
+ (1,
+ 1,
+ 1,
+ 1,
+ 1)),
+ ((1, 1),
+ (8, 12),
+ (12, 12),
+ (275, 275),
+ (1,
+ 1,
+ 1,
+ 1,
+ 1)),
+ ((1, 1),
+ (9, 12),
+ (12, 12),
+ (206, 206),
+ (1,
+ 1,
+ 1,
+ 1,
+ 1),
+ (1,)),
+ ((1, 1),
+ (1, 5),
+ (5, 5),
+ (33, 33),
+ (1,
+ 1,
+ 1,
+ 1,
+ 1)),
+ ((1, 1),
+ (4, 5),
+ (5, 5),
+ (50, 50),
+ (1,
+ 1,
+ 1,
+ 1,
+ 1)))
+SUIT_BLDG_INFO_FLOORS = 0
+SUIT_BLDG_INFO_SUIT_LVLS = 1
+SUIT_BLDG_INFO_BOSS_LVLS = 2
+SUIT_BLDG_INFO_LVL_POOL = 3
+SUIT_BLDG_INFO_LVL_POOL_MULTS = 4
+SUIT_BLDG_INFO_REVIVES = 5
+VICTORY_RUN_TIME = ElevatorData[ELEVATOR_NORMAL]['openTime'] + TOON_VICTORY_EXIT_TIME
+TO_TOON_BLDG_TIME = 8
+VICTORY_SEQUENCE_TIME = VICTORY_RUN_TIME + TO_TOON_BLDG_TIME
+CLEAR_OUT_TOON_BLDG_TIME = 4
+TO_SUIT_BLDG_TIME = 8
diff --git a/toontown/building/SuitInterior.py b/toontown/building/SuitInterior.py
new file mode 100644
index 0000000..5cdb1c1
--- /dev/null
+++ b/toontown/building/SuitInterior.py
@@ -0,0 +1,215 @@
+from pandac.PandaModules import *
+from toontown.toonbase.ToonBaseGlobal import *
+from direct.directnotify import DirectNotifyGlobal
+from toontown.hood import Place
+from direct.showbase import DirectObject
+from direct.fsm import StateData
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from toontown.town import TownBattle
+from toontown.suit import Suit
+import Elevator
+from direct.task.Task import Task
+from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import ToontownBattleGlobals
+
+class SuitInterior(Place.Place):
+ notify = DirectNotifyGlobal.directNotify.newCategory('SuitInterior')
+
+ def __init__(self, loader, parentFSM, doneEvent):
+ Place.Place.__init__(self, loader, doneEvent)
+ self.fsm = ClassicFSM.ClassicFSM('SuitInterior', [State.State('entrance', self.enterEntrance, self.exitEntrance, ['battle', 'walk']),
+ State.State('Elevator', self.enterElevator, self.exitElevator, ['battle', 'walk']),
+ State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'died']),
+ State.State('walk', self.enterWalk, self.exitWalk, ['stickerBook',
+ 'stopped',
+ 'sit',
+ 'died',
+ 'teleportOut',
+ 'Elevator',
+ 'DFA',
+ 'trialerFA']),
+ State.State('sit', self.enterSit, self.exitSit, ['walk']),
+ State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk',
+ 'stopped',
+ 'sit',
+ 'died',
+ 'DFA',
+ 'trialerFA',
+ 'teleportOut',
+ 'Elevator']),
+ State.State('trialerFA', self.enterTrialerFA, self.exitTrialerFA, ['trialerFAReject', 'DFA']),
+ State.State('trialerFAReject', self.enterTrialerFAReject, self.exitTrialerFAReject, ['walk']),
+ State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject', 'teleportOut']),
+ State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walk']),
+ State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']),
+ State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn']),
+ State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'elevatorOut']),
+ State.State('died', self.enterDied, self.exitDied, []),
+ State.State('elevatorOut', self.enterElevatorOut, self.exitElevatorOut, [])], 'entrance', 'elevatorOut')
+ self.parentFSM = parentFSM
+ self.elevatorDoneEvent = 'elevatorDoneSI'
+ self.currentFloor = 0
+
+ def enter(self, requestStatus):
+ self.fsm.enterInitialState()
+ self._telemLimiter = TLGatherAllAvs('SuitInterior', RotationLimitToH)
+ self.zoneId = requestStatus['zoneId']
+ self.accept('DSIDoneEvent', self.handleDSIDoneEvent)
+
+ def exit(self):
+ self.ignoreAll()
+ self._telemLimiter.destroy()
+ del self._telemLimiter
+
+ def load(self):
+ Place.Place.load(self)
+ self.parentFSM.getStateNamed('suitInterior').addChild(self.fsm)
+ self.townBattle = TownBattle.TownBattle('town-battle-done')
+ self.townBattle.load()
+ for i in range(1, 3):
+ Suit.loadSuits(i)
+
+ def unload(self):
+ Place.Place.unload(self)
+ self.parentFSM.getStateNamed('suitInterior').removeChild(self.fsm)
+ del self.parentFSM
+ del self.fsm
+ self.ignoreAll()
+ ModelPool.garbageCollect()
+ TexturePool.garbageCollect()
+ self.townBattle.unload()
+ self.townBattle.cleanup()
+ del self.townBattle
+ for i in range(1, 3):
+ Suit.unloadSuits(i)
+
+ def setState(self, state, battleEvent = None):
+ if battleEvent:
+ self.fsm.request(state, [battleEvent])
+ else:
+ self.fsm.request(state)
+
+ def getZoneId(self):
+ return self.zoneId
+
+ def enterZone(self, zoneId):
+ pass
+
+ def isPeriodTimerEffective(self):
+ return 0
+
+ def handleDSIDoneEvent(self, requestStatus):
+ self.doneStatus = requestStatus
+ messenger.send(self.doneEvent)
+
+ def doRequestLeave(self, requestStatus):
+ self.fsm.request('trialerFA', [requestStatus])
+
+ def enterEntrance(self):
+ pass
+
+ def exitEntrance(self):
+ pass
+
+ def enterElevator(self, distElevator):
+ self.accept(self.elevatorDoneEvent, self.handleElevatorDone)
+ self.elevator = Elevator.Elevator(self.fsm.getStateNamed('Elevator'), self.elevatorDoneEvent, distElevator)
+ self.elevator.load()
+ self.elevator.enter()
+ base.localAvatar.cantLeaveGame = 1
+
+ def exitElevator(self):
+ base.localAvatar.cantLeaveGame = 0
+ self.ignore(self.elevatorDoneEvent)
+ self.elevator.unload()
+ self.elevator.exit()
+ del self.elevator
+ return None
+
+ def detectedElevatorCollision(self, distElevator):
+ self.fsm.request('Elevator', [distElevator])
+ return None
+
+ def handleElevatorDone(self, doneStatus):
+ self.notify.debug('handling elevator done event')
+ where = doneStatus['where']
+ if where == 'reject':
+ if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen():
+ pass
+ else:
+ self.fsm.request('walk')
+ elif where == 'exit':
+ self.fsm.request('walk')
+ elif where == 'suitInterior':
+ pass
+ else:
+ self.notify.error('Unknown mode: ' + +' in handleElevatorDone')
+
+ def enterBattle(self, event):
+ mult = ToontownBattleGlobals.getCreditMultiplier(self.currentFloor)
+ self.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult)
+ base.localAvatar.b_setAnimState('off', 1)
+ base.localAvatar.cantLeaveGame = 1
+
+ def exitBattle(self):
+ self.townBattle.exit()
+ base.localAvatar.cantLeaveGame = 0
+
+ def enterWalk(self, teleportIn = 0):
+ Place.Place.enterWalk(self, teleportIn)
+ self.ignore('teleportQuery')
+ base.localAvatar.setTeleportAvailable(0)
+
+ def enterStickerBook(self, page = None):
+ Place.Place.enterStickerBook(self, page)
+ self.ignore('teleportQuery')
+ base.localAvatar.setTeleportAvailable(0)
+
+ def enterSit(self):
+ Place.Place.enterSit(self)
+ self.ignore('teleportQuery')
+ base.localAvatar.setTeleportAvailable(0)
+
+ def enterStopped(self):
+ Place.Place.enterStopped(self)
+ self.ignore('teleportQuery')
+ base.localAvatar.setTeleportAvailable(0)
+
+ def enterTeleportIn(self, requestStatus):
+ base.localAvatar.setPosHpr(2.5, 11.5, ToontownGlobals.FloorOffset, 45.0, 0.0, 0.0)
+ Place.Place.enterTeleportIn(self, requestStatus)
+
+ def enterTeleportOut(self, requestStatus):
+ Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone)
+
+ def __teleportOutDone(self, requestStatus):
+ hoodId = requestStatus['hoodId']
+ if hoodId == ToontownGlobals.MyEstate:
+ self.getEstateZoneAndGoHome(requestStatus)
+ else:
+ messenger.send('localToonLeft')
+ self.doneStatus = requestStatus
+ messenger.send(self.doneEvent)
+
+ def exitTeleportOut(self):
+ Place.Place.exitTeleportOut(self)
+
+ def goHomeFailed(self, task):
+ self.notifyUserGoHomeFailed()
+ self.ignore('setLocalEstateZone')
+ self.doneStatus['avId'] = -1
+ self.doneStatus['zoneId'] = self.getZoneId()
+ self.fsm.request('teleportIn', [self.doneStatus])
+ return Task.done
+
+ def enterElevatorOut(self):
+ return None
+
+ def __elevatorOutDone(self, requestStatus):
+ self.doneStatus = requestStatus
+ messenger.send(self.doneEvent)
+
+ def exitElevatorOut(self):
+ return None
diff --git a/toontown/building/SuitPlannerInteriorAI.py b/toontown/building/SuitPlannerInteriorAI.py
new file mode 100644
index 0000000..75d75cb
--- /dev/null
+++ b/toontown/building/SuitPlannerInteriorAI.py
@@ -0,0 +1,185 @@
+from otp.ai.AIBaseGlobal import *
+import random
+from toontown.suit import SuitDNA
+from direct.directnotify import DirectNotifyGlobal
+from toontown.suit import DistributedSuitAI
+import SuitBuildingGlobals, types
+
+class SuitPlannerInteriorAI:
+ notify = DirectNotifyGlobal.directNotify.newCategory('SuitPlannerInteriorAI')
+
+ def __init__(self, numFloors, bldgLevel, bldgTrack, zone, respectInvasions=1):
+ self.dbg_nSuits1stRound = config.GetBool('n-suits-1st-round', 0)
+ self.dbg_4SuitsPerFloor = config.GetBool('4-suits-per-floor', 0)
+ self.dbg_1SuitPerFloor = config.GetBool('1-suit-per-floor', 0)
+ self.zoneId = zone
+ self.numFloors = numFloors
+ self.respectInvasions = respectInvasions
+ dbg_defaultSuitName = simbase.config.GetString('suit-type', 'random')
+ if dbg_defaultSuitName == 'random':
+ self.dbg_defaultSuitType = None
+ else:
+ self.dbg_defaultSuitType = SuitDNA.getSuitType(dbg_defaultSuitName)
+ if isinstance(bldgLevel, types.StringType):
+ self.notify.warning('bldgLevel is a string!')
+ bldgLevel = int(bldgLevel)
+ self._genSuitInfos(numFloors, bldgLevel, bldgTrack)
+ return
+
+ def __genJoinChances(self, num):
+ joinChances = []
+ for currChance in range(num):
+ joinChances.append(random.randint(1, 100))
+
+ joinChances.sort(cmp)
+ return joinChances
+
+ def _genSuitInfos(self, numFloors, bldgLevel, bldgTrack):
+ self.suitInfos = []
+ self.notify.debug('\n\ngenerating suitsInfos with numFloors (' + str(numFloors) + ') bldgLevel (' + str(bldgLevel) + '+1) and bldgTrack (' + str(bldgTrack) + ')')
+ for currFloor in range(numFloors):
+ infoDict = {}
+ lvls = self.__genLevelList(bldgLevel, currFloor, numFloors)
+ activeDicts = []
+ maxActive = min(4, len(lvls))
+ if self.dbg_nSuits1stRound:
+ numActive = min(self.dbg_nSuits1stRound, maxActive)
+ else:
+ numActive = random.randint(1, maxActive)
+ if currFloor + 1 == numFloors and len(lvls) > 1:
+ origBossSpot = len(lvls) - 1
+ if numActive == 1:
+ newBossSpot = numActive - 1
+ else:
+ newBossSpot = numActive - 2
+ tmp = lvls[newBossSpot]
+ lvls[newBossSpot] = lvls[origBossSpot]
+ lvls[origBossSpot] = tmp
+ bldgInfo = SuitBuildingGlobals.SuitBuildingInfo[bldgLevel]
+ if len(bldgInfo) > SuitBuildingGlobals.SUIT_BLDG_INFO_REVIVES:
+ revives = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_REVIVES][0]
+ else:
+ revives = 0
+ for currActive in range(numActive - 1, -1, -1):
+ level = lvls[currActive]
+ type = self.__genNormalSuitType(level)
+ activeDict = {}
+ activeDict['type'] = type
+ activeDict['track'] = bldgTrack
+ activeDict['level'] = level
+ activeDict['revives'] = revives
+ activeDicts.append(activeDict)
+
+ infoDict['activeSuits'] = activeDicts
+ reserveDicts = []
+ numReserve = len(lvls) - numActive
+ joinChances = self.__genJoinChances(numReserve)
+ for currReserve in range(numReserve):
+ level = lvls[currReserve + numActive]
+ type = self.__genNormalSuitType(level)
+ reserveDict = {}
+ reserveDict['type'] = type
+ reserveDict['track'] = bldgTrack
+ reserveDict['level'] = level
+ reserveDict['revives'] = revives
+ reserveDict['joinChance'] = joinChances[currReserve]
+ reserveDicts.append(reserveDict)
+
+ infoDict['reserveSuits'] = reserveDicts
+ self.suitInfos.append(infoDict)
+
+ def __genNormalSuitType(self, lvl):
+ if self.dbg_defaultSuitType != None:
+ return self.dbg_defaultSuitType
+ return SuitDNA.getRandomSuitType(lvl)
+
+ def __genLevelList(self, bldgLevel, currFloor, numFloors):
+ bldgInfo = SuitBuildingGlobals.SuitBuildingInfo[bldgLevel]
+ if self.dbg_1SuitPerFloor:
+ return [1]
+ else:
+ if self.dbg_4SuitsPerFloor:
+ return [5, 6, 7, 10]
+ lvlPoolRange = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_LVL_POOL]
+ maxFloors = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_FLOORS][1]
+ lvlPoolMults = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_LVL_POOL_MULTS]
+ floorIdx = min(currFloor, maxFloors - 1)
+ lvlPoolMin = lvlPoolRange[0] * lvlPoolMults[floorIdx]
+ lvlPoolMax = lvlPoolRange[1] * lvlPoolMults[floorIdx]
+ lvlPool = random.randint(int(lvlPoolMin), int(lvlPoolMax))
+ lvlMin = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_SUIT_LVLS][0]
+ lvlMax = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_SUIT_LVLS][1]
+ self.notify.debug('Level Pool: ' + str(lvlPool))
+ lvlList = []
+ while lvlPool >= lvlMin:
+ newLvl = random.randint(lvlMin, min(lvlPool, lvlMax))
+ lvlList.append(newLvl)
+ lvlPool -= newLvl
+
+ if currFloor + 1 == numFloors:
+ bossLvlRange = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_BOSS_LVLS]
+ newLvl = random.randint(bossLvlRange[0], bossLvlRange[1])
+ lvlList.append(newLvl)
+ lvlList.sort(cmp)
+ self.notify.debug('LevelList: ' + repr(lvlList))
+ return lvlList
+
+ def __setupSuitInfo(self, suit, bldgTrack, suitLevel, suitType):
+ suitName, skeleton = simbase.air.suitInvasionManager.getInvadingCog()
+ if suitName and self.respectInvasions:
+ suitType = SuitDNA.getSuitType(suitName)
+ bldgTrack = SuitDNA.getSuitDept(suitName)
+ suitLevel = min(max(suitLevel, suitType), suitType + 4)
+ dna = SuitDNA.SuitDNA()
+ dna.newSuitRandom(suitType, bldgTrack)
+ suit.dna = dna
+ self.notify.debug('Creating suit type ' + suit.dna.name + ' of level ' + str(suitLevel) + ' from type ' + str(suitType) + ' and track ' + str(bldgTrack))
+ suit.setLevel(suitLevel)
+ return skeleton
+
+ def __genSuitObject(self, suitZone, suitType, bldgTrack, suitLevel, revives=0):
+ newSuit = DistributedSuitAI.DistributedSuitAI(simbase.air, None)
+ skel = self.__setupSuitInfo(newSuit, bldgTrack, suitLevel, suitType)
+ if skel:
+ newSuit.setSkelecog(1)
+ newSuit.setSkeleRevives(revives)
+ newSuit.generateWithRequired(suitZone)
+ newSuit.node().setName('suit-%s' % newSuit.doId)
+ return newSuit
+
+ def myPrint(self):
+ self.notify.info('Generated suits for building: ')
+ for currInfo in suitInfos:
+ whichSuitInfo = suitInfos.index(currInfo) + 1
+ self.notify.debug(' Floor ' + str(whichSuitInfo) + ' has ' + str(len(currInfo[0])) + ' active suits.')
+ for currActive in range(len(currInfo[0])):
+ self.notify.debug(' Active suit ' + str(currActive + 1) + ' is of type ' + str(currInfo[0][currActive][0]) + ' and of track ' + str(currInfo[0][currActive][1]) + ' and of level ' + str(currInfo[0][currActive][2]))
+
+ self.notify.debug(' Floor ' + str(whichSuitInfo) + ' has ' + str(len(currInfo[1])) + ' reserve suits.')
+ for currReserve in range(len(currInfo[1])):
+ self.notify.debug(' Reserve suit ' + str(currReserve + 1) + ' is of type ' + str(currInfo[1][currReserve][0]) + ' and of track ' + str(currInfo[1][currReserve][1]) + ' and of lvel ' + str(currInfo[1][currReserve][2]) + ' and has ' + str(currInfo[1][currReserve][3]) + '% join restriction.')
+
+ def genFloorSuits(self, floor):
+ suitHandles = {}
+ floorInfo = self.suitInfos[floor]
+ activeSuits = []
+ for activeSuitInfo in floorInfo['activeSuits']:
+ suit = self.__genSuitObject(self.zoneId, activeSuitInfo['type'], activeSuitInfo['track'], activeSuitInfo['level'], activeSuitInfo['revives'])
+ activeSuits.append(suit)
+
+ suitHandles['activeSuits'] = activeSuits
+ reserveSuits = []
+ for reserveSuitInfo in floorInfo['reserveSuits']:
+ suit = self.__genSuitObject(self.zoneId, reserveSuitInfo['type'], reserveSuitInfo['track'], reserveSuitInfo['level'], reserveSuitInfo['revives'])
+ reserveSuits.append((suit, reserveSuitInfo['joinChance']))
+
+ suitHandles['reserveSuits'] = reserveSuits
+ return suitHandles
+
+ def genSuits(self):
+ suitHandles = []
+ for floor in range(len(self.suitInfos)):
+ floorSuitHandles = self.genFloorSuits(floor)
+ suitHandles.append(floorSuitHandles)
+
+ return suitHandles
diff --git a/toontown/building/ToonInterior.py b/toontown/building/ToonInterior.py
new file mode 100644
index 0000000..c5c9512
--- /dev/null
+++ b/toontown/building/ToonInterior.py
@@ -0,0 +1,221 @@
+from pandac.PandaModules import *
+from toontown.toonbase.ToonBaseGlobal import *
+from direct.directnotify import DirectNotifyGlobal
+from toontown.hood import Place
+from toontown.hood import ZoneUtil
+from direct.showbase import DirectObject
+from direct.fsm import StateData
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task import Task
+from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.toon import NPCForceAcknowledge
+from toontown.toon import HealthForceAcknowledge
+
+class ToonInterior(Place.Place):
+ notify = DirectNotifyGlobal.directNotify.newCategory('ToonInterior')
+
+ def __init__(self, loader, parentFSMState, doneEvent):
+ Place.Place.__init__(self, loader, doneEvent)
+ self.dnaFile = 'phase_7/models/modules/toon_interior'
+ self.isInterior = 1
+ self.tfaDoneEvent = 'tfaDoneEvent'
+ self.hfaDoneEvent = 'hfaDoneEvent'
+ self.npcfaDoneEvent = 'npcfaDoneEvent'
+ self.fsm = ClassicFSM.ClassicFSM('ToonInterior', [State.State('start', self.enterStart, self.exitStart, ['doorIn', 'teleportIn', 'tutorial']),
+ State.State('walk', self.enterWalk, self.exitWalk, ['sit',
+ 'stickerBook',
+ 'doorOut',
+ 'DFA',
+ 'trialerFA',
+ 'teleportOut',
+ 'quest',
+ 'purchase',
+ 'phone',
+ 'stopped',
+ 'pet']),
+ State.State('sit', self.enterSit, self.exitSit, ['walk']),
+ State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk',
+ 'DFA',
+ 'trialerFA',
+ 'sit',
+ 'doorOut',
+ 'teleportOut',
+ 'quest',
+ 'purchase',
+ 'phone',
+ 'stopped',
+ 'pet']),
+ State.State('trialerFA', self.enterTrialerFA, self.exitTrialerFA, ['trialerFAReject', 'DFA']),
+ State.State('trialerFAReject', self.enterTrialerFAReject, self.exitTrialerFAReject, ['walk']),
+ State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject',
+ 'HFA',
+ 'NPCFA',
+ 'teleportOut',
+ 'doorOut']),
+ State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walk']),
+ State.State('NPCFA', self.enterNPCFA, self.exitNPCFA, ['NPCFAReject', 'HFA', 'teleportOut']),
+ State.State('NPCFAReject', self.enterNPCFAReject, self.exitNPCFAReject, ['walk']),
+ State.State('HFA', self.enterHFA, self.exitHFA, ['HFAReject', 'teleportOut', 'tunnelOut']),
+ State.State('HFAReject', self.enterHFAReject, self.exitHFAReject, ['walk']),
+ State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk']),
+ State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk']),
+ State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']),
+ State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn']),
+ State.State('quest', self.enterQuest, self.exitQuest, ['walk', 'doorOut']),
+ State.State('tutorial', self.enterTutorial, self.exitTutorial, ['walk', 'quest']),
+ State.State('purchase', self.enterPurchase, self.exitPurchase, ['walk', 'doorOut']),
+ State.State('pet', self.enterPet, self.exitPet, ['walk']),
+ State.State('phone', self.enterPhone, self.exitPhone, ['walk', 'doorOut']),
+ State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'doorOut']),
+ State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final')
+ self.parentFSMState = parentFSMState
+
+ def load(self):
+ Place.Place.load(self)
+ self.parentFSMState.addChild(self.fsm)
+
+ def unload(self):
+ Place.Place.unload(self)
+ self.parentFSMState.removeChild(self.fsm)
+ del self.parentFSMState
+ del self.fsm
+ ModelPool.garbageCollect()
+ TexturePool.garbageCollect()
+
+ def enter(self, requestStatus):
+ self.zoneId = requestStatus['zoneId']
+ self.fsm.enterInitialState()
+ messenger.send('enterToonInterior')
+ self.accept('doorDoneEvent', self.handleDoorDoneEvent)
+ self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger)
+ volume = requestStatus.get('musicVolume', 0.7)
+ base.playMusic(self.loader.activityMusic, looping=1, volume=volume)
+ self._telemLimiter = TLGatherAllAvs('ToonInterior', RotationLimitToH)
+ NametagGlobals.setMasterArrowsOn(1)
+ self.fsm.request(requestStatus['how'], [requestStatus])
+
+ def exit(self):
+ self.ignoreAll()
+ messenger.send('exitToonInterior')
+ self._telemLimiter.destroy()
+ del self._telemLimiter
+ NametagGlobals.setMasterArrowsOn(0)
+ self.loader.activityMusic.stop()
+
+ def setState(self, state):
+ self.fsm.request(state)
+
+ def enterTutorial(self, requestStatus):
+ self.fsm.request('walk')
+ base.localAvatar.b_setParent(ToontownGlobals.SPRender)
+ globalClock.tick()
+ base.transitions.irisIn()
+ messenger.send('enterTutorialInterior')
+
+ def exitTutorial(self):
+ pass
+
+ def doRequestLeave(self, requestStatus):
+ self.fsm.request('trialerFA', [requestStatus])
+
+ def enterDFACallback(self, requestStatus, doneStatus):
+ self.dfa.exit()
+ del self.dfa
+ ds = doneStatus['mode']
+ if ds == 'complete':
+ self.fsm.request('NPCFA', [requestStatus])
+ elif ds == 'incomplete':
+ self.fsm.request('DFAReject')
+ else:
+ self.notify.error('Unknown done status for DownloadForceAcknowledge: ' + `doneStatus`)
+
+ def enterNPCFA(self, requestStatus):
+ self.acceptOnce(self.npcfaDoneEvent, self.enterNPCFACallback, [requestStatus])
+ self.npcfa = NPCForceAcknowledge.NPCForceAcknowledge(self.npcfaDoneEvent)
+ self.npcfa.enter()
+
+ def exitNPCFA(self):
+ self.ignore(self.npcfaDoneEvent)
+
+ def enterNPCFACallback(self, requestStatus, doneStatus):
+ self.npcfa.exit()
+ del self.npcfa
+ if doneStatus['mode'] == 'complete':
+ outHow = {'teleportIn': 'teleportOut',
+ 'tunnelIn': 'tunnelOut',
+ 'doorIn': 'doorOut'}
+ self.fsm.request(outHow[requestStatus['how']], [requestStatus])
+ elif doneStatus['mode'] == 'incomplete':
+ self.fsm.request('NPCFAReject')
+ else:
+ self.notify.error('Unknown done status for NPCForceAcknowledge: ' + `doneStatus`)
+
+ def enterNPCFAReject(self):
+ self.fsm.request('walk')
+
+ def exitNPCFAReject(self):
+ pass
+
+ def enterHFA(self, requestStatus):
+ self.acceptOnce(self.hfaDoneEvent, self.enterHFACallback, [requestStatus])
+ self.hfa = HealthForceAcknowledge.HealthForceAcknowledge(self.hfaDoneEvent)
+ self.hfa.enter(1)
+
+ def exitHFA(self):
+ self.ignore(self.hfaDoneEvent)
+
+ def enterHFACallback(self, requestStatus, doneStatus):
+ self.hfa.exit()
+ del self.hfa
+ if doneStatus['mode'] == 'complete':
+ outHow = {'teleportIn': 'teleportOut',
+ 'tunnelIn': 'tunnelOut',
+ 'doorIn': 'doorOut'}
+ self.fsm.request(outHow[requestStatus['how']], [requestStatus])
+ elif doneStatus['mode'] == 'incomplete':
+ self.fsm.request('HFAReject')
+ else:
+ self.notify.error('Unknown done status for HealthForceAcknowledge: ' + `doneStatus`)
+
+ def enterHFAReject(self):
+ self.fsm.request('walk')
+
+ def exitHFAReject(self):
+ pass
+
+ def enterTeleportIn(self, requestStatus):
+ if ZoneUtil.isPetshop(self.zoneId):
+ base.localAvatar.setPosHpr(0, 0, ToontownGlobals.FloorOffset, 45.0, 0.0, 0.0)
+ else:
+ base.localAvatar.setPosHpr(2.5, 11.5, ToontownGlobals.FloorOffset, 45.0, 0.0, 0.0)
+ Place.Place.enterTeleportIn(self, requestStatus)
+
+ def enterTeleportOut(self, requestStatus):
+ Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone)
+
+ def __teleportOutDone(self, requestStatus):
+ hoodId = requestStatus['hoodId']
+ zoneId = requestStatus['zoneId']
+ shardId = requestStatus['shardId']
+ if hoodId == self.loader.hood.id and zoneId == self.zoneId and shardId == None:
+ self.fsm.request('teleportIn', [requestStatus])
+ elif hoodId == ToontownGlobals.MyEstate:
+ self.getEstateZoneAndGoHome(requestStatus)
+ else:
+ self.doneStatus = requestStatus
+ messenger.send(self.doneEvent)
+ return
+
+ def goHomeFailed(self, task):
+ self.notifyUserGoHomeFailed()
+ self.ignore('setLocalEstateZone')
+ self.doneStatus['avId'] = -1
+ self.doneStatus['zoneId'] = self.getZoneId()
+ self.fsm.request('teleportIn', [self.doneStatus])
+ return Task.done
+
+ def exitTeleportOut(self):
+ Place.Place.exitTeleportOut(self)
diff --git a/toontown/building/ToonInteriorColors.py b/toontown/building/ToonInteriorColors.py
new file mode 100644
index 0000000..fccef5b
--- /dev/null
+++ b/toontown/building/ToonInteriorColors.py
@@ -0,0 +1,42 @@
+from toontown.toonbase.ToontownGlobals import *
+wainscottingBase = [Vec4(0.8, 0.5, 0.3, 1.0), Vec4(0.699, 0.586, 0.473, 1.0), Vec4(0.473, 0.699, 0.488, 1.0)]
+wallpaperBase = [Vec4(1.0, 1.0, 0.7, 1.0),
+ Vec4(0.8, 1.0, 0.7, 1.0),
+ Vec4(0.4, 0.5, 0.4, 1.0),
+ Vec4(0.5, 0.7, 0.6, 1.0)]
+wallpaperBorderBase = [Vec4(1.0, 1.0, 0.7, 1.0),
+ Vec4(0.8, 1.0, 0.7, 1.0),
+ Vec4(0.4, 0.5, 0.4, 1.0),
+ Vec4(0.5, 0.7, 0.6, 1.0)]
+doorBase = [Vec4(1.0, 1.0, 0.7, 1.0)]
+floorBase = [Vec4(0.746, 1.0, 0.477, 1.0), Vec4(1.0, 0.684, 0.477, 1.0)]
+baseScheme = {'TI_wainscotting': wainscottingBase,
+ 'TI_wallpaper': wallpaperBase,
+ 'TI_wallpaper_border': wallpaperBorderBase,
+ 'TI_door': doorBase,
+ 'TI_floor': floorBase}
+colors = {DonaldsDock: {'TI_wainscotting': wainscottingBase,
+ 'TI_wallpaper': wallpaperBase,
+ 'TI_wallpaper_border': wallpaperBorderBase,
+ 'TI_door': doorBase,
+ 'TI_floor': floorBase},
+ ToontownCentral: {'TI_wainscotting': wainscottingBase,
+ 'TI_wallpaper': wallpaperBase,
+ 'TI_wallpaper_border': wallpaperBorderBase,
+ 'TI_door': doorBase + [Vec4(0.8, 0.5, 0.3, 1.0)],
+ 'TI_floor': floorBase},
+ TheBrrrgh: baseScheme,
+ MinniesMelodyland: baseScheme,
+ DaisyGardens: baseScheme,
+ GoofySpeedway: baseScheme,
+ DonaldsDreamland: {'TI_wainscotting': wainscottingBase,
+ 'TI_wallpaper': wallpaperBase,
+ 'TI_wallpaper_border': wallpaperBorderBase,
+ 'TI_door': doorBase,
+ 'TI_floor': floorBase},
+ Tutorial: {'TI_wainscotting': wainscottingBase,
+ 'TI_wallpaper': wallpaperBase,
+ 'TI_wallpaper_border': wallpaperBorderBase,
+ 'TI_door': doorBase + [Vec4(0.8, 0.5, 0.3, 1.0)],
+ 'TI_floor': floorBase},
+ MyEstate: baseScheme}
diff --git a/toontown/building/__init__.py b/toontown/building/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/toontown/catalog/CatalogAccessoryItem.py b/toontown/catalog/CatalogAccessoryItem.py
new file mode 100644
index 0000000..c291a8b
--- /dev/null
+++ b/toontown/catalog/CatalogAccessoryItem.py
@@ -0,0 +1,425 @@
+import CatalogItem
+from CatalogAccessoryItemGlobals import *
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.toon import ToonDNA
+import random, types
+from direct.showbase import PythonUtil
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+
+class CatalogAccessoryItem(CatalogItem.CatalogItem):
+
+ def makeNewItem(self, accessoryType, loyaltyDays = 0):
+ self.accessoryType = accessoryType
+ self.loyaltyDays = loyaltyDays
+ CatalogItem.CatalogItem.makeNewItem(self)
+
+ def storedInTrunk(self):
+ return 1
+
+ def notOfferedTo(self, avatar):
+ article = AccessoryTypes[self.accessoryType][ATArticle]
+ if article in [AHat,
+ AGlasses,
+ ABackpack,
+ AShoes]:
+ return 0
+ forBoys = article in [ABoysHat,
+ ABoysGlasses,
+ ABoysBackpack,
+ ABoysShoes]
+ if avatar.getStyle().getGender() == 'm':
+ return not forBoys
+ else:
+ return forBoys
+
+ def forBoysOnly(self):
+ article = AccessoryTypes[self.accessoryType][ATArticle]
+ if article in [ABoysHat,
+ ABoysGlasses,
+ ABoysBackpack,
+ ABoysShoes]:
+ return 1
+ else:
+ return 0
+
+ def forGirlsOnly(self):
+ article = AccessoryTypes[self.accessoryType][ATArticle]
+ if article in [AGirlsHat,
+ AGirlsGlasses,
+ AGirlsBackpack,
+ AGirlsShoes]:
+ return 1
+ else:
+ return 0
+
+ def getPurchaseLimit(self):
+ return 1
+
+ def reachedPurchaseLimit(self, avatar):
+ if avatar.onOrder.count(self) != 0:
+ return 1
+ if avatar.onGiftOrder.count(self) != 0:
+ return 1
+ if avatar.mailboxContents.count(self) != 0:
+ return 1
+ if self in avatar.awardMailboxContents or self in avatar.onAwardOrder:
+ return 1
+ str = AccessoryTypes[self.accessoryType][ATString]
+ if self.isHat():
+ defn = ToonDNA.HatStyles[str]
+ hat = avatar.getHat()
+ if hat[0] == defn[0] and hat[1] == defn[1] and hat[2] == defn[2]:
+ return 1
+ l = avatar.hatList
+ for i in range(0, len(l), 3):
+ if l[i] == defn[0] and l[i + 1] == defn[1] and l[i + 2] == defn[2]:
+ return 1
+
+ elif self.areGlasses():
+ defn = ToonDNA.GlassesStyles[str]
+ glasses = avatar.getGlasses()
+ if glasses[0] == defn[0] and glasses[1] == defn[1] and glasses[2] == defn[2]:
+ return 1
+ l = avatar.glassesList
+ for i in range(0, len(l), 3):
+ if l[i] == defn[0] and l[i + 1] == defn[1] and l[i + 2] == defn[2]:
+ return 1
+
+ elif self.isBackpack():
+ defn = ToonDNA.BackpackStyles[str]
+ backpack = avatar.getBackpack()
+ if backpack[0] == defn[0] and backpack[1] == defn[1] and backpack[2] == defn[2]:
+ return 1
+ l = avatar.backpackList
+ for i in range(0, len(l), 3):
+ if l[i] == defn[0] and l[i + 1] == defn[1] and l[i + 2] == defn[2]:
+ return 1
+
+ else:
+ defn = ToonDNA.ShoesStyles[str]
+ shoes = avatar.getShoes()
+ if shoes[0] == defn[0] and shoes[1] == defn[1] and shoes[2] == defn[2]:
+ return 1
+ l = avatar.shoesList
+ for i in range(0, len(l), 3):
+ if l[i] == defn[0] and l[i + 1] == defn[1] and l[i + 2] == defn[2]:
+ return 1
+
+ return 0
+
+ def getTypeName(self):
+ return TTLocalizer.AccessoryTypeName
+
+ def getName(self):
+ typeName = TTLocalizer.AccessoryTypeNames.get(self.accessoryType, 0)
+ if typeName:
+ return typeName
+ else:
+ article = AccessoryTypes[self.accessoryType][ATArticle]
+ return TTLocalizer.AccessoryArticleNames[article]
+
+ def recordPurchase(self, avatar, optional):
+ if avatar.isTrunkFull():
+ if avatar.getMaxAccessories() == 0:
+ return ToontownGlobals.P_NoTrunk
+ else:
+ return ToontownGlobals.P_NoRoomForItem
+ str = AccessoryTypes[self.accessoryType][ATString]
+ if self.isHat():
+ defn = ToonDNA.HatStyles[str]
+ if not avatar.checkAccessorySanity(ToonDNA.HAT, defn[0], defn[1], defn[2]):
+ self.notify.warning('Avatar %s lost hat %d %d %d; invalid item.' % (avatar.doId,
+ defn[0],
+ defn[1],
+ defn[2]))
+ return ToontownGlobals.P_ItemAvailable
+ hat = avatar.getHat()
+ added = avatar.addToAccessoriesList(ToonDNA.HAT, hat[0], hat[1], hat[2])
+ if added:
+ avatar.b_setHatList(avatar.getHatList())
+ self.notify.info('Avatar %s put hat %d,%d,%d in trunk.' % (avatar.doId,
+ hat[0],
+ hat[1],
+ hat[2]))
+ else:
+ self.notify.warning('Avatar %s lost current hat %d %d %d; trunk full.' % (avatar.doId,
+ hat[0],
+ hat[1],
+ hat[2]))
+ avatar.b_setHat(defn[0], defn[1], defn[2])
+ elif self.areGlasses():
+ defn = ToonDNA.GlassesStyles[str]
+ if not avatar.checkAccessorySanity(ToonDNA.GLASSES, defn[0], defn[1], defn[2]):
+ self.notify.warning('Avatar %s lost glasses %d %d %d; invalid item.' % (avatar.doId,
+ defn[0],
+ defn[1],
+ defn[2]))
+ return ToontownGlobals.P_ItemAvailable
+ glasses = avatar.getGlasses()
+ added = avatar.addToAccessoriesList(ToonDNA.GLASSES, glasses[0], glasses[1], glasses[2])
+ if added:
+ avatar.b_setGlassesList(avatar.getGlassesList())
+ self.notify.info('Avatar %s put glasses %d,%d,%d in trunk.' % (avatar.doId,
+ glasses[0],
+ glasses[1],
+ glasses[2]))
+ else:
+ self.notify.warning('Avatar %s lost current glasses %d %d %d; trunk full.' % (avatar.doId,
+ glasses[0],
+ glasses[1],
+ glasses[2]))
+ avatar.b_setGlasses(defn[0], defn[1], defn[2])
+ elif self.isBackpack():
+ defn = ToonDNA.BackpackStyles[str]
+ if not avatar.checkAccessorySanity(ToonDNA.BACKPACK, defn[0], defn[1], defn[2]):
+ self.notify.warning('Avatar %s lost backpack %d %d %d; invalid item.' % (avatar.doId,
+ defn[0],
+ defn[1],
+ defn[2]))
+ return ToontownGlobals.P_ItemAvailable
+ backpack = avatar.getBackpack()
+ added = avatar.addToAccessoriesList(ToonDNA.BACKPACK, backpack[0], backpack[1], backpack[2])
+ if added:
+ avatar.b_setBackpackList(avatar.getBackpackList())
+ self.notify.info('Avatar %s put backpack %d,%d,%d in trunk.' % (avatar.doId,
+ backpack[0],
+ backpack[1],
+ backpack[2]))
+ else:
+ self.notify.warning('Avatar %s lost current backpack %d %d %d; trunk full.' % (avatar.doId,
+ backpack[0],
+ backpack[1],
+ backpack[2]))
+ avatar.b_setBackpack(defn[0], defn[1], defn[2])
+ else:
+ defn = ToonDNA.ShoesStyles[str]
+ if not avatar.checkAccessorySanity(ToonDNA.SHOES, defn[0], defn[1], defn[2]):
+ self.notify.warning('Avatar %s lost shoes %d %d %d; invalid item.' % (avatar.doId,
+ defn[0],
+ defn[1],
+ defn[2]))
+ return ToontownGlobals.P_ItemAvailable
+ shoes = avatar.getShoes()
+ added = avatar.addToAccessoriesList(ToonDNA.SHOES, shoes[0], shoes[1], shoes[2])
+ if added:
+ avatar.b_setShoesList(avatar.getShoesList())
+ self.notify.info('Avatar %s put shoes %d,%d,%d in trunk.' % (avatar.doId,
+ shoes[0],
+ shoes[1],
+ shoes[2]))
+ else:
+ self.notify.warning('Avatar %s lost current shoes %d %d %d; trunk full.' % (avatar.doId,
+ shoes[0],
+ shoes[1],
+ shoes[2]))
+ avatar.b_setShoes(defn[0], defn[1], defn[2])
+ avatar.d_catalogGenAccessories()
+ return ToontownGlobals.P_ItemAvailable
+
+ def getDeliveryTime(self):
+ return 60
+
+ def getPicture(self, avatar):
+ model = self.loadModel()
+ spin = 1
+ model.setBin('unsorted', 0, 1)
+ self.hasPicture = True
+ return self.makeFrameModel(model, spin)
+
+ def applyColor(self, model, color):
+ if model == None or color == None:
+ return
+ if isinstance(color, types.StringType):
+ tex = loader.loadTexture(color)
+ tex.setMinfilter(Texture.FTLinearMipmapLinear)
+ tex.setMagfilter(Texture.FTLinear)
+ model.setTexture(tex, 1)
+ else:
+ needsAlpha = color[3] != 1
+ color = VBase4(color[0], color[1], color[2], color[3])
+ model.setColorScale(color, 1)
+ if needsAlpha:
+ model.setTransparency(1)
+ return
+
+ def loadModel(self):
+ modelPath = self.getFilename()
+ if self.areShoes():
+ str = AccessoryTypes[self.accessoryType][ATString]
+ defn = ToonDNA.ShoesStyles[str]
+ legModel = loader.loadModel('phase_3/models/char/tt_a_chr_dgm_shorts_legs_1000')
+ model = legModel.find('**/' + modelPath)
+ else:
+ model = loader.loadModel(modelPath)
+ texture = self.getTexture()
+ if texture:
+ self.applyColor(model, texture)
+ colorVec4 = self.getColor()
+ if colorVec4:
+ modelColor = (colorVec4.getX(), colorVec4.getY(), colorVec4.getZ())
+ self.applyColor(model, modelColor)
+ model.flattenLight()
+ return model
+
+ def requestPurchase(self, phone, callback):
+ from toontown.toontowngui import TTDialog
+ avatar = base.localAvatar
+ accessoriesOnOrder = 0
+ for item in avatar.onOrder + avatar.mailboxContents:
+ if item.storedInTrunk():
+ accessoriesOnOrder += 1
+
+ if avatar.isTrunkFull(accessoriesOnOrder):
+ self.requestPurchaseCleanup()
+ buttonCallback = PythonUtil.Functor(self.__handleFullPurchaseDialog, phone, callback)
+ if avatar.getMaxAccessories() == 0:
+ text = TTLocalizer.CatalogPurchaseNoTrunk
+ else:
+ text = TTLocalizer.CatalogPurchaseTrunkFull
+ self.dialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=text, text_wordwrap=15, command=buttonCallback)
+ self.dialog.show()
+ else:
+ CatalogItem.CatalogItem.requestPurchase(self, phone, callback)
+
+ def requestPurchaseCleanup(self):
+ if hasattr(self, 'dialog'):
+ self.dialog.cleanup()
+ del self.dialog
+
+ def __handleFullPurchaseDialog(self, phone, callback, buttonValue):
+ from toontown.toontowngui import TTDialog
+ self.requestPurchaseCleanup()
+ if buttonValue == DGG.DIALOG_OK:
+ CatalogItem.CatalogItem.requestPurchase(self, phone, callback)
+ else:
+ callback(ToontownGlobals.P_UserCancelled, self)
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ if self.isHat():
+ return TTLocalizer.CatalogAcceptHat
+ elif self.areGlasses():
+ return TTLocalizer.CatalogAcceptGlasses
+ elif self.isBackpack():
+ return TTLocalizer.CatalogAcceptBackpack
+ else:
+ return TTLocalizer.CatalogAcceptShoes
+ elif retcode == ToontownGlobals.P_NoRoomForItem:
+ return TTLocalizer.CatalogAcceptTrunkFull
+ elif retcode == ToontownGlobals.P_NoTrunk:
+ return TTLocalizer.CatalogAcceptNoTrunk
+ return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode)
+
+ def isHat(self):
+ article = AccessoryTypes[self.accessoryType][ATArticle]
+ return article in [AHat, ABoysHat, AGirlsHat]
+
+ def areGlasses(self):
+ article = AccessoryTypes[self.accessoryType][ATArticle]
+ return article in [AGlasses, ABoysGlasses, AGirlsGlasses]
+
+ def isBackpack(self):
+ article = AccessoryTypes[self.accessoryType][ATArticle]
+ return article in [ABackpack, ABoysBackpack, AGirlsBackpack]
+
+ def areShoes(self):
+ article = AccessoryTypes[self.accessoryType][ATArticle]
+ return article in [AShoes, ABoysShoes, AGirlsShoes]
+
+ def output(self, store = -1):
+ return 'CatalogAccessoryItem(%s, %s)' % (self.accessoryType, self.formatOptionalData(store))
+
+ def getFilename(self):
+ str = AccessoryTypes[self.accessoryType][ATString]
+ if self.isHat():
+ defn = ToonDNA.HatStyles[str]
+ modelPath = ToonDNA.HatModels[defn[0]]
+ elif self.areGlasses():
+ defn = ToonDNA.GlassesStyles[str]
+ modelPath = ToonDNA.GlassesModels[defn[0]]
+ elif self.isBackpack():
+ defn = ToonDNA.BackpackStyles[str]
+ modelPath = ToonDNA.BackpackModels[defn[0]]
+ else:
+ defn = ToonDNA.ShoesStyles[str]
+ modelPath = ToonDNA.ShoesModels[defn[0]]
+ return modelPath
+
+ def getTexture(self):
+ str = AccessoryTypes[self.accessoryType][ATString]
+ if self.isHat():
+ defn = ToonDNA.HatStyles[str]
+ modelPath = ToonDNA.HatTextures[defn[1]]
+ elif self.areGlasses():
+ defn = ToonDNA.GlassesStyles[str]
+ modelPath = ToonDNA.GlassesTextures[defn[1]]
+ elif self.isBackpack():
+ defn = ToonDNA.BackpackStyles[str]
+ modelPath = ToonDNA.BackpackTextures[defn[1]]
+ else:
+ defn = ToonDNA.ShoesStyles[str]
+ modelPath = ToonDNA.ShoesTextures[defn[1]]
+ return modelPath
+
+ def getColor(self):
+ return None
+
+ def compareTo(self, other):
+ return self.accessoryType - other.accessoryType
+
+ def getHashContents(self):
+ return self.accessoryType
+
+ def getBasePrice(self):
+ return AccessoryTypes[self.accessoryType][ATBasePrice]
+
+ def getEmblemPrices(self):
+ result = ()
+ info = AccessoryTypes[self.accessoryType]
+ if ATEmblemPrices <= len(info) - 1:
+ result = info[ATEmblemPrices]
+ return result
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store)
+ self.accessoryType = di.getUint16()
+ if versionNumber >= 6:
+ self.loyaltyDays = di.getUint16()
+ else:
+ self.loyaltyDays = 0
+ str = AccessoryTypes[self.accessoryType][ATString]
+ if self.isHat():
+ defn = ToonDNA.HatStyles[str]
+ elif self.areGlasses():
+ defn = ToonDNA.GlassesStyles[str]
+ elif self.isBackpack():
+ defn = ToonDNA.BackpackStyles[str]
+ else:
+ defn = ToonDNA.ShoesStyles[str]
+ color = ToonDNA.ClothesColors[defn[2]]
+
+ def encodeDatagram(self, dg, store):
+ CatalogItem.CatalogItem.encodeDatagram(self, dg, store)
+ dg.addUint16(self.accessoryType)
+ dg.addUint16(self.loyaltyDays)
+
+ def isGift(self):
+ if self.getEmblemPrices():
+ return 0
+ if self.loyaltyRequirement() > 0:
+ return 0
+ elif self.accessoryType in LoyaltyAccessoryItems:
+ return 0
+ else:
+ return 1
+
+
+def getAllAccessories(*accessoryTypes):
+ list = []
+ for accessoryType in accessoryTypes:
+ base = CatalogAccessoryItem(accessoryType)
+ list.append(base)
+
+ return list
diff --git a/toontown/catalog/CatalogAccessoryItemGlobals.py b/toontown/catalog/CatalogAccessoryItemGlobals.py
new file mode 100644
index 0000000..268c236
--- /dev/null
+++ b/toontown/catalog/CatalogAccessoryItemGlobals.py
@@ -0,0 +1,739 @@
+ATArticle = 0
+ATString = 1
+ATBasePrice = 2
+ATReleased = 3
+ATEmblemPrices = 4
+AHat = 0
+AGlasses = 1
+ABackpack = 2
+AShoes = 3
+ABoysHat = 4
+ABoysGlasses = 5
+ABoysBackpack = 6
+ABoysShoes = 7
+AGirlsHat = 8
+AGirlsGlasses = 9
+AGirlsBackpack = 10
+AGirlsShoes = 11
+APriceTest = 5
+APriceBasic = 250
+APriceBasicPlus = 400
+APriceCool = 800
+APriceAwesome = 1500
+AccessoryTypes = {101: (AHat,
+ 'hbb1',
+ APriceBasic,
+ 1),
+ 102: (AHat,
+ 'hsf1',
+ APriceCool,
+ 5),
+ 103: (AGirlsHat,
+ 'hrb1',
+ APriceBasic,
+ 1),
+ 104: (AHat,
+ 'hsf2',
+ APriceCool,
+ 0),
+ 105: (AHat,
+ 'hsf3',
+ APriceCool,
+ 0),
+ 106: (AGirlsHat,
+ 'hrb2',
+ APriceBasicPlus,
+ 3),
+ 107: (AGirlsHat,
+ 'hrb3',
+ APriceBasicPlus,
+ 0),
+ 108: (AHat,
+ 'hht1',
+ APriceCool,
+ 4),
+ 109: (AHat,
+ 'hht2',
+ APriceCool,
+ 3),
+ 110: (AHat,
+ 'htp1',
+ APriceCool,
+ 3),
+ 111: (AHat,
+ 'htp2',
+ APriceCool,
+ 0),
+ 112: (AHat,
+ 'hav1',
+ 3500,
+ 0),
+ 113: (AHat,
+ 'hfp1',
+ 3500,
+ 0),
+ 114: (AHat,
+ 'hsg1',
+ 3500,
+ 0),
+ 115: (AHat,
+ 'hwt1',
+ 3500,
+ 0),
+ 116: (AHat,
+ 'hfz1',
+ APriceCool,
+ 5),
+ 117: (AHat,
+ 'hgf1',
+ APriceCool,
+ 1),
+ 118: (AHat,
+ 'hpt1',
+ APriceBasicPlus,
+ 1),
+ 119: (AHat,
+ 'hpb1',
+ APriceBasicPlus,
+ 6),
+ 120: (ABoysHat,
+ 'hcr1',
+ 10000,
+ 5),
+ 121: (AHat,
+ 'hbb2',
+ APriceBasic,
+ 2),
+ 122: (AHat,
+ 'hbb3',
+ APriceBasic,
+ 2),
+ 123: (AHat,
+ 'hcw1',
+ APriceCool,
+ 1),
+ 124: (AHat,
+ 'hpr1',
+ APriceAwesome,
+ 1),
+ 125: (AHat,
+ 'hpp1',
+ APriceBasicPlus,
+ 1),
+ 126: (AHat,
+ 'hfs1',
+ APriceCool,
+ 1),
+ 127: (AHat,
+ 'hsb1',
+ APriceAwesome,
+ 1),
+ 128: (AHat,
+ 'hst1',
+ APriceBasicPlus,
+ 1),
+ 129: (AGirlsHat,
+ 'hsu1',
+ APriceCool,
+ 1),
+ 130: (AGirlsHat,
+ 'hrb4',
+ APriceBasic,
+ 1),
+ 131: (AGirlsHat,
+ 'hrb5',
+ APriceBasicPlus,
+ 4),
+ 132: (AGirlsHat,
+ 'hrb6',
+ APriceBasic,
+ 2),
+ 133: (AGirlsHat,
+ 'hrb7',
+ APriceBasicPlus,
+ 6),
+ 134: (AHat,
+ 'hat1',
+ APriceCool,
+ 2),
+ 135: (AGirlsHat,
+ 'hhd1',
+ APriceCool,
+ 2),
+ 136: (AHat,
+ 'hbw1',
+ APriceCool,
+ 6),
+ 137: (AHat,
+ 'hch1',
+ APriceCool,
+ 5),
+ 138: (AHat,
+ 'hdt1',
+ APriceAwesome,
+ 6),
+ 139: (AHat,
+ 'hft1',
+ APriceCool,
+ 4),
+ 140: (AHat,
+ 'hfd1',
+ APriceCool,
+ 6),
+ 141: (AHat,
+ 'hmk1',
+ APriceAwesome,
+ 2),
+ 142: (AHat,
+ 'hft2',
+ APriceCool,
+ 6),
+ 143: (ABoysHat,
+ 'hhd2',
+ APriceCool,
+ 3),
+ 144: (AGirlsHat,
+ 'hpc1',
+ APriceCool,
+ 5),
+ 145: (AHat,
+ 'hrh1',
+ APriceCool,
+ 2),
+ 146: (AHat,
+ 'hhm1',
+ 2500,
+ 2),
+ 147: (AHat,
+ 'hat2',
+ APriceCool,
+ 2),
+ 148: (AGirlsHat,
+ 'htr1',
+ 10000,
+ 3),
+ 149: (AHat,
+ 'hhm2',
+ APriceAwesome,
+ 2),
+ 150: (AHat,
+ 'hwz1',
+ APriceCool,
+ 2),
+ 151: (AHat,
+ 'hwz2',
+ APriceCool,
+ 2),
+ 152: (AHat,
+ 'hhm3',
+ APriceAwesome,
+ 6),
+ 153: (AHat,
+ 'hhm4',
+ APriceAwesome,
+ 5),
+ 154: (AHat,
+ 'hfp2',
+ APriceCool,
+ 5),
+ 155: (AHat,
+ 'hhm5',
+ APriceAwesome,
+ 4),
+ 156: (AHat,
+ 'hnp1',
+ APriceAwesome,
+ 6),
+ 157: (AHat,
+ 'hpc2',
+ APriceAwesome,
+ 3),
+ 158: (AHat,
+ 'hph1',
+ APriceAwesome,
+ 4),
+ 159: (AHat,
+ 'hwg1',
+ APriceCool,
+ 5),
+ 160: (AHat,
+ 'hbb4',
+ APriceBasic,
+ 5),
+ 161: (AHat,
+ 'hbb5',
+ APriceBasic,
+ 2),
+ 162: (AHat,
+ 'hbb6',
+ APriceBasic,
+ 5),
+ 163: (AHat,
+ 'hsl1',
+ APriceCool,
+ 5),
+ 164: (AHat,
+ 'hfr1',
+ 3000,
+ 4),
+ 165: (AHat,
+ 'hby1',
+ APriceAwesome,
+ 5),
+ 166: (AGirlsHat,
+ 'hrb8',
+ APriceBasicPlus,
+ 6),
+ 167: (AHat,
+ 'hjh1',
+ APriceAwesome,
+ 3),
+ 168: (AHat,
+ 'hbb7',
+ APriceBasic,
+ 6),
+ 169: (AGirlsHat,
+ 'hrb9',
+ APriceBasicPlus,
+ 6),
+ 170: (AHat,
+ 'hwt2',
+ APriceAwesome,
+ 4),
+ 171: (AGirlsHat,
+ 'hhw1',
+ APriceBasicPlus,
+ 7),
+ 172: (AHat,
+ 'hhw2',
+ 900,
+ 7),
+ 173: (AHat,
+ 'hob1',
+ APriceAwesome,
+ 6),
+ 174: (AHat,
+ 'hbn1',
+ APriceAwesome,
+ 8),
+ 175: (AHat,
+ 'hpt2',
+ APriceCool,
+ 9),
+ 201: (AGlasses,
+ 'grd1',
+ APriceBasicPlus,
+ 0),
+ 202: (AGlasses,
+ 'gmb1',
+ APriceCool,
+ 1),
+ 203: (AGlasses,
+ 'gnr1',
+ APriceCool,
+ 0),
+ 204: (AGlasses,
+ 'gst1',
+ APriceBasicPlus,
+ 1),
+ 205: (AGlasses,
+ 'g3d1',
+ APriceCool,
+ 1),
+ 206: (AGlasses,
+ 'gav1',
+ APriceCool,
+ 1),
+ 207: (AGlasses,
+ 'gce1',
+ APriceCool,
+ 2),
+ 208: (AGlasses,
+ 'gdk1',
+ APriceBasic,
+ 1),
+ 209: (AGlasses,
+ 'gjo1',
+ APriceBasicPlus,
+ 1),
+ 210: (AGlasses,
+ 'gsb1',
+ APriceAwesome,
+ 1),
+ 211: (AGlasses,
+ 'ggl1',
+ APriceCool,
+ 6),
+ 212: (AGlasses,
+ 'ggm1',
+ APriceBasicPlus,
+ 2),
+ 213: (AGlasses,
+ 'ghg1',
+ APriceAwesome,
+ 3),
+ 214: (AGlasses,
+ 'gie1',
+ APriceCool,
+ 2),
+ 215: (AGlasses,
+ 'gmt1',
+ APriceCool,
+ 2),
+ 216: (AGlasses,
+ 'gmt2',
+ APriceCool,
+ 2),
+ 217: (AGlasses,
+ 'gmt3',
+ 3500,
+ 5),
+ 218: (AGlasses,
+ 'gmt4',
+ 3500,
+ 5),
+ 219: (AGlasses,
+ 'gmt5',
+ 3500,
+ 5),
+ 220: (AGlasses,
+ 'gmn1',
+ APriceAwesome,
+ 6),
+ 221: (AGlasses,
+ 'gmo1',
+ APriceAwesome,
+ 4),
+ 222: (AGlasses,
+ 'gsr1',
+ APriceBasicPlus,
+ 5),
+ 223: (ABoysGlasses,
+ 'ghw1',
+ APriceTest,
+ 0),
+ 224: (ABoysGlasses,
+ 'ghw2',
+ APriceBasic,
+ 7),
+ 225: (AGlasses,
+ 'gag1',
+ APriceAwesome,
+ 8),
+ 301: (ABackpack,
+ 'bpb1',
+ APriceBasic,
+ 4),
+ 302: (ABackpack,
+ 'bpb2',
+ APriceBasic,
+ 1),
+ 303: (ABackpack,
+ 'bpb3',
+ APriceBasic,
+ 5),
+ 304: (ABackpack,
+ 'bpd1',
+ APriceBasicPlus,
+ 4),
+ 305: (ABackpack,
+ 'bpd2',
+ APriceBasicPlus,
+ 5),
+ 306: (ABackpack,
+ 'bwg1',
+ APriceCool,
+ 2),
+ 307: (ABackpack,
+ 'bwg2',
+ APriceCool,
+ 2),
+ 308: (ABackpack,
+ 'bwg3',
+ APriceCool,
+ 1),
+ 309: (ABackpack,
+ 'bst1',
+ APriceAwesome,
+ 1),
+ 310: (ABackpack,
+ 'bfn1',
+ APriceCool,
+ 1),
+ 311: (ABackpack,
+ 'baw1',
+ APriceCool,
+ 3),
+ 312: (ABackpack,
+ 'baw2',
+ APriceAwesome,
+ 2),
+ 313: (ABackpack,
+ 'bwt1',
+ 3000,
+ 3),
+ 314: (ABackpack,
+ 'bwg4',
+ APriceAwesome,
+ 6),
+ 315: (ABackpack,
+ 'bwg5',
+ 3000,
+ 5),
+ 316: (ABackpack,
+ 'bwg6',
+ 3000,
+ 4),
+ 317: (ABackpack,
+ 'bjp1',
+ 3000,
+ 1),
+ 318: (ABackpack,
+ 'blg1',
+ APriceCool,
+ 2),
+ 319: (ABackpack,
+ 'bsa1',
+ 2500,
+ 5),
+ 320: (ABackpack,
+ 'bwg7',
+ APriceAwesome,
+ 6),
+ 321: (ABackpack,
+ 'bsa2',
+ 2000,
+ 2),
+ 322: (ABackpack,
+ 'bsa3',
+ 2000,
+ 2),
+ 323: (ABackpack,
+ 'bap1',
+ 5000,
+ 4),
+ 324: (ABackpack,
+ 'bhw1',
+ 900,
+ 7),
+ 325: (ABackpack,
+ 'bhw2',
+ APriceBasicPlus,
+ 7),
+ 326: (ABackpack,
+ 'bhw3',
+ APriceBasicPlus,
+ 7),
+ 327: (ABackpack,
+ 'bhw4',
+ 900,
+ 7),
+ 328: (ABackpack,
+ 'bob1',
+ 3000,
+ 6),
+ 329: (ABackpack,
+ 'bfg1',
+ 3000,
+ 6),
+ 330: (ABackpack,
+ 'bfl1',
+ APriceAwesome,
+ 8),
+ 401: (AShoes,
+ 'sat1',
+ APriceBasic,
+ 3),
+ 402: (AShoes,
+ 'sat2',
+ APriceBasic,
+ 1),
+ 403: (AShoes,
+ 'smb1',
+ APriceAwesome,
+ 1),
+ 404: (AShoes,
+ 'scs1',
+ APriceBasicPlus,
+ 6),
+ 405: (ABoysShoes,
+ 'swt1',
+ APriceBasicPlus,
+ 1),
+ 406: (AGirlsShoes,
+ 'smj1',
+ APriceBasicPlus,
+ 1),
+ 407: (AShoes,
+ 'sdk1',
+ APriceBasic,
+ 1),
+ 408: (AShoes,
+ 'sat3',
+ APriceBasic,
+ 1),
+ 409: (AShoes,
+ 'scs2',
+ APriceBasicPlus,
+ 1),
+ 410: (AShoes,
+ 'scs3',
+ APriceBasicPlus,
+ 1),
+ 411: (AShoes,
+ 'scs4',
+ APriceBasicPlus,
+ 1),
+ 412: (AShoes,
+ 'scb1',
+ APriceAwesome,
+ 1),
+ 413: (AShoes,
+ 'sfb1',
+ APriceCool,
+ 1),
+ 414: (AShoes,
+ 'sht1',
+ APriceAwesome,
+ 4),
+ 415: (AGirlsShoes,
+ 'smj2',
+ APriceBasicPlus,
+ 3),
+ 416: (AGirlsShoes,
+ 'smj3',
+ APriceBasicPlus,
+ 4),
+ 417: (AShoes,
+ 'ssb1',
+ APriceAwesome,
+ 2),
+ 418: (AShoes,
+ 'sts1',
+ APriceBasic,
+ 5),
+ 419: (AShoes,
+ 'sts2',
+ APriceBasic,
+ 4),
+ 420: (AShoes,
+ 'scs5',
+ APriceBasicPlus,
+ 4),
+ 421: (AShoes,
+ 'smb2',
+ APriceAwesome,
+ 3),
+ 422: (AShoes,
+ 'smb3',
+ APriceAwesome,
+ 2),
+ 423: (AShoes,
+ 'smb4',
+ APriceAwesome,
+ 5),
+ 424: (AShoes,
+ 'sfb2',
+ 2000,
+ 6),
+ 425: (AShoes,
+ 'sfb3',
+ 2000,
+ 4),
+ 426: (AShoes,
+ 'sfb4',
+ 2000,
+ 3),
+ 427: (AShoes,
+ 'sfb5',
+ 2000,
+ 5),
+ 428: (AShoes,
+ 'sfb6',
+ 2000,
+ 4),
+ 429: (AShoes,
+ 'slf1',
+ APriceBasicPlus,
+ 3),
+ 430: (AGirlsShoes,
+ 'smj4',
+ APriceBasicPlus,
+ 2),
+ 431: (AShoes,
+ 'smt1',
+ APriceAwesome,
+ 4),
+ 432: (AShoes,
+ 'sox1',
+ APriceAwesome,
+ 5),
+ 433: (AShoes,
+ 'srb1',
+ APriceAwesome,
+ 6),
+ 434: (AShoes,
+ 'sst1',
+ 3000,
+ 3),
+ 435: (AShoes,
+ 'swb1',
+ APriceCool,
+ 3),
+ 436: (AShoes,
+ 'swb2',
+ APriceCool,
+ 4),
+ 437: (AShoes,
+ 'swk1',
+ APriceAwesome,
+ 3),
+ 438: (AShoes,
+ 'scs6',
+ APriceBasicPlus,
+ 0),
+ 439: (AShoes,
+ 'smb5',
+ APriceAwesome,
+ 3),
+ 440: (AShoes,
+ 'sht2',
+ APriceAwesome,
+ 4),
+ 441: (AShoes,
+ 'srb2',
+ APriceAwesome,
+ 3),
+ 442: (AShoes,
+ 'sts3',
+ APriceBasic,
+ 6),
+ 443: (AShoes,
+ 'sts4',
+ APriceBasic,
+ 3),
+ 444: (AShoes,
+ 'sts5',
+ APriceBasic,
+ 2),
+ 445: (AShoes,
+ 'srb3',
+ APriceCool,
+ 5),
+ 446: (AShoes,
+ 'srb4',
+ APriceCool,
+ 3),
+ 447: (AShoes,
+ 'sat4',
+ APriceBasic,
+ 3),
+ 448: (AShoes,
+ 'shw1',
+ APriceCool,
+ 7),
+ 449: (AShoes,
+ 'shw2',
+ APriceCool,
+ 7)}
+LoyaltyAccessoryItems = []
diff --git a/toontown/catalog/CatalogAnimatedFurnitureItem.py b/toontown/catalog/CatalogAnimatedFurnitureItem.py
new file mode 100644
index 0000000..8184892
--- /dev/null
+++ b/toontown/catalog/CatalogAnimatedFurnitureItem.py
@@ -0,0 +1,27 @@
+from CatalogFurnitureItem import *
+FTAnimRate = 6
+AnimatedFurnitureItemKeys = (10020, 270, 990, 460, 470, 480, 490, 491, 492)
+
+class CatalogAnimatedFurnitureItem(CatalogFurnitureItem):
+
+ def loadModel(self):
+ model = CatalogFurnitureItem.loadModel(self)
+ self.setAnimRate(model, self.getAnimRate())
+ return model
+
+ def getAnimRate(self):
+ item = FurnitureTypes[self.furnitureType]
+ if FTAnimRate < len(item):
+ animRate = item[FTAnimRate]
+ if not animRate == None:
+ return item[FTAnimRate]
+ else:
+ return 1
+ else:
+ return 1
+ return
+
+ def setAnimRate(self, model, rate):
+ seqNodes = model.findAllMatches('**/seqNode*')
+ for seqNode in seqNodes:
+ seqNode.node().setPlayRate(rate)
diff --git a/toontown/catalog/CatalogAtticItem.py b/toontown/catalog/CatalogAtticItem.py
new file mode 100644
index 0000000..44cd6e7
--- /dev/null
+++ b/toontown/catalog/CatalogAtticItem.py
@@ -0,0 +1,65 @@
+import CatalogItem
+from toontown.toonbase import TTLocalizer
+from direct.showbase import PythonUtil
+from direct.gui.DirectGui import *
+from toontown.toonbase import ToontownGlobals
+
+class CatalogAtticItem(CatalogItem.CatalogItem):
+
+ def storedInAttic(self):
+ return 1
+
+ def isDeletable(self):
+ return 1
+
+ def getHouseInfo(self, avatar):
+ houseId = avatar.houseId
+ if not houseId:
+ self.notify.warning('Avatar %s has no houseId associated.' % avatar.doId)
+ return (None, ToontownGlobals.P_InvalidIndex)
+ house = simbase.air.doId2do.get(houseId)
+ if not house:
+ self.notify.warning('House %s (for avatar %s) not instantiated.' % (houseId, avatar.doId))
+ return (None, ToontownGlobals.P_InvalidIndex)
+ numAtticItems = len(house.atticItems) + len(house.atticWallpaper) + len(house.atticWindows)
+ numHouseItems = numAtticItems + len(house.interiorItems)
+ if numHouseItems >= ToontownGlobals.MaxHouseItems and not self.replacesExisting():
+ return (house, ToontownGlobals.P_NoRoomForItem)
+ return (house, ToontownGlobals.P_ItemAvailable)
+
+ def requestPurchase(self, phone, callback):
+ from toontown.toontowngui import TTDialog
+ avatar = base.localAvatar
+ itemsOnOrder = 0
+ for item in avatar.onOrder + avatar.mailboxContents:
+ if item.storedInAttic() and not item.replacesExisting():
+ itemsOnOrder += 1
+
+ numHouseItems = phone.numHouseItems + itemsOnOrder
+ if numHouseItems >= ToontownGlobals.MaxHouseItems and not self.replacesExisting():
+ self.requestPurchaseCleanup()
+ buttonCallback = PythonUtil.Functor(self.__handleFullPurchaseDialog, phone, callback)
+ self.dialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.CatalogPurchaseHouseFull, text_wordwrap=15, command=buttonCallback)
+ self.dialog.show()
+ else:
+ CatalogItem.CatalogItem.requestPurchase(self, phone, callback)
+
+ def requestPurchaseCleanup(self):
+ if hasattr(self, 'dialog'):
+ self.dialog.cleanup()
+ del self.dialog
+
+ def __handleFullPurchaseDialog(self, phone, callback, buttonValue):
+ from toontown.toontowngui import TTDialog
+ self.requestPurchaseCleanup()
+ if buttonValue == DGG.DIALOG_OK:
+ CatalogItem.CatalogItem.requestPurchase(self, phone, callback)
+ else:
+ callback(ToontownGlobals.P_UserCancelled, self)
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ return TTLocalizer.CatalogAcceptInAttic
+ elif retcode == ToontownGlobals.P_NoRoomForItem:
+ return TTLocalizer.CatalogAcceptHouseFull
+ return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode)
diff --git a/toontown/catalog/CatalogBeanItem.py b/toontown/catalog/CatalogBeanItem.py
new file mode 100644
index 0000000..b2f09b6
--- /dev/null
+++ b/toontown/catalog/CatalogBeanItem.py
@@ -0,0 +1,75 @@
+import CatalogItem
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from otp.otpbase import OTPLocalizer
+from direct.interval.IntervalGlobal import *
+
+class CatalogBeanItem(CatalogItem.CatalogItem):
+ sequenceNumber = 0
+
+ def makeNewItem(self, beanAmount, tagCode = 1):
+ self.beanAmount = beanAmount
+ self.giftCode = tagCode
+ CatalogItem.CatalogItem.makeNewItem(self)
+
+ def getPurchaseLimit(self):
+ return 0
+
+ def reachedPurchaseLimit(self, avatar):
+ if self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or self in avatar.awardMailboxContents or self in avatar.onAwardOrder:
+ return 1
+ return 0
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ if self.giftCode == ToontownGlobals.GIFT_RAT:
+ return TTLocalizer.CatalogAcceptRATBeans
+ elif self.giftCode == ToontownGlobals.GIFT_partyrefund:
+ return TTLocalizer.CatalogAcceptPartyRefund
+ else:
+ return TTLocalizer.CatalogAcceptBeans
+ return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode)
+
+ def saveHistory(self):
+ return 0
+
+ def getTypeName(self):
+ return TTLocalizer.BeanTypeName
+
+ def getName(self):
+ name = '%s %s' % (self.beanAmount, TTLocalizer.BeanTypeName)
+ return name
+
+ def recordPurchase(self, avatar, optional):
+ if avatar:
+ avatar.addMoney(self.beanAmount)
+ return ToontownGlobals.P_ItemAvailable
+
+ def getPicture(self, avatar):
+ beanJar = loader.loadModel('phase_3.5/models/gui/jar_gui')
+ frame = self.makeFrame()
+ beanJar.reparentTo(frame)
+ beanJar.setPos(0, 0, 0)
+ beanJar.setScale(2.5)
+ self.hasPicture = True
+ return (frame, None)
+
+ def output(self, store = -1):
+ return 'CatalogBeanItem(%s%s)' % (self.beanAmount, self.formatOptionalData(store))
+
+ def compareTo(self, other):
+ return self.beanAmount - other.beanAmount
+
+ def getHashContents(self):
+ return self.beanAmount
+
+ def getBasePrice(self):
+ return self.beanAmount
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store)
+ self.beanAmount = di.getUint16()
+
+ def encodeDatagram(self, dg, store):
+ CatalogItem.CatalogItem.encodeDatagram(self, dg, store)
+ dg.addUint16(self.beanAmount)
diff --git a/toontown/catalog/CatalogChatItem.py b/toontown/catalog/CatalogChatItem.py
new file mode 100644
index 0000000..4726a9e
--- /dev/null
+++ b/toontown/catalog/CatalogChatItem.py
@@ -0,0 +1,148 @@
+from pandac.PandaModules import *
+import CatalogItem
+from toontown.toonbase import ToontownGlobals
+from otp.otpbase import OTPLocalizer
+from toontown.toonbase import TTLocalizer
+bannedPhrases = [11009]
+
+class CatalogChatItem(CatalogItem.CatalogItem):
+
+ def makeNewItem(self, customIndex):
+ self.customIndex = customIndex
+ CatalogItem.CatalogItem.makeNewItem(self)
+
+ def getPurchaseLimit(self):
+ return 1
+
+ def reachedPurchaseLimit(self, avatar):
+ if self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or self in avatar.awardMailboxContents or self in avatar.onAwardOrder:
+ return 1
+ return avatar.customMessages.count(self.customIndex) != 0
+
+ def getTypeName(self):
+ return TTLocalizer.ChatTypeName
+
+ def getName(self):
+ return TTLocalizer.ChatItemQuotes % OTPLocalizer.CustomSCStrings[self.customIndex]
+
+ def getDisplayName(self):
+ return OTPLocalizer.CustomSCStrings[self.customIndex]
+
+ def recordPurchase(self, avatar, optional):
+ if avatar.customMessages.count(self.customIndex) != 0:
+ return ToontownGlobals.P_ReachedPurchaseLimit
+ if len(avatar.customMessages) >= ToontownGlobals.MaxCustomMessages:
+ if optional >= 0 and optional < len(avatar.customMessages):
+ del avatar.customMessages[optional]
+ if len(avatar.customMessages) >= ToontownGlobals.MaxCustomMessages:
+ return ToontownGlobals.P_NoRoomForItem
+ avatar.customMessages.append(self.customIndex)
+ avatar.d_setCustomMessages(avatar.customMessages)
+ return ToontownGlobals.P_ItemAvailable
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ return TTLocalizer.CatalogAcceptChat
+ return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode)
+
+ def output(self, store = -1):
+ return 'CatalogChatItem(%s%s)' % (self.customIndex, self.formatOptionalData(store))
+
+ def compareTo(self, other):
+ return self.customIndex - other.customIndex
+
+ def getHashContents(self):
+ return self.customIndex
+
+ def getBasePrice(self):
+ if self.customIndex >= 10000:
+ return 150
+ return 100
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store)
+ self.customIndex = di.getUint16()
+ text = OTPLocalizer.CustomSCStrings[self.customIndex]
+
+ def encodeDatagram(self, dg, store):
+ CatalogItem.CatalogItem.encodeDatagram(self, dg, store)
+ dg.addUint16(self.customIndex)
+
+ def acceptItem(self, mailbox, index, callback):
+ if len(base.localAvatar.customMessages) < ToontownGlobals.MaxCustomMessages:
+ mailbox.acceptItem(self, index, callback)
+ else:
+ self.showMessagePickerOnAccept(mailbox, index, callback)
+
+ def requestPurchase(self, phone, callback):
+ if len(base.localAvatar.customMessages) < ToontownGlobals.MaxCustomMessages:
+ CatalogItem.CatalogItem.requestPurchase(self, phone, callback)
+ else:
+ self.showMessagePicker(phone, callback)
+
+ def showMessagePicker(self, phone, callback):
+ self.phone = phone
+ self.callback = callback
+ import CatalogChatItemPicker
+ self.messagePicker = CatalogChatItemPicker.CatalogChatItemPicker(self.__handlePickerDone, self.customIndex)
+ self.messagePicker.show()
+
+ def showMessagePickerOnAccept(self, mailbox, index, callback):
+ self.mailbox = mailbox
+ self.callback = callback
+ self.index = index
+ import CatalogChatItemPicker
+ self.messagePicker = CatalogChatItemPicker.CatalogChatItemPicker(self.__handlePickerOnAccept, self.customIndex)
+ self.messagePicker.show()
+
+ def __handlePickerOnAccept(self, status, pickedMessage = None):
+ print 'Picker Status%s' % status
+ if status == 'pick':
+ self.mailbox.acceptItem(self, self.index, self.callback, pickedMessage)
+ else:
+ print 'picker canceled'
+ self.callback(ToontownGlobals.P_UserCancelled, None, self.index)
+ self.messagePicker.hide()
+ self.messagePicker.destroy()
+ del self.messagePicker
+ del self.callback
+ del self.mailbox
+ return
+
+ def __handlePickerDone(self, status, pickedMessage = None):
+ if status == 'pick':
+ CatalogItem.CatalogItem.requestPurchase(self, self.phone, self.callback, pickedMessage)
+ self.messagePicker.hide()
+ self.messagePicker.destroy()
+ del self.messagePicker
+ del self.callback
+ del self.phone
+
+ def getPicture(self, avatar):
+ chatBalloon = loader.loadModel('phase_3/models/props/chatbox.bam')
+ chatBalloon.find('**/top').setPos(1, 0, 5)
+ chatBalloon.find('**/middle').setScale(1, 1, 3)
+ frame = self.makeFrame()
+ chatBalloon.reparentTo(frame)
+ chatBalloon.setPos(-2.19, 0, -1.74)
+ chatBalloon.setScale(0.4)
+ self.hasPicture = True
+ return (frame, None)
+
+
+def getChatRange(fromIndex, toIndex, *otherRanges):
+ list = []
+ froms = [fromIndex]
+ tos = [toIndex]
+ i = 0
+ while i < len(otherRanges):
+ froms.append(otherRanges[i])
+ tos.append(otherRanges[i + 1])
+ i += 2
+
+ for chatId in OTPLocalizer.CustomSCStrings.keys():
+ for fromIndex, toIndex in zip(froms, tos):
+ if chatId >= fromIndex and chatId <= toIndex and chatId not in bannedPhrases:
+ list.append(CatalogChatItem(chatId))
+
+ return list
diff --git a/toontown/catalog/CatalogChatItemPicker.py b/toontown/catalog/CatalogChatItemPicker.py
new file mode 100644
index 0000000..2f36608
--- /dev/null
+++ b/toontown/catalog/CatalogChatItemPicker.py
@@ -0,0 +1,91 @@
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from direct.showbase import DirectObject
+import CatalogItem
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from otp.otpbase import OTPLocalizer
+from toontown.toontowngui import TTDialog
+NUM_ITEMS_SHOWN = 15
+
+class CatalogChatItemPicker(DirectObject.DirectObject):
+
+ def __init__(self, callback, newMsg):
+ self.confirmDelete = None
+ self.doneCallback = callback
+ self.panel = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.4, 1, 1.6), text=TTLocalizer.MessagePickerTitle % OTPLocalizer.CustomSCStrings[newMsg], text_pos=(0, 0.68), text_scale=0.05, text_wordwrap=24, pos=(0, 0, 0))
+ msgStrings = []
+ for msg in base.localAvatar.customMessages:
+ msgStrings.append(OTPLocalizer.CustomSCStrings[msg])
+
+ gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui')
+ self.picker = DirectScrolledList(parent=self.panel, relief=None, pos=(0, 0, 0), incButton_image=(gui.find('**/FndsLst_ScrollUp'),
+ gui.find('**/FndsLst_ScrollDN'),
+ gui.find('**/FndsLst_ScrollUp_Rllvr'),
+ gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(1.3, 1.3, -1.3), incButton_pos=(0, 0, -0.5), incButton_image3_color=Vec4(1, 1, 1, 0.2), decButton_image=(gui.find('**/FndsLst_ScrollUp'),
+ gui.find('**/FndsLst_ScrollDN'),
+ gui.find('**/FndsLst_ScrollUp_Rllvr'),
+ gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(1.3, 1.3, 1.3), decButton_pos=(0, 0, 0.5), decButton_image3_color=Vec4(1, 1, 1, 0.2), itemFrame_pos=(0, 0, 0.39), itemFrame_scale=1.0, itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(-0.55,
+ 0.55,
+ -0.85,
+ 0.06), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), itemMakeFunction=self.makeMessageButton, itemMakeExtraArgs=[base.localAvatar.customMessages], numItemsVisible=NUM_ITEMS_SHOWN, items=msgStrings)
+ clipper = PlaneNode('clipper')
+ clipper.setPlane(Plane(Vec3(-1, 0, 0), Point3(0.55, 0, 0)))
+ clipNP = self.picker.attachNewNode(clipper)
+ self.picker.setClipPlane(clipNP)
+ gui.removeNode()
+ buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui')
+ upButton = buttonModels.find('**/InventoryButtonUp')
+ downButton = buttonModels.find('**/InventoryButtonDown')
+ rolloverButton = buttonModels.find('**/InventoryButtonRollover')
+ exitButton = DirectButton(parent=self.panel, relief=None, pos=(0, 0, -0.7), text=TTLocalizer.MessagePickerCancel, text_scale=TTLocalizer.CCIPexitButton, text_pos=(-0.005, -0.01), text_fg=Vec4(1, 1, 1, 1), textMayChange=0, image=(upButton, downButton, rolloverButton), image_scale=1.1, image_color=(0, 0.6, 1, 1), command=self.__handleCancel)
+ buttonModels.removeNode()
+ return
+
+ def hide(self):
+ base.transitions.noTransitions()
+ self.panel.hide()
+
+ def show(self):
+ base.transitions.fadeScreen(0.5)
+ self.panel.setBin('gui-popup', 0)
+ self.panel.show()
+
+ def destroy(self):
+ base.transitions.noTransitions()
+ self.panel.destroy()
+ del self.panel
+ del self.picker
+ del self.doneCallback
+ if self.confirmDelete:
+ self.confirmDelete.cleanup()
+ del self.confirmDelete
+ self.confirmDelete = None
+ return
+
+ def makeMessageButton(self, name, number, *extraArgs):
+ msg = extraArgs[0][0][number]
+ return DirectButton(relief=None, text=OTPLocalizer.CustomSCStrings[msg], text_pos=(-0.5, 0, 0), text_scale=0.05, text_align=TextNode.ALeft, text1_bg=Vec4(1, 1, 0, 1), text2_bg=Vec4(0.5, 0.9, 1, 1), text3_fg=Vec4(0.4, 0.8, 0.4, 1), command=self.__handleDelete, extraArgs=[msg])
+
+ def __handleDelete(self, msg):
+ self.confirmDelete = TTDialog.TTGlobalDialog(doneEvent='confirmDelete', message=TTLocalizer.MessageConfirmDelete % OTPLocalizer.CustomSCStrings[msg], style=TTDialog.TwoChoice)
+ self.confirmDelete.msg = msg
+ self.hide()
+ self.confirmDelete.show()
+ self.accept('confirmDelete', self.__handleDeleteConfirm)
+
+ def __handleCancel(self):
+ self.doneCallback('cancel')
+
+ def __handleDeleteConfirm(self):
+ status = self.confirmDelete.doneStatus
+ msg = self.confirmDelete.msg
+ self.ignore('confirmDelete')
+ self.confirmDelete.cleanup()
+ del self.confirmDelete
+ self.confirmDelete = None
+ if status == 'ok':
+ self.doneCallback('pick', base.localAvatar.customMessages.index(msg))
+ else:
+ self.show()
+ return
diff --git a/toontown/catalog/CatalogClothingItem.py b/toontown/catalog/CatalogClothingItem.py
new file mode 100644
index 0000000..429986e
--- /dev/null
+++ b/toontown/catalog/CatalogClothingItem.py
@@ -0,0 +1,602 @@
+import CatalogItem
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.toon import ToonDNA
+import random
+from direct.showbase import PythonUtil
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+CTArticle = 0
+CTString = 1
+CTBasePrice = 2
+CTEmblemPrices = 3
+ABoysShirt = 0
+AGirlsShirt = 1
+AShirt = 2
+ABoysShorts = 3
+AGirlsShorts = 4
+AGirlsSkirt = 5
+AShorts = 6
+ClothingTypes = {101: (ABoysShirt, 'bss1', 40),
+ 102: (ABoysShirt, 'bss2', 40),
+ 103: (ABoysShirt, 'bss3', 40),
+ 105: (ABoysShirt, 'bss4', 40),
+ 104: (ABoysShirt, 'bss5', 40),
+ 106: (ABoysShirt, 'bss6', 40),
+ 107: (ABoysShirt, 'bss7', 40),
+ 108: (ABoysShirt, 'bss8', 40),
+ 109: (ABoysShirt, 'bss9', 40),
+ 111: (ABoysShirt, 'bss11', 40),
+ 115: (ABoysShirt, 'bss15', 40),
+ 116: (ABoysShirt, 'c_ss1', 80),
+ 117: (ABoysShirt, 'c_ss2', 80),
+ 118: (ABoysShirt, 'c_bss1', 80),
+ 119: (ABoysShirt, 'c_bss2', 80),
+ 120: (ABoysShirt, 'c_ss3', 80),
+ 121: (ABoysShirt, 'c_bss3', 80),
+ 122: (ABoysShirt, 'c_bss4', 80),
+ 123: (ABoysShirt, 'c_ss4', 120),
+ 124: (ABoysShirt, 'c_ss5', 120),
+ 125: (AShirt, 'c_ss6', 120),
+ 126: (AShirt, 'c_ss7', 120),
+ 127: (AShirt, 'c_ss8', 120),
+ 128: (AShirt, 'c_ss9', 120),
+ 129: (AShirt, 'c_ss10', 120),
+ 130: (AShirt, 'c_ss11', 120),
+ 131: (ABoysShirt, 'c_ss12', 160),
+ 201: (AGirlsShirt, 'gss1', 40),
+ 202: (AGirlsShirt, 'gss2', 40),
+ 203: (AGirlsShirt, 'gss3', 40),
+ 205: (AGirlsShirt, 'gss4', 40),
+ 204: (AGirlsShirt, 'gss5', 40),
+ 206: (AGirlsShirt, 'gss6', 40),
+ 207: (AGirlsShirt, 'gss7', 40),
+ 208: (AGirlsShirt, 'gss8', 40),
+ 209: (AGirlsShirt, 'gss9', 40),
+ 211: (AGirlsShirt, 'gss11', 40),
+ 215: (AGirlsShirt, 'gss15', 40),
+ 216: (AGirlsShirt, 'c_ss1', 80),
+ 217: (AGirlsShirt, 'c_ss2', 80),
+ 218: (AGirlsShirt, 'c_gss1', 80),
+ 219: (AGirlsShirt, 'c_gss2', 80),
+ 220: (AGirlsShirt, 'c_ss3', 80),
+ 221: (AGirlsShirt, 'c_gss3', 80),
+ 222: (AGirlsShirt, 'c_gss4', 80),
+ 223: (AGirlsShirt, 'c_gss5', 80),
+ 224: (AGirlsShirt, 'c_ss4', 120),
+ 225: (AGirlsShirt, 'c_ss13', 160),
+ 301: (ABoysShorts, 'bbs1', 50),
+ 302: (ABoysShorts, 'bbs2', 50),
+ 303: (ABoysShorts, 'bbs3', 50),
+ 304: (ABoysShorts, 'bbs4', 50),
+ 305: (ABoysShorts, 'bbs5', 50),
+ 308: (ABoysShorts, 'bbs8', 50),
+ 310: (ABoysShorts, 'c_bs1', 120),
+ 311: (ABoysShorts, 'c_bs2', 120),
+ 312: (ABoysShorts, 'c_bs3', 120),
+ 313: (ABoysShorts, 'c_bs4', 120),
+ 314: (ABoysShorts, 'c_bs5', 160),
+ 401: (AGirlsSkirt, 'gsk1', 50),
+ 403: (AGirlsSkirt, 'gsk3', 50),
+ 404: (AGirlsSkirt, 'gsk4', 50),
+ 405: (AGirlsSkirt, 'gsk5', 50),
+ 407: (AGirlsSkirt, 'gsk7', 50),
+ 408: (AGirlsSkirt, 'c_gsk1', 100),
+ 409: (AGirlsSkirt, 'c_gsk2', 100),
+ 410: (AGirlsSkirt, 'c_gsk3', 100),
+ 411: (AGirlsSkirt, 'c_gsk4', 120),
+ 412: (AGirlsSkirt, 'c_gsk5', 120),
+ 413: (AGirlsSkirt, 'c_gsk6', 120),
+ 414: (AGirlsSkirt, 'c_gsk7', 160),
+ 451: (AGirlsShorts, 'gsh1', 50),
+ 452: (AGirlsShorts, 'gsh2', 50),
+ 453: (AGirlsShorts, 'gsh3', 50),
+ 1001: (AShirt, 'hw_ss1', 200),
+ 1002: (AShirt, 'hw_ss2', 200),
+ 1100: (AShirt, 'wh_ss1', 200),
+ 1101: (AShirt, 'wh_ss2', 200),
+ 1102: (AShirt, 'wh_ss3', 200),
+ 1103: (AShirt, 'wh_ss4', 200),
+ 1104: (ABoysShorts, 'wh_bs1', 200),
+ 1105: (ABoysShorts, 'wh_bs2', 200),
+ 1106: (ABoysShorts, 'wh_bs3', 200),
+ 1107: (ABoysShorts, 'wh_bs4', 200),
+ 1108: (AGirlsSkirt, 'wh_gsk1', 200),
+ 1109: (AGirlsSkirt, 'wh_gsk2', 200),
+ 1110: (AGirlsSkirt, 'wh_gsk3', 200),
+ 1111: (AGirlsSkirt, 'wh_gsk4', 200),
+ 1112: (AShirt, 'hw_ss5', 200),
+ 1113: (AShirt, 'hw_ss6', 300),
+ 1114: (AShirt, 'hw_ss7', 200),
+ 1115: (AShirt, 'hw_ss8', 200),
+ 1116: (AShirt, 'hw_ss9', 300),
+ 1117: (ABoysShorts, 'hw_bs1', 200),
+ 1118: (ABoysShorts, 'hw_bs2', 300),
+ 1119: (ABoysShorts, 'hw_bs5', 200),
+ 1120: (ABoysShorts, 'hw_bs6', 200),
+ 1121: (ABoysShorts, 'hw_bs7', 300),
+ 1122: (AGirlsShorts, 'hw_gs1', 200),
+ 1123: (AGirlsShorts, 'hw_gs2', 300),
+ 1124: (AGirlsShorts, 'hw_gs5', 200),
+ 1125: (AGirlsShorts, 'hw_gs6', 200),
+ 1126: (AGirlsShorts, 'hw_gs7', 300),
+ 1127: (AGirlsSkirt, 'hw_gsk1', 300),
+ 1200: (AGirlsShirt, 'vd_ss1', 200),
+ 1201: (AShirt, 'vd_ss2', 200),
+ 1202: (ABoysShirt, 'vd_ss3', 200),
+ 1203: (AGirlsShirt, 'vd_ss4', 200),
+ 1204: (AGirlsSkirt, 'vd_gs1', 200),
+ 1205: (ABoysShorts, 'vd_bs1', 200),
+ 1206: (AShirt, 'vd_ss5', 200),
+ 1207: (AShirt, 'vd_ss6', 200),
+ 1208: (ABoysShorts, 'vd_bs2', 200),
+ 1209: (ABoysShorts, 'vd_bs3', 200),
+ 1210: (AGirlsSkirt, 'vd_gs2', 200),
+ 1211: (AGirlsSkirt, 'vd_gs3', 200),
+ 1212: (AShirt, 'vd_ss7', 200),
+ 1300: (AShirt, 'sd_ss1', 200),
+ 1301: (AShirt, 'sd_ss2', 225),
+ 1302: (AGirlsShorts, 'sd_gs1', 200),
+ 1303: (ABoysShorts, 'sd_bs1', 200),
+ 1304: (AShirt, 'sd_ss3', 25),
+ 1305: (ABoysShorts, 'sd_bs2', 25),
+ 1306: (AGirlsSkirt, 'sd_gs2', 25),
+ 1400: (AShirt, 'tc_ss1', 200),
+ 1401: (AShirt, 'tc_ss2', 200),
+ 1402: (AShirt, 'tc_ss3', 200),
+ 1403: (AShirt, 'tc_ss4', 200),
+ 1404: (AShirt, 'tc_ss5', 200),
+ 1405: (AShirt, 'tc_ss6', 200),
+ 1406: (AShirt, 'tc_ss7', 200),
+ 1500: (AShirt, 'j4_ss1', 200),
+ 1501: (AShirt, 'j4_ss2', 200),
+ 1502: (ABoysShorts, 'j4_bs1', 200),
+ 1503: (AGirlsSkirt, 'j4_gs1', 200),
+ 1600: (AShirt, 'pj_ss1', 500),
+ 1601: (AShirt, 'pj_ss2', 500),
+ 1602: (AShirt, 'pj_ss3', 500),
+ 1603: (ABoysShorts, 'pj_bs1', 500),
+ 1604: (ABoysShorts, 'pj_bs2', 500),
+ 1605: (ABoysShorts, 'pj_bs3', 500),
+ 1606: (AGirlsShorts, 'pj_gs1', 500),
+ 1607: (AGirlsShorts, 'pj_gs2', 500),
+ 1608: (AGirlsShorts, 'pj_gs3', 500),
+ 1700: (AShirt, 'sa_ss1', 200),
+ 1701: (AShirt, 'sa_ss2', 200),
+ 1702: (AShirt, 'sa_ss3', 200),
+ 1703: (AShirt, 'sa_ss4', 200),
+ 1704: (AShirt, 'sa_ss5', 200),
+ 1705: (AShirt, 'sa_ss6', 200),
+ 1706: (AShirt, 'sa_ss7', 200),
+ 1707: (AShirt, 'sa_ss8', 200),
+ 1708: (AShirt, 'sa_ss9', 200),
+ 1709: (AShirt, 'sa_ss10', 200),
+ 1710: (AShirt, 'sa_ss11', 200),
+ 1711: (ABoysShorts, 'sa_bs1', 200),
+ 1712: (ABoysShorts, 'sa_bs2', 200),
+ 1713: (ABoysShorts, 'sa_bs3', 200),
+ 1714: (ABoysShorts, 'sa_bs4', 200),
+ 1715: (ABoysShorts, 'sa_bs5', 200),
+ 1716: (AGirlsSkirt, 'sa_gs1', 200),
+ 1717: (AGirlsSkirt, 'sa_gs2', 200),
+ 1718: (AGirlsSkirt, 'sa_gs3', 200),
+ 1719: (AGirlsSkirt, 'sa_gs4', 200),
+ 1720: (AGirlsSkirt, 'sa_gs5', 200),
+ 1721: (AShirt, 'sa_ss12', 200),
+ 1722: (AShirt, 'sa_ss13', 200),
+ 1723: (AShirt, 'sa_ss14', 250),
+ 1724: (AShirt, 'sa_ss15', 250),
+ 1725: (AShirt, 'sa_ss16', 200),
+ 1726: (AShirt, 'sa_ss17', 200),
+ 1727: (AShirt, 'sa_ss18', 200),
+ 1728: (AShirt, 'sa_ss19', 200),
+ 1729: (AShirt, 'sa_ss20', 200),
+ 1730: (AShirt, 'sa_ss21', 200),
+ 1731: (AShirt, 'sa_ss22', 200),
+ 1732: (AShirt, 'sa_ss23', 200),
+ 1733: (ABoysShorts, 'sa_bs6', 200),
+ 1734: (ABoysShorts, 'sa_bs7', 250),
+ 1735: (ABoysShorts, 'sa_bs8', 250),
+ 1736: (ABoysShorts, 'sa_bs9', 200),
+ 1737: (ABoysShorts, 'sa_bs10', 200),
+ 1738: (AGirlsSkirt, 'sa_gs6', 200),
+ 1739: (AGirlsSkirt, 'sa_gs7', 250),
+ 1740: (AGirlsSkirt, 'sa_gs8', 250),
+ 1741: (AGirlsSkirt, 'sa_gs9', 200),
+ 1742: (AGirlsSkirt, 'sa_gs10', 200),
+ 1743: (AShirt, 'sa_ss24', 250),
+ 1744: (AShirt, 'sa_ss25', 250),
+ 1745: (ABoysShorts, 'sa_bs11', 250),
+ 1746: (ABoysShorts, 'sa_bs12', 250),
+ 1747: (AGirlsSkirt, 'sa_gs11', 250),
+ 1748: (AGirlsSkirt, 'sa_gs12', 250),
+ 1749: (AShirt, 'sil_1', 1),
+ 1750: (AShirt, 'sil_2', 1),
+ 1751: (AShirt, 'sil_3', 1),
+ 1752: (AShirt, 'sil_4', 5000),
+ 1753: (AShirt, 'sil_5', 5000),
+ 1754: (AShirt, 'sil_6', 1),
+ 1755: (ABoysShorts, 'sil_bs1', 1),
+ 1756: (AGirlsShorts, 'sil_gs1', 1),
+ 1757: (AShirt, 'sil_7', 20),
+ 1758: (AShirt, 'sil_8', 20),
+ 1759: (AShirt,
+ 'emb_us1',
+ 0,
+ (20, 5)),
+ 1760: (AShirt,
+ 'emb_us2',
+ 234,
+ (0, 7)),
+ 1761: (AShirt,
+ 'emb_us3',
+ 345,
+ (8, 0)),
+ 1762: (AShirt, 'sa_ss26', 5000),
+ 1763: (AShirt, 'sb_1', 20),
+ 1764: (AShirt, 'sa_ss27', 5000),
+ 1765: (AShirt, 'sa_ss28', 5000),
+ 1766: (ABoysShorts, 'sa_bs13', 5000),
+ 1767: (AGirlsShorts, 'sa_gs13', 5000),
+ 1768: (AShirt, 'jb_1', 20),
+ 1769: (AShirt, 'jb_2', 20),
+ 1770: (AShirt, 'hw_ss3', 250),
+ 1771: (AShirt, 'hw_ss4', 250),
+ 1772: (ABoysShorts, 'hw_bs3', 250),
+ 1773: (AGirlsShorts, 'hw_gs3', 250),
+ 1774: (ABoysShorts, 'hw_bs4', 250),
+ 1775: (AGirlsShorts, 'hw_gs4', 250),
+ 1776: (AShirt, 'ugcms', 15000),
+ 1777: (AShirt, 'lb_1', 20),
+ 1778: (AShirt, 'sa_ss29', 5000),
+ 1779: (AShirt, 'sa_ss30', 5000),
+ 1780: (ABoysShorts, 'sa_bs14', 5000),
+ 1781: (AGirlsShorts, 'sa_gs14', 5000),
+ 1782: (AShirt, 'sa_ss31', 5000),
+ 1783: (ABoysShorts, 'sa_bs15', 5000),
+ 1784: (AGirlsSkirt, 'sa_gs15', 5000),
+ 1785: (AShirt, 'sa_ss32', 5000),
+ 1786: (AShirt, 'sa_ss33', 5000),
+ 1787: (AShirt, 'sa_ss34', 5000),
+ 1788: (AShirt, 'sa_ss35', 5000),
+ 1789: (AShirt, 'sa_ss36', 5000),
+ 1790: (AShirt, 'sa_ss37', 5000),
+ 1791: (ABoysShorts, 'sa_bs16', 5000),
+ 1792: (ABoysShorts, 'sa_bs17', 5000),
+ 1793: (AGirlsSkirt, 'sa_gs16', 5000),
+ 1794: (AGirlsSkirt, 'sa_gs17', 5000),
+ 1795: (AShirt, 'sa_ss38', 5000),
+ 1796: (AShirt, 'sa_ss39', 5000),
+ 1797: (ABoysShorts, 'sa_bs18', 5000),
+ 1798: (AGirlsSkirt, 'sa_gs18', 5000),
+ 1799: (AShirt, 'sa_ss40', 5000),
+ 1800: (AShirt, 'sa_ss41', 5000),
+ 1801: (AShirt, 'sa_ss42', 250),
+ 1802: (AGirlsShirt, 'sa_ss43', 250),
+ 1803: (AShirt, 'sa_ss44', 5000),
+ 1804: (AShirt, 'sa_ss45', 5000),
+ 1805: (AShirt, 'sa_ss46', 5000),
+ 1806: (AShirt, 'sa_ss47', 5000),
+ 1807: (AShirt, 'sa_ss48', 5000),
+ 1808: (AShirt, 'sa_ss49', 5000),
+ 1809: (AShirt, 'sa_ss50', 5000),
+ 1810: (AShirt, 'sa_ss51', 5000),
+ 1811: (AShirt, 'sa_ss52', 5000),
+ 1812: (AShirt, 'sa_ss53', 5000),
+ 1813: (AShirt, 'sa_ss54', 5000),
+ 1814: (ABoysShorts, 'sa_bs19', 5000),
+ 1815: (ABoysShorts, 'sa_bs20', 5000),
+ 1816: (ABoysShorts, 'sa_bs21', 5000),
+ 1817: (AGirlsSkirt, 'sa_gs19', 5000),
+ 1818: (AGirlsSkirt, 'sa_gs20', 5000),
+ 1819: (AGirlsSkirt, 'sa_gs21', 5000),
+ 1820: (AShirt, 'sa_ss55', 5000)}
+LoyaltyClothingItems = (1600,
+ 1601,
+ 1602,
+ 1603,
+ 1604,
+ 1605,
+ 1606,
+ 1607,
+ 1608)
+
+class CatalogClothingItem(CatalogItem.CatalogItem):
+
+ def makeNewItem(self, clothingType, colorIndex, loyaltyDays = 0):
+ self.clothingType = clothingType
+ self.colorIndex = colorIndex
+ self.loyaltyDays = loyaltyDays
+ CatalogItem.CatalogItem.makeNewItem(self)
+
+ def storedInCloset(self):
+ return 1
+
+ def notOfferedTo(self, avatar):
+ article = ClothingTypes[self.clothingType][CTArticle]
+ if article == AShirt or article == AShorts:
+ return 0
+ forBoys = (article == ABoysShirt or article == ABoysShorts)
+ if avatar.getStyle().getGender() == 'm':
+ return not forBoys
+ else:
+ return forBoys
+
+ def forBoysOnly(self):
+ article = ClothingTypes[self.clothingType][CTArticle]
+ if article == ABoysShirt or article == ABoysShorts:
+ return 1
+ else:
+ return 0
+
+ def forGirlsOnly(self):
+ article = ClothingTypes[self.clothingType][CTArticle]
+ if article == AGirlsShirt or article == AGirlsSkirt or article == AGirlsShorts:
+ return 1
+ else:
+ return 0
+
+ def getPurchaseLimit(self):
+ return 1
+
+ def reachedPurchaseLimit(self, avatar):
+ if avatar.onOrder.count(self) != 0:
+ return 1
+ if avatar.onGiftOrder.count(self) != 0:
+ return 1
+ if avatar.mailboxContents.count(self) != 0:
+ return 1
+ if self in avatar.awardMailboxContents or self in avatar.onAwardOrder:
+ return 1
+ str = ClothingTypes[self.clothingType][CTString]
+ dna = avatar.getStyle()
+ if self.isShirt():
+ defn = ToonDNA.ShirtStyles[str]
+ if dna.topTex == defn[0] and dna.topTexColor == defn[2][self.colorIndex][0] and dna.sleeveTex == defn[1] and dna.sleeveTexColor == defn[2][self.colorIndex][1]:
+ return 1
+ l = avatar.clothesTopsList
+ for i in range(0, len(l), 4):
+ if l[i] == defn[0] and l[i + 1] == defn[2][self.colorIndex][0] and l[i + 2] == defn[1] and l[i + 3] == defn[2][self.colorIndex][1]:
+ return 1
+
+ else:
+ defn = ToonDNA.BottomStyles[str]
+ if dna.botTex == defn[0] and dna.botTexColor == defn[1][self.colorIndex]:
+ return 1
+ l = avatar.clothesBottomsList
+ for i in range(0, len(l), 2):
+ if l[i] == defn[0] and l[i + 1] == defn[1][self.colorIndex]:
+ return 1
+
+ return 0
+
+ def getTypeName(self):
+ return TTLocalizer.ClothingTypeName
+
+ def getName(self):
+ typeName = TTLocalizer.ClothingTypeNames.get(self.clothingType, 0)
+ if typeName:
+ return typeName
+ else:
+ article = ClothingTypes[self.clothingType][CTArticle]
+ return TTLocalizer.ClothingArticleNames[article]
+
+ def recordPurchase(self, avatar, optional):
+ if avatar.isClosetFull():
+ return ToontownGlobals.P_NoRoomForItem
+ str = ClothingTypes[self.clothingType][CTString]
+ dna = avatar.getStyle()
+ if self.isShirt():
+ added = avatar.addToClothesTopsList(dna.topTex, dna.topTexColor, dna.sleeveTex, dna.sleeveTexColor)
+ if added:
+ avatar.b_setClothesTopsList(avatar.getClothesTopsList())
+ self.notify.info('Avatar %s put shirt %d,%d,%d,%d in closet.' % (avatar.doId,
+ dna.topTex,
+ dna.topTexColor,
+ dna.sleeveTex,
+ dna.sleeveTexColor))
+ else:
+ self.notify.warning('Avatar %s %s lost current shirt; closet full.' % (avatar.doId, dna.asTuple()))
+ defn = ToonDNA.ShirtStyles[str]
+ dna.topTex = defn[0]
+ dna.topTexColor = defn[2][self.colorIndex][0]
+ dna.sleeveTex = defn[1]
+ dna.sleeveTexColor = defn[2][self.colorIndex][1]
+ else:
+ added = avatar.addToClothesBottomsList(dna.botTex, dna.botTexColor)
+ if added:
+ avatar.b_setClothesBottomsList(avatar.getClothesBottomsList())
+ self.notify.info('Avatar %s put bottoms %d,%d in closet.' % (avatar.doId, dna.botTex, dna.botTexColor))
+ else:
+ self.notify.warning('Avatar %s %s lost current bottoms; closet full.' % (avatar.doId, dna.asTuple()))
+ defn = ToonDNA.BottomStyles[str]
+ dna.botTex = defn[0]
+ dna.botTexColor = defn[1][self.colorIndex]
+ if dna.getGender() == 'f':
+ try:
+ bottomPair = ToonDNA.GirlBottoms[dna.botTex]
+ except:
+ bottomPair = ToonDNA.GirlBottoms[0]
+
+ if dna.torso[1] == 's' and bottomPair[1] == ToonDNA.SKIRT:
+ dna.torso = dna.torso[0] + 'd'
+ elif dna.torso[1] == 'd' and bottomPair[1] == ToonDNA.SHORTS:
+ dna.torso = dna.torso[0] + 's'
+ avatar.b_setDNAString(dna.makeNetString())
+ avatar.d_catalogGenClothes()
+ return ToontownGlobals.P_ItemAvailable
+
+ def getDeliveryTime(self):
+ return 60
+
+ def getPicture(self, avatar):
+ from toontown.toon import Toon
+ self.hasPicture = True
+ dna = ToonDNA.ToonDNA(type='t', dna=avatar.style)
+ str = ClothingTypes[self.clothingType][CTString]
+ if self.isShirt():
+ defn = ToonDNA.ShirtStyles[str]
+ dna.topTex = defn[0]
+ dna.topTexColor = defn[2][self.colorIndex][0]
+ dna.sleeveTex = defn[1]
+ dna.sleeveTexColor = defn[2][self.colorIndex][1]
+ pieceNames = ('**/1000/**/torso-top', '**/1000/**/sleeves')
+ else:
+ defn = ToonDNA.BottomStyles[str]
+ dna.botTex = defn[0]
+ dna.botTexColor = defn[1][self.colorIndex]
+ pieceNames = ('**/1000/**/torso-bot',)
+ toon = Toon.Toon()
+ toon.setDNA(dna)
+ model = NodePath('clothing')
+ for name in pieceNames:
+ for piece in toon.findAllMatches(name):
+ piece.wrtReparentTo(model)
+
+ model.setH(180)
+ toon.delete()
+ return self.makeFrameModel(model)
+
+ def requestPurchase(self, phone, callback):
+ from toontown.toontowngui import TTDialog
+ avatar = base.localAvatar
+ clothesOnOrder = 0
+ for item in avatar.onOrder + avatar.mailboxContents:
+ if item.storedInCloset():
+ clothesOnOrder += 1
+
+ if avatar.isClosetFull(clothesOnOrder):
+ self.requestPurchaseCleanup()
+ buttonCallback = PythonUtil.Functor(self.__handleFullPurchaseDialog, phone, callback)
+ self.dialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.CatalogPurchaseClosetFull, text_wordwrap=15, command=buttonCallback)
+ self.dialog.show()
+ else:
+ CatalogItem.CatalogItem.requestPurchase(self, phone, callback)
+
+ def requestPurchaseCleanup(self):
+ if hasattr(self, 'dialog'):
+ self.dialog.cleanup()
+ del self.dialog
+
+ def __handleFullPurchaseDialog(self, phone, callback, buttonValue):
+ from toontown.toontowngui import TTDialog
+ self.requestPurchaseCleanup()
+ if buttonValue == DGG.DIALOG_OK:
+ CatalogItem.CatalogItem.requestPurchase(self, phone, callback)
+ else:
+ callback(ToontownGlobals.P_UserCancelled, self)
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ if self.isShirt():
+ return TTLocalizer.CatalogAcceptShirt
+ elif self.isSkirt():
+ return TTLocalizer.CatalogAcceptSkirt
+ else:
+ return TTLocalizer.CatalogAcceptShorts
+ elif retcode == ToontownGlobals.P_NoRoomForItem:
+ return TTLocalizer.CatalogAcceptClosetFull
+ return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode)
+
+ def getColorChoices(self):
+ str = ClothingTypes[self.clothingType][CTString]
+ if self.isShirt():
+ return ToonDNA.ShirtStyles[str][2]
+ else:
+ return ToonDNA.BottomStyles[str][1]
+
+ def isShirt(self):
+ article = ClothingTypes[self.clothingType][CTArticle]
+ return article < ABoysShorts
+
+ def isSkirt(self):
+ article = ClothingTypes[self.clothingType][CTArticle]
+ return article == AGirlsSkirt
+
+ def output(self, store = -1):
+ return 'CatalogClothingItem(%s, %s%s)' % (self.clothingType, self.colorIndex, self.formatOptionalData(store))
+
+ def getFilename(self):
+ str = ClothingTypes[self.clothingType][CTString]
+ if self.isShirt():
+ defn = ToonDNA.ShirtStyles[str]
+ topTex = defn[0]
+ return ToonDNA.Shirts[topTex]
+ else:
+ defn = ToonDNA.BottomStyles[str]
+ botTex = defn[0]
+ article = ClothingTypes[self.clothingType][CTArticle]
+ if article == ABoysShorts:
+ return ToonDNA.BoyShorts[botTex]
+ else:
+ return ToonDNA.GirlBottoms[botTex][0]
+
+ def getColor(self):
+ str = ClothingTypes[self.clothingType][CTString]
+ if self.isShirt():
+ defn = ToonDNA.ShirtStyles[str]
+ topTexColor = defn[2][self.colorIndex][0]
+ return ToonDNA.ClothesColors[topTexColor]
+ else:
+ defn = ToonDNA.BottomStyles[str]
+ botTexColor = defn[1][self.colorIndex]
+ return ToonDNA.ClothesColors[botTexColor]
+
+ def compareTo(self, other):
+ if self.clothingType != other.clothingType:
+ return self.clothingType - other.clothingType
+ return self.colorIndex - other.colorIndex
+
+ def getHashContents(self):
+ return (self.clothingType, self.colorIndex)
+
+ def getBasePrice(self):
+ return ClothingTypes[self.clothingType][CTBasePrice]
+
+ def getEmblemPrices(self):
+ result = ()
+ info = ClothingTypes[self.clothingType]
+ if CTEmblemPrices <= len(info) - 1:
+ result = info[CTEmblemPrices]
+ return result
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store)
+ self.clothingType = di.getUint16()
+ self.colorIndex = di.getUint8()
+ if versionNumber >= 6:
+ self.loyaltyDays = di.getUint16()
+ else:
+ self.loyaltyDays = 0
+ str = ClothingTypes[self.clothingType][CTString]
+ if self.isShirt():
+ color = ToonDNA.ShirtStyles[str][2][self.colorIndex]
+ else:
+ color = ToonDNA.BottomStyles[str][1][self.colorIndex]
+
+ def encodeDatagram(self, dg, store):
+ CatalogItem.CatalogItem.encodeDatagram(self, dg, store)
+ dg.addUint16(self.clothingType)
+ dg.addUint8(self.colorIndex)
+ dg.addUint16(self.loyaltyDays)
+
+ def isGift(self):
+ if self.getEmblemPrices():
+ return 0
+ if self.loyaltyRequirement() > 0:
+ return 0
+ elif self.clothingType in LoyaltyClothingItems:
+ return 0
+ else:
+ return 1
+
+
+def getAllClothes(*clothingTypes):
+ list = []
+ for clothingType in clothingTypes:
+ base = CatalogClothingItem(clothingType, 0)
+ list.append(base)
+ for n in range(1, len(base.getColorChoices())):
+ list.append(CatalogClothingItem(clothingType, n))
+
+ return list
diff --git a/toontown/catalog/CatalogEmoteItem.py b/toontown/catalog/CatalogEmoteItem.py
new file mode 100644
index 0000000..ed421a1
--- /dev/null
+++ b/toontown/catalog/CatalogEmoteItem.py
@@ -0,0 +1,138 @@
+import CatalogItem
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from otp.otpbase import OTPLocalizer
+from direct.interval.IntervalGlobal import *
+LoyaltyEmoteItems = (20, 21, 22, 23, 24)
+
+class CatalogEmoteItem(CatalogItem.CatalogItem):
+ sequenceNumber = 0
+ pictureToon = None
+
+ def makeNewItem(self, emoteIndex, loyaltyDays = 0):
+ self.emoteIndex = emoteIndex
+ self.loyaltyDays = loyaltyDays
+ CatalogItem.CatalogItem.makeNewItem(self)
+
+ def getPurchaseLimit(self):
+ return 1
+
+ def reachedPurchaseLimit(self, avatar):
+ if self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or self in avatar.awardMailboxContents or self in avatar.onAwardOrder:
+ return 1
+ if self.emoteIndex >= len(avatar.emoteAccess):
+ return 0
+ return avatar.emoteAccess[self.emoteIndex] != 0
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ return TTLocalizer.CatalogAcceptEmote
+ return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode)
+
+ def saveHistory(self):
+ return 1
+
+ def getTypeName(self):
+ return TTLocalizer.EmoteTypeName
+
+ def getName(self):
+ return OTPLocalizer.EmoteList[self.emoteIndex]
+
+ def recordPurchase(self, avatar, optional):
+ if self.emoteIndex < 0 or self.emoteIndex > len(avatar.emoteAccess):
+ self.notify.warning('Invalid emote access: %s for avatar %s' % (self.emoteIndex, avatar.doId))
+ return ToontownGlobals.P_InvalidIndex
+ avatar.emoteAccess[self.emoteIndex] = 1
+ avatar.d_setEmoteAccess(avatar.emoteAccess)
+ return ToontownGlobals.P_ItemAvailable
+
+ def getPicture(self, avatar):
+ from toontown.toon import Toon
+ from toontown.toon import ToonHead
+ from toontown.toon import TTEmote
+ from otp.avatar import Emote
+ self.hasPicture = True
+ if self.emoteIndex in Emote.globalEmote.getHeadEmotes():
+ toon = ToonHead.ToonHead()
+ toon.setupHead(avatar.style, forGui=1)
+ else:
+ toon = Toon.Toon()
+ toon.setDNA(avatar.style)
+ toon.loop('neutral')
+ toon.setH(180)
+ model, ival = self.makeFrameModel(toon, 0)
+ track, duration = Emote.globalEmote.doEmote(toon, self.emoteIndex, volume=self.volume)
+ if duration == None:
+ duration = 0
+ name = 'emote-item-%s' % self.sequenceNumber
+ CatalogEmoteItem.sequenceNumber += 1
+ if track != None:
+ track = Sequence(Sequence(track, duration=0), Wait(duration + 2), name=name)
+ else:
+ track = Sequence(Func(Emote.globalEmote.doEmote, toon, self.emoteIndex), Wait(duration + 4), name=name)
+ self.pictureToon = toon
+ return (model, track)
+
+ def changeIval(self, volume):
+ from toontown.toon import Toon
+ from toontown.toon import ToonHead
+ from toontown.toon import TTEmote
+ from otp.avatar import Emote
+ self.volume = volume
+ if not hasattr(self, 'pictureToon'):
+ return Sequence()
+ track, duration = Emote.globalEmote.doEmote(self.pictureToon, self.emoteIndex, volume=self.volume)
+ if duration == None:
+ duration = 0
+ name = 'emote-item-%s' % self.sequenceNumber
+ CatalogEmoteItem.sequenceNumber += 1
+ if track != None:
+ track = Sequence(Sequence(track, duration=0), Wait(duration + 2), name=name)
+ else:
+ track = Sequence(Func(Emote.globalEmote.doEmote, toon, self.emoteIndex), Wait(duration + 4), name=name)
+ return track
+
+ def cleanupPicture(self):
+ CatalogItem.CatalogItem.cleanupPicture(self)
+ self.pictureToon.emote.finish()
+ self.pictureToon.emote = None
+ self.pictureToon.delete()
+ self.pictureToon = None
+ return
+
+ def output(self, store = -1):
+ return 'CatalogEmoteItem(%s%s)' % (self.emoteIndex, self.formatOptionalData(store))
+
+ def compareTo(self, other):
+ return self.emoteIndex - other.emoteIndex
+
+ def getHashContents(self):
+ return self.emoteIndex
+
+ def getBasePrice(self):
+ return 550
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store)
+ self.emoteIndex = di.getUint8()
+ if versionNumber >= 6:
+ self.loyaltyDays = di.getUint16()
+ else:
+ self.loyaltyDays = 0
+ if self.emoteIndex > len(OTPLocalizer.EmoteList):
+ raise ValueError
+
+ def encodeDatagram(self, dg, store):
+ CatalogItem.CatalogItem.encodeDatagram(self, dg, store)
+ dg.addUint8(self.emoteIndex)
+ dg.addUint16(self.loyaltyDays)
+
+ def isGift(self):
+ if self.getEmblemPrices():
+ return 0
+ if self.loyaltyRequirement() > 0:
+ return 0
+ elif self.emoteIndex in LoyaltyEmoteItems:
+ return 0
+ else:
+ return 1
diff --git a/toontown/catalog/CatalogFlooringItem.py b/toontown/catalog/CatalogFlooringItem.py
new file mode 100644
index 0000000..01abba8
--- /dev/null
+++ b/toontown/catalog/CatalogFlooringItem.py
@@ -0,0 +1,172 @@
+from CatalogSurfaceItem import *
+FTTextureName = 0
+FTColor = 1
+FTBasePrice = 2
+FlooringTypes = {1000: ('phase_5.5/maps/floor_wood_neutral.jpg', CTBasicWoodColorOnWhite, 150),
+ 1010: ('phase_5.5/maps/flooring_carpetA_neutral.jpg', CTFlatColorDark, 150),
+ 1020: ('phase_4/maps/flooring_tile_neutral.jpg', CTFlatColorDark, 150),
+ 1030: ('phase_5.5/maps/flooring_tileB2.jpg', None, 150),
+ 1040: ('phase_4/maps/grass.jpg', None, 150),
+ 1050: ('phase_4/maps/floor_tile_brick_diagonal2.jpg', None, 150),
+ 1060: ('phase_4/maps/floor_tile_brick_diagonal.jpg', None, 150),
+ 1070: ('phase_4/maps/plazz_tile.jpg', None, 150),
+ 1080: ('phase_4/maps/sidewalk.jpg', CTFlatColorDark, 150),
+ 1090: ('phase_3.5/maps/boardwalk_floor.jpg', None, 150),
+ 1100: ('phase_3.5/maps/dustroad.jpg', None, 150),
+ 1110: ('phase_5.5/maps/floor_woodtile_neutral.jpg', CTBasicWoodColorOnWhite, 150),
+ 1120: ('phase_5.5/maps/floor_tile_neutral.jpg', CTBasicWoodColorOnWhite + CTFlatColorDark, 150),
+ 1130: ('phase_5.5/maps/floor_tile_honeycomb_neutral.jpg', CTBasicWoodColorOnWhite, 150),
+ 1140: ('phase_5.5/maps/UWwaterFloor1.jpg', None, 150),
+ 1150: ('phase_5.5/maps/UWtileFloor4.jpg', None, 150),
+ 1160: ('phase_5.5/maps/UWtileFloor3.jpg', None, 150),
+ 1170: ('phase_5.5/maps/UWtileFloor2.jpg', None, 150),
+ 1180: ('phase_5.5/maps/UWtileFloor1.jpg', None, 150),
+ 1190: ('phase_5.5/maps/UWsandyFloor1.jpg', None, 150),
+ 10000: ('phase_5.5/maps/floor_icecube.jpg', CTWhite, 225),
+ 10010: ('phase_5.5/maps/floor_snow.jpg', CTWhite, 225),
+ 11000: ('phase_5.5/maps/StPatsFloor1.jpg', CTWhite, 225),
+ 11010: ('phase_5.5/maps/StPatsFloor2.jpg', CTWhite, 225)}
+
+class CatalogFlooringItem(CatalogSurfaceItem):
+
+ def makeNewItem(self, patternIndex, colorIndex = None):
+ self.patternIndex = patternIndex
+ self.colorIndex = colorIndex
+ CatalogSurfaceItem.makeNewItem(self)
+
+ def needsCustomize(self):
+ return self.colorIndex == None
+
+ def getTypeName(self):
+ return TTLocalizer.SurfaceNames[STFlooring]
+
+ def getName(self):
+ name = TTLocalizer.FlooringNames.get(self.patternIndex)
+ if name:
+ return name
+ return self.getTypeName()
+
+ def getSurfaceType(self):
+ return STFlooring
+
+ def getPicture(self, avatar):
+ frame = self.makeFrame()
+ sample = loader.loadModel('phase_5.5/models/estate/wallpaper_sample')
+ a = sample.find('**/a')
+ b = sample.find('**/b')
+ c = sample.find('**/c')
+ a.setTexture(self.loadTexture(), 1)
+ a.setColorScale(*self.getColor())
+ b.setTexture(self.loadTexture(), 1)
+ b.setColorScale(*self.getColor())
+ c.setTexture(self.loadTexture(), 1)
+ c.setColorScale(*self.getColor())
+ sample.reparentTo(frame)
+ self.hasPicture = True
+ return (frame, None)
+
+ def output(self, store = -1):
+ return 'CatalogFlooringItem(%s, %s%s)' % (self.patternIndex, self.colorIndex, self.formatOptionalData(store))
+
+ def getFilename(self):
+ return FlooringTypes[self.patternIndex][FTTextureName]
+
+ def compareTo(self, other):
+ if self.patternIndex != other.patternIndex:
+ return self.patternIndex - other.patternIndex
+ return 0
+
+ def getHashContents(self):
+ return self.patternIndex
+
+ def getBasePrice(self):
+ return FlooringTypes[self.patternIndex][FTBasePrice]
+
+ def loadTexture(self):
+ from pandac.PandaModules import Texture
+ filename = FlooringTypes[self.patternIndex][FTTextureName]
+ texture = loader.loadTexture(filename)
+ texture.setMinfilter(Texture.FTLinearMipmapLinear)
+ texture.setMagfilter(Texture.FTLinear)
+ return texture
+
+ def getColor(self):
+ if self.colorIndex == None:
+ colorIndex = 0
+ else:
+ colorIndex = self.colorIndex
+ colors = FlooringTypes[self.patternIndex][FTColor]
+ if colors:
+ if colorIndex < len(colors):
+ return colors[colorIndex]
+ else:
+ print 'Warning: colorIndex not in colors. Returning white.'
+ return CT_WHITE
+ else:
+ return CT_WHITE
+ return
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store)
+ if versionNumber < 3:
+ self.patternIndex = di.getUint8()
+ else:
+ self.patternIndex = di.getUint16()
+ if versionNumber < 4 or store & CatalogItem.Customization:
+ self.colorIndex = di.getUint8()
+ else:
+ self.colorIndex = None
+ wtype = FlooringTypes[self.patternIndex]
+ return
+
+ def encodeDatagram(self, dg, store):
+ CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store)
+ dg.addUint16(self.patternIndex)
+ if store & CatalogItem.Customization:
+ dg.addUint8(self.colorIndex)
+
+
+def getFloorings(*indexList):
+ list = []
+ for index in indexList:
+ list.append(CatalogFlooringItem(index))
+
+ return list
+
+
+def getAllFloorings(*indexList):
+ list = []
+ for index in indexList:
+ colors = FlooringTypes[index][FTColor]
+ if colors:
+ for n in range(len(colors)):
+ list.append(CatalogFlooringItem(index, n))
+
+ else:
+ list.append(CatalogFlooringItem(index, 0))
+
+ return list
+
+
+def getFlooringRange(fromIndex, toIndex, *otherRanges):
+ list = []
+ froms = [fromIndex]
+ tos = [toIndex]
+ i = 0
+ while i < len(otherRanges):
+ froms.append(otherRanges[i])
+ tos.append(otherRanges[i + 1])
+ i += 2
+
+ for patternIndex in FlooringTypes.keys():
+ for fromIndex, toIndex in zip(froms, tos):
+ if patternIndex >= fromIndex and patternIndex <= toIndex:
+ colors = FlooringTypes[patternIndex][FTColor]
+ if colors:
+ for n in range(len(colors)):
+ list.append(CatalogFlooringItem(patternIndex, n))
+
+ else:
+ list.append(CatalogFlooringItem(patternIndex, 0))
+
+ return list
diff --git a/toontown/catalog/CatalogFurnitureItem.py b/toontown/catalog/CatalogFurnitureItem.py
new file mode 100644
index 0000000..5afbce0
--- /dev/null
+++ b/toontown/catalog/CatalogFurnitureItem.py
@@ -0,0 +1,1196 @@
+import CatalogAtticItem
+import CatalogItem
+import random
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase import ToontownGlobals
+FTModelName = 0
+FTColor = 1
+FTColorOptions = 2
+FTBasePrice = 3
+FTFlags = 4
+FTScale = 5
+FLBank = 1
+FLCloset = 2
+FLRug = 4
+FLPainting = 8
+FLOnTable = 16
+FLIsTable = 32
+FLPhone = 64
+FLBillboard = 128
+FLTrunk = 256
+furnitureColors = [(0.792,
+ 0.353,
+ 0.29,
+ 1.0),
+ (0.176,
+ 0.592,
+ 0.439,
+ 1.0),
+ (0.439,
+ 0.424,
+ 0.682,
+ 1.0),
+ (0.325,
+ 0.58,
+ 0.835,
+ 1.0),
+ (0.753,
+ 0.345,
+ 0.557,
+ 1.0),
+ (0.992,
+ 0.843,
+ 0.392,
+ 1.0)]
+woodColors = [(0.933,
+ 0.773,
+ 0.569,
+ 1.0),
+ (0.9333,
+ 0.6785,
+ 0.055,
+ 1.0),
+ (0.545,
+ 0.451,
+ 0.333,
+ 1.0),
+ (0.541,
+ 0.0,
+ 0.0,
+ 1.0),
+ (0.5451,
+ 0.2706,
+ 0.0745,
+ 1.0),
+ (0.5451,
+ 0.4118,
+ 0.4118,
+ 1.0)]
+BankToMoney = {1300: 12000,
+ 1310: 12000,
+ 1320: 12000,
+ 1330: 12000,
+ 1340: 12000,
+ 1350: 12000}
+MoneyToBank = {}
+for bankId, maxMoney in BankToMoney.items():
+ MoneyToBank[maxMoney] = bankId
+
+MaxBankId = 1350
+ClosetToClothes = {500: 10,
+ 502: 15,
+ 504: 20,
+ 506: 25,
+ 508: 50,
+ 510: 10,
+ 512: 15,
+ 514: 20,
+ 516: 25,
+ 518: 50}
+ClothesToCloset = {}
+for closetId, maxClothes in ClosetToClothes.items():
+ if not ClothesToCloset.has_key(maxClothes):
+ ClothesToCloset[maxClothes] = (closetId,)
+ else:
+ ClothesToCloset[maxClothes] += (closetId,)
+
+MaxClosetIds = (508, 518)
+MaxTrunkIds = (4000, 4010)
+FurnitureTypes = {100: ('phase_5.5/models/estate/chairA',
+ None,
+ None,
+ 80),
+ 105: ('phase_5.5/models/estate/chairAdesat',
+ None,
+ {0: (('**/cushion*', furnitureColors[0]), ('**/arm*', furnitureColors[0])),
+ 1: (('**/cushion*', furnitureColors[1]), ('**/arm*', furnitureColors[1])),
+ 2: (('**/cushion*', furnitureColors[2]), ('**/arm*', furnitureColors[2])),
+ 3: (('**/cushion*', furnitureColors[3]), ('**/arm*', furnitureColors[3])),
+ 4: (('**/cushion*', furnitureColors[4]), ('**/arm*', furnitureColors[4])),
+ 5: (('**/cushion*', furnitureColors[5]), ('**/arm*', furnitureColors[5]))},
+ 160),
+ 110: ('phase_3.5/models/modules/chair',
+ None,
+ None,
+ 40),
+ 120: ('phase_5.5/models/estate/deskChair',
+ None,
+ None,
+ 60),
+ 130: ('phase_5.5/models/estate/BugRoomChair',
+ None,
+ None,
+ 160),
+ 140: ('phase_5.5/models/estate/UWlobsterChair',
+ None,
+ None,
+ 200),
+ 145: ('phase_5.5/models/estate/UWlifeSaverChair',
+ None,
+ None,
+ 200),
+ 150: ('phase_5.5/models/estate/West_saddleStool2',
+ None,
+ None,
+ 160),
+ 160: ('phase_5.5/models/estate/West_nativeChair',
+ None,
+ None,
+ 160),
+ 170: ('phase_5.5/models/estate/cupcakeChair',
+ None,
+ None,
+ 240),
+ 200: ('phase_5.5/models/estate/regular_bed',
+ None,
+ None,
+ 400),
+ 205: ('phase_5.5/models/estate/regular_bed_desat',
+ None,
+ {0: (('**/bar*', woodColors[0]),
+ ('**/post*', woodColors[0]),
+ ('**/*support', woodColors[0]),
+ ('**/top', woodColors[0]),
+ ('**/bottom', woodColors[0]),
+ ('**/pPlane*', woodColors[0])),
+ 1: (('**/bar*', woodColors[1]),
+ ('**/post*', woodColors[1]),
+ ('**/*support', woodColors[1]),
+ ('**/top', woodColors[1]),
+ ('**/bottom', woodColors[1]),
+ ('**/pPlane*', woodColors[1])),
+ 2: (('**/bar*', woodColors[2]),
+ ('**/post*', woodColors[2]),
+ ('**/*support', woodColors[2]),
+ ('**/top', woodColors[2]),
+ ('**/bottom', woodColors[2]),
+ ('**/pPlane*', woodColors[2])),
+ 3: (('**/bar*', woodColors[3]),
+ ('**/post*', woodColors[3]),
+ ('**/*support', woodColors[3]),
+ ('**/top', woodColors[3]),
+ ('**/bottom', woodColors[3]),
+ ('**/pPlane*', woodColors[3])),
+ 4: (('**/bar*', woodColors[4]),
+ ('**/post*', woodColors[4]),
+ ('**/*support', woodColors[4]),
+ ('**/top', woodColors[4]),
+ ('**/bottom', woodColors[4]),
+ ('**/pPlane*', woodColors[4])),
+ 5: (('**/bar*', woodColors[5]),
+ ('**/post*', woodColors[5]),
+ ('**/*support', woodColors[5]),
+ ('**/top', woodColors[5]),
+ ('**/bottom', woodColors[5]),
+ ('**/pPlane*', woodColors[5]))},
+ 800),
+ 210: ('phase_5.5/models/estate/girly_bed',
+ None,
+ None,
+ 450),
+ 220: ('phase_5.5/models/estate/bathtub_bed',
+ None,
+ None,
+ 550),
+ 230: ('phase_5.5/models/estate/bugRoomBed',
+ None,
+ None,
+ 600),
+ 240: ('phase_5.5/models/estate/UWBoatBed',
+ None,
+ None,
+ 600),
+ 250: ('phase_5.5/models/estate/West_cactusHammoc',
+ None,
+ None,
+ 550),
+ 260: ('phase_5.5/models/estate/icecreamBed',
+ None,
+ None,
+ 700),
+ 270: ('phase_5.5/models/estate/trolley_bed',
+ None,
+ None,
+ 1200,
+ None,
+ None,
+ 0.25),
+ 300: ('phase_5.5/models/estate/Piano',
+ None,
+ None,
+ 1000,
+ FLIsTable),
+ 310: ('phase_5.5/models/estate/Organ',
+ None,
+ None,
+ 2500),
+ 400: ('phase_5.5/models/estate/FireplaceSq',
+ None,
+ None,
+ 800),
+ 410: ('phase_5.5/models/estate/FireplaceGirlee',
+ None,
+ None,
+ 800),
+ 420: ('phase_5.5/models/estate/FireplaceRound',
+ None,
+ None,
+ 800),
+ 430: ('phase_5.5/models/estate/bugRoomFireplace',
+ None,
+ None,
+ 800),
+ 440: ('phase_5.5/models/estate/CarmelAppleFireplace',
+ None,
+ None,
+ 800),
+ 450: ('phase_5.5/models/estate/fireplace_coral',
+ None,
+ None,
+ 950),
+ 460: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_coral',
+ None,
+ None,
+ 1250,
+ None,
+ None,
+ 0.5),
+ 470: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_square',
+ None,
+ None,
+ 1100,
+ None,
+ None,
+ 0.5),
+ 480: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_round',
+ None,
+ None,
+ 1100,
+ None,
+ None,
+ 0.5),
+ 490: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_girlee',
+ None,
+ None,
+ 1100,
+ None,
+ None,
+ 0.5),
+ 491: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_bugRoom',
+ None,
+ None,
+ 1100,
+ None,
+ None,
+ 0.5),
+ 492: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_caramelApple',
+ None,
+ None,
+ 1100,
+ None,
+ None,
+ 0.5),
+ 500: ('phase_5.5/models/estate/closetBoy',
+ None,
+ None,
+ 500,
+ FLCloset,
+ 0.85),
+ 502: ('phase_5.5/models/estate/closetBoy',
+ None,
+ None,
+ 500,
+ FLCloset,
+ 1.0),
+ 504: ('phase_5.5/models/estate/closetBoy',
+ None,
+ None,
+ 500,
+ FLCloset,
+ 1.15),
+ 506: ('phase_5.5/models/estate/closetBoy',
+ None,
+ None,
+ 500,
+ FLCloset,
+ 1.3),
+ 508: ('phase_5.5/models/estate/closetBoy',
+ None,
+ None,
+ 500,
+ FLCloset,
+ 1.3),
+ 510: ('phase_5.5/models/estate/closetGirl',
+ None,
+ None,
+ 500,
+ FLCloset,
+ 0.85),
+ 512: ('phase_5.5/models/estate/closetGirl',
+ None,
+ None,
+ 500,
+ FLCloset,
+ 1.0),
+ 514: ('phase_5.5/models/estate/closetGirl',
+ None,
+ None,
+ 500,
+ FLCloset,
+ 1.15),
+ 516: ('phase_5.5/models/estate/closetGirl',
+ None,
+ None,
+ 500,
+ FLCloset,
+ 1.3),
+ 518: ('phase_5.5/models/estate/closetGirl',
+ None,
+ None,
+ 500,
+ FLCloset,
+ 1.3),
+ 600: ('phase_3.5/models/modules/lamp_short',
+ None,
+ None,
+ 45,
+ FLOnTable),
+ 610: ('phase_3.5/models/modules/lamp_tall',
+ None,
+ None,
+ 45),
+ 620: ('phase_5.5/models/estate/lampA',
+ None,
+ None,
+ 35,
+ FLOnTable),
+ 625: ('phase_5.5/models/estate/lampADesat',
+ None,
+ {0: (('**/top', furnitureColors[0]),),
+ 1: (('**/top', furnitureColors[1]),),
+ 2: (('**/top', furnitureColors[2]),),
+ 3: (('**/top', furnitureColors[3]),),
+ 4: (('**/top', furnitureColors[4]),),
+ 5: (('**/top', furnitureColors[5]),)},
+ 70,
+ FLOnTable),
+ 630: ('phase_5.5/models/estate/bugRoomDaisyLamp1',
+ None,
+ None,
+ 55),
+ 640: ('phase_5.5/models/estate/bugRoomDaisyLamp2',
+ None,
+ None,
+ 55),
+ 650: ('phase_5.5/models/estate/UWlamp_jellyfish',
+ None,
+ None,
+ 55,
+ FLOnTable),
+ 660: ('phase_5.5/models/estate/UWlamps_jellyfishB',
+ None,
+ None,
+ 55,
+ FLOnTable),
+ 670: ('phase_5.5/models/estate/West_cowboyLamp',
+ None,
+ None,
+ 55,
+ FLOnTable),
+ 680: ('phase_5.5/models/estate/tt_m_ara_int_candlestick',
+ None,
+ {0: (('**/candlestick/candlestick', (1.0,
+ 1.0,
+ 1.0,
+ 1.0)),),
+ 1: (('**/candlestick/candlestick', furnitureColors[1]),),
+ 2: (('**/candlestick/candlestick', furnitureColors[2]),),
+ 3: (('**/candlestick/candlestick', furnitureColors[3]),),
+ 4: (('**/candlestick/candlestick', furnitureColors[4]),),
+ 5: (('**/candlestick/candlestick', furnitureColors[5]),),
+ 6: (('**/candlestick/candlestick', furnitureColors[0]),)},
+ 20,
+ FLOnTable),
+ 681: ('phase_5.5/models/estate/tt_m_ara_int_candlestickLit',
+ None,
+ {0: (('**/candlestick/candlestick', (1.0,
+ 1.0,
+ 1.0,
+ 1.0)),),
+ 1: (('**/candlestickLit/candlestick', furnitureColors[1]),),
+ 2: (('**/candlestickLit/candlestick', furnitureColors[2]),),
+ 3: (('**/candlestickLit/candlestick', furnitureColors[3]),),
+ 4: (('**/candlestickLit/candlestick', furnitureColors[4]),),
+ 5: (('**/candlestickLit/candlestick', furnitureColors[5]),),
+ 6: (('**/candlestickLit/candlestick', furnitureColors[0]),)},
+ 25,
+ FLOnTable),
+ 700: ('phase_3.5/models/modules/couch_1person',
+ None,
+ None,
+ 230),
+ 705: ('phase_5.5/models/estate/couch_1personDesat',
+ None,
+ {0: (('**/*couch', furnitureColors[0]),),
+ 1: (('**/*couch', furnitureColors[1]),),
+ 2: (('**/*couch', furnitureColors[2]),),
+ 3: (('**/*couch', furnitureColors[3]),),
+ 4: (('**/*couch', furnitureColors[4]),),
+ 5: (('**/*couch', furnitureColors[5]),)},
+ 460),
+ 710: ('phase_3.5/models/modules/couch_2person',
+ None,
+ None,
+ 230),
+ 715: ('phase_5.5/models/estate/couch_2personDesat',
+ None,
+ {0: (('**/*couch', furnitureColors[0]),),
+ 1: (('**/*couch', furnitureColors[1]),),
+ 2: (('**/*couch', furnitureColors[2]),),
+ 3: (('**/*couch', furnitureColors[3]),),
+ 4: (('**/*couch', furnitureColors[4]),),
+ 5: (('**/*couch', furnitureColors[5]),)},
+ 460),
+ 720: ('phase_5.5/models/estate/West_HayCouch',
+ None,
+ None,
+ 420),
+ 730: ('phase_5.5/models/estate/twinkieCouch',
+ None,
+ None,
+ 480),
+ 800: ('phase_3.5/models/modules/desk_only_wo_phone',
+ None,
+ None,
+ 65,
+ FLIsTable),
+ 810: ('phase_5.5/models/estate/BugRoomDesk',
+ None,
+ None,
+ 125,
+ FLIsTable),
+ 900: ('phase_3.5/models/modules/umbrella_stand',
+ None,
+ None,
+ 30),
+ 910: ('phase_3.5/models/modules/coatrack',
+ None,
+ None,
+ 75),
+ 920: ('phase_3.5/models/modules/paper_trashcan',
+ None,
+ None,
+ 30),
+ 930: ('phase_5.5/models/estate/BugRoomRedMushroomPot',
+ None,
+ None,
+ 60),
+ 940: ('phase_5.5/models/estate/BugRoomYellowMushroomPot',
+ None,
+ None,
+ 60),
+ 950: ('phase_5.5/models/estate/UWcoralClothRack',
+ None,
+ None,
+ 75),
+ 960: ('phase_5.5/models/estate/west_barrelStand',
+ None,
+ None,
+ 75),
+ 970: ('phase_5.5/models/estate/West_fatCactus',
+ None,
+ None,
+ 75),
+ 980: ('phase_5.5/models/estate/West_Tepee',
+ None,
+ None,
+ 150),
+ 990: ('phase_5.5/models/estate/gag_fan',
+ None,
+ None,
+ 500,
+ None,
+ None,
+ 0.5),
+ 1000: ('phase_3.5/models/modules/rug',
+ None,
+ None,
+ 75,
+ FLRug),
+ 1010: ('phase_5.5/models/estate/rugA',
+ None,
+ None,
+ 75,
+ FLRug),
+ 1015: ('phase_5.5/models/estate/rugADesat',
+ None,
+ {0: (('**/pPlane*', furnitureColors[0]),),
+ 1: (('**/pPlane*', furnitureColors[1]),),
+ 2: (('**/pPlane*', furnitureColors[2]),),
+ 3: (('**/pPlane*', furnitureColors[3]),),
+ 4: (('**/pPlane*', furnitureColors[4]),),
+ 5: (('**/pPlane*', furnitureColors[5]),)},
+ 150,
+ FLRug),
+ 1020: ('phase_5.5/models/estate/rugB',
+ None,
+ None,
+ 75,
+ FLRug,
+ 2.5),
+ 1030: ('phase_5.5/models/estate/bugRoomLeafMat',
+ None,
+ None,
+ 75,
+ FLRug),
+ 1040: ('phase_5.5/models/estate/tt_m_ara_int_presents',
+ None,
+ None,
+ 300),
+ 1050: ('phase_5.5/models/estate/tt_m_ara_int_sled',
+ None,
+ None,
+ 400),
+ 1100: ('phase_5.5/models/estate/cabinetRwood',
+ None,
+ None,
+ 825),
+ 1110: ('phase_5.5/models/estate/cabinetYwood',
+ None,
+ None,
+ 825),
+ 1120: ('phase_3.5/models/modules/bookcase',
+ None,
+ None,
+ 650,
+ FLIsTable),
+ 1130: ('phase_3.5/models/modules/bookcase_low',
+ None,
+ None,
+ 650,
+ FLIsTable),
+ 1140: ('phase_5.5/models/estate/icecreamChest',
+ None,
+ None,
+ 750),
+ 1200: ('phase_3.5/models/modules/ending_table',
+ None,
+ None,
+ 60,
+ FLIsTable),
+ 1210: ('phase_5.5/models/estate/table_radio',
+ None,
+ None,
+ 60,
+ FLIsTable,
+ 50.0),
+ 1215: ('phase_5.5/models/estate/table_radioDesat',
+ None,
+ {0: (('**/RADIOTABLE_*', woodColors[0]),),
+ 1: (('**/RADIOTABLE_*', woodColors[1]),),
+ 2: (('**/RADIOTABLE_*', woodColors[2]),),
+ 3: (('**/RADIOTABLE_*', woodColors[3]),),
+ 4: (('**/RADIOTABLE_*', woodColors[4]),),
+ 5: (('**/RADIOTABLE_*', woodColors[5]),)},
+ 120,
+ FLIsTable,
+ 50.0),
+ 1220: ('phase_5.5/models/estate/coffeetableSq',
+ None,
+ None,
+ 180,
+ FLIsTable),
+ 1230: ('phase_5.5/models/estate/coffeetableSq_BW',
+ None,
+ None,
+ 180,
+ FLIsTable),
+ 1240: ('phase_5.5/models/estate/UWtable',
+ None,
+ None,
+ 180,
+ FLIsTable),
+ 1250: ('phase_5.5/models/estate/cookieTableA',
+ None,
+ None,
+ 220,
+ FLIsTable),
+ 1260: ('phase_5.5/models/estate/TABLE_Bedroom_Desat',
+ None,
+ {0: (('**/Bedroom_Table', woodColors[0]),),
+ 1: (('**/Bedroom_Table', woodColors[1]),),
+ 2: (('**/Bedroom_Table', woodColors[2]),),
+ 3: (('**/Bedroom_Table', woodColors[3]),),
+ 4: (('**/Bedroom_Table', woodColors[4]),),
+ 5: (('**/Bedroom_Table', woodColors[5]),)},
+ 220,
+ FLIsTable),
+ 1300: ('phase_5.5/models/estate/jellybeanBank',
+ None,
+ None,
+ 0,
+ FLBank,
+ 1.0),
+ 1310: ('phase_5.5/models/estate/jellybeanBank',
+ None,
+ None,
+ 0,
+ FLBank,
+ 1.0),
+ 1320: ('phase_5.5/models/estate/jellybeanBank',
+ None,
+ None,
+ 0,
+ FLBank,
+ 1.0),
+ 1330: ('phase_5.5/models/estate/jellybeanBank',
+ None,
+ None,
+ 0,
+ FLBank,
+ 1.0),
+ 1340: ('phase_5.5/models/estate/jellybeanBank',
+ None,
+ None,
+ 0,
+ FLBank,
+ 1.0),
+ 1350: ('phase_5.5/models/estate/jellybeanBank',
+ None,
+ None,
+ 0,
+ FLBank,
+ 1.0),
+ 1399: ('phase_5.5/models/estate/prop_phone-mod',
+ None,
+ None,
+ 0,
+ FLPhone),
+ 1400: ('phase_5.5/models/estate/cezanne_toon',
+ None,
+ None,
+ 425,
+ FLPainting,
+ 2.0),
+ 1410: ('phase_5.5/models/estate/flowers',
+ None,
+ None,
+ 425,
+ FLPainting,
+ 2.0),
+ 1420: ('phase_5.5/models/estate/modernistMickey',
+ None,
+ None,
+ 425,
+ FLPainting,
+ 2.0),
+ 1430: ('phase_5.5/models/estate/rembrandt_toon',
+ None,
+ None,
+ 425,
+ FLPainting,
+ 2.0),
+ 1440: ('phase_5.5/models/estate/landscape',
+ None,
+ None,
+ 425,
+ FLPainting,
+ 100.0),
+ 1441: ('phase_5.5/models/estate/whistler-horse',
+ None,
+ None,
+ 425,
+ FLPainting,
+ 2.0),
+ 1442: ('phase_5.5/models/estate/degasHorseStar',
+ None,
+ None,
+ 425,
+ FLPainting,
+ 2.5),
+ 1443: ('phase_5.5/models/estate/MagPie',
+ None,
+ None,
+ 425,
+ FLPainting,
+ 2.0),
+ 1450: ('phase_5.5/models/estate/tt_m_prp_int_painting_valentine',
+ None,
+ None,
+ 425,
+ FLPainting),
+ 1500: ('phase_5.5/models/estate/RADIO_A',
+ None,
+ None,
+ 25,
+ FLOnTable,
+ 15.0),
+ 1510: ('phase_5.5/models/estate/RADIO_B',
+ None,
+ None,
+ 25,
+ FLOnTable,
+ 15.0),
+ 1520: ('phase_5.5/models/estate/radio_c',
+ None,
+ None,
+ 25,
+ FLOnTable,
+ 15.0),
+ 1530: ('phase_5.5/models/estate/bugRoomTV',
+ None,
+ None,
+ 675),
+ 1600: ('phase_5.5/models/estate/vaseA_short',
+ None,
+ None,
+ 120,
+ FLOnTable),
+ 1610: ('phase_5.5/models/estate/vaseA_tall',
+ None,
+ None,
+ 120,
+ FLOnTable),
+ 1620: ('phase_5.5/models/estate/vaseB_short',
+ None,
+ None,
+ 120,
+ FLOnTable),
+ 1630: ('phase_5.5/models/estate/vaseB_tall',
+ None,
+ None,
+ 120,
+ FLOnTable),
+ 1640: ('phase_5.5/models/estate/vaseC_short',
+ None,
+ None,
+ 120,
+ FLOnTable),
+ 1650: ('phase_5.5/models/estate/vaseD_short',
+ None,
+ None,
+ 120,
+ FLOnTable),
+ 1660: ('phase_5.5/models/estate/UWcoralVase',
+ None,
+ None,
+ 120,
+ FLOnTable | FLBillboard),
+ 1661: ('phase_5.5/models/estate/UWshellVase',
+ None,
+ None,
+ 120,
+ FLOnTable | FLBillboard),
+ 1670: ('phase_5.5/models/estate/tt_m_prp_int_roseVase_valentine',
+ None,
+ None,
+ 200,
+ FLOnTable),
+ 1680: ('phase_5.5/models/estate/tt_m_prp_int_roseWatercan_valentine',
+ None,
+ None,
+ 200,
+ FLOnTable),
+ 1700: ('phase_5.5/models/estate/popcornCart',
+ None,
+ None,
+ 400),
+ 1710: ('phase_5.5/models/estate/bugRoomLadyBug',
+ None,
+ None,
+ 260),
+ 1720: ('phase_5.5/models/estate/UWfountain',
+ None,
+ None,
+ 450),
+ 1725: ('phase_5.5/models/estate/UWOceanDryer',
+ None,
+ None,
+ 400),
+ 1800: ('phase_5.5/models/estate/UWskullBowl',
+ None,
+ None,
+ 120,
+ FLOnTable),
+ 1810: ('phase_5.5/models/estate/UWlizardBowl',
+ None,
+ None,
+ 120,
+ FLOnTable),
+ 1900: ('phase_5.5/models/estate/UWswordFish',
+ None,
+ None,
+ 425,
+ FLPainting,
+ 0.5),
+ 1910: ('phase_5.5/models/estate/UWhammerhead',
+ None,
+ None,
+ 425,
+ FLPainting),
+ 1920: ('phase_5.5/models/estate/West_hangingHorns',
+ None,
+ None,
+ 475,
+ FLPainting),
+ 1930: ('phase_5.5/models/estate/West_Sombrero',
+ None,
+ None,
+ 425,
+ FLPainting),
+ 1940: ('phase_5.5/models/estate/West_fancySombrero',
+ None,
+ None,
+ 450,
+ FLPainting),
+ 1950: ('phase_5.5/models/estate/West_CoyotePawdecor',
+ None,
+ None,
+ 475,
+ FLPainting),
+ 1960: ('phase_5.5/models/estate/West_Horseshoe',
+ None,
+ None,
+ 475,
+ FLPainting),
+ 1970: ('phase_5.5/models/estate/West_bisonPortrait',
+ None,
+ None,
+ 475,
+ FLPainting),
+ 2000: ('phase_5.5/models/estate/candySwingSet',
+ None,
+ None,
+ 300),
+ 2010: ('phase_5.5/models/estate/cakeSlide',
+ None,
+ None,
+ 200),
+ 3000: ('phase_5.5/models/estate/BanannaSplitShower',
+ None,
+ None,
+ 400),
+ 4000: ('phase_5.5/models/estate/tt_m_ara_est_accessoryTrunkBoy',
+ None,
+ None,
+ 5,
+ FLTrunk,
+ 0.9),
+ 4010: ('phase_5.5/models/estate/tt_m_ara_est_accessoryTrunkGirl',
+ None,
+ None,
+ 5,
+ FLTrunk,
+ 0.9),
+ 10000: ('phase_4/models/estate/pumpkin_short',
+ None,
+ None,
+ 200,
+ FLOnTable),
+ 10010: ('phase_4/models/estate/pumpkin_tall',
+ None,
+ None,
+ 250,
+ FLOnTable),
+ 10020: ('phase_5.5/models/estate/tt_m_prp_int_winter_tree',
+ None,
+ None,
+ 500,
+ None,
+ None,
+ 0.1),
+ 10030: ('phase_5.5/models/estate/tt_m_prp_int_winter_wreath',
+ None,
+ None,
+ 200,
+ FLPainting)}
+
+class CatalogFurnitureItem(CatalogAtticItem.CatalogAtticItem):
+
+ def makeNewItem(self, furnitureType, colorOption = None, posHpr = None):
+ self.furnitureType = furnitureType
+ self.colorOption = colorOption
+ self.posHpr = posHpr
+ CatalogAtticItem.CatalogAtticItem.makeNewItem(self)
+
+ def needsCustomize(self):
+ return self.colorOption == None and FurnitureTypes[self.furnitureType][FTColorOptions] != None
+
+ def saveHistory(self):
+ return 1
+
+ def replacesExisting(self):
+ return self.getFlags() & (FLCloset | FLBank | FLTrunk) != 0
+
+ def hasExisting(self):
+ return 1
+
+ def getYourOldDesc(self):
+ if self.getFlags() & FLCloset:
+ return TTLocalizer.FurnitureYourOldCloset
+ elif self.getFlags() & FLBank:
+ return TTLocalizer.FurnitureYourOldBank
+ elif self.getFlags() & FLTrunk:
+ return TTLocalizer.FurnitureYourOldTrunk
+ else:
+ return None
+ return None
+
+ def notOfferedTo(self, avatar):
+ if self.getFlags() & FLCloset or self.getFlags() & FLTrunk:
+ decade = self.furnitureType - self.furnitureType % 10
+ forBoys = (decade == 500 or decade == 4000)
+ if avatar.getStyle().getGender() == 'm':
+ return not forBoys
+ else:
+ return forBoys
+ return 0
+
+ def isDeletable(self):
+ return self.getFlags() & (FLBank | FLCloset | FLPhone | FLTrunk) == 0
+
+ def getMaxAccessories(self):
+ return ToontownGlobals.MaxAccessories
+
+ def getMaxBankMoney(self):
+ return BankToMoney.get(self.furnitureType)
+
+ def getMaxClothes(self):
+ index = self.furnitureType % 10
+ if index == 0:
+ return 10
+ elif index == 2:
+ return 15
+ elif index == 4:
+ return 20
+ elif index == 6:
+ return 25
+ elif index == 8:
+ return 50
+ else:
+ return None
+ return None
+
+ def reachedPurchaseLimit(self, avatar):
+ if self.getFlags() & FLBank:
+ if self.getMaxBankMoney() <= avatar.getMaxBankMoney():
+ return 1
+ if self in avatar.onOrder or self in avatar.mailboxContents:
+ return 1
+ if self.getFlags() & FLCloset:
+ if self.getMaxClothes() <= avatar.getMaxClothes():
+ return 1
+ if self in avatar.onOrder or self in avatar.mailboxContents:
+ return 1
+ if self.getFlags() & FLTrunk:
+ if self.getMaxAccessories() <= avatar.getMaxAccessories():
+ return 1
+ if self in avatar.onOrder or self in avatar.mailboxContents:
+ return 1
+ return 0
+
+ def getTypeName(self):
+ flags = self.getFlags()
+ if flags & FLPainting:
+ return TTLocalizer.PaintingTypeName
+ else:
+ return TTLocalizer.FurnitureTypeName
+
+ def getName(self):
+ return TTLocalizer.FurnitureNames[self.furnitureType]
+
+ def getFlags(self):
+ defn = FurnitureTypes[self.furnitureType]
+ if FTFlags < len(defn):
+ flag = defn[FTFlags]
+ if flag == None:
+ return 0
+ else:
+ return flag
+ else:
+ return 0
+ return
+
+ def isGift(self):
+ if self.getEmblemPrices():
+ return 0
+ if self.getFlags() & (FLCloset | FLBank | FLTrunk):
+ return 0
+ else:
+ return 1
+
+ def recordPurchase(self, avatar, optional):
+ house, retcode = self.getHouseInfo(avatar)
+ self.giftTag = None
+ if retcode >= 0:
+ if self.getFlags() & FLCloset:
+ if avatar.getMaxClothes() > self.getMaxClothes():
+ return ToontownGlobals.P_AlreadyOwnBiggerCloset
+ avatar.b_setMaxClothes(self.getMaxClothes())
+ if self.getFlags() & FLTrunk:
+ avatar.b_setMaxAccessories(self.getMaxAccessories())
+ house.addAtticItem(self)
+ if self.getFlags() & FLBank:
+ avatar.b_setMaxBankMoney(self.getMaxBankMoney())
+ return retcode
+
+ def getDeliveryTime(self):
+ return 24 * 60
+
+ def getPicture(self, avatar):
+ model = self.loadModel()
+ spin = 1
+ flags = self.getFlags()
+ if flags & FLRug:
+ spin = 0
+ model.setP(90)
+ elif flags & FLPainting:
+ spin = 0
+ elif flags & FLBillboard:
+ spin = 0
+ model.setBin('unsorted', 0, 1)
+ self.hasPicture = True
+ return self.makeFrameModel(model, spin)
+
+ def output(self, store = -1):
+ return 'CatalogFurnitureItem(%s%s)' % (self.furnitureType, self.formatOptionalData(store))
+
+ def getFilename(self):
+ type = FurnitureTypes[self.furnitureType]
+ return type[FTModelName]
+
+ def compareTo(self, other):
+ return self.furnitureType - other.furnitureType
+
+ def getHashContents(self):
+ return self.furnitureType
+
+ def getSalePrice(self):
+ if self.furnitureType in [508, 518]:
+ return 50
+ else:
+ return CatalogItem.CatalogItem.getSalePrice(self)
+
+ def getBasePrice(self):
+ return FurnitureTypes[self.furnitureType][FTBasePrice]
+
+ def loadModel(self):
+ type = FurnitureTypes[self.furnitureType]
+ model = loader.loadModel(type[FTModelName])
+ self.applyColor(model, type[FTColor])
+ if type[FTColorOptions] != None:
+ if self.colorOption == None:
+ option = random.choice(type[FTColorOptions].values())
+ else:
+ option = type[FTColorOptions].get(self.colorOption)
+ self.applyColor(model, option)
+ if FTScale < len(type):
+ scale = type[FTScale]
+ if not scale == None:
+ model.setScale(scale)
+ model.flattenLight()
+ return model
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store)
+ self.furnitureType = di.getInt16()
+ self.colorOption = None
+ type = FurnitureTypes[self.furnitureType]
+ if type[FTColorOptions]:
+ if store & CatalogItem.Customization:
+ self.colorOption = di.getUint8()
+ option = type[FTColorOptions][self.colorOption]
+ return
+
+ def encodeDatagram(self, dg, store):
+ CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store)
+ dg.addInt16(self.furnitureType)
+ if FurnitureTypes[self.furnitureType][FTColorOptions]:
+ if store & CatalogItem.Customization:
+ dg.addUint8(self.colorOption)
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_AlreadyOwnBiggerCloset:
+ return TTLocalizer.CatalogAcceptClosetError
+ return CatalogAtticItem.CatalogAtticItem.getAcceptItemErrorText(self, retcode)
+
+
+def nextAvailableCloset(avatar, duplicateItems):
+ if avatar.getStyle().getGender() == 'm':
+ index = 0
+ else:
+ index = 1
+ if not hasattr(avatar, 'maxClothes'):
+ return None
+ closetIds = ClothesToCloset.get(avatar.getMaxClothes())
+ closetIds = list(closetIds)
+ closetIds.sort()
+ closetId = closetIds[index]
+ if closetId == None or closetId == MaxClosetIds[index]:
+ return
+ closetId += 2
+ item = CatalogFurnitureItem(closetId)
+ while item in avatar.onOrder or item in avatar.mailboxContents:
+ closetId += 2
+ if closetId > MaxClosetIds[index]:
+ return
+ item = CatalogFurnitureItem(closetId)
+
+ return item
+
+
+def get50ItemCloset(avatar, duplicateItems):
+ if avatar.getStyle().getGender() == 'm':
+ index = 0
+ else:
+ index = 1
+ closetId = MaxClosetIds[index]
+ item = CatalogFurnitureItem(closetId)
+ if item in avatar.onOrder or item in avatar.mailboxContents:
+ return None
+ return item
+
+
+def getMaxClosets():
+ list = []
+ for closetId in MaxClosetIds:
+ list.append(CatalogFurnitureItem(closetId))
+
+ return list
+
+
+def getAllClosets():
+ list = []
+ for closetId in ClosetToClothes.keys():
+ list.append(CatalogFurnitureItem(closetId))
+
+ return list
+
+
+def get50ItemTrunk(avatar, duplicateItems):
+ if avatar.getStyle().getGender() == 'm':
+ index = 0
+ else:
+ index = 1
+ trunkId = MaxTrunkIds[index]
+ item = CatalogFurnitureItem(trunkId)
+ if item in avatar.onOrder or item in avatar.mailboxContents:
+ return None
+ return item
+
+
+def getMaxTrunks():
+ list = []
+ for trunkId in MaxTrunkIds:
+ list.append(CatalogFurnitureItem(trunkId))
+
+ return list
+
+
+def getAllFurnitures(index):
+ list = []
+ colors = FurnitureTypes[index][FTColorOptions]
+ for n in range(len(colors)):
+ list.append(CatalogFurnitureItem(index, n))
+
+ return list
diff --git a/toontown/catalog/CatalogGardenItem.py b/toontown/catalog/CatalogGardenItem.py
new file mode 100644
index 0000000..97ed7fb
--- /dev/null
+++ b/toontown/catalog/CatalogGardenItem.py
@@ -0,0 +1,175 @@
+import CatalogItem
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from otp.otpbase import OTPLocalizer
+from direct.interval.IntervalGlobal import *
+from toontown.estate import GardenGlobals
+from direct.actor import Actor
+from pandac.PandaModules import NodePath
+
+class CatalogGardenItem(CatalogItem.CatalogItem):
+ sequenceNumber = 0
+
+ def makeNewItem(self, itemIndex = 0, count = 3, tagCode = 1):
+ self.gardenIndex = itemIndex
+ self.numItems = count
+ self.giftCode = tagCode
+ CatalogItem.CatalogItem.makeNewItem(self)
+
+ def getPurchaseLimit(self):
+ if self.gardenIndex == GardenGlobals.GardenAcceleratorSpecial:
+ return 1
+ else:
+ return 100
+
+ def reachedPurchaseLimit(self, avatar):
+ if self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or self in avatar.awardMailboxContents or self in avatar.onAwardOrder:
+ return 1
+ return 0
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ return TTLocalizer.CatalogAcceptGarden
+ return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode)
+
+ def saveHistory(self):
+ return 1
+
+ def getTypeName(self):
+ return TTLocalizer.GardenTypeName
+
+ def getName(self):
+ name = GardenGlobals.Specials[self.gardenIndex]['photoName']
+ return name
+
+ def recordPurchase(self, avatar, optional):
+ if avatar:
+ avatar.addGardenItem(self.gardenIndex, self.numItems)
+ if 1:
+ return ToontownGlobals.P_ItemAvailable
+
+ def getPicture(self, avatar):
+ photoModel = GardenGlobals.Specials[self.gardenIndex]['photoModel']
+ if GardenGlobals.Specials[self.gardenIndex].has_key('photoAnimation'):
+ modelPath = photoModel + GardenGlobals.Specials[self.gardenIndex]['photoAnimation'][0]
+ animationName = GardenGlobals.Specials[self.gardenIndex]['photoAnimation'][1]
+ animationPath = photoModel + animationName
+ self.model = Actor.Actor()
+ self.model.loadModel(modelPath)
+ self.model.loadAnims(dict([[animationName, animationPath]]))
+ frame, ival = self.makeFrameModel(self.model, 0)
+ ival = ActorInterval(self.model, animationName, 2.0)
+ photoPos = GardenGlobals.Specials[self.gardenIndex]['photoPos']
+ frame.setPos(photoPos)
+ photoScale = GardenGlobals.Specials[self.gardenIndex]['photoScale']
+ self.model.setScale(photoScale)
+ self.hasPicture = True
+ return (frame, ival)
+ else:
+ self.model = loader.loadModel(photoModel)
+ frame = self.makeFrame()
+ self.model.reparentTo(frame)
+ photoPos = GardenGlobals.Specials[self.gardenIndex]['photoPos']
+ self.model.setPos(*photoPos)
+ photoScale = GardenGlobals.Specials[self.gardenIndex]['photoScale']
+ self.model.setScale(photoScale)
+ self.hasPicture = True
+ return (frame, None)
+ return None
+
+ def cleanupPicture(self):
+ CatalogItem.CatalogItem.cleanupPicture(self)
+ self.model.detachNode()
+ self.model = None
+ return
+
+ def output(self, store = -1):
+ return 'CatalogGardenItem(%s%s)' % (self.gardenIndex, self.formatOptionalData(store))
+
+ def compareTo(self, other):
+ return 0
+
+ def getHashContents(self):
+ return self.gardenIndex
+
+ def getBasePrice(self):
+ beanCost = GardenGlobals.Specials[self.gardenIndex]['beanCost']
+ return beanCost
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store)
+ self.gardenIndex = di.getUint8()
+ self.numItems = di.getUint8()
+
+ def encodeDatagram(self, dg, store):
+ CatalogItem.CatalogItem.encodeDatagram(self, dg, store)
+ dg.addUint8(self.gardenIndex)
+ dg.addUint8(self.numItems)
+
+ def getRequestPurchaseErrorText(self, retcode):
+ retval = CatalogItem.CatalogItem.getRequestPurchaseErrorText(self, retcode)
+ origText = retval
+ if retval == TTLocalizer.CatalogPurchaseItemAvailable or retval == TTLocalizer.CatalogPurchaseItemOnOrder:
+ recipeKey = GardenGlobals.getRecipeKeyUsingSpecial(self.gardenIndex)
+ if not recipeKey == -1:
+ retval += GardenGlobals.getPlantItWithString(self.gardenIndex)
+ if self.gardenIndex == GardenGlobals.GardenAcceleratorSpecial:
+ if GardenGlobals.ACCELERATOR_USED_FROM_SHTIKER_BOOK:
+ retval = origText
+ retval += TTLocalizer.UseFromSpecialsTab
+ retval += TTLocalizer.MakeSureWatered
+ return retval
+
+ def getRequestPurchaseErrorTextTimeout(self):
+ return 20
+
+ def getDeliveryTime(self):
+ if self.gardenIndex == GardenGlobals.GardenAcceleratorSpecial:
+ return 24 * 60
+ else:
+ return 0
+
+ def getPurchaseLimit(self):
+ if self.gardenIndex == GardenGlobals.GardenAcceleratorSpecial:
+ return 1
+ else:
+ return 0
+
+ def compareTo(self, other):
+ if self.gardenIndex != other.gardenIndex:
+ return self.gardenIndex - other.gardenIndex
+ return self.gardenIndex - other.gardenIndex
+
+ def reachedPurchaseLimit(self, avatar):
+ if avatar.onOrder.count(self) != 0:
+ return 1
+ if avatar.mailboxContents.count(self) != 0:
+ return 1
+ for specials in avatar.getGardenSpecials():
+ if specials[0] == self.gardenIndex:
+ if self.gardenIndex == GardenGlobals.GardenAcceleratorSpecial:
+ return 1
+
+ return 0
+
+ def isSkillTooLow(self, avatar):
+ recipeKey = GardenGlobals.getRecipeKeyUsingSpecial(self.gardenIndex)
+ recipe = GardenGlobals.Recipes[recipeKey]
+ numBeansRequired = len(recipe['beans'])
+ canPlant = avatar.getBoxCapability()
+ result = False
+ if canPlant < numBeansRequired:
+ result = True
+ if not result and GardenGlobals.Specials.has_key(self.gardenIndex) and GardenGlobals.Specials[self.gardenIndex].has_key('minSkill'):
+ minSkill = GardenGlobals.Specials[self.gardenIndex]['minSkill']
+ if avatar.shovelSkill < minSkill:
+ result = True
+ else:
+ result = False
+ return result
+
+ def noGarden(self, avatar):
+ return not avatar.getGardenStarted()
+
+ def isGift(self):
+ return 0
diff --git a/toontown/catalog/CatalogGardenStarterItem.py b/toontown/catalog/CatalogGardenStarterItem.py
new file mode 100644
index 0000000..6e2f3ec
--- /dev/null
+++ b/toontown/catalog/CatalogGardenStarterItem.py
@@ -0,0 +1,106 @@
+import CatalogItem
+import time
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from otp.otpbase import OTPLocalizer
+from direct.interval.IntervalGlobal import *
+from toontown.toontowngui import TTDialog
+from toontown.estate import GardenTutorial
+
+class CatalogGardenStarterItem(CatalogItem.CatalogItem):
+
+ def makeNewItem(self):
+ CatalogItem.CatalogItem.makeNewItem(self)
+
+ def getPurchaseLimit(self):
+ return 0
+
+ def reachedPurchaseLimit(self, avatar):
+ if self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or self in avatar.awardMailboxContents or self in avatar.onAwardOrder or hasattr(avatar, 'gardenStarted') and avatar.getGardenStarted():
+ return 1
+ return 0
+
+ def saveHistory(self):
+ return 1
+
+ def getTypeName(self):
+ return TTLocalizer.GardenStarterTypeName
+
+ def getName(self):
+ return TTLocalizer.GardenStarterTypeName
+
+ def recordPurchase(self, avatar, optional):
+ print 'rental-- record purchase'
+ if avatar:
+ print 'starter garden-- has avater'
+ estate = simbase.air.estateMgr.estate.get(avatar.doId)
+ if estate:
+ print 'starter garden-- has estate'
+ estate.placeStarterGarden(avatar.doId)
+ else:
+ print 'starter garden-- something not there'
+ return ToontownGlobals.P_ItemAvailable
+
+ def getPicture(self, avatar):
+ self.hasPicture = True
+ scale = 1
+ heading = 0
+ pitch = 30
+ roll = 0
+ spin = 1
+ down = -1
+ modelParent = loader.loadModel('phase_5.5/models/estate/watering_cans')
+ model = modelParent.find('**/water_canA')
+ scale = 0.5
+ heading = 45
+ return self.makeFrameModel(model, spin)
+
+ def output(self, store = -1):
+ return 'CatalogGardenStarterItem(%s)' % self.formatOptionalData(store)
+
+ def compareTo(self, other):
+ return 0
+
+ def getHashContents(self):
+ return 0
+
+ def getBasePrice(self):
+ return 50
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store)
+
+ def encodeDatagram(self, dg, store):
+ CatalogItem.CatalogItem.encodeDatagram(self, dg, store)
+
+ def getDeliveryTime(self):
+ return 1
+
+ def isRental(self):
+ return 0
+
+ def isGift(self):
+ return 0
+
+ def acceptItem(self, mailbox, index, callback):
+ self.confirmGarden = TTDialog.TTGlobalDialog(doneEvent='confirmGarden', message=TTLocalizer.MessageConfirmGarden, command=Functor(self.handleGardenConfirm, mailbox, index, callback), style=TTDialog.TwoChoice)
+ self.confirmGarden.show()
+
+ def handleGardenConfirm(self, mailbox, index, callback, choice):
+ if choice > 0:
+
+ def handleTutorialDone():
+ self.gardenTutorial.destroy()
+ self.gardenTutorial = None
+ return
+
+ self.gardenTutorial = GardenTutorial.GardenTutorial(callback=handleTutorialDone)
+ if hasattr(mailbox, 'mailboxGui') and mailbox.mailboxGui:
+ mailbox.acceptItem(self, index, callback)
+ mailbox.mailboxGui.justExit()
+ else:
+ callback(ToontownGlobals.P_UserCancelled, self, index)
+ if self.confirmGarden:
+ self.confirmGarden.cleanup()
+ self.confirmGarden = None
+ return
diff --git a/toontown/catalog/CatalogGenerator.py b/toontown/catalog/CatalogGenerator.py
new file mode 100644
index 0000000..90ab5f4
--- /dev/null
+++ b/toontown/catalog/CatalogGenerator.py
@@ -0,0 +1,1785 @@
+from direct.directnotify import DirectNotifyGlobal
+import CatalogItem
+import CatalogItemList
+from CatalogFurnitureItem import CatalogFurnitureItem, nextAvailableCloset, getAllClosets, get50ItemCloset, getMaxClosets, get50ItemTrunk
+from CatalogAnimatedFurnitureItem import CatalogAnimatedFurnitureItem
+from CatalogClothingItem import CatalogClothingItem, getAllClothes
+from CatalogChatItem import CatalogChatItem, getChatRange
+from CatalogEmoteItem import CatalogEmoteItem
+from CatalogWallpaperItem import CatalogWallpaperItem, getWallpapers
+from CatalogFlooringItem import CatalogFlooringItem, getFloorings
+from CatalogMouldingItem import CatalogMouldingItem, getAllMouldings
+from CatalogWainscotingItem import CatalogWainscotingItem, getAllWainscotings
+from CatalogWindowItem import CatalogWindowItem
+from CatalogPoleItem import nextAvailablePole, getAllPoles
+from CatalogPetTrickItem import CatalogPetTrickItem, getAllPetTricks
+from CatalogGardenItem import CatalogGardenItem
+from CatalogToonStatueItem import CatalogToonStatueItem
+from CatalogRentalItem import CatalogRentalItem
+from CatalogGardenStarterItem import CatalogGardenStarterItem
+from CatalogNametagItem import CatalogNametagItem
+from CatalogAccessoryItem import CatalogAccessoryItem
+from direct.actor import Actor
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase import ToontownGlobals
+import types
+import random
+import time
+from pandac.PandaModules import *
+MetaItems = {100: getAllClothes(101, 102, 103, 104, 105, 106, 107, 108, 109, 109, 111, 115, 201, 202, 203, 204, 205, 206, 207, 208, 209, 209, 211, 215),
+ 300: getAllClothes(301, 302, 303, 304, 305, 308, 401, 403, 404, 405, 407, 451, 452, 453),
+ 2000: getChatRange(0, 1999),
+ 2010: getChatRange(2000, 2999),
+ 2020: getChatRange(3000, 3999),
+ 2030: getChatRange(4000, 4999),
+ 2040: getChatRange(6000, 6999),
+ 2050: getChatRange(7000, 7999),
+ 2900: getChatRange(10000, 10002, 10005, 10005, 10007, 10008, 10010, 10099),
+ 2910: getChatRange(11000, 11005, 11008, 11008, 11012, 11015, 11017, 11019, 11021, 11022),
+ 2920: getChatRange(12000, 12049),
+ 2921: getChatRange(12050, 12099),
+ 2930: getChatRange(13000, 13099),
+ 2940: getChatRange(14000, 14099),
+ 3000: getWallpapers(1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100),
+ 3010: getWallpapers(2200, 2300, 2400, 2500, 2600, 2700, 2800),
+ 3020: getWallpapers(2900, 3000, 3100, 3200, 3300, 3400, 3500, 3600),
+ 3030: getWallpapers(3700, 3800, 3900),
+ 3500: getAllWainscotings(1000, 1010),
+ 3510: getAllWainscotings(1020),
+ 3520: getAllWainscotings(1030),
+ 3530: getAllWainscotings(1040),
+ 4000: getFloorings(1000, 1010, 1020, 1030, 1040, 1050, 1060, 1070, 1080, 1090, 1100),
+ 4010: getFloorings(1110, 1120, 1130),
+ 4020: getFloorings(1140, 1150, 1160, 1170, 1180, 1190),
+ 4500: getAllMouldings(1000, 1010),
+ 4510: getAllMouldings(1020, 1030, 1040),
+ 4520: getAllMouldings(1070),
+ 5000: getAllPetTricks()}
+MetaItemChatKeysSold = (2000,
+ 2010,
+ 2020,
+ 2030,
+ 2040,
+ 2050,
+ 2900,
+ 2910,
+ 2920,
+ 2921,
+ 2930)
+
+def getAllChatItemsSold():
+ result = []
+ for key in MetaItemChatKeysSold:
+ result += MetaItems[key]
+
+ return result
+
+
+class Sale:
+
+ def __init__(self, *args):
+ self.args = args
+
+
+MonthlySchedule = ((7,
+ 1,
+ 8,
+ 31,
+ (CatalogAccessoryItem(101),
+ CatalogAccessoryItem(103),
+ CatalogAccessoryItem(117),
+ CatalogAccessoryItem(118),
+ CatalogAccessoryItem(123),
+ CatalogAccessoryItem(124),
+ CatalogAccessoryItem(125),
+ CatalogAccessoryItem(126),
+ CatalogAccessoryItem(127),
+ CatalogAccessoryItem(128),
+ CatalogAccessoryItem(129),
+ CatalogAccessoryItem(130),
+ CatalogAccessoryItem(202),
+ CatalogAccessoryItem(204),
+ CatalogAccessoryItem(205),
+ CatalogAccessoryItem(206),
+ CatalogAccessoryItem(208),
+ CatalogAccessoryItem(209),
+ CatalogAccessoryItem(210),
+ CatalogAccessoryItem(302),
+ CatalogAccessoryItem(308),
+ CatalogAccessoryItem(309),
+ CatalogAccessoryItem(310),
+ CatalogAccessoryItem(317),
+ CatalogAccessoryItem(402),
+ CatalogAccessoryItem(403),
+ CatalogAccessoryItem(405),
+ CatalogAccessoryItem(406),
+ CatalogAccessoryItem(407),
+ CatalogAccessoryItem(408),
+ CatalogAccessoryItem(409),
+ CatalogAccessoryItem(410),
+ CatalogAccessoryItem(411),
+ CatalogAccessoryItem(412),
+ CatalogAccessoryItem(413))),
+ (9,
+ 1,
+ 10,
+ 31,
+ (CatalogAccessoryItem(306),
+ CatalogAccessoryItem(318),
+ CatalogAccessoryItem(121),
+ CatalogAccessoryItem(212),
+ CatalogAccessoryItem(214),
+ CatalogAccessoryItem(312),
+ CatalogAccessoryItem(150),
+ CatalogAccessoryItem(151),
+ CatalogAccessoryItem(147),
+ CatalogAccessoryItem(422),
+ CatalogAccessoryItem(141),
+ CatalogAccessoryItem(146),
+ CatalogAccessoryItem(444),
+ CatalogAccessoryItem(122),
+ CatalogAccessoryItem(430),
+ CatalogAccessoryItem(145),
+ CatalogAccessoryItem(132),
+ CatalogAccessoryItem(161),
+ CatalogAccessoryItem(134),
+ CatalogAccessoryItem(149),
+ CatalogAccessoryItem(207),
+ CatalogAccessoryItem(215),
+ CatalogAccessoryItem(216),
+ CatalogAccessoryItem(417),
+ CatalogAccessoryItem(222),
+ CatalogAccessoryItem(321),
+ CatalogAccessoryItem(322),
+ CatalogAccessoryItem(307),
+ CatalogAccessoryItem(135),
+ CatalogAccessoryItem(174))),
+ (11,
+ 1,
+ 12,
+ 31,
+ (CatalogAccessoryItem(434),
+ CatalogAccessoryItem(435),
+ CatalogAccessoryItem(441),
+ CatalogAccessoryItem(446),
+ CatalogAccessoryItem(429),
+ CatalogAccessoryItem(110),
+ CatalogAccessoryItem(148),
+ CatalogAccessoryItem(443),
+ CatalogAccessoryItem(426),
+ CatalogAccessoryItem(439),
+ CatalogAccessoryItem(143),
+ CatalogAccessoryItem(313),
+ CatalogAccessoryItem(311),
+ CatalogAccessoryItem(437),
+ CatalogAccessoryItem(415),
+ CatalogAccessoryItem(167),
+ CatalogAccessoryItem(157),
+ CatalogAccessoryItem(106),
+ CatalogAccessoryItem(109),
+ CatalogAccessoryItem(421),
+ CatalogAccessoryItem(401),
+ CatalogAccessoryItem(447),
+ CatalogAccessoryItem(213),
+ CatalogAccessoryItem(330))),
+ (1,
+ 1,
+ 2,
+ 29,
+ (CatalogAccessoryItem(440),
+ CatalogAccessoryItem(425),
+ CatalogAccessoryItem(158),
+ CatalogAccessoryItem(431),
+ CatalogAccessoryItem(420),
+ CatalogAccessoryItem(155),
+ CatalogAccessoryItem(419),
+ CatalogAccessoryItem(436),
+ CatalogAccessoryItem(428),
+ CatalogAccessoryItem(304),
+ CatalogAccessoryItem(301),
+ CatalogAccessoryItem(416),
+ CatalogAccessoryItem(414),
+ CatalogAccessoryItem(164),
+ CatalogAccessoryItem(323),
+ CatalogAccessoryItem(108),
+ CatalogAccessoryItem(139),
+ CatalogAccessoryItem(316),
+ CatalogAccessoryItem(131),
+ CatalogAccessoryItem(170),
+ CatalogAccessoryItem(221),
+ CatalogAccessoryItem(225))),
+ (3,
+ 1,
+ 4,
+ 30,
+ (CatalogAccessoryItem(305),
+ CatalogAccessoryItem(303),
+ CatalogAccessoryItem(144),
+ CatalogAccessoryItem(120),
+ CatalogAccessoryItem(116),
+ CatalogAccessoryItem(217),
+ CatalogAccessoryItem(218),
+ CatalogAccessoryItem(219),
+ CatalogAccessoryItem(445),
+ CatalogAccessoryItem(418),
+ CatalogAccessoryItem(432),
+ CatalogAccessoryItem(427),
+ CatalogAccessoryItem(423),
+ CatalogAccessoryItem(137),
+ CatalogAccessoryItem(163),
+ CatalogAccessoryItem(165),
+ CatalogAccessoryItem(153),
+ CatalogAccessoryItem(319),
+ CatalogAccessoryItem(154),
+ CatalogAccessoryItem(159),
+ CatalogAccessoryItem(162),
+ CatalogAccessoryItem(315),
+ CatalogAccessoryItem(160),
+ CatalogAccessoryItem(102))),
+ (5,
+ 1,
+ 6,
+ 30,
+ (CatalogAccessoryItem(119),
+ CatalogAccessoryItem(136),
+ CatalogAccessoryItem(169),
+ CatalogAccessoryItem(140),
+ CatalogAccessoryItem(168),
+ CatalogAccessoryItem(138),
+ CatalogAccessoryItem(220),
+ CatalogAccessoryItem(433),
+ CatalogAccessoryItem(442),
+ CatalogAccessoryItem(424),
+ CatalogAccessoryItem(404),
+ CatalogAccessoryItem(156),
+ CatalogAccessoryItem(142),
+ CatalogAccessoryItem(152),
+ CatalogAccessoryItem(133),
+ CatalogAccessoryItem(166),
+ CatalogAccessoryItem(211),
+ CatalogAccessoryItem(314),
+ CatalogAccessoryItem(320),
+ CatalogAccessoryItem(173),
+ CatalogAccessoryItem(328),
+ CatalogAccessoryItem(329))),
+ (10,
+ 3,
+ 11,
+ 2,
+ ((3, 2900),
+ CatalogChatItem(10003),
+ CatalogClothingItem(1001, 0),
+ CatalogClothingItem(1002, 0),
+ CatalogWallpaperItem(10100),
+ CatalogWallpaperItem(10200),
+ CatalogFurnitureItem(10000),
+ CatalogFurnitureItem(10010),
+ CatalogNametagItem(9))),
+ (10,
+ 3,
+ 11,
+ 2,
+ (CatalogClothingItem(1744, 0),
+ CatalogClothingItem(1745, 0),
+ CatalogClothingItem(1748, 0),
+ CatalogClothingItem(1771, 0),
+ CatalogClothingItem(1774, 0),
+ CatalogClothingItem(1775, 0),
+ CatalogClothingItem(1743, 0),
+ CatalogClothingItem(1746, 0),
+ CatalogClothingItem(1747, 0),
+ CatalogClothingItem(1112, 0),
+ CatalogClothingItem(1113, 0),
+ CatalogClothingItem(1114, 0),
+ CatalogClothingItem(1115, 0),
+ CatalogClothingItem(1116, 0),
+ CatalogClothingItem(1117, 0),
+ CatalogClothingItem(1118, 0),
+ CatalogClothingItem(1119, 0),
+ CatalogClothingItem(1120, 0),
+ CatalogClothingItem(1121, 0),
+ CatalogClothingItem(1122, 0),
+ CatalogClothingItem(1123, 0),
+ CatalogClothingItem(1124, 0),
+ CatalogClothingItem(1125, 0),
+ CatalogClothingItem(1126, 0),
+ CatalogClothingItem(1127, 0),
+ CatalogAccessoryItem(171),
+ CatalogAccessoryItem(172),
+ CatalogAccessoryItem(224),
+ CatalogAccessoryItem(324),
+ CatalogAccessoryItem(325),
+ CatalogAccessoryItem(326),
+ CatalogAccessoryItem(327),
+ CatalogAccessoryItem(448),
+ CatalogAccessoryItem(449),
+ CatalogClothingItem(1801, 0))),
+ (2,
+ 1,
+ 2,
+ 28,
+ ((3, 2920),
+ (2, 2921),
+ CatalogClothingItem(1200, 0),
+ CatalogClothingItem(1201, 0),
+ CatalogClothingItem(1202, 0),
+ CatalogClothingItem(1203, 0),
+ CatalogClothingItem(1204, 0),
+ CatalogClothingItem(1205, 0),
+ CatalogWallpaperItem(12000),
+ CatalogWallpaperItem(12100),
+ CatalogWallpaperItem(12200),
+ CatalogWallpaperItem(12300),
+ CatalogWainscotingItem(1030, 0),
+ CatalogWainscotingItem(1030, 1),
+ CatalogMouldingItem(1060, 0),
+ CatalogMouldingItem(1060, 1),
+ CatalogClothingItem(1206, 0),
+ CatalogClothingItem(1207, 0),
+ CatalogClothingItem(1208, 0),
+ CatalogClothingItem(1209, 0),
+ CatalogClothingItem(1210, 0),
+ CatalogClothingItem(1211, 0),
+ CatalogClothingItem(1212, 0),
+ CatalogFurnitureItem(1670),
+ CatalogFurnitureItem(1680),
+ CatalogFurnitureItem(1450),
+ CatalogMouldingItem(1100, 0),
+ CatalogMouldingItem(1110, 0),
+ CatalogMouldingItem(1120, 0))),
+ (3,
+ 1,
+ 3,
+ 20,
+ ((3, 2930),
+ CatalogClothingItem(1300, 0),
+ CatalogClothingItem(1301, 0),
+ CatalogClothingItem(1302, 0),
+ CatalogClothingItem(1303, 0),
+ CatalogClothingItem(1304, 0),
+ CatalogClothingItem(1305, 0),
+ CatalogClothingItem(1306, 0),
+ CatalogWallpaperItem(13000),
+ CatalogWallpaperItem(13100),
+ CatalogWallpaperItem(13200),
+ CatalogWallpaperItem(13300),
+ CatalogFlooringItem(11000),
+ CatalogFlooringItem(11010))),
+ (5,
+ 25,
+ 6,
+ 25,
+ (CatalogClothingItem(1400, 0), CatalogClothingItem(1401, 0), CatalogClothingItem(1402, 0))),
+ (8,
+ 1,
+ 8,
+ 31,
+ (CatalogClothingItem(1403, 0),
+ CatalogClothingItem(1404, 0),
+ CatalogClothingItem(1405, 0),
+ CatalogClothingItem(1406, 0))),
+ (9,
+ 24,
+ 10,
+ 24,
+ (CatalogFurnitureItem(450),
+ CatalogAnimatedFurnitureItem(460),
+ CatalogAnimatedFurnitureItem(270),
+ CatalogAnimatedFurnitureItem(990))),
+ (6,
+ 15,
+ 8,
+ 15,
+ 2010,
+ 2010,
+ ((4, 2940),)),
+ (9,
+ 1,
+ 9,
+ 30,
+ (CatalogGardenItem(135, 1),)),
+ (1,
+ 1,
+ 1,
+ 31,
+ (CatalogGardenItem(135, 1),)),
+ (4,
+ 1,
+ 4,
+ 30,
+ (CatalogGardenItem(135, 1),)),
+ (6,
+ 1,
+ 6,
+ 30,
+ (CatalogGardenItem(135, 1),)),
+ (6,
+ 26,
+ 7,
+ 16,
+ (CatalogClothingItem(1500, 0),
+ CatalogClothingItem(1501, 0),
+ CatalogClothingItem(1502, 0),
+ CatalogClothingItem(1503, 0))),
+ (12,
+ 4,
+ 1,
+ 4,
+ ((3, 2910),)),
+ (12,
+ 4,
+ 1,
+ 4,
+ (CatalogFurnitureItem(680),
+ CatalogFurnitureItem(681),
+ CatalogGardenItem(130, 1),
+ CatalogGardenItem(131, 1),
+ CatalogAnimatedFurnitureItem(10020),
+ CatalogFurnitureItem(10030, 0))),
+ (12,
+ 4,
+ 1,
+ 4,
+ (CatalogWallpaperItem(11000),
+ CatalogWallpaperItem(11100),
+ CatalogFlooringItem(10010),
+ CatalogMouldingItem(1090, 0),
+ CatalogClothingItem(1100, 0),
+ CatalogClothingItem(1101, 0),
+ CatalogClothingItem(1104, 0),
+ CatalogClothingItem(1105, 0),
+ CatalogClothingItem(1108, 0),
+ CatalogClothingItem(1109, 0),
+ CatalogClothingItem(1802, 0))),
+ (12,
+ 11,
+ 1,
+ 4,
+ (CatalogFurnitureItem(1040),
+ CatalogFurnitureItem(1050),
+ CatalogWallpaperItem(11200),
+ CatalogFlooringItem(10000),
+ CatalogMouldingItem(1080, 0),
+ CatalogMouldingItem(1085, 0),
+ CatalogClothingItem(1102, 0),
+ CatalogClothingItem(1103, 0),
+ CatalogClothingItem(1106, 0),
+ CatalogClothingItem(1107, 0),
+ CatalogClothingItem(1110, 0),
+ CatalogClothingItem(1111, 0))),
+ (6,
+ 9,
+ 7,
+ 15,
+ 2010,
+ 2010,
+ (CatalogClothingItem(1751, 0),)),
+ (6,
+ 14,
+ 7,
+ 15,
+ 2010,
+ 2010,
+ (CatalogClothingItem(1754, 0), CatalogClothingItem(1755, 0), CatalogClothingItem(1756, 0))),
+ (7,
+ 21,
+ 8,
+ 17,
+ 2010,
+ 2010,
+ (CatalogClothingItem(1749, 0),
+ CatalogClothingItem(1750, 0),
+ CatalogClothingItem(1757, 0),
+ CatalogClothingItem(1758, 0))),
+ (8,
+ 25,
+ 9,
+ 21,
+ 2010,
+ 2010,
+ (CatalogClothingItem(1763, 0),)),
+ (6,
+ 5,
+ 7,
+ 1,
+ (CatalogClothingItem(1768, 0), CatalogClothingItem(1769, 0))),
+ (1,
+ 1,
+ 12,
+ 31,
+ (CatalogGardenItem(100, 1),
+ CatalogGardenItem(101, 1),
+ CatalogGardenItem(103, 1),
+ CatalogGardenItem(104, 1),
+ CatalogToonStatueItem(105, endPoseIndex=108),
+ CatalogRentalItem(1, 2880, 1000),
+ CatalogGardenStarterItem(),
+ CatalogNametagItem(100),
+ CatalogNametagItem(0),
+ CatalogClothingItem(1608, 0, 720),
+ CatalogClothingItem(1605, 0, 720),
+ CatalogClothingItem(1602, 0, 720),
+ CatalogClothingItem(1607, 0, 540),
+ CatalogClothingItem(1604, 0, 540),
+ CatalogClothingItem(1601, 0, 540),
+ CatalogClothingItem(1606, 0, 360),
+ CatalogClothingItem(1603, 0, 360),
+ CatalogClothingItem(1600, 0, 360),
+ CatalogEmoteItem(20, 90),
+ CatalogEmoteItem(21, 180),
+ CatalogEmoteItem(22, 360),
+ CatalogEmoteItem(23, 540),
+ CatalogEmoteItem(24, 720))),
+ (5,
+ 26,
+ 6,
+ 30,
+ 2013,
+ 2013,
+ (CatalogAccessoryItem(175),)),
+ (8,
+ 27,
+ 9,
+ 5,
+ 2013,
+ 2013,
+ ((3, 2900),
+ CatalogChatItem(10003),
+ CatalogClothingItem(1001, 0),
+ CatalogClothingItem(1002, 0),
+ CatalogWallpaperItem(10100),
+ CatalogWallpaperItem(10200),
+ CatalogFurnitureItem(10000),
+ CatalogFurnitureItem(10010),
+ CatalogNametagItem(9),
+ CatalogClothingItem(1744, 0),
+ CatalogClothingItem(1745, 0),
+ CatalogClothingItem(1748, 0),
+ CatalogClothingItem(1771, 0),
+ CatalogClothingItem(1774, 0),
+ CatalogClothingItem(1775, 0),
+ CatalogClothingItem(1743, 0),
+ CatalogClothingItem(1746, 0),
+ CatalogClothingItem(1747, 0),
+ CatalogClothingItem(1112, 0),
+ CatalogClothingItem(1113, 0),
+ CatalogClothingItem(1114, 0),
+ CatalogClothingItem(1115, 0),
+ CatalogClothingItem(1116, 0),
+ CatalogClothingItem(1117, 0),
+ CatalogClothingItem(1118, 0),
+ CatalogClothingItem(1119, 0),
+ CatalogClothingItem(1120, 0),
+ CatalogClothingItem(1121, 0),
+ CatalogClothingItem(1122, 0),
+ CatalogClothingItem(1123, 0),
+ CatalogClothingItem(1124, 0),
+ CatalogClothingItem(1125, 0),
+ CatalogClothingItem(1126, 0),
+ CatalogClothingItem(1127, 0),
+ CatalogAccessoryItem(171),
+ CatalogAccessoryItem(172),
+ CatalogAccessoryItem(224),
+ CatalogAccessoryItem(324),
+ CatalogAccessoryItem(325),
+ CatalogAccessoryItem(326),
+ CatalogAccessoryItem(327),
+ CatalogAccessoryItem(448),
+ CatalogAccessoryItem(449),
+ CatalogClothingItem(1801, 0),
+ CatalogAccessoryItem(175))),
+ (9,
+ 3,
+ 9,
+ 12,
+ 2013,
+ 2013,
+ ((3, 2910),
+ CatalogFurnitureItem(680),
+ CatalogFurnitureItem(681),
+ CatalogGardenItem(130, 1),
+ CatalogGardenItem(131, 1),
+ CatalogAnimatedFurnitureItem(10020),
+ CatalogFurnitureItem(10030, 0),
+ CatalogWallpaperItem(11000),
+ CatalogWallpaperItem(11100),
+ CatalogFlooringItem(10010),
+ CatalogMouldingItem(1090, 0),
+ CatalogClothingItem(1100, 0),
+ CatalogClothingItem(1101, 0),
+ CatalogClothingItem(1104, 0),
+ CatalogClothingItem(1105, 0),
+ CatalogClothingItem(1108, 0),
+ CatalogClothingItem(1109, 0),
+ CatalogClothingItem(1802, 0),
+ CatalogFurnitureItem(1040),
+ CatalogFurnitureItem(1050),
+ CatalogWallpaperItem(11200),
+ CatalogFlooringItem(10000),
+ CatalogMouldingItem(1080, 0),
+ CatalogMouldingItem(1085, 0),
+ CatalogClothingItem(1102, 0),
+ CatalogClothingItem(1103, 0),
+ CatalogClothingItem(1106, 0),
+ CatalogClothingItem(1107, 0),
+ CatalogClothingItem(1110, 0),
+ CatalogClothingItem(1111, 0))),
+ (8,
+ 20,
+ 9,
+ 19,
+ 2013,
+ 2013,
+ (CatalogAccessoryItem(101),
+ CatalogAccessoryItem(103),
+ CatalogAccessoryItem(117),
+ CatalogAccessoryItem(118),
+ CatalogAccessoryItem(123),
+ CatalogAccessoryItem(124),
+ CatalogAccessoryItem(125),
+ CatalogAccessoryItem(126),
+ CatalogAccessoryItem(127),
+ CatalogAccessoryItem(128),
+ CatalogAccessoryItem(129),
+ CatalogAccessoryItem(130),
+ CatalogAccessoryItem(202),
+ CatalogAccessoryItem(204),
+ CatalogAccessoryItem(205),
+ CatalogAccessoryItem(206),
+ CatalogAccessoryItem(208),
+ CatalogAccessoryItem(209),
+ CatalogAccessoryItem(210),
+ CatalogAccessoryItem(302),
+ CatalogAccessoryItem(308),
+ CatalogAccessoryItem(309),
+ CatalogAccessoryItem(310),
+ CatalogAccessoryItem(317),
+ CatalogAccessoryItem(402),
+ CatalogAccessoryItem(403),
+ CatalogAccessoryItem(405),
+ CatalogAccessoryItem(406),
+ CatalogAccessoryItem(407),
+ CatalogAccessoryItem(408),
+ CatalogAccessoryItem(409),
+ CatalogAccessoryItem(410),
+ CatalogAccessoryItem(411),
+ CatalogAccessoryItem(412),
+ CatalogAccessoryItem(413),
+ CatalogAccessoryItem(306),
+ CatalogAccessoryItem(318),
+ CatalogAccessoryItem(121),
+ CatalogAccessoryItem(212),
+ CatalogAccessoryItem(214),
+ CatalogAccessoryItem(312),
+ CatalogAccessoryItem(150),
+ CatalogAccessoryItem(151),
+ CatalogAccessoryItem(147),
+ CatalogAccessoryItem(422),
+ CatalogAccessoryItem(141),
+ CatalogAccessoryItem(146),
+ CatalogAccessoryItem(444),
+ CatalogAccessoryItem(122),
+ CatalogAccessoryItem(430),
+ CatalogAccessoryItem(145),
+ CatalogAccessoryItem(132),
+ CatalogAccessoryItem(161),
+ CatalogAccessoryItem(134),
+ CatalogAccessoryItem(149),
+ CatalogAccessoryItem(207),
+ CatalogAccessoryItem(215),
+ CatalogAccessoryItem(216),
+ CatalogAccessoryItem(417),
+ CatalogAccessoryItem(222),
+ CatalogAccessoryItem(321),
+ CatalogAccessoryItem(322),
+ CatalogAccessoryItem(307),
+ CatalogAccessoryItem(135),
+ CatalogAccessoryItem(174),
+ CatalogAccessoryItem(434),
+ CatalogAccessoryItem(435),
+ CatalogAccessoryItem(441),
+ CatalogAccessoryItem(446),
+ CatalogAccessoryItem(429),
+ CatalogAccessoryItem(110),
+ CatalogAccessoryItem(148),
+ CatalogAccessoryItem(443),
+ CatalogAccessoryItem(426),
+ CatalogAccessoryItem(439),
+ CatalogAccessoryItem(143),
+ CatalogAccessoryItem(313),
+ CatalogAccessoryItem(311),
+ CatalogAccessoryItem(437),
+ CatalogAccessoryItem(415),
+ CatalogAccessoryItem(167),
+ CatalogAccessoryItem(157),
+ CatalogAccessoryItem(106),
+ CatalogAccessoryItem(109),
+ CatalogAccessoryItem(421),
+ CatalogAccessoryItem(401),
+ CatalogAccessoryItem(447),
+ CatalogAccessoryItem(213),
+ CatalogAccessoryItem(330),
+ CatalogAccessoryItem(440),
+ CatalogAccessoryItem(425),
+ CatalogAccessoryItem(158),
+ CatalogAccessoryItem(431),
+ CatalogAccessoryItem(420),
+ CatalogAccessoryItem(155),
+ CatalogAccessoryItem(419),
+ CatalogAccessoryItem(436),
+ CatalogAccessoryItem(428),
+ CatalogAccessoryItem(304),
+ CatalogAccessoryItem(301),
+ CatalogAccessoryItem(416),
+ CatalogAccessoryItem(414),
+ CatalogAccessoryItem(164),
+ CatalogAccessoryItem(323),
+ CatalogAccessoryItem(108),
+ CatalogAccessoryItem(139),
+ CatalogAccessoryItem(316),
+ CatalogAccessoryItem(131),
+ CatalogAccessoryItem(170),
+ CatalogAccessoryItem(221),
+ CatalogAccessoryItem(225),
+ CatalogAccessoryItem(305),
+ CatalogAccessoryItem(303),
+ CatalogAccessoryItem(144),
+ CatalogAccessoryItem(120),
+ CatalogAccessoryItem(116),
+ CatalogAccessoryItem(217),
+ CatalogAccessoryItem(218),
+ CatalogAccessoryItem(219),
+ CatalogAccessoryItem(445),
+ CatalogAccessoryItem(418),
+ CatalogAccessoryItem(432),
+ CatalogAccessoryItem(427),
+ CatalogAccessoryItem(423),
+ CatalogAccessoryItem(137),
+ CatalogAccessoryItem(163),
+ CatalogAccessoryItem(165),
+ CatalogAccessoryItem(153),
+ CatalogAccessoryItem(319),
+ CatalogAccessoryItem(154),
+ CatalogAccessoryItem(159),
+ CatalogAccessoryItem(162),
+ CatalogAccessoryItem(315),
+ CatalogAccessoryItem(160),
+ CatalogAccessoryItem(102),
+ CatalogAccessoryItem(119),
+ CatalogAccessoryItem(136),
+ CatalogAccessoryItem(169),
+ CatalogAccessoryItem(140),
+ CatalogAccessoryItem(168),
+ CatalogAccessoryItem(138),
+ CatalogAccessoryItem(220),
+ CatalogAccessoryItem(433),
+ CatalogAccessoryItem(442),
+ CatalogAccessoryItem(424),
+ CatalogAccessoryItem(404),
+ CatalogAccessoryItem(156),
+ CatalogAccessoryItem(142),
+ CatalogAccessoryItem(152),
+ CatalogAccessoryItem(133),
+ CatalogAccessoryItem(166),
+ CatalogAccessoryItem(211),
+ CatalogAccessoryItem(314),
+ CatalogAccessoryItem(320),
+ CatalogAccessoryItem(173),
+ CatalogAccessoryItem(328),
+ CatalogAccessoryItem(329))))
+WeeklySchedule = ((100,
+ (5, 2000),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogEmoteItem(5),
+ CatalogFurnitureItem(210, 0),
+ CatalogFurnitureItem(220, 0)),
+ (100,
+ (5, 2000),
+ CatalogFurnitureItem(1400),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogFurnitureItem(600),
+ CatalogFurnitureItem(610),
+ CatalogClothingItem(116, 0),
+ CatalogClothingItem(216, 0)),
+ (300,
+ (5, 2000),
+ CatalogFurnitureItem(1410),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogFurnitureItem(1100),
+ CatalogFurnitureItem(1020),
+ CatalogClothingItem(408, 0),
+ 5000),
+ (100,
+ (5, 2000),
+ CatalogWindowItem(40),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogFurnitureItem(110),
+ CatalogFurnitureItem(100),
+ nextAvailablePole,
+ nextAvailableCloset),
+ (100,
+ (5, 2000),
+ CatalogFurnitureItem(1420),
+ CatalogEmoteItem(9),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogFurnitureItem(700),
+ CatalogFurnitureItem(710)),
+ (300,
+ (5, 2000),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogFurnitureItem(410),
+ CatalogAnimatedFurnitureItem(490),
+ CatalogFurnitureItem(1000),
+ CatalogClothingItem(117, 0),
+ CatalogClothingItem(217, 0),
+ nextAvailableCloset),
+ (100,
+ (5, 2000),
+ CatalogFurnitureItem(1430),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogFurnitureItem(1510),
+ CatalogFurnitureItem(1610),
+ 5000,
+ CatalogNametagItem(1)),
+ (100,
+ (5, 2000),
+ CatalogWindowItem(70),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogFurnitureItem(1210),
+ CatalogClothingItem(409, 0),
+ nextAvailablePole,
+ nextAvailableCloset),
+ (300,
+ (5, 2000),
+ CatalogEmoteItem(13),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogFurnitureItem(1200),
+ CatalogFurnitureItem(900)),
+ (100,
+ (5, 2000),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogFurnitureItem(910),
+ CatalogFurnitureItem(1600),
+ CatalogClothingItem(118, 0),
+ CatalogClothingItem(218, 0),
+ nextAvailableCloset),
+ (100,
+ (5, 2000),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogFurnitureItem(800),
+ CatalogFurnitureItem(1010),
+ CatalogClothingItem(410, 0),
+ 5000),
+ (300,
+ (5, 2000),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogFurnitureItem(620),
+ nextAvailablePole,
+ nextAvailableCloset),
+ (300,
+ (5, 2000),
+ 3000,
+ 3500,
+ 4000,
+ 4500,
+ CatalogClothingItem(119, 0),
+ CatalogClothingItem(219, 0)),
+ (100,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(1110),
+ CatalogFurnitureItem(630),
+ CatalogFurnitureItem(1630),
+ CatalogEmoteItem(11),
+ CatalogNametagItem(11),
+ nextAvailableCloset),
+ (100,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(230),
+ CatalogFurnitureItem(920),
+ CatalogFurnitureItem(1440)),
+ (300,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(420),
+ CatalogAnimatedFurnitureItem(480),
+ CatalogFurnitureItem(120),
+ CatalogClothingItem(120, 0),
+ CatalogClothingItem(220, 0),
+ nextAvailablePole,
+ 5000,
+ nextAvailableCloset),
+ (100,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(1700),
+ CatalogFurnitureItem(640),
+ CatalogWindowItem(50)),
+ (100,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(1120),
+ CatalogFurnitureItem(930),
+ CatalogFurnitureItem(1500),
+ CatalogEmoteItem(6),
+ nextAvailableCloset),
+ (300,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(430),
+ CatalogAnimatedFurnitureItem(491),
+ CatalogFurnitureItem(1620),
+ CatalogFurnitureItem(1442)),
+ (100,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(610),
+ CatalogFurnitureItem(940),
+ CatalogClothingItem(121, 0),
+ CatalogClothingItem(221, 0),
+ nextAvailablePole,
+ 5000),
+ (100,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(1710),
+ CatalogFurnitureItem(1030),
+ CatalogWindowItem(60),
+ CatalogNametagItem(7)),
+ (300,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(1130),
+ CatalogFurnitureItem(130),
+ CatalogEmoteItem(8)),
+ (100,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(1530),
+ CatalogFurnitureItem(1640),
+ CatalogFurnitureItem(1441)),
+ (100,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(300),
+ CatalogFurnitureItem(1220),
+ nextAvailablePole,
+ 5000),
+ (300,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(810),
+ CatalogFurnitureItem(1230),
+ CatalogFurnitureItem(1443)),
+ (300,
+ (2, 2000),
+ (3, 2010),
+ 3010,
+ 3510,
+ 4010,
+ 4510,
+ CatalogFurnitureItem(310),
+ CatalogFurnitureItem(1520),
+ CatalogFurnitureItem(1650),
+ CatalogWindowItem(80),
+ CatalogClothingItem(222, 0)),
+ (100,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(1240),
+ CatalogFurnitureItem(1661),
+ CatalogEmoteItem(5)),
+ (100,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(1800),
+ CatalogFurnitureItem(240),
+ CatalogFurnitureItem(1200),
+ CatalogNametagItem(12)),
+ (300,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(145),
+ CatalogClothingItem(123, 0),
+ CatalogClothingItem(224, 0),
+ nextAvailablePole,
+ 5000),
+ (100,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogWindowItem(100),
+ CatalogFurnitureItem(1810)),
+ (100,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(650),
+ CatalogFurnitureItem(1900)),
+ (300,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(1725)),
+ (100,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogWindowItem(90),
+ CatalogClothingItem(124, 0),
+ CatalogClothingItem(411, 0),
+ nextAvailablePole),
+ (100,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(140),
+ CatalogFurnitureItem(1020),
+ CatalogEmoteItem(13)),
+ (300,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(950),
+ CatalogFurnitureItem(1660),
+ CatalogClothingItem(310, 0),
+ CatalogNametagItem(2)),
+ (100,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(400),
+ CatalogAnimatedFurnitureItem(470),
+ CatalogFurnitureItem(660),
+ CatalogFurnitureItem(1200),
+ 5000),
+ (100,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(1910),
+ nextAvailablePole,
+ CatalogFurnitureItem(1000)),
+ (300,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(1720),
+ CatalogEmoteItem(9)),
+ (300,
+ (1, 2000),
+ (2, 2010),
+ (3, 2020),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogWindowItem(110),
+ CatalogClothingItem(311, 0)),
+ (100,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogWindowItem(120),
+ CatalogClothingItem(125, 0),
+ 5000),
+ (300,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogClothingItem(412, 0),
+ CatalogClothingItem(312, 0),
+ CatalogFurnitureItem(1920)),
+ (100,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ nextAvailablePole,
+ CatalogWallpaperItem(3900),
+ CatalogFurnitureItem(980),
+ CatalogNametagItem(13)),
+ (300,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogClothingItem(130, 0),
+ CatalogFurnitureItem(150)),
+ (100,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogClothingItem(128, 0),
+ CatalogWallpaperItem(3700),
+ CatalogFurnitureItem(160)),
+ (300,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogClothingItem(313, 0),
+ CatalogClothingItem(413, 0),
+ CatalogFurnitureItem(960),
+ CatalogEmoteItem(7)),
+ (100,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ nextAvailablePole,
+ CatalogFurnitureItem(1930),
+ CatalogFurnitureItem(670)),
+ (300,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogClothingItem(126, 0),
+ CatalogFurnitureItem(1970),
+ 5000),
+ (100,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(720),
+ CatalogFurnitureItem(970)),
+ (300,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogClothingItem(127, 0),
+ CatalogFurnitureItem(1950),
+ CatalogNametagItem(4)),
+ (100,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ nextAvailablePole,
+ CatalogFurnitureItem(1940),
+ CatalogWindowItem(130)),
+ (300,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogWallpaperItem(3800),
+ CatalogClothingItem(129, 0),
+ CatalogEmoteItem(10)),
+ (100,
+ (1, 2010),
+ (2, 2020),
+ (3, 2030),
+ 3020,
+ 3530,
+ 4020,
+ 4520,
+ CatalogFurnitureItem(250),
+ CatalogFurnitureItem(1960),
+ nextAvailablePole),
+ Sale(CatalogFurnitureItem(210, 0), CatalogFurnitureItem(220, 0), CatalogFurnitureItem(1100), CatalogFurnitureItem(110), CatalogFurnitureItem(100), CatalogFurnitureItem(700), CatalogFurnitureItem(710), CatalogFurnitureItem(410), CatalogAnimatedFurnitureItem(490), CatalogFurnitureItem(1210), CatalogFurnitureItem(1200), CatalogFurnitureItem(800), CatalogFurnitureItem(1110), CatalogFurnitureItem(230), CatalogFurnitureItem(420), CatalogAnimatedFurnitureItem(480), CatalogFurnitureItem(120), CatalogFurnitureItem(1700), CatalogFurnitureItem(1120), CatalogFurnitureItem(430), CatalogAnimatedFurnitureItem(491), CatalogFurnitureItem(1130), CatalogFurnitureItem(130), CatalogFurnitureItem(300), CatalogFurnitureItem(1220), CatalogFurnitureItem(810), CatalogFurnitureItem(1230), CatalogFurnitureItem(310), CatalogFurnitureItem(1240), CatalogFurnitureItem(240), CatalogFurnitureItem(145), CatalogFurnitureItem(1725), CatalogFurnitureItem(140), CatalogFurnitureItem(950), CatalogFurnitureItem(1720)),
+ Sale(CatalogClothingItem(116, 0), CatalogClothingItem(216, 0), CatalogClothingItem(408, 0), CatalogClothingItem(117, 0), CatalogClothingItem(217, 0), CatalogClothingItem(409, 0), CatalogClothingItem(118, 0), CatalogClothingItem(218, 0), CatalogClothingItem(410, 0), CatalogClothingItem(119, 0), CatalogClothingItem(219, 0), CatalogClothingItem(120, 0), CatalogClothingItem(220, 0), CatalogClothingItem(121, 0), CatalogClothingItem(221, 0), CatalogClothingItem(222, 0), CatalogClothingItem(123, 0), CatalogClothingItem(224, 0), CatalogClothingItem(411, 0), CatalogClothingItem(311, 0), CatalogClothingItem(310, 0)),
+ Sale(CatalogWindowItem(40), CatalogWindowItem(70), CatalogWindowItem(50), CatalogWindowItem(60), CatalogWindowItem(80), CatalogWindowItem(100), CatalogWindowItem(90), CatalogWindowItem(110)),
+ Sale(CatalogEmoteItem(5), CatalogEmoteItem(9), CatalogEmoteItem(13), CatalogEmoteItem(11), CatalogEmoteItem(6), CatalogEmoteItem(8), CatalogNametagItem(10)),
+ Sale(CatalogFurnitureItem(600), CatalogFurnitureItem(610), CatalogFurnitureItem(620), CatalogFurnitureItem(630), CatalogFurnitureItem(640), CatalogFurnitureItem(650), CatalogFurnitureItem(660), CatalogFurnitureItem(900), CatalogFurnitureItem(910), CatalogFurnitureItem(920), CatalogFurnitureItem(930), CatalogFurnitureItem(940), CatalogFurnitureItem(1000), CatalogFurnitureItem(1010), CatalogFurnitureItem(1020), CatalogFurnitureItem(1030), CatalogFurnitureItem(1400), CatalogFurnitureItem(1410), CatalogFurnitureItem(1420), CatalogFurnitureItem(1430), CatalogFurnitureItem(1440), CatalogFurnitureItem(1441), CatalogFurnitureItem(1442), CatalogFurnitureItem(1443), CatalogFurnitureItem(1500), CatalogFurnitureItem(1510), CatalogFurnitureItem(1520), CatalogFurnitureItem(1530), CatalogFurnitureItem(1600), CatalogFurnitureItem(1610), CatalogFurnitureItem(1620), CatalogFurnitureItem(1630), CatalogFurnitureItem(1640), CatalogFurnitureItem(1650), CatalogFurnitureItem(1660), CatalogFurnitureItem(1661), CatalogFurnitureItem(1710), CatalogFurnitureItem(1800), CatalogFurnitureItem(1810), CatalogFurnitureItem(1900), CatalogFurnitureItem(1910)),
+ (300,
+ (1, 2020),
+ (2, 2030),
+ (3, 2040),
+ CatalogFurnitureItem(730),
+ nextAvailablePole),
+ (100,
+ (1, 2020),
+ (2, 2030),
+ (3, 2040),
+ CatalogFurnitureItem(260)),
+ (300,
+ (1, 2020),
+ (2, 2030),
+ (3, 2040),
+ CatalogFurnitureItem(440),
+ CatalogAnimatedFurnitureItem(492),
+ 5000),
+ (100,
+ (1, 2020),
+ (2, 2030),
+ (3, 2040),
+ CatalogFurnitureItem(170),
+ CatalogFurnitureItem(1250)),
+ (300,
+ (1, 2020),
+ (2, 2030),
+ (3, 2040),
+ CatalogFurnitureItem(1140),
+ nextAvailablePole),
+ (100,
+ (1, 2020),
+ (2, 2030),
+ (3, 2040),
+ CatalogFurnitureItem(2010),
+ CatalogNametagItem(8)),
+ (300,
+ (1, 2020),
+ (2, 2030),
+ (3, 2040),
+ CatalogFurnitureItem(2000),
+ 5000),
+ (100,
+ (1, 2020),
+ (2, 2030),
+ (3, 2040),
+ CatalogFurnitureItem(3000)),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ CatalogClothingItem(131, 0),
+ CatalogClothingItem(225, 0),
+ nextAvailablePole),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ CatalogFurnitureItem(105)),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ CatalogFurnitureItem(205)),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ CatalogFurnitureItem(625)),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ nextAvailablePole,
+ CatalogEmoteItem(12),
+ CatalogNametagItem(5)),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ CatalogClothingItem(314, 0),
+ CatalogClothingItem(414, 0)),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ CatalogFurnitureItem(715)),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ CatalogFurnitureItem(1015),
+ CatalogNametagItem(6)),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ CatalogFurnitureItem(1215),
+ nextAvailablePole),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ CatalogEmoteItem(14)),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ CatalogFurnitureItem(1260)),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ CatalogFurnitureItem(705),
+ CatalogNametagItem(3)),
+ (300,
+ (1, 2030),
+ (2, 2040),
+ (3, 2050),
+ nextAvailablePole))
+
+class CatalogGenerator:
+ notify = DirectNotifyGlobal.directNotify.newCategory('CatalogGenerator')
+
+ def __init__(self):
+ self.__itemLists = {}
+ self.__releasedItemLists = {}
+
+ def getReleasedCatalogList(self, weekStart):
+ dayNumber = int(weekStart / (24 * 60))
+ itemLists = self.__getReleasedItemLists(dayNumber, weekStart)
+ return itemLists
+
+ def generateMonthlyCatalog(self, avatar, weekStart):
+ dayNumber = int(weekStart / (24 * 60))
+ itemLists = self.__getMonthlyItemLists(dayNumber, weekStart)
+ monthlyCatalog = CatalogItemList.CatalogItemList()
+ for list in itemLists:
+ saleItem = 0
+ if isinstance(list, Sale):
+ list = list.args
+ saleItem = 1
+ for item in list:
+ monthlyCatalog += self.__selectItem(avatar, item, [], saleItem=saleItem)
+
+ return monthlyCatalog
+
+ def generateWeeklyCatalog(self, avatar, week, monthlyCatalog):
+ weeklyCatalog = CatalogItemList.CatalogItemList()
+ self.notify.debug('Generating catalog for %s for week %s.' % (avatar.doId, week))
+ if week >= 1 and week <= len(WeeklySchedule):
+ saleItem = 0
+ schedule = WeeklySchedule[week - 1]
+ if isinstance(schedule, Sale):
+ schedule = schedule.args
+ saleItem = 1
+ for item in schedule:
+ weeklyCatalog += self.__selectItem(avatar, item, monthlyCatalog, saleItem=saleItem)
+
+ if nextAvailableCloset not in schedule:
+ weeklyCatalog += self.__selectItem(avatar, nextAvailableCloset, monthlyCatalog, saleItem=0)
+ weeklyCatalog += self.__selectItem(avatar, get50ItemTrunk, monthlyCatalog, saleItem=0)
+ if time.time() < 1096617600.0:
+
+ def hasPetTrick(catalog):
+ for item in catalog:
+ if isinstance(item, CatalogPetTrickItem):
+ return 1
+
+ return 0
+
+ if not hasPetTrick(weeklyCatalog) and not hasPetTrick(avatar.weeklyCatalog) and not hasPetTrick(avatar.backCatalog):
+ self.notify.debug('Artificially adding pet trick to catalog')
+ weeklyCatalog += self.__selectItem(avatar, 5000, monthlyCatalog, saleItem=saleItem)
+ self.notify.debug('Generated catalog: %s' % weeklyCatalog)
+ return weeklyCatalog
+
+ def generateBackCatalog(self, avatar, week, previousWeek, weeklyCatalog):
+ backCatalog = CatalogItemList.CatalogItemList()
+ lastBackCatalog = avatar.backCatalog[:]
+ thisWeek = min(len(WeeklySchedule), week - 1)
+ lastWeek = min(len(WeeklySchedule), previousWeek)
+ for week in range(thisWeek, lastWeek, -1):
+ self.notify.debug('Adding items from week %s to back catalog' % week)
+ schedule = WeeklySchedule[week - 1]
+ if not isinstance(schedule, Sale):
+ for item in schedule:
+ for item in self.__selectItem(avatar, item, weeklyCatalog + backCatalog):
+ item.putInBackCatalog(backCatalog, lastBackCatalog)
+
+ if previousWeek < week:
+ self.notify.debug('Adding current items from week %s to back catalog' % previousWeek)
+ for item in avatar.weeklyCatalog:
+ item.putInBackCatalog(backCatalog, lastBackCatalog)
+
+ backCatalog += lastBackCatalog
+ for item in weeklyCatalog:
+ while item in backCatalog:
+ backCatalog.remove(item)
+
+ return backCatalog
+
+ def __getReleasedItemLists(self, dayNumber, weekStart):
+ itemLists = self.__releasedItemLists.get(dayNumber)
+ if itemLists != None:
+ return itemLists
+ else:
+ self.__releasedItemLists.clear()
+ testDaysAhead = simbase.config.GetInt('test-server-holiday-days-ahead', 0)
+ nowtuple = time.localtime(weekStart * 60 + testDaysAhead * 24 * 60 * 60)
+ year = nowtuple[0]
+ month = nowtuple[1]
+ day = nowtuple[2]
+ itemLists = []
+ for monthlyItems in MonthlySchedule:
+ startMM = monthlyItems[0]
+ startDD = monthlyItems[1]
+ endMM = monthlyItems[2]
+ endDD = monthlyItems[3]
+ if len(monthlyItems) == 7:
+ startYYYY = monthlyItems[4]
+ endYYYY = monthlyItems[5]
+ list = monthlyItems[6]
+ else:
+ startYYYY = 1969
+ endYYYY = year
+ list = monthlyItems[4]
+ pastStart = year > startYYYY or (year == startYYYY and (month > startMM or (month == startMM and day >= startDD)))
+ if pastStart:
+ itemLists.append(list)
+
+ self.__releasedItemLists[dayNumber] = itemLists
+ return itemLists
+
+ def __getMonthlyItemLists(self, dayNumber, weekStart):
+ itemLists = self.__itemLists.get(dayNumber)
+ if itemLists != None:
+ return itemLists
+ testDaysAhead = simbase.config.GetInt('test-server-holiday-days-ahead', 0)
+ nowtuple = time.localtime(weekStart * 60 + testDaysAhead * 24 * 60 * 60)
+ year = nowtuple[0]
+ month = nowtuple[1]
+ day = nowtuple[2]
+ self.notify.debug('Generating seasonal itemLists for %s/%s.' % (month, day))
+ itemLists = []
+ for monthlyItems in MonthlySchedule:
+ startMM = monthlyItems[0]
+ startDD = monthlyItems[1]
+ endMM = monthlyItems[2]
+ endDD = monthlyItems[3]
+ if len(monthlyItems) == 7:
+ startYYYY = monthlyItems[4]
+ endYYYY = monthlyItems[5]
+ list = monthlyItems[6]
+ else:
+ startYYYY = 1969
+ endYYYY = year
+ list = monthlyItems[4]
+ pastStart = year >= startYYYY and (month > startMM or (month == startMM and day >= startDD))
+ beforeEnd = year <= endYYYY and (month < endMM or (month == endMM and day <= endDD))
+ if endMM < startMM:
+ if pastStart or beforeEnd:
+ itemLists.append(list)
+ elif pastStart and beforeEnd:
+ itemLists.append(list)
+
+ self.__itemLists[dayNumber] = itemLists
+ return itemLists
+
+ def __selectItem(self, avatar, item, duplicateItems, saleItem = 0):
+ chooseCount = 1
+ if isinstance(item, Sale):
+ item = item.args[0]
+ saleItem = 1
+ if callable(item):
+ item = item(avatar, duplicateItems)
+ if isinstance(item, types.TupleType):
+ chooseCount, item = item
+ if isinstance(item, types.IntType):
+ item = MetaItems[item]
+ selection = []
+ if isinstance(item, CatalogItem.CatalogItem):
+ if not item.notOfferedTo(avatar):
+ item.saleItem = saleItem
+ selection.append(item)
+ elif item != None:
+ list = item[:]
+ for i in range(chooseCount):
+ if len(list) == 0:
+ return selection
+ item = self.__chooseFromList(avatar, list, duplicateItems)
+ if item != None:
+ item.saleItem = saleItem
+ selection.append(item)
+
+ return selection
+
+ def __chooseFromList(self, avatar, list, duplicateItems):
+ index = random.randrange(len(list))
+ item = list[index]
+ del list[index]
+ while item.notOfferedTo(avatar) or item.reachedPurchaseLimit(avatar) or item in duplicateItems or item in avatar.backCatalog or item in avatar.weeklyCatalog:
+ if len(list) == 0:
+ return None
+ index = random.randrange(len(list))
+ item = list[index]
+ del list[index]
+
+ return item
+
+ def outputSchedule(self, filename):
+ out = open(Filename(filename).toOsSpecific(), 'w')
+ sched = self.generateScheduleDictionary()
+ items = sched.keys()
+ items.sort()
+ for item in items:
+ weeklist, maybeWeeklist = sched[item]
+ color = self.__formatColor(item.getColor())
+ seriesDict = {}
+ self.__determineSeries(seriesDict, weeklist)
+ self.__determineSeries(seriesDict, maybeWeeklist)
+ seriesList = seriesDict.keys()
+ seriesList.sort()
+ series = str(seriesList)[1:-1]
+ week = self.__formatWeeklist(weeklist)
+ maybeWeek = self.__formatWeeklist(maybeWeeklist)
+ line = '"%s"\t"%s"\t"%s"\t%s\t"%s"\t"%s"\t"%s"\t"%s"\t"%s"' % (item.output(store=0),
+ item.getTypeName(),
+ item.getDisplayName(),
+ item.getBasePrice(),
+ item.getFilename(),
+ color,
+ series,
+ week,
+ maybeWeek)
+ out.write(line + '\n')
+
+ out.close()
+
+ def __formatColor(self, color):
+ if color == None:
+ return ''
+ else:
+ return '(%0.2f, %0.2f, %0.2f)' % (color[0], color[1], color[2])
+ return
+
+ def __determineSeries(self, seriesDict, weeklist):
+ for week in weeklist:
+ if isinstance(week, types.IntType):
+ series = (week - 1) / ToontownGlobals.CatalogNumWeeksPerSeries + 1
+ seriesDict[series] = None
+
+ return
+
+ def __formatWeeklist(self, weeklist):
+ str = ''
+ for week in weeklist:
+ str += ', %s' % week
+
+ return str[2:]
+
+ def generateScheduleDictionary(self):
+ sched = {}
+ for index in range(len(WeeklySchedule)):
+ week = index + 1
+ schedule = WeeklySchedule[index]
+ if isinstance(schedule, Sale):
+ schedule = schedule.args
+ self.__recordSchedule(sched, week, schedule)
+
+ for monthlyItems in MonthlySchedule:
+ startMM = monthlyItems[0]
+ startDD = monthlyItems[1]
+ endMM = monthlyItems[2]
+ endDD = monthlyItems[3]
+ if len(monthlyItems) == 7:
+ list = monthlyItems[6]
+ else:
+ list = monthlyItems[4]
+ string = '%02d/%02d - %02d/%02d' % (startMM,
+ startDD,
+ endMM,
+ endDD)
+ self.__recordSchedule(sched, string, list)
+
+ return sched
+
+ def __recordSchedule(self, sched, weekCode, schedule):
+ if isinstance(schedule, Sale):
+ schedule = schedule.args
+ for item in schedule:
+ if callable(item):
+ if item == nextAvailablePole:
+ item = getAllPoles()
+ elif item == nextAvailableCloset:
+ item = getAllClosets()
+ elif item == get50ItemCloset:
+ item = getMaxClosets()
+ elif item == get50ItemTrunk:
+ item = getMaxTrunks()
+ else:
+ self.notify.warning("Don't know how to interpret function " % repr(name))
+ item = None
+ elif isinstance(item, types.TupleType):
+ item = item[1]
+ if isinstance(item, types.IntType):
+ item = MetaItems[item]
+ if isinstance(item, CatalogItem.CatalogItem):
+ self.__recordScheduleItem(sched, weekCode, None, item)
+ elif item != None:
+ for i in item:
+ self.__recordScheduleItem(sched, None, weekCode, i)
+
+ return
+
+ def __recordScheduleItem(self, sched, weekCode, maybeWeekCode, item):
+ if not sched.has_key(item):
+ sched[item] = [[], []]
+ if weekCode != None:
+ sched[item][0].append(weekCode)
+ if maybeWeekCode != None:
+ sched[item][1].append(maybeWeekCode)
+ return
diff --git a/toontown/catalog/CatalogInvalidItem.py b/toontown/catalog/CatalogInvalidItem.py
new file mode 100644
index 0000000..15ee9b4
--- /dev/null
+++ b/toontown/catalog/CatalogInvalidItem.py
@@ -0,0 +1,15 @@
+import CatalogItem
+from toontown.toonbase import TTLocalizer
+from direct.showbase import PythonUtil
+from toontown.toonbase import ToontownGlobals
+
+class CatalogInvalidItem(CatalogItem.CatalogItem):
+
+ def requestPurchase(self, phone, callback):
+ self.notify.error('Attempt to purchase invalid item.')
+
+ def acceptItem(self, mailbox, index, callback):
+ self.notify.error('Attempt to accept invalid item.')
+
+ def output(self, store = -1):
+ return 'CatalogInvalidItem()'
diff --git a/toontown/catalog/CatalogItem.py b/toontown/catalog/CatalogItem.py
new file mode 100644
index 0000000..1670961
--- /dev/null
+++ b/toontown/catalog/CatalogItem.py
@@ -0,0 +1,466 @@
+from direct.directnotify import DirectNotifyGlobal
+from pandac.PandaModules import *
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase import ToontownGlobals
+from direct.interval.IntervalGlobal import *
+from direct.distributed.PyDatagram import PyDatagram
+from direct.distributed.PyDatagramIterator import PyDatagramIterator
+import types
+import sys
+CatalogReverseType = None
+CatalogItemVersion = 8
+CatalogBackorderMarkup = 1.2
+CatalogSaleMarkdown = 0.75
+Customization = 1
+DeliveryDate = 2
+Location = 4
+WindowPlacement = 8
+GiftTag = 16
+CatalogTypeUnspecified = 0
+CatalogTypeWeekly = 1
+CatalogTypeBackorder = 2
+CatalogTypeMonthly = 3
+CatalogTypeLoyalty = 4
+
+class CatalogItem:
+ notify = DirectNotifyGlobal.directNotify.newCategory('CatalogItem')
+
+ def __init__(self, *args, **kw):
+ self.saleItem = 0
+ self.deliveryDate = None
+ self.posHpr = None
+ self.giftTag = None
+ self.giftCode = 0
+ self.hasPicture = False
+ self.volume = 0
+ self.specialEventId = 0
+ if len(args) >= 1 and isinstance(args[0], DatagramIterator):
+ self.decodeDatagram(*args, **kw)
+ else:
+ self.makeNewItem(*args, **kw)
+ return
+
+ def isAward(self):
+ result = self.specialEventId != 0
+ return result
+
+ def makeNewItem(self):
+ pass
+
+ def needsCustomize(self):
+ return 0
+
+ def saveHistory(self):
+ return 0
+
+ def getBackSticky(self):
+ itemType = 0
+ numSticky = 0
+ return (itemType, numSticky)
+
+ def putInBackCatalog(self, backCatalog, lastBackCatalog):
+ if self.saveHistory() and not self.isSaleItem():
+ if self not in backCatalog:
+ if self in lastBackCatalog:
+ lastBackCatalog.remove(self)
+ backCatalog.append(self)
+
+ def replacesExisting(self):
+ return 0
+
+ def hasExisting(self):
+ return 0
+
+ def getYourOldDesc(self):
+ return None
+
+ def storedInCloset(self):
+ return 0
+
+ def storedInTrunk(self):
+ return 0
+
+ def storedInAttic(self):
+ return 0
+
+ def notOfferedTo(self, avatar):
+ return 0
+
+ def getPurchaseLimit(self):
+ return 0
+
+ def reachedPurchaseLimit(self, avatar):
+ return 0
+
+ def hasBeenGifted(self, avatar):
+ if avatar.onGiftOrder.count(self) != 0:
+ return 1
+ return 0
+
+ def getTypeName(self):
+ return 'Unknown Type Item'
+
+ def getName(self):
+ return 'Unnamed Item'
+
+ def getDisplayName(self):
+ return self.getName()
+
+ def recordPurchase(self, avatar, optional):
+ self.notify.warning('%s has no purchase method.' % self)
+ return ToontownGlobals.P_NoPurchaseMethod
+
+ def isSaleItem(self):
+ return self.saleItem
+
+ def isGift(self):
+ if self.getEmblemPrices():
+ return 0
+ return 1
+
+ def isRental(self):
+ return 0
+
+ def forBoysOnly(self):
+ return 0
+
+ def forGirlsOnly(self):
+ return 0
+
+ def setLoyaltyRequirement(self, days):
+ self.loyaltyDays = days
+
+ def loyaltyRequirement(self):
+ if not hasattr(self, 'loyaltyDays'):
+ return 0
+ else:
+ return self.loyaltyDays
+
+ def getPrice(self, catalogType):
+ if catalogType == CatalogTypeBackorder:
+ return self.getBackPrice()
+ elif self.isSaleItem():
+ return self.getSalePrice()
+ else:
+ return self.getCurrentPrice()
+
+ def getCurrentPrice(self):
+ return int(self.getBasePrice())
+
+ def getBackPrice(self):
+ return int(self.getBasePrice() * CatalogBackorderMarkup)
+
+ def getSalePrice(self):
+ return int(self.getBasePrice() * CatalogSaleMarkdown)
+
+ def getDeliveryTime(self):
+ return 0
+
+ def getPicture(self, avatar):
+ self.hasPicture = True
+ return (None, None)
+
+ def cleanupPicture(self):
+ self.hasPicture = False
+
+ def requestPurchase(self, phone, callback, optional = -1):
+ phone.requestPurchase(self, callback, optional)
+
+ def requestGiftPurchase(self, phone, targetDoID, callback, optional = -1):
+ phone.requestGiftPurchase(self, targetDoID, callback, optional)
+
+ def requestPurchaseCleanup(self):
+ pass
+
+ def getRequestPurchaseErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ return TTLocalizer.CatalogPurchaseItemAvailable
+ elif retcode == ToontownGlobals.P_ItemOnOrder:
+ return TTLocalizer.CatalogPurchaseItemOnOrder
+ elif retcode == ToontownGlobals.P_MailboxFull:
+ return TTLocalizer.CatalogPurchaseMailboxFull
+ elif retcode == ToontownGlobals.P_OnOrderListFull:
+ return TTLocalizer.CatalogPurchaseOnOrderListFull
+ else:
+ return TTLocalizer.CatalogPurchaseGeneralError % retcode
+
+ def getRequestGiftPurchaseErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ return TTLocalizer.CatalogPurchaseGiftItemAvailable
+ elif retcode == ToontownGlobals.P_ItemOnOrder:
+ return TTLocalizer.CatalogPurchaseGiftItemOnOrder
+ elif retcode == ToontownGlobals.P_MailboxFull:
+ return TTLocalizer.CatalogPurchaseGiftMailboxFull
+ elif retcode == ToontownGlobals.P_OnOrderListFull:
+ return TTLocalizer.CatalogPurchaseGiftOnOrderListFull
+ elif retcode == ToontownGlobals.P_NotAGift:
+ return TTLocalizer.CatalogPurchaseGiftNotAGift
+ elif retcode == ToontownGlobals.P_WillNotFit:
+ return TTLocalizer.CatalogPurchaseGiftWillNotFit
+ elif retcode == ToontownGlobals.P_ReachedPurchaseLimit:
+ return TTLocalizer.CatalogPurchaseGiftLimitReached
+ elif retcode == ToontownGlobals.P_NotEnoughMoney:
+ return TTLocalizer.CatalogPurchaseGiftNotEnoughMoney
+ else:
+ return TTLocalizer.CatalogPurchaseGiftGeneralError % {'friend': '%s',
+ 'error': retcode}
+
+ def acceptItem(self, mailbox, index, callback):
+ mailbox.acceptItem(self, index, callback)
+
+ def discardItem(self, mailbox, index, callback):
+ print 'Item discardItem'
+ mailbox.discardItem(self, index, callback)
+
+ def acceptItemCleanup(self):
+ pass
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_NoRoomForItem:
+ return TTLocalizer.CatalogAcceptRoomError
+ elif retcode == ToontownGlobals.P_ReachedPurchaseLimit:
+ return TTLocalizer.CatalogAcceptLimitError
+ elif retcode == ToontownGlobals.P_WillNotFit:
+ return TTLocalizer.CatalogAcceptFitError
+ elif retcode == ToontownGlobals.P_InvalidIndex:
+ return TTLocalizer.CatalogAcceptInvalidError
+ else:
+ return TTLocalizer.CatalogAcceptGeneralError % retcode
+
+ def output(self, store = -1):
+ return 'CatalogItem'
+
+ def getFilename(self):
+ return ''
+
+ def getColor(self):
+ return None
+
+ def formatOptionalData(self, store = -1):
+ result = ''
+ if store & Location and self.posHpr != None:
+ result += ', posHpr = (%s, %s, %s, %s, %s, %s)' % self.posHpr
+ return result
+
+ def __str__(self):
+ return self.output()
+
+ def __repr__(self):
+ return self.output()
+
+ def compareTo(self, other):
+ return 0
+
+ def getHashContents(self):
+ return None
+
+ def __cmp__(self, other):
+ c = cmp(self.__class__, other.__class__)
+ if c != 0:
+ return c
+ return self.compareTo(other)
+
+ def __hash__(self):
+ return hash((self.__class__, self.getHashContents()))
+
+ def getBasePrice(self):
+ return 0
+
+ def getEmblemPrices(self):
+ return ()
+
+ def loadModel(self):
+ return None
+
+ def decodeDatagram(self, di, versionNumber, store):
+ if store & DeliveryDate:
+ self.deliveryDate = di.getUint32()
+ if store & Location:
+ x = di.getArg(STInt16, 10)
+ y = di.getArg(STInt16, 10)
+ z = di.getArg(STInt16, 100)
+ if versionNumber < 2:
+ h = di.getArg(STInt16, 10)
+ p = 0.0
+ r = 0.0
+ elif versionNumber < 5:
+ h = di.getArg(STInt8, 256.0 / 360.0)
+ p = di.getArg(STInt8, 256.0 / 360.0)
+ r = di.getArg(STInt8, 256.0 / 360.0)
+ hpr = oldToNewHpr(VBase3(h, p, r))
+ h = hpr[0]
+ p = hpr[1]
+ r = hpr[2]
+ else:
+ h = di.getArg(STInt8, 256.0 / 360.0)
+ p = di.getArg(STInt8, 256.0 / 360.0)
+ r = di.getArg(STInt8, 256.0 / 360.0)
+ self.posHpr = (x,
+ y,
+ z,
+ h,
+ p,
+ r)
+ if store & GiftTag:
+ self.giftTag = di.getString()
+ if versionNumber >= 8:
+ self.specialEventId = di.getUint8()
+ else:
+ self.specialEventId = 0
+
+ def encodeDatagram(self, dg, store):
+ if store & DeliveryDate:
+ dg.addUint32(self.deliveryDate)
+ if store & Location:
+ dg.putArg(self.posHpr[0], STInt16, 10)
+ dg.putArg(self.posHpr[1], STInt16, 10)
+ dg.putArg(self.posHpr[2], STInt16, 100)
+ dg.putArg(self.posHpr[3], STInt8, 256.0 / 360.0)
+ dg.putArg(self.posHpr[4], STInt8, 256.0 / 360.0)
+ dg.putArg(self.posHpr[5], STInt8, 256.0 / 360.0)
+ if store & GiftTag:
+ dg.addString(self.giftTag)
+ dg.addUint8(self.specialEventId)
+
+ def getTypeCode(self):
+ import CatalogItemTypes
+ return CatalogItemTypes.CatalogItemTypes[self.__class__]
+
+ def applyColor(self, model, colorDesc):
+ if model == None or colorDesc == None:
+ return
+ for partName, color in colorDesc:
+ matches = model.findAllMatches(partName)
+ if color == None:
+ matches.hide()
+ elif isinstance(color, types.StringType):
+ tex = loader.loadTexture(color)
+ tex.setMinfilter(Texture.FTLinearMipmapLinear)
+ tex.setMagfilter(Texture.FTLinear)
+ for i in range(matches.getNumPaths()):
+ matches.getPath(i).setTexture(tex, 1)
+
+ else:
+ needsAlpha = color[3] != 1
+ color = VBase4(color[0], color[1], color[2], color[3])
+ for i in range(matches.getNumPaths()):
+ matches.getPath(i).setColorScale(color, 1)
+ if needsAlpha:
+ matches.getPath(i).setTransparency(1)
+
+ return
+
+ def makeFrame(self):
+ from direct.gui.DirectGui import DirectFrame
+ frame = DirectFrame(parent=hidden, frameSize=(-1.0, 1.0, -1.0, 1.0), relief=None)
+ return frame
+
+ def makeFrameModel(self, model, spin = 1):
+ frame = self.makeFrame()
+ ival = None
+ if model:
+ model.setDepthTest(1)
+ model.setDepthWrite(1)
+ if spin:
+ pitch = frame.attachNewNode('pitch')
+ rotate = pitch.attachNewNode('rotate')
+ scale = rotate.attachNewNode('scale')
+ model.reparentTo(scale)
+ bMin, bMax = model.getTightBounds()
+ center = (bMin + bMax) / 2.0
+ model.setPos(-center[0], -center[1], -center[2])
+ pitch.setP(20)
+ bMin, bMax = pitch.getTightBounds()
+ center = (bMin + bMax) / 2.0
+ corner = Vec3(bMax - center)
+ scale.setScale(1.0 / max(corner[0], corner[1], corner[2]))
+ pitch.setY(2)
+ ival = LerpHprInterval(rotate, 10, VBase3(-270, 0, 0), startHpr=VBase3(90, 0, 0))
+ else:
+ scale = frame.attachNewNode('scale')
+ model.reparentTo(scale)
+ bMin, bMax = model.getTightBounds()
+ center = (bMin + bMax) / 2.0
+ model.setPos(-center[0], 2, -center[2])
+ corner = Vec3(bMax - center)
+ scale.setScale(1.0 / max(corner[0], corner[1], corner[2]))
+ return (frame, ival)
+
+ def getBlob(self, store = 0):
+ dg = PyDatagram()
+ dg.addUint8(CatalogItemVersion)
+ encodeCatalogItem(dg, self, store)
+ return dg.getMessage()
+
+ def getRequestPurchaseErrorTextTimeout(self):
+ return 6
+
+ def getDaysToGo(self, avatar):
+ accountDays = avatar.getAccountDays()
+ daysToGo = self.loyaltyRequirement() - accountDays
+ if daysToGo < 0:
+ daysToGo = 0
+ return int(daysToGo)
+
+
+def encodeCatalogItem(dg, item, store):
+ import CatalogItemTypes
+ flags = item.getTypeCode()
+ if item.isSaleItem():
+ flags |= CatalogItemTypes.CatalogItemSaleFlag
+ if item.giftTag != None:
+ flags |= CatalogItemTypes.CatalogItemGiftTag
+ dg.addUint8(flags)
+ if item.giftTag != None:
+ dg.addUint32(item.giftTag)
+ if not item.giftCode:
+ item.giftCode = 0
+ dg.addUint8(item.giftCode)
+ item.encodeDatagram(dg, store)
+ return
+
+
+def decodeCatalogItem(di, versionNumber, store):
+ global CatalogReverseType
+ import CatalogItemTypes
+ if CatalogReverseType == None:
+ CatalogReverseType = {}
+ for itemClass, index in CatalogItemTypes.CatalogItemTypes.items():
+ CatalogReverseType[index] = itemClass
+
+ startIndex = di.getCurrentIndex()
+ try:
+ flags = di.getUint8()
+ typeIndex = flags & CatalogItemTypes.CatalogItemTypeMask
+ gift = None
+ code = None
+ if flags & CatalogItemTypes.CatalogItemGiftTag:
+ gift = di.getUint32()
+ code = di.getUint8()
+ itemClass = CatalogReverseType[typeIndex]
+ item = itemClass(di, versionNumber, store=store)
+ except Exception, e:
+ CatalogItem.notify.warning('Invalid catalog item in stream: %s, %s' % (sys.exc_info()[0], e))
+ d = Datagram(di.getDatagram().getMessage()[startIndex:])
+ d.dumpHex(Notify.out())
+ import CatalogInvalidItem
+ return CatalogInvalidItem.CatalogInvalidItem()
+
+ if flags & CatalogItemTypes.CatalogItemSaleFlag:
+ item.saleItem = 1
+ item.giftTag = gift
+ item.giftCode = code
+ return item
+
+
+def getItem(blob, store = 0):
+ dg = PyDatagram(blob)
+ di = PyDatagramIterator(dg)
+ try:
+ versionNumber = di.getUint8()
+ return decodeCatalogItem(di, versionNumber, store)
+ except Exception, e:
+ CatalogItem.notify.warning('Invalid catalog item: %s, %s' % (sys.exc_info()[0], e))
+ dg.dumpHex(Notify.out())
+ import CatalogInvalidItem
+ return CatalogInvalidItem.CatalogInvalidItem()
diff --git a/toontown/catalog/CatalogItemList.py b/toontown/catalog/CatalogItemList.py
new file mode 100644
index 0000000..ae8f4eb
--- /dev/null
+++ b/toontown/catalog/CatalogItemList.py
@@ -0,0 +1,235 @@
+import CatalogItem
+from pandac.PandaModules import *
+import types
+from direct.distributed.PyDatagram import PyDatagram
+from direct.distributed.PyDatagramIterator import PyDatagramIterator
+
+class CatalogItemList:
+
+ def __init__(self, source = None, store = 0):
+ self.store = store
+ self.__blob = None
+ self.__list = None
+ if isinstance(source, types.StringType):
+ self.__blob = source
+ elif isinstance(source, types.ListType):
+ self.__list = source[:]
+ elif isinstance(source, CatalogItemList):
+ if source.store == store:
+ if source.__list != None:
+ self.__list = source.__list[:]
+ self.__blob = source.__blob
+ else:
+ self.__list = source[:]
+ return
+
+ def markDirty(self):
+ if self.__list:
+ self.__blob = None
+ return
+
+ def getBlob(self, store = None):
+ if store == None or store == self.store:
+ if self.__blob == None:
+ self.__encodeList()
+ return self.__blob
+ return self.__makeBlob(store)
+
+ def getNextDeliveryDate(self):
+ if len(self) == 0:
+ return
+ nextDeliveryDate = None
+ for item in self:
+ if item:
+ if nextDeliveryDate == None or item.deliveryDate < nextDeliveryDate:
+ nextDeliveryDate = item.deliveryDate
+
+ return nextDeliveryDate
+
+ def getNextDeliveryItem(self):
+ if len(self) == 0:
+ return
+ nextDeliveryDate = None
+ nextDeliveryItem = None
+ for item in self:
+ if item:
+ if nextDeliveryDate == None or item.deliveryDate < nextDeliveryDate:
+ nextDeliveryDate = item.deliveryDate
+ nextDeliveryItem = item
+
+ return nextDeliveryItem
+
+ def extractDeliveryItems(self, cutoffTime):
+ beforeTime = []
+ afterTime = []
+ for item in self:
+ if item.deliveryDate <= cutoffTime:
+ beforeTime.append(item)
+ else:
+ afterTime.append(item)
+
+ return (CatalogItemList(beforeTime, store=self.store), CatalogItemList(afterTime, store=self.store))
+
+ def extractOldestItems(self, count):
+ return (self[0:count], self[count:])
+
+ def __encodeList(self):
+ self.__blob = self.__makeBlob(self.store)
+
+ def __makeBlob(self, store):
+ dg = PyDatagram()
+ if self.__list:
+ dg.addUint8(CatalogItem.CatalogItemVersion)
+ for item in self.__list:
+ CatalogItem.encodeCatalogItem(dg, item, store)
+
+ return dg.getMessage()
+
+ def __decodeList(self):
+ self.__list = self.__makeList(self.store)
+
+ def __makeList(self, store):
+ list = []
+ if self.__blob:
+ dg = PyDatagram(self.__blob)
+ di = PyDatagramIterator(dg)
+ versionNumber = di.getUint8()
+ while di.getRemainingSize() > 0:
+ item = CatalogItem.decodeCatalogItem(di, versionNumber, store)
+ list.append(item)
+
+ return list
+
+ def append(self, item):
+ if self.__list == None:
+ self.__decodeList()
+ self.__list.append(item)
+ self.__blob = None
+ return
+
+ def extend(self, items):
+ self += items
+
+ def count(self, item):
+ if self.__list == None:
+ self.__decodeList()
+ return self.__list.count(item)
+
+ def index(self, item):
+ if self.__list == None:
+ self.__decodeList()
+ return self.__list.index(item)
+
+ def insert(self, index, item):
+ if self.__list == None:
+ self.__decodeList()
+ self.__list.insert(index, item)
+ self.__blob = None
+ return
+
+ def pop(self, index = None):
+ if self.__list == None:
+ self.__decodeList()
+ self.__blob = None
+ if index == None:
+ return self.__list.pop()
+ else:
+ return self.__list.pop(index)
+ return
+
+ def remove(self, item):
+ if self.__list == None:
+ self.__decodeList()
+ self.__list.remove(item)
+ self.__blob = None
+ return
+
+ def reverse(self):
+ if self.__list == None:
+ self.__decodeList()
+ self.__list.reverse()
+ self.__blob = None
+ return
+
+ def sort(self, cmpfunc = None):
+ if self.__list == None:
+ self.__decodeList()
+ if cmpfunc == None:
+ self.__list.sort()
+ else:
+ self.__list.sort(cmpfunc)
+ self.__blob = None
+ return
+
+ def __len__(self):
+ if self.__list == None:
+ self.__decodeList()
+ return len(self.__list)
+
+ def __getitem__(self, index):
+ if self.__list == None:
+ self.__decodeList()
+ return self.__list[index]
+
+ def __setitem__(self, index, item):
+ if self.__list == None:
+ self.__decodeList()
+ self.__list[index] = item
+ self.__blob = None
+ return
+
+ def __delitem__(self, index):
+ if self.__list == None:
+ self.__decodeList()
+ del self.__list[index]
+ self.__blob = None
+ return
+
+ def __getslice__(self, i, j):
+ if self.__list == None:
+ self.__decodeList()
+ return CatalogItemList(self.__list[i:j], store=self.store)
+
+ def __setslice__(self, i, j, s):
+ if self.__list == None:
+ self.__decodeList()
+ if isinstance(s, CatalogItemList):
+ self.__list[i:j] = s.__list
+ else:
+ self.__list[i:j] = s
+ self.__blob = None
+ return
+
+ def __delslice__(self, i, j):
+ if self.__list == None:
+ self.__decodeList()
+ del self.__list[i:j]
+ self.__blob = None
+ return
+
+ def __iadd__(self, other):
+ if self.__list == None:
+ self.__decodeList()
+ self.__list += list(other)
+ self.__blob = None
+ return self
+
+ def __add__(self, other):
+ copy = CatalogItemList(self, store=self.store)
+ copy += other
+ return copy
+
+ def __repr__(self):
+ return self.output()
+
+ def __str__(self):
+ return self.output()
+
+ def output(self, store = -1):
+ if self.__list == None:
+ self.__decodeList()
+ inner = ''
+ for item in self.__list:
+ inner += ', %s' % item.output(store)
+
+ return 'CatalogItemList([%s])' % inner[2:]
diff --git a/toontown/catalog/CatalogItemPanel.py b/toontown/catalog/CatalogItemPanel.py
new file mode 100644
index 0000000..183dfe9
--- /dev/null
+++ b/toontown/catalog/CatalogItemPanel.py
@@ -0,0 +1,531 @@
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from toontown.toonbase import ToontownGlobals
+from toontown.toontowngui import TTDialog
+from toontown.toonbase import TTLocalizer
+import CatalogItemTypes
+import CatalogItem
+from CatalogWallpaperItem import getAllWallpapers
+from CatalogFlooringItem import getAllFloorings
+from CatalogMouldingItem import getAllMouldings
+from CatalogWainscotingItem import getAllWainscotings
+from CatalogFurnitureItem import getAllFurnitures
+from CatalogFurnitureItem import FLTrunk
+from toontown.toontowngui.TeaserPanel import TeaserPanel
+from otp.otpbase import OTPGlobals
+from direct.directnotify import DirectNotifyGlobal
+CATALOG_PANEL_WORDWRAP = 10
+CATALOG_PANEL_CHAT_WORDWRAP = 9
+CATALOG_PANEL_ACCESSORY_WORDWRAP = 11
+
+class CatalogItemPanel(DirectFrame):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CatalogItemPanel')
+
+ def __init__(self, parent = aspect2d, parentCatalogScreen = None, **kw):
+ optiondefs = (('item', None, DGG.INITOPT), ('type', CatalogItem.CatalogTypeUnspecified, DGG.INITOPT), ('relief', None, None))
+ self.parentCatalogScreen = parentCatalogScreen
+ self.defineoptions(kw, optiondefs)
+ DirectFrame.__init__(self, parent)
+ self.loaded = 0
+ self.initialiseoptions(CatalogItemPanel)
+ return
+
+ def load(self):
+ if self.loaded:
+ return
+ self.loaded = 1
+ self.verify = None
+ self.pictureFrame = self.attachNewNode('pictureFrame')
+ self.pictureFrame.setScale(0.15)
+ self.itemIndex = 0
+ self.ival = None
+ typeCode = self['item'].getTypeCode()
+ if self['item'].needsCustomize() and (typeCode == CatalogItemTypes.WALLPAPER_ITEM or typeCode == CatalogItemTypes.FLOORING_ITEM or typeCode == CatalogItemTypes.MOULDING_ITEM or typeCode == CatalogItemTypes.FURNITURE_ITEM or typeCode == CatalogItemTypes.WAINSCOTING_ITEM or typeCode == CatalogItemTypes.TOON_STATUE_ITEM):
+ if typeCode == CatalogItemTypes.WALLPAPER_ITEM:
+ self.items = getAllWallpapers(self['item'].patternIndex)
+ elif typeCode == CatalogItemTypes.FLOORING_ITEM:
+ self.items = getAllFloorings(self['item'].patternIndex)
+ elif typeCode == CatalogItemTypes.MOULDING_ITEM:
+ self.items = getAllMouldings(self['item'].patternIndex)
+ elif typeCode == CatalogItemTypes.FURNITURE_ITEM:
+ self.items = getAllFurnitures(self['item'].furnitureType)
+ elif typeCode == CatalogItemTypes.TOON_STATUE_ITEM:
+ self.items = self['item'].getAllToonStatues()
+ elif typeCode == CatalogItemTypes.WAINSCOTING_ITEM:
+ self.items = getAllWainscotings(self['item'].patternIndex)
+ self.numItems = len(self.items)
+ if self.numItems > 1:
+ guiItems = loader.loadModel('phase_5.5/models/gui/catalog_gui')
+ nextUp = guiItems.find('**/arrow_up')
+ nextRollover = guiItems.find('**/arrow_Rollover')
+ nextDown = guiItems.find('**/arrow_Down')
+ prevUp = guiItems.find('**/arrowUp')
+ prevDown = guiItems.find('**/arrowDown1')
+ prevRollover = guiItems.find('**/arrowRollover')
+ self.nextVariant = DirectButton(parent=self, relief=None, image=(nextUp,
+ nextDown,
+ nextRollover,
+ nextUp), image3_color=(1, 1, 1, 0.4), pos=(0.13, 0, 0), command=self.showNextVariant)
+ self.prevVariant = DirectButton(parent=self, relief=None, image=(prevUp,
+ prevDown,
+ prevRollover,
+ prevUp), image3_color=(1, 1, 1, 0.4), pos=(-0.13, 0, 0), command=self.showPrevVariant, state=DGG.DISABLED)
+ self.variantPictures = [(None, None)] * self.numItems
+ else:
+ self.variantPictures = [(None, None)]
+ self.showCurrentVariant()
+ else:
+ picture, self.ival = self['item'].getPicture(base.localAvatar)
+ if picture:
+ picture.reparentTo(self)
+ picture.setScale(0.15)
+ self.items = [self['item']]
+ self.variantPictures = [(picture, self.ival)]
+ self.typeLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.24), scale=TTLocalizer.CIPtypeLabel, text=self['item'].getTypeName(), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=CATALOG_PANEL_WORDWRAP)
+ self.auxText = DirectLabel(parent=self, relief=None, scale=0.05, pos=(-0.2, 0, 0.16))
+ self.auxText.setHpr(0, 0, -30)
+ self.nameLabel = DirectLabel(parent=self, relief=None, text=self['item'].getDisplayName(), text_fg=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), text_scale=TTLocalizer.CIPnameLabel, text_wordwrap=CATALOG_PANEL_WORDWRAP + TTLocalizer.CIPwordwrapOffset)
+ if self['item'].getTypeCode() == CatalogItemTypes.CHAT_ITEM:
+ self.nameLabel['text_wordwrap'] = CATALOG_PANEL_CHAT_WORDWRAP
+ numRows = self.nameLabel.component('text0').textNode.getNumRows()
+ if numRows == 1:
+ namePos = (0, 0, -0.06)
+ elif numRows == 2:
+ namePos = (0, 0, -0.03)
+ else:
+ namePos = (0, 0, 0)
+ nameScale = 0.063
+ elif self['item'].getTypeCode() == CatalogItemTypes.ACCESSORY_ITEM:
+ self.nameLabel['text_wordwrap'] = CATALOG_PANEL_ACCESSORY_WORDWRAP
+ namePos = (0, 0, -.22)
+ nameScale = 0.06
+ else:
+ namePos = (0, 0, -.22)
+ nameScale = 0.06
+ self.nameLabel.setPos(*namePos)
+ self.nameLabel.setScale(nameScale)
+ numericBeanPrice = self['item'].getPrice(self['type'])
+ priceStr = str(numericBeanPrice) + ' ' + TTLocalizer.CatalogCurrency
+ priceScale = 0.07
+ if self['item'].isSaleItem():
+ priceStr = TTLocalizer.CatalogSaleItem + priceStr
+ priceScale = 0.06
+ self.priceLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.3), scale=priceScale, text=priceStr, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ACenter)
+ self.createEmblemPrices(numericBeanPrice)
+ buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui')
+ upButton = buttonModels.find('**/InventoryButtonUp')
+ downButton = buttonModels.find('**/InventoryButtonDown')
+ rolloverButton = buttonModels.find('**/InventoryButtonRollover')
+ buyText = TTLocalizer.CatalogBuyText
+ buyTextScale = TTLocalizer.CIPbuyButton
+ if self['item'].isRental():
+ buyText = TTLocalizer.CatalogRentText
+ self.buyButton = DirectButton(parent=self, relief=None, pos=(0.2, 0, 0.15), scale=(0.7, 1, 0.8), text=buyText, text_scale=buyTextScale, text_pos=(-0.005, -0.01), image=(upButton,
+ downButton,
+ rolloverButton,
+ upButton), image_color=(1.0, 0.2, 0.2, 1), image0_color=Vec4(1.0, 0.4, 0.4, 1), image3_color=Vec4(1.0, 0.4, 0.4, 0.4), command=self.__handlePurchaseRequest)
+ soundIcons = loader.loadModel('phase_5.5/models/gui/catalogSoundIcons')
+ soundOn = soundIcons.find('**/sound07')
+ soundOff = soundIcons.find('**/sound08')
+ self.soundOnButton = DirectButton(parent=self, relief=None, pos=(0.2, 0, -0.15), scale=(0.7, 1, 0.8), text_scale=buyTextScale, text_pos=(-0.005, -0.01), image=(upButton,
+ downButton,
+ rolloverButton,
+ upButton), image_color=(0.2, 0.5, 0.2, 1), image0_color=Vec4(0.4, 0.5, 0.4, 1), image3_color=Vec4(0.4, 0.5, 0.4, 0.4), command=self.handleSoundOnButton)
+ self.soundOnButton.hide()
+ soundOn.setScale(0.1)
+ soundOn.reparentTo(self.soundOnButton)
+ self.soundOffButton = DirectButton(parent=self, relief=None, pos=(0.2, 0, -0.15), scale=(0.7, 1, 0.8), text_scale=buyTextScale, text_pos=(-0.005, -0.01), image=(upButton,
+ downButton,
+ rolloverButton,
+ upButton), image_color=(0.2, 1.0, 0.2, 1), image0_color=Vec4(0.4, 1.0, 0.4, 1), image3_color=Vec4(0.4, 1.0, 0.4, 0.4), command=self.handleSoundOffButton)
+ self.soundOffButton.hide()
+ soundOff = self.soundOffButton.attachNewNode('soundOff')
+ soundOn.copyTo(soundOff)
+ soundOff.reparentTo(self.soundOffButton)
+ upGButton = buttonModels.find('**/InventoryButtonUp')
+ downGButton = buttonModels.find('**/InventoryButtonDown')
+ rolloverGButton = buttonModels.find('**/InventoryButtonRollover')
+ self.giftButton = DirectButton(parent=self, relief=None, pos=(0.2, 0, 0.15), scale=(0.7, 1, 0.8), text=TTLocalizer.CatalogGiftText, text_scale=buyTextScale, text_pos=(-0.005, -0.01), image=(upButton,
+ downButton,
+ rolloverButton,
+ upButton), image_color=(1.0, 0.2, 0.2, 1), image0_color=Vec4(1.0, 0.4, 0.4, 1), image3_color=Vec4(1.0, 0.4, 0.4, 0.4), command=self.__handleGiftRequest)
+ self.updateButtons()
+ return
+
+ def createEmblemPrices(self, numericBeanPrice):
+ priceScale = 0.07
+ emblemPrices = self['item'].getEmblemPrices()
+ if emblemPrices:
+ if numericBeanPrice:
+ self.priceLabel.hide()
+ beanModel = loader.loadModel('phase_5.5/models/estate/jellyBean')
+ beanModel.setColorScale(1, 0, 0, 1)
+ self.beanPriceLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.3), scale=priceScale, image=beanModel, image_pos=(-0.4, 0, 0.4), text=str(numericBeanPrice), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft)
+ else:
+ self.priceLabel.hide()
+ goldPrice = 0
+ silverPrice = 0
+ emblemIcon = loader.loadModel('phase_3.5/models/gui/tt_m_gui_gen_emblemIcons')
+ silverModel = emblemIcon.find('**/tt_t_gui_gen_emblemSilver')
+ goldModel = emblemIcon.find('**/tt_t_gui_gen_emblemGold')
+ if ToontownGlobals.EmblemTypes.Silver < len(emblemPrices):
+ silverPrice = emblemPrices[ToontownGlobals.EmblemTypes.Silver]
+ if silverPrice:
+ self.silverPriceLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.3), scale=priceScale, image=silverModel, image_pos=(-0.4, 0, 0.4), text=str(silverPrice), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft)
+ if ToontownGlobals.EmblemTypes.Gold < len(emblemPrices):
+ goldPrice = emblemPrices[ToontownGlobals.EmblemTypes.Gold]
+ if goldPrice:
+ self.goldPriceLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.3), scale=priceScale, image=goldModel, image_pos=(-0.4, 0, 0.4), text=str(goldPrice), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft)
+ numPrices = 0
+ if numericBeanPrice:
+ numPrices += 1
+ if silverPrice:
+ numPrices += 1
+ if goldPrice:
+ numPrices += 1
+ if numPrices == 2:
+ if not numericBeanPrice:
+ self.silverPriceLabel.setX(-0.15)
+ self.goldPriceLabel.setX(0.15)
+ if not silverPrice:
+ self.goldPriceLabel.setX(-0.15)
+ self.beanPriceLabel.setX(0.15)
+ if not goldPrice:
+ self.silverPriceLabel.setX(-0.15)
+ self.beanPriceLabel.setX(0.15)
+ elif numPrices == 3:
+ self.silverPriceLabel.setX(-0.2)
+ self.goldPriceLabel.setX(0)
+ self.beanPriceLabel.setX(0.15)
+ return
+
+ def showNextVariant(self):
+ messenger.send('wakeup')
+ self.hideCurrentVariant()
+ self.itemIndex += 1
+ if self.itemIndex >= self.numItems - 1:
+ self.itemIndex = self.numItems - 1
+ self.nextVariant['state'] = DGG.DISABLED
+ else:
+ self.nextVariant['state'] = DGG.NORMAL
+ self.prevVariant['state'] = DGG.NORMAL
+ self.showCurrentVariant()
+
+ def showPrevVariant(self):
+ messenger.send('wakeup')
+ self.hideCurrentVariant()
+ self.itemIndex -= 1
+ if self.itemIndex < 0:
+ self.itemIndex = 0
+ self.prevVariant['state'] = DGG.DISABLED
+ else:
+ self.prevVariant['state'] = DGG.NORMAL
+ self.nextVariant['state'] = DGG.NORMAL
+ self.showCurrentVariant()
+
+ def showCurrentVariant(self):
+ newPic, self.ival = self.variantPictures[self.itemIndex]
+ if self.ival:
+ self.ival.finish()
+ if not newPic:
+ variant = self.items[self.itemIndex]
+ newPic, self.ival = variant.getPicture(base.localAvatar)
+ self.variantPictures[self.itemIndex] = (newPic, self.ival)
+ newPic.reparentTo(self.pictureFrame)
+ if self.ival:
+ self.ival.loop()
+ if self['item'].getTypeCode() == CatalogItemTypes.TOON_STATUE_ITEM:
+ if hasattr(self, 'nameLabel'):
+ self.nameLabel['text'] = self.items[self.itemIndex].getDisplayName()
+ self['item'].gardenIndex = self.items[self.itemIndex].gardenIndex
+
+ def hideCurrentVariant(self):
+ currentPic = self.variantPictures[self.itemIndex][0]
+ if currentPic:
+ currentPic.detachNode()
+
+ def unload(self):
+ if not self.loaded:
+ DirectFrame.destroy(self)
+ return
+ self.loaded = 0
+ if self['item'].getTypeCode() == CatalogItemTypes.TOON_STATUE_ITEM:
+ self['item'].deleteAllToonStatues()
+ self['item'].gardenIndex = self['item'].startPoseIndex
+ self.nameLabel['text'] = self['item'].getDisplayName()
+ self['item'].requestPurchaseCleanup()
+ for picture, ival in self.variantPictures:
+ if picture:
+ picture.destroy()
+ if ival:
+ ival.finish()
+
+ self.variantPictures = None
+ if self.ival:
+ self.ival.finish()
+ self.ival = None
+ if len(self.items):
+ self.items[0].cleanupPicture()
+ self.pictureFrame.remove()
+ self.pictureFrame = None
+ self.items = []
+ if self.verify:
+ self.verify.cleanup()
+ DirectFrame.destroy(self)
+ return
+
+ def destroy(self):
+ self.parentCatalogScreen = None
+ self.unload()
+ return
+
+ def getTeaserPanel(self):
+ typeName = self['item'].getTypeName()
+ if typeName == TTLocalizer.EmoteTypeName or typeName == TTLocalizer.ChatTypeName:
+ page = 'emotions'
+ elif typeName == TTLocalizer.GardenTypeName or typeName == TTLocalizer.GardenStarterTypeName:
+ page = 'gardening'
+ else:
+ page = 'clothing'
+
+ def showTeaserPanel():
+ TeaserPanel(pageName=page)
+
+ return showTeaserPanel
+
+ def updateBuyButton(self):
+ if not self.loaded:
+ return
+ if not base.cr.isPaid():
+ self.buyButton['command'] = self.getTeaserPanel()
+ self.buyButton.show()
+ typeCode = self['item'].getTypeCode()
+ orderCount = base.localAvatar.onOrder.count(self['item'])
+ if orderCount > 0:
+ if orderCount > 1:
+ auxText = '%d %s' % (orderCount, TTLocalizer.CatalogOnOrderText)
+ else:
+ auxText = TTLocalizer.CatalogOnOrderText
+ else:
+ auxText = ''
+ isNameTag = typeCode == CatalogItemTypes.NAMETAG_ITEM
+ if isNameTag and not localAvatar.getGameAccess() == OTPGlobals.AccessFull:
+ if self['item'].nametagStyle == 100:
+ if localAvatar.getFont() == ToontownGlobals.getToonFont():
+ auxText = TTLocalizer.CatalogCurrent
+ self.buyButton['state'] = DGG.DISABLED
+ elif self['item'].getPrice(self['type']) > base.localAvatar.getMoney() + base.localAvatar.getBankMoney():
+ self.buyButton['state'] = DGG.DISABLED
+ elif isNameTag and self['item'].nametagStyle == localAvatar.getNametagStyle():
+ auxText = TTLocalizer.CatalogCurrent
+ self.buyButton['state'] = DGG.DISABLED
+ elif self['item'].reachedPurchaseLimit(base.localAvatar):
+ max = self['item'].getPurchaseLimit()
+ if max <= 1:
+ auxText = TTLocalizer.CatalogPurchasedText
+ if self['item'].hasBeenGifted(base.localAvatar):
+ auxText = TTLocalizer.CatalogGiftedText
+ else:
+ auxText = TTLocalizer.CatalogPurchasedMaxText
+ self.buyButton['state'] = DGG.DISABLED
+ elif hasattr(self['item'], 'noGarden') and self['item'].noGarden(base.localAvatar):
+ auxText = TTLocalizer.NoGarden
+ self.buyButton['state'] = DGG.DISABLED
+ elif hasattr(self['item'], 'isSkillTooLow') and self['item'].isSkillTooLow(base.localAvatar):
+ auxText = TTLocalizer.SkillTooLow
+ self.buyButton['state'] = DGG.DISABLED
+ elif hasattr(self['item'], 'getDaysToGo') and self['item'].getDaysToGo(base.localAvatar):
+ auxText = TTLocalizer.DaysToGo % self['item'].getDaysToGo(base.localAvatar)
+ self.buyButton['state'] = DGG.DISABLED
+ elif self['item'].getEmblemPrices() and not base.localAvatar.isEnoughMoneyAndEmblemsToBuy(self['item'].getPrice(self['type']), self['item'].getEmblemPrices()):
+ self.buyButton['state'] = DGG.DISABLED
+ elif self['item'].getPrice(self['type']) <= base.localAvatar.getMoney() + base.localAvatar.getBankMoney():
+ self.buyButton['state'] = DGG.NORMAL
+ self.buyButton.show()
+ else:
+ self.buyButton['state'] = DGG.DISABLED
+ self.buyButton.show()
+ self.auxText['text'] = auxText
+
+ def __handlePurchaseRequest(self):
+ if self['item'].replacesExisting() and self['item'].hasExisting():
+ if self['item'].getFlags() & FLTrunk:
+ message = TTLocalizer.CatalogVerifyPurchase % {'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type'])}
+ else:
+ message = TTLocalizer.CatalogOnlyOnePurchase % {'old': self['item'].getYourOldDesc(),
+ 'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type'])}
+ elif self['item'].isRental():
+ message = TTLocalizer.CatalogVerifyRent % {'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type'])}
+ else:
+ emblemPrices = self['item'].getEmblemPrices()
+ if emblemPrices:
+ silver = emblemPrices[ToontownGlobals.EmblemTypes.Silver]
+ gold = emblemPrices[ToontownGlobals.EmblemTypes.Gold]
+ price = self['item'].getPrice(self['type'])
+ if price and silver and gold:
+ message = TTLocalizer.CatalogVerifyPurchaseBeanSilverGold % {'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type']),
+ 'silver': silver,
+ 'gold': gold}
+ elif price and silver:
+ message = TTLocalizer.CatalogVerifyPurchaseBeanSilver % {'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type']),
+ 'silver': silver,
+ 'gold': gold}
+ elif price and gold:
+ message = TTLocalizer.CatalogVerifyPurchaseBeanGold % {'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type']),
+ 'silver': silver,
+ 'gold': gold}
+ elif silver and gold:
+ message = TTLocalizer.CatalogVerifyPurchaseSilverGold % {'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type']),
+ 'silver': silver,
+ 'gold': gold}
+ elif silver:
+ message = TTLocalizer.CatalogVerifyPurchaseSilver % {'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type']),
+ 'silver': silver,
+ 'gold': gold}
+ elif gold:
+ message = TTLocalizer.CatalogVerifyPurchaseGold % {'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type']),
+ 'silver': silver,
+ 'gold': gold}
+ else:
+ self.notify.warning('is this a completely free item %s?' % self['item'].getName())
+ message = TTLocalizer.CatalogVerifyPurchase % {'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type'])}
+ else:
+ message = TTLocalizer.CatalogVerifyPurchase % {'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type'])}
+ self.verify = TTDialog.TTGlobalDialog(doneEvent='verifyDone', message=message, style=TTDialog.TwoChoice)
+ self.verify.show()
+ self.accept('verifyDone', self.__handleVerifyPurchase)
+
+ def __handleVerifyPurchase(self):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: CATALOG: Order item')
+ status = self.verify.doneStatus
+ self.ignore('verifyDone')
+ self.verify.cleanup()
+ del self.verify
+ self.verify = None
+ if status == 'ok':
+ item = self.items[self.itemIndex]
+ messenger.send('CatalogItemPurchaseRequest', [item])
+ self.buyButton['state'] = DGG.DISABLED
+ return
+
+ def __handleGiftRequest(self):
+ if self['item'].replacesExisting() and self['item'].hasExisting():
+ message = TTLocalizer.CatalogOnlyOnePurchase % {'old': self['item'].getYourOldDesc(),
+ 'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type'])}
+ else:
+ friendIndex = self.parentCatalogScreen.friendGiftIndex
+ friendText = 'Error'
+ numFriends = len(base.localAvatar.friendsList) + len(base.cr.avList) - 1
+ if numFriends > 0:
+ friendText = self.parentCatalogScreen.receiverName
+ message = TTLocalizer.CatalogVerifyGift % {'item': self['item'].getName(),
+ 'price': self['item'].getPrice(self['type']),
+ 'friend': friendText}
+ self.verify = TTDialog.TTGlobalDialog(doneEvent='verifyGiftDone', message=message, style=TTDialog.TwoChoice)
+ self.verify.show()
+ self.accept('verifyGiftDone', self.__handleVerifyGift)
+
+ def __handleVerifyGift(self):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: CATALOG: Gift item')
+ status = self.verify.doneStatus
+ self.ignore('verifyGiftDone')
+ self.verify.cleanup()
+ del self.verify
+ self.verify = None
+ if status == 'ok':
+ self.giftButton['state'] = DGG.DISABLED
+ item = self.items[self.itemIndex]
+ messenger.send('CatalogItemGiftPurchaseRequest', [item])
+ return
+
+ def updateButtons(self, giftActivate = 0):
+ if self.parentCatalogScreen.gifting == -1:
+ self.updateBuyButton()
+ if self.loaded:
+ self.giftButton.hide()
+ else:
+ self.updateGiftButton(giftActivate)
+ if self.loaded:
+ self.buyButton.hide()
+
+ def updateGiftButton(self, giftUpdate = 0):
+ if not self.loaded:
+ return
+ self.giftButton.show()
+ if giftUpdate == 0:
+ return
+ if not base.cr.isPaid():
+ self.giftButton['command'] = self.getTeaserPanel()
+ self.auxText['text'] = ' '
+ numFriends = len(base.localAvatar.friendsList) + len(base.cr.avList) - 1
+ if numFriends > 0:
+ self.giftButton['state'] = DGG.DISABLED
+ self.giftButton.show()
+ auxText = ' '
+ if self['item'].isGift() <= 0:
+ self.giftButton.show()
+ self.giftButton['state'] = DGG.DISABLED
+ auxText = TTLocalizer.CatalogNotAGift
+ self.auxText['text'] = auxText
+ return
+ elif self.parentCatalogScreen.gotAvatar == 1:
+ avatar = self.parentCatalogScreen.giftAvatar
+ if self['item'].forBoysOnly() and avatar.getStyle().getGender() == 'f' or self['item'].forGirlsOnly() and avatar.getStyle().getGender() == 'm':
+ self.giftButton.show()
+ self.giftButton['state'] = DGG.DISABLED
+ auxText = TTLocalizer.CatalogNoFit
+ self.auxText['text'] = auxText
+ return
+ elif self['item'].reachedPurchaseLimit(avatar):
+ self.giftButton.show()
+ self.giftButton['state'] = DGG.DISABLED
+ auxText = TTLocalizer.CatalogPurchasedGiftText
+ self.auxText['text'] = auxText
+ return
+ elif len(avatar.mailboxContents) + len(avatar.onGiftOrder) >= ToontownGlobals.MaxMailboxContents:
+ self.giftButton.show()
+ self.giftButton['state'] = DGG.DISABLED
+ auxText = TTLocalizer.CatalogMailboxFull
+ self.auxText['text'] = auxText
+ return
+ elif self['item'].getPrice(self['type']) <= base.localAvatar.getMoney() + base.localAvatar.getBankMoney():
+ self.giftButton['state'] = DGG.NORMAL
+ self.giftButton.show()
+
+ def handleSoundOnButton(self):
+ item = self.items[self.itemIndex]
+ self.soundOnButton.hide()
+ self.soundOffButton.show()
+ if hasattr(item, 'changeIval'):
+ if self.ival:
+ self.ival.finish()
+ self.ival = None
+ self.ival = item.changeIval(volume=1)
+ self.ival.loop()
+ return
+
+ def handleSoundOffButton(self):
+ item = self.items[self.itemIndex]
+ self.soundOffButton.hide()
+ self.soundOnButton.show()
+ if hasattr(item, 'changeIval'):
+ if self.ival:
+ self.ival.finish()
+ self.ival = None
+ self.ival = item.changeIval(volume=0)
+ self.ival.loop()
+ return
diff --git a/toontown/catalog/CatalogItemTypes.py b/toontown/catalog/CatalogItemTypes.py
new file mode 100644
index 0000000..8162ba1
--- /dev/null
+++ b/toontown/catalog/CatalogItemTypes.py
@@ -0,0 +1,85 @@
+import CatalogFurnitureItem
+import CatalogChatItem
+import CatalogClothingItem
+import CatalogEmoteItem
+import CatalogWallpaperItem
+import CatalogFlooringItem
+import CatalogWainscotingItem
+import CatalogMouldingItem
+import CatalogWindowItem
+import CatalogPoleItem
+import CatalogPetTrickItem
+import CatalogBeanItem
+import CatalogGardenItem
+import CatalogInvalidItem
+import CatalogRentalItem
+import CatalogGardenStarterItem
+import CatalogNametagItem
+import CatalogToonStatueItem
+import CatalogAnimatedFurnitureItem
+import CatalogAccessoryItem
+INVALID_ITEM = 0
+FURNITURE_ITEM = 1
+CHAT_ITEM = 2
+CLOTHING_ITEM = 3
+EMOTE_ITEM = 4
+WALLPAPER_ITEM = 5
+WINDOW_ITEM = 6
+FLOORING_ITEM = 7
+MOULDING_ITEM = 8
+WAINSCOTING_ITEM = 9
+POLE_ITEM = 10
+PET_TRICK_ITEM = 11
+BEAN_ITEM = 12
+GARDEN_ITEM = 13
+RENTAL_ITEM = 14
+GARDENSTARTER_ITEM = 15
+NAMETAG_ITEM = 16
+TOON_STATUE_ITEM = 17
+ANIMATED_FURNITURE_ITEM = 18
+ACCESSORY_ITEM = 19
+NonPermanentItemTypes = (RENTAL_ITEM,)
+CatalogItemTypes = {CatalogInvalidItem.CatalogInvalidItem: INVALID_ITEM,
+ CatalogFurnitureItem.CatalogFurnitureItem: FURNITURE_ITEM,
+ CatalogChatItem.CatalogChatItem: CHAT_ITEM,
+ CatalogClothingItem.CatalogClothingItem: CLOTHING_ITEM,
+ CatalogEmoteItem.CatalogEmoteItem: EMOTE_ITEM,
+ CatalogWallpaperItem.CatalogWallpaperItem: WALLPAPER_ITEM,
+ CatalogWindowItem.CatalogWindowItem: WINDOW_ITEM,
+ CatalogFlooringItem.CatalogFlooringItem: FLOORING_ITEM,
+ CatalogMouldingItem.CatalogMouldingItem: MOULDING_ITEM,
+ CatalogWainscotingItem.CatalogWainscotingItem: WAINSCOTING_ITEM,
+ CatalogPoleItem.CatalogPoleItem: POLE_ITEM,
+ CatalogPetTrickItem.CatalogPetTrickItem: PET_TRICK_ITEM,
+ CatalogBeanItem.CatalogBeanItem: BEAN_ITEM,
+ CatalogGardenItem.CatalogGardenItem: GARDEN_ITEM,
+ CatalogRentalItem.CatalogRentalItem: RENTAL_ITEM,
+ CatalogGardenStarterItem.CatalogGardenStarterItem: GARDENSTARTER_ITEM,
+ CatalogNametagItem.CatalogNametagItem: NAMETAG_ITEM,
+ CatalogToonStatueItem.CatalogToonStatueItem: TOON_STATUE_ITEM,
+ CatalogAnimatedFurnitureItem.CatalogAnimatedFurnitureItem: ANIMATED_FURNITURE_ITEM,
+ CatalogAccessoryItem.CatalogAccessoryItem: ACCESSORY_ITEM}
+CatalogItemType2multipleAllowed = {INVALID_ITEM: False,
+ FURNITURE_ITEM: True,
+ CHAT_ITEM: False,
+ CLOTHING_ITEM: False,
+ EMOTE_ITEM: False,
+ WALLPAPER_ITEM: True,
+ WINDOW_ITEM: True,
+ FLOORING_ITEM: True,
+ MOULDING_ITEM: True,
+ WAINSCOTING_ITEM: True,
+ POLE_ITEM: False,
+ PET_TRICK_ITEM: False,
+ BEAN_ITEM: False,
+ GARDEN_ITEM: False,
+ RENTAL_ITEM: False,
+ GARDENSTARTER_ITEM: False,
+ NAMETAG_ITEM: False,
+ TOON_STATUE_ITEM: False,
+ ANIMATED_FURNITURE_ITEM: True,
+ ACCESSORY_ITEM: False}
+SingleCodeRedemption = (BEAN_ITEM,)
+CatalogItemTypeMask = 31
+CatalogItemSaleFlag = 128
+CatalogItemGiftTag = 64
diff --git a/toontown/catalog/CatalogManager.py b/toontown/catalog/CatalogManager.py
new file mode 100644
index 0000000..c558879
--- /dev/null
+++ b/toontown/catalog/CatalogManager.py
@@ -0,0 +1,31 @@
+from direct.distributed import DistributedObject
+from direct.directnotify import DirectNotifyGlobal
+
+class CatalogManager(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CatalogManager')
+ neverDisable = 1
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+
+ def generate(self):
+ if base.cr.catalogManager != None:
+ base.cr.catalogManager.delete()
+ base.cr.catalogManager = self
+ DistributedObject.DistributedObject.generate(self)
+ if hasattr(base.localAvatar, 'catalogScheduleNextTime') and base.localAvatar.catalogScheduleNextTime == 0:
+ self.d_startCatalog()
+ return
+
+ def disable(self):
+ base.cr.catalogManager = None
+ DistributedObject.DistributedObject.disable(self)
+ return
+
+ def delete(self):
+ base.cr.catalogManager = None
+ DistributedObject.DistributedObject.delete(self)
+ return
+
+ def d_startCatalog(self):
+ self.sendUpdate('startCatalog', [])
diff --git a/toontown/catalog/CatalogMouldingItem.py b/toontown/catalog/CatalogMouldingItem.py
new file mode 100644
index 0000000..858c904
--- /dev/null
+++ b/toontown/catalog/CatalogMouldingItem.py
@@ -0,0 +1,152 @@
+from CatalogSurfaceItem import *
+MTTextureName = 0
+MTColor = 1
+MTBasePrice = 2
+MouldingTypes = {1000: ('phase_3.5/maps/molding_wood1.jpg', CTBasicWoodColorOnWhite, 150),
+ 1010: ('phase_5.5/maps/bd_grey_border1.jpg', CTFlatColorDark, 150),
+ 1020: ('phase_5.5/maps/dental_Border_wood_neutral.jpg', CTFlatColorDark, 150),
+ 1030: ('phase_5.5/maps/littleFlowers_border.jpg', CTWhite, 150),
+ 1040: ('phase_5.5/maps/littleFlowers_border_neutral.jpg', CTFlatColorDark, 150),
+ 1050: ('phase_5.5/maps/ladybugs2_Border.jpg', CTFlatColorDark, 150),
+ 1060: ('phase_5.5/maps/bd_grey_border1.jpg', CTValentinesColors, 150),
+ 1070: ('phase_5.5/maps/bd_grey_border1.jpg', CTUnderwaterColors, 150),
+ 1080: ('phase_5.5/maps/tt_t_ara_int_border_winterLights1.jpg', CTWhite, 150),
+ 1085: ('phase_5.5/maps/tt_t_ara_int_border_winterLights2.jpg', CTWhite, 150),
+ 1090: ('phase_5.5/maps/tt_t_ara_int_border_winterLights3.jpg', CTWhite, 150),
+ 1100: ('phase_5.5/maps/tt_t_ara_int_border_valentine_cupid.jpg', CTWhite, 150),
+ 1110: ('phase_5.5/maps/tt_t_ara_int_border_valentine_heart1.jpg', CTWhite, 150),
+ 1120: ('phase_5.5/maps/tt_t_ara_int_border_valentine_heart2.jpg', CTWhite, 150)}
+
+class CatalogMouldingItem(CatalogSurfaceItem):
+
+ def makeNewItem(self, patternIndex, colorIndex):
+ self.patternIndex = patternIndex
+ self.colorIndex = colorIndex
+ CatalogSurfaceItem.makeNewItem(self)
+
+ def getTypeName(self):
+ return TTLocalizer.SurfaceNames[STMoulding]
+
+ def getName(self):
+ name = TTLocalizer.MouldingNames.get(self.patternIndex)
+ if name:
+ return name
+ return self.getTypeName()
+
+ def getSurfaceType(self):
+ return STMoulding
+
+ def getPicture(self, avatar):
+ self.hasPicture = True
+ frame = self.makeFrame()
+ sample = loader.loadModel('phase_5.5/models/estate/wallpaper_sample')
+ a = sample.find('**/a')
+ b = sample.find('**/b')
+ c = sample.find('**/c')
+ a.setTexture(self.loadTexture(), 1)
+ a.setColorScale(*self.getColor())
+ b.hide()
+ c.hide()
+ sample.reparentTo(frame)
+ return (frame, None)
+
+ def output(self, store = -1):
+ return 'CatalogMouldingItem(%s, %s%s)' % (self.patternIndex, self.colorIndex, self.formatOptionalData(store))
+
+ def getFilename(self):
+ return MouldingTypes[self.patternIndex][MTTextureName]
+
+ def compareTo(self, other):
+ if self.patternIndex != other.patternIndex:
+ return self.patternIndex - other.patternIndex
+ return self.colorIndex - other.colorIndex
+
+ def getHashContents(self):
+ return (self.patternIndex, self.colorIndex)
+
+ def getBasePrice(self):
+ return MouldingTypes[self.patternIndex][MTBasePrice]
+
+ def loadTexture(self):
+ from pandac.PandaModules import Texture
+ filename = MouldingTypes[self.patternIndex][MTTextureName]
+ texture = loader.loadTexture(filename)
+ texture.setMinfilter(Texture.FTLinearMipmapLinear)
+ texture.setMagfilter(Texture.FTLinear)
+ return texture
+
+ def getColor(self):
+ if self.colorIndex == None:
+ colorIndex = 0
+ else:
+ colorIndex = self.colorIndex
+ colors = MouldingTypes[self.patternIndex][MTColor]
+ if colors:
+ if colorIndex < len(colors):
+ return colors[colorIndex]
+ else:
+ print 'Warning: colorIndex not in colors. Returning white.'
+ return CT_WHITE
+ else:
+ return CT_WHITE
+ return
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store)
+ if versionNumber < 3:
+ self.patternIndex = di.getUint8()
+ else:
+ self.patternIndex = di.getUint16()
+ self.colorIndex = di.getUint8()
+ wtype = MouldingTypes[self.patternIndex]
+
+ def encodeDatagram(self, dg, store):
+ CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store)
+ dg.addUint16(self.patternIndex)
+ dg.addUint8(self.colorIndex)
+
+
+def getMouldings(*indexList):
+ list = []
+ for index in indexList:
+ list.append(CatalogMouldingItem(index))
+
+ return list
+
+
+def getAllMouldings(*indexList):
+ list = []
+ for index in indexList:
+ colors = MouldingTypes[index][MTColor]
+ if colors:
+ for n in range(len(colors)):
+ list.append(CatalogMouldingItem(index, n))
+
+ else:
+ list.append(CatalogMouldingItem(index, 0))
+
+ return list
+
+
+def getMouldingRange(fromIndex, toIndex, *otherRanges):
+ list = []
+ froms = [fromIndex]
+ tos = [toIndex]
+ i = 0
+ while i < len(otherRanges):
+ froms.append(otherRanges[i])
+ tos.append(otherRanges[i + 1])
+ i += 2
+
+ for patternIndex in MouldingTypes.keys():
+ for fromIndex, toIndex in zip(froms, tos):
+ if patternIndex >= fromIndex and patternIndex <= toIndex:
+ colors = MouldingTypes[patternIndex][MTColor]
+ if colors:
+ for n in range(len(colors)):
+ list.append(CatalogMouldingItem(patternIndex, n))
+
+ else:
+ list.append(CatalogMouldingItem(patternIndex, 0))
+
+ return list
diff --git a/toontown/catalog/CatalogNametagItem.py b/toontown/catalog/CatalogNametagItem.py
new file mode 100644
index 0000000..21c3d13
--- /dev/null
+++ b/toontown/catalog/CatalogNametagItem.py
@@ -0,0 +1,104 @@
+import CatalogItem
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from otp.otpbase import OTPLocalizer
+from direct.interval.IntervalGlobal import *
+from direct.gui.DirectGui import *
+
+class CatalogNametagItem(CatalogItem.CatalogItem):
+ sequenceNumber = 0
+
+ def makeNewItem(self, nametagStyle):
+ self.nametagStyle = nametagStyle
+ CatalogItem.CatalogItem.makeNewItem(self)
+
+ def getPurchaseLimit(self):
+ return 1
+
+ def reachedPurchaseLimit(self, avatar):
+ if self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or self in avatar.awardMailboxContents or self in avatar.onAwardOrder:
+ return 1
+ if avatar.nametagStyle == self.nametagStyle:
+ return 1
+ return 0
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ return TTLocalizer.CatalogAcceptNametag
+ return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode)
+
+ def saveHistory(self):
+ return 1
+
+ def getTypeName(self):
+ return TTLocalizer.NametagTypeName
+
+ def getName(self):
+ if self.nametagStyle == 100:
+ name = TTLocalizer.UnpaidNameTag
+ else:
+ name = TTLocalizer.NametagFontNames[self.nametagStyle]
+ if TTLocalizer.NametagReverse:
+ name = TTLocalizer.NametagLabel + name
+ else:
+ name = name + TTLocalizer.NametagLabel
+ return name
+ if self.nametagStyle == 0:
+ name = TTLocalizer.NametagPaid
+ elif self.nametagStyle == 1:
+ name = TTLocalizer.NametagAction
+ elif self.nametagStyle == 2:
+ name = TTLocalizer.NametagFrilly
+
+ def recordPurchase(self, avatar, optional):
+ if avatar:
+ avatar.b_setNametagStyle(self.nametagStyle)
+ return ToontownGlobals.P_ItemAvailable
+
+ def getPicture(self, avatar):
+ frame = self.makeFrame()
+ if self.nametagStyle == 100:
+ inFont = ToontownGlobals.getToonFont()
+ else:
+ inFont = ToontownGlobals.getNametagFont(self.nametagStyle)
+ nameTagDemo = DirectLabel(parent=frame, relief=None, pos=(0, 0, 0.24), scale=0.5, text=localAvatar.getName(), text_fg=(1.0, 1.0, 1.0, 1), text_shadow=(0, 0, 0, 1), text_font=inFont, text_wordwrap=9)
+ self.hasPicture = True
+ return (frame, None)
+
+ def output(self, store = -1):
+ return 'CatalogNametagItem(%s%s)' % (self.nametagStyle, self.formatOptionalData(store))
+
+ def compareTo(self, other):
+ return self.nametagStyle - other.nametagStyle
+
+ def getHashContents(self):
+ return self.nametagStyle
+
+ def getBasePrice(self):
+ return 500
+ cost = 500
+ if self.nametagStyle == 0:
+ cost = 600
+ elif self.nametagStyle == 1:
+ cost = 600
+ elif self.nametagStyle == 2:
+ cost = 600
+ elif self.nametagStyle == 100:
+ cost = 50
+ return cost
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store)
+ self.nametagStyle = di.getUint16()
+
+ def encodeDatagram(self, dg, store):
+ CatalogItem.CatalogItem.encodeDatagram(self, dg, store)
+ dg.addUint16(self.nametagStyle)
+
+ def isGift(self):
+ return 0
+
+ def getBackSticky(self):
+ itemType = 1
+ numSticky = 4
+ return (itemType, numSticky)
diff --git a/toontown/catalog/CatalogNotifyDialog.py b/toontown/catalog/CatalogNotifyDialog.py
new file mode 100644
index 0000000..838b6d5
--- /dev/null
+++ b/toontown/catalog/CatalogNotifyDialog.py
@@ -0,0 +1,53 @@
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+CatalogNotifyBaseXPos = 0.4
+
+class CatalogNotifyDialog:
+ notify = DirectNotifyGlobal.directNotify.newCategory('CatalogNotifyDialog')
+
+ def __init__(self, message):
+ self.message = message
+ self.messageIndex = 0
+ framePosX = CatalogNotifyBaseXPos
+ from toontown.toon import LocalToon
+ if LocalToon.WantNewsPage:
+ framePosX += LocalToon.AdjustmentForNewsButton
+ self.frame = DirectFrame(relief=None, sortOrder=DGG.BACKGROUND_SORT_INDEX - 2, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.2, 1.0, 0.4), text=message[0], text_wordwrap=16, text_scale=0.06, text_pos=(-0.1, 0.1), pos=(framePosX, 0, 0.78))
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr'))
+ okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ self.nextButton = DirectButton(parent=self.frame, relief=None, image=okImageList, command=self.handleButton, pos=(0, 0, -0.14))
+ self.doneButton = DirectButton(parent=self.frame, relief=None, image=cancelImageList, command=self.handleButton, pos=(0, 0, -0.14))
+ if len(message) == 1:
+ self.nextButton.hide()
+ else:
+ self.doneButton.hide()
+ return
+
+ def handleButton(self):
+ self.messageIndex += 1
+ if self.messageIndex >= len(self.message):
+ self.cleanup()
+ return
+ self.frame['text'] = self.message[self.messageIndex]
+ if self.messageIndex + 1 == len(self.message):
+ self.nextButton.hide()
+ self.doneButton.show()
+
+ def cleanup(self):
+ if self.frame:
+ self.frame.destroy()
+ self.frame = None
+ if self.nextButton:
+ self.nextButton.destroy()
+ self.nextButton = None
+ if self.doneButton:
+ self.doneButton.destroy()
+ self.doneButton = None
+ return
+
+ def __handleButton(self, value):
+ self.cleanup()
diff --git a/toontown/catalog/CatalogPetTrickItem.py b/toontown/catalog/CatalogPetTrickItem.py
new file mode 100644
index 0000000..b05260f
--- /dev/null
+++ b/toontown/catalog/CatalogPetTrickItem.py
@@ -0,0 +1,103 @@
+import CatalogItem
+from toontown.pets import PetTricks
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from otp.otpbase import OTPLocalizer
+from direct.interval.IntervalGlobal import *
+
+class CatalogPetTrickItem(CatalogItem.CatalogItem):
+ sequenceNumber = 0
+ petPicture = None
+
+ def makeNewItem(self, trickId):
+ self.trickId = trickId
+ CatalogItem.CatalogItem.makeNewItem(self)
+
+ def getPurchaseLimit(self):
+ return 1
+
+ def reachedPurchaseLimit(self, avatar):
+ if self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or self in avatar.awardMailboxContents or self in avatar.onAwardOrder:
+ return 1
+ return self.trickId in avatar.petTrickPhrases
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ return TTLocalizer.CatalogAcceptPet
+ return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode)
+
+ def saveHistory(self):
+ return 1
+
+ def getTypeName(self):
+ return TTLocalizer.PetTrickTypeName
+
+ def getName(self):
+ phraseId = PetTricks.TrickId2scIds[self.trickId][0]
+ return OTPLocalizer.SpeedChatStaticText[phraseId]
+
+ def recordPurchase(self, avatar, optional):
+ avatar.petTrickPhrases.append(self.trickId)
+ avatar.d_setPetTrickPhrases(avatar.petTrickPhrases)
+ return ToontownGlobals.P_ItemAvailable
+
+ def getPicture(self, avatar):
+ from toontown.pets import PetDNA, Pet
+ pet = Pet.Pet(forGui=1)
+ dna = avatar.petDNA
+ if dna == None:
+ dna = PetDNA.getRandomPetDNA()
+ pet.setDNA(dna)
+ pet.setH(180)
+ model, ival = self.makeFrameModel(pet, 0)
+ pet.setScale(2.0)
+ pet.setP(-40)
+ track = PetTricks.getTrickIval(pet, self.trickId)
+ name = 'petTrick-item-%s' % self.sequenceNumber
+ CatalogPetTrickItem.sequenceNumber += 1
+ if track != None:
+ track = Sequence(Sequence(track), ActorInterval(pet, 'neutral', duration=2), name=name)
+ else:
+ pet.animFSM.request('neutral')
+ track = Sequence(Wait(4), name=name)
+ self.petPicture = pet
+ self.hasPicture = True
+ return (model, track)
+
+ def cleanupPicture(self):
+ CatalogItem.CatalogItem.cleanupPicture(self)
+ self.petPicture.delete()
+ self.petPicture = None
+ return
+
+ def output(self, store = -1):
+ return 'CatalogPetTrickItem(%s%s)' % (self.trickId, self.formatOptionalData(store))
+
+ def compareTo(self, other):
+ return self.trickId - other.trickId
+
+ def getHashContents(self):
+ return self.trickId
+
+ def getBasePrice(self):
+ return 500
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store)
+ self.trickId = di.getUint8()
+ self.dna = None
+ if self.trickId not in PetTricks.TrickId2scIds:
+ raise ValueError
+ return
+
+ def encodeDatagram(self, dg, store):
+ CatalogItem.CatalogItem.encodeDatagram(self, dg, store)
+ dg.addUint8(self.trickId)
+
+
+def getAllPetTricks():
+ list = []
+ for trickId in PetTricks.TrickId2scIds.keys():
+ list.append(CatalogPetTrickItem(trickId))
+
+ return list
diff --git a/toontown/catalog/CatalogPoleItem.py b/toontown/catalog/CatalogPoleItem.py
new file mode 100644
index 0000000..1011b8e
--- /dev/null
+++ b/toontown/catalog/CatalogPoleItem.py
@@ -0,0 +1,112 @@
+import CatalogItem
+from toontown.toonbase import ToontownGlobals
+from toontown.fishing import FishGlobals
+from direct.actor import Actor
+from toontown.toonbase import TTLocalizer
+from direct.interval.IntervalGlobal import *
+
+class CatalogPoleItem(CatalogItem.CatalogItem):
+ sequenceNumber = 0
+
+ def makeNewItem(self, rodId):
+ self.rodId = rodId
+ CatalogItem.CatalogItem.makeNewItem(self)
+
+ def getPurchaseLimit(self):
+ return 1
+
+ def reachedPurchaseLimit(self, avatar):
+ return avatar.getFishingRod() >= self.rodId or self in avatar.onOrder or self in avatar.mailboxContents
+
+ def saveHistory(self):
+ return 1
+
+ def getTypeName(self):
+ return TTLocalizer.PoleTypeName
+
+ def getName(self):
+ return TTLocalizer.FishingRod % TTLocalizer.FishingRodNameDict[self.rodId]
+
+ def recordPurchase(self, avatar, optional):
+ if self.rodId < 0 or self.rodId > FishGlobals.MaxRodId:
+ self.notify.warning('Invalid fishing pole: %s for avatar %s' % (self.rodId, avatar.doId))
+ return ToontownGlobals.P_InvalidIndex
+ if self.rodId < avatar.getFishingRod():
+ self.notify.warning('Avatar already has pole: %s for avatar %s' % (self.rodId, avatar.doId))
+ return ToontownGlobals.P_ItemUnneeded
+ avatar.b_setFishingRod(self.rodId)
+ return ToontownGlobals.P_ItemAvailable
+
+ def isGift(self):
+ return 0
+
+ def getDeliveryTime(self):
+ return 24 * 60
+
+ def getPicture(self, avatar):
+ rodPath = FishGlobals.RodFileDict.get(self.rodId)
+ pole = Actor.Actor(rodPath, {'cast': 'phase_4/models/props/fishing-pole-chan'})
+ pole.setPosHpr(1.47, 0, -1.67, 90, 55, -90)
+ pole.setScale(0.8)
+ pole.setDepthTest(1)
+ pole.setDepthWrite(1)
+ frame = self.makeFrame()
+ frame.attachNewNode(pole.node())
+ name = 'pole-item-%s' % self.sequenceNumber
+ CatalogPoleItem.sequenceNumber += 1
+ track = Sequence(Func(pole.pose, 'cast', 130), Wait(100), name=name)
+ self.hasPicture = True
+ return (frame, track)
+
+ def getAcceptItemErrorText(self, retcode):
+ if retcode == ToontownGlobals.P_ItemAvailable:
+ return TTLocalizer.CatalogAcceptPole
+ elif retcode == ToontownGlobals.P_ItemUnneeded:
+ return TTLocalizer.CatalogAcceptPoleUnneeded
+ return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode)
+
+ def output(self, store = -1):
+ return 'CatalogPoleItem(%s%s)' % (self.rodId, self.formatOptionalData(store))
+
+ def getFilename(self):
+ return FishGlobals.RodFileDict.get(self.rodId)
+
+ def compareTo(self, other):
+ return self.rodId - other.rodId
+
+ def getHashContents(self):
+ return self.rodId
+
+ def getBasePrice(self):
+ return FishGlobals.RodPriceDict[self.rodId]
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store)
+ self.rodId = di.getUint8()
+ price = FishGlobals.RodPriceDict[self.rodId]
+
+ def encodeDatagram(self, dg, store):
+ CatalogItem.CatalogItem.encodeDatagram(self, dg, store)
+ dg.addUint8(self.rodId)
+
+
+def nextAvailablePole(avatar, duplicateItems):
+ rodId = avatar.getFishingRod() + 1
+ if rodId > FishGlobals.MaxRodId:
+ return None
+ item = CatalogPoleItem(rodId)
+ while item in avatar.onOrder or item in avatar.mailboxContents:
+ rodId += 1
+ if rodId > FishGlobals.MaxRodId:
+ return None
+ item = CatalogPoleItem(rodId)
+
+ return item
+
+
+def getAllPoles():
+ list = []
+ for rodId in range(0, FishGlobals.MaxRodId + 1):
+ list.append(CatalogPoleItem(rodId))
+
+ return list
diff --git a/toontown/catalog/CatalogRentalItem.py b/toontown/catalog/CatalogRentalItem.py
new file mode 100644
index 0000000..1027a41
--- /dev/null
+++ b/toontown/catalog/CatalogRentalItem.py
@@ -0,0 +1,135 @@
+import CatalogItem
+import time
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from otp.otpbase import OTPLocalizer
+from direct.interval.IntervalGlobal import *
+from toontown.toontowngui import TTDialog
+
+class CatalogRentalItem(CatalogItem.CatalogItem):
+
+ def makeNewItem(self, typeIndex, duration, cost):
+ self.typeIndex = typeIndex
+ self.duration = duration
+ self.cost = cost
+ CatalogItem.CatalogItem.makeNewItem(self)
+
+ def getRentalType(self):
+ return self.typeIndex
+
+ def getDuration(self):
+ return self.duration
+
+ def getPurchaseLimit(self):
+ return 0
+
+ def reachedPurchaseLimit(self, avatar):
+ if self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or self in avatar.awardMailboxContents or self in avatar.onAwardOrder:
+ return 1
+ return 0
+
+ def saveHistory(self):
+ return 1
+
+ def getTypeName(self):
+ return TTLocalizer.RentalTypeName
+
+ def getName(self):
+ hours = int(self.duration / 60)
+ if self.typeIndex == ToontownGlobals.RentalCannon:
+ return '%s %s %s %s' % (hours,
+ TTLocalizer.RentalHours,
+ TTLocalizer.RentalOf,
+ TTLocalizer.RentalCannon)
+ elif self.typeIndex == ToontownGlobals.RentalGameTable:
+ return '%s %s %s' % (hours, TTLocalizer.RentalHours, TTLocalizer.RentalGameTable)
+ else:
+ return TTLocalizer.RentalTypeName
+
+ def recordPurchase(self, avatar, optional):
+ self.notify.debug('rental -- record purchase')
+ if avatar:
+ self.notify.debug('rental -- has avater')
+ estate = simbase.air.estateMgr.estate.get(avatar.doId)
+ if estate:
+ self.notify.debug('rental -- has estate')
+ estate.rentItem(self.typeIndex, self.duration)
+ else:
+ self.notify.debug('rental -- something not there')
+ return ToontownGlobals.P_ItemAvailable
+
+ def getPicture(self, avatar):
+ scale = 1
+ heading = 0
+ pitch = 30
+ roll = 0
+ spin = 1
+ down = -1
+ if self.typeIndex == ToontownGlobals.RentalCannon:
+ model = loader.loadModel('phase_4/models/minigames/toon_cannon')
+ scale = 0.5
+ heading = 45
+ elif self.typeIndex == ToontownGlobals.RentalGameTable:
+ model = loader.loadModel('phase_6/models/golf/game_table')
+ self.hasPicture = True
+ return self.makeFrameModel(model, spin)
+
+ def output(self, store = -1):
+ return 'CatalogRentalItem(%s%s)' % (self.typeIndex, self.formatOptionalData(store))
+
+ def compareTo(self, other):
+ return self.typeIndex - other.typeIndex
+
+ def getHashContents(self):
+ return self.typeIndex
+
+ def getBasePrice(self):
+ if self.typeIndex == ToontownGlobals.RentalCannon:
+ return self.cost
+ elif self.typeIndex == ToontownGlobals.RentalGameTable:
+ return self.cost
+ else:
+ return 50
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store)
+ if versionNumber >= 7:
+ self.cost = di.getUint16()
+ else:
+ self.cost = 1000
+ self.duration = di.getUint16()
+ self.typeIndex = di.getUint16()
+
+ def encodeDatagram(self, dg, store):
+ CatalogItem.CatalogItem.encodeDatagram(self, dg, store)
+ dg.addUint16(self.cost)
+ dg.addUint16(self.duration)
+ dg.addUint16(self.typeIndex)
+
+ def getDeliveryTime(self):
+ return 1
+
+ def isRental(self):
+ return 1
+
+ def acceptItem(self, mailbox, index, callback):
+ self.confirmRent = TTDialog.TTGlobalDialog(doneEvent='confirmRent', message=TTLocalizer.MessageConfirmRent, command=Functor(self.handleRentConfirm, mailbox, index, callback), style=TTDialog.TwoChoice)
+ self.confirmRent.show()
+
+ def handleRentConfirm(self, mailbox, index, callback, choice):
+ if choice > 0:
+ mailbox.acceptItem(self, index, callback)
+ else:
+ callback(ToontownGlobals.P_UserCancelled, self, index)
+ if self.confirmRent:
+ self.confirmRent.cleanup()
+ self.confirmRent = None
+ return
+
+
+def getAllRentalItems():
+ list = []
+ for rentalType in (ToontownGlobals.RentalCannon,):
+ list.append(CatalogRentalItem(rentalType, 2880, 1000))
+
+ return list
diff --git a/toontown/catalog/CatalogScreen.py b/toontown/catalog/CatalogScreen.py
new file mode 100644
index 0000000..111d345
--- /dev/null
+++ b/toontown/catalog/CatalogScreen.py
@@ -0,0 +1,1099 @@
+from pandac.PandaModules import *
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from direct.gui.DirectScrolledList import *
+from toontown.toonbase import ToontownGlobals
+from toontown.toontowngui import TTDialog
+import CatalogItem
+import CatalogInvalidItem
+import CatalogFurnitureItem
+from toontown.toonbase import TTLocalizer
+import CatalogItemPanel
+import CatalogItemTypes
+from direct.actor import Actor
+import random
+from toontown.toon import DistributedToon
+from direct.directnotify import DirectNotifyGlobal
+NUM_CATALOG_ROWS = 3
+NUM_CATALOG_COLS = 2
+CatalogPanelCenters = [[Point3(-0.95, 0, 0.91), Point3(-0.275, 0, 0.91)], [Point3(-0.95, 0, 0.275), Point3(-0.275, 0, 0.275)], [Point3(-0.95, 0, -0.4), Point3(-0.275, 0, -0.4)]]
+CatalogPanelColors = {CatalogItemTypes.FURNITURE_ITEM: Vec4(0.733, 0.78, 0.933, 1.0),
+ CatalogItemTypes.CHAT_ITEM: Vec4(0.922, 0.922, 0.753, 1.0),
+ CatalogItemTypes.CLOTHING_ITEM: Vec4(0.918, 0.69, 0.69, 1.0),
+ CatalogItemTypes.EMOTE_ITEM: Vec4(0.922, 0.922, 0.753, 1.0),
+ CatalogItemTypes.WALLPAPER_ITEM: Vec4(0.749, 0.984, 0.608, 1.0),
+ CatalogItemTypes.WINDOW_ITEM: Vec4(0.827, 0.91, 0.659, 1.0)}
+
+class CatalogScreen(DirectFrame):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CatalogScreen')
+
+ def __init__(self, parent = aspect2d, **kw):
+ guiItems = loader.loadModel('phase_5.5/models/gui/catalog_gui')
+ background = guiItems.find('**/catalog_background')
+ guiButton = loader.loadModel('phase_3/models/gui/quit_button')
+ guiBack = loader.loadModel('phase_5.5/models/gui/package_delivery_panel')
+ optiondefs = (('scale', 0.667, None),
+ ('pos', (0, 1, 0.025), None),
+ ('phone', None, None),
+ ('doneEvent', None, None),
+ ('image', background, None),
+ ('relief', None, None))
+ self.defineoptions(kw, optiondefs)
+ DirectFrame.__init__(self, parent)
+ self.friendGiftIndex = 0
+ self.friendGiftHandle = None
+ self.frienddoId = None
+ self.receiverName = 'Error Nameless Toon'
+ self.friends = {}
+ self.family = {}
+ self.ffList = []
+ self.textRolloverColor = Vec4(1, 1, 0, 1)
+ self.textDownColor = Vec4(0.5, 0.9, 1, 1)
+ self.textDisabledColor = Vec4(0.4, 0.8, 0.4, 1)
+ self.giftAvatar = None
+ self.gotAvatar = 0
+ self.allowGetDetails = 1
+ self.load(guiItems, guiButton, guiBack)
+ self.initialiseoptions(CatalogScreen)
+ self.enableBackorderCatalogButton()
+ self.setMaxPageIndex(self.numNewPages)
+ self.setPageIndex(-1)
+ self.showPageItems()
+ self.hide()
+ self.clarabelleChatNP = None
+ self.clarabelleChatBalloon = None
+ self.gifting = -1
+ self.createdGiftGui = None
+ self.viewing = None
+ return
+
+ def show(self):
+ self.accept('CatalogItemPurchaseRequest', self.__handlePurchaseRequest)
+ self.accept('CatalogItemGiftPurchaseRequest', self.__handleGiftPurchaseRequest)
+ self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange)
+ self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__bankMoneyChange)
+ self.accept(localAvatar.uniqueName('emblemsChange'), self.__emblemChange)
+ deliveryText = 'setDeliverySchedule-%s' % base.localAvatar.doId
+ self.accept(deliveryText, self.remoteUpdate)
+ render.hide()
+ DirectFrame.show(self)
+
+ def clarabelleGreeting(task):
+ self.setClarabelleChat(TTLocalizer.CatalogGreeting)
+
+ def clarabelleHelpText1(task):
+ self.setClarabelleChat(TTLocalizer.CatalogHelpText1)
+
+ taskMgr.doMethodLater(1.0, clarabelleGreeting, 'clarabelleGreeting')
+ taskMgr.doMethodLater(12.0, clarabelleHelpText1, 'clarabelleHelpText1')
+ if hasattr(self, 'giftToggle'):
+ self.giftToggle['state'] = DGG.DISABLED
+ self.giftToggle['text'] = TTLocalizer.CatalogGiftToggleWait
+ base.cr.deliveryManager.sendAck()
+ self.accept('DeliveryManagerAck', self.__handleUDack)
+ taskMgr.doMethodLater(10.0, self.__handleNoAck, 'ackTimeOut')
+
+ def hide(self):
+ self.ignore('CatalogItemPurchaseRequest')
+ self.ignore('CatalogItemGiftPurchaseRequest')
+ self.ignore('DeliveryManagerAck')
+ taskMgr.remove('ackTimeOut')
+ self.ignore(localAvatar.uniqueName('moneyChange'))
+ self.ignore(localAvatar.uniqueName('bankMoneyChange'))
+ self.ignore(localAvatar.uniqueName('emblemsChange'))
+ deliveryText = 'setDeliverySchedule-%s' % base.localAvatar.doId
+ self.ignore(deliveryText)
+ render.show()
+ DirectFrame.hide(self)
+
+ def setNumNewPages(self, numNewPages):
+ self.numNewPages = numNewPages
+
+ def setNumBackPages(self, numBackPages):
+ self.numBackPages = numBackPages
+
+ def setNumLoyaltyPages(self, numLoyaltyPages):
+ self.numLoyaltyPages = numLoyaltyPages
+
+ def setNumEmblemPages(self, numEmblemPages):
+ self.numEmblemPages = numEmblemPages
+
+ def setPageIndex(self, index):
+ self.pageIndex = index
+
+ def setMaxPageIndex(self, numPages):
+ self.maxPageIndex = max(numPages - 1, -1)
+
+ def enableBackorderCatalogButton(self):
+ self.backCatalogButton['state'] = DGG.NORMAL
+ self.newCatalogButton['state'] = DGG.DISABLED
+ self.loyaltyCatalogButton['state'] = DGG.DISABLED
+ self.emblemCatalogButton['state'] = DGG.DISABLED
+
+ def enableNewCatalogButton(self):
+ self.backCatalogButton['state'] = DGG.DISABLED
+ self.newCatalogButton['state'] = DGG.NORMAL
+ self.loyaltyCatalogButton['state'] = DGG.DISABLED
+ self.emblemCatalogButton['state'] = DGG.DISABLED
+
+ def enableLoyaltyCatalogButton(self):
+ self.backCatalogButton['state'] = DGG.DISABLED
+ self.newCatalogButton['state'] = DGG.DISABLED
+ self.loyaltyCatalogButton['state'] = DGG.NORMAL
+ self.emblemCatalogButton['state'] = DGG.DISABLED
+
+ def enableEmblemCatalogButton(self):
+ self.backCatalogButton['state'] = DGG.DISABLED
+ self.newCatalogButton['state'] = DGG.DISABLED
+ self.loyaltyCatalogButton['state'] = DGG.DISABLED
+ self.emblemCatalogButton['state'] = DGG.NORMAL
+
+ def modeBackorderCatalog(self):
+ self.backCatalogButton['state'] = DGG.DISABLED
+ self.newCatalogButton['state'] = DGG.NORMAL
+ self.loyaltyCatalogButton['state'] = DGG.NORMAL
+ self.emblemCatalogButton['state'] = DGG.NORMAL
+
+ def modeNewCatalog(self):
+ self.backCatalogButton['state'] = DGG.NORMAL
+ self.newCatalogButton['state'] = DGG.DISABLED
+ self.loyaltyCatalogButton['state'] = DGG.NORMAL
+ self.emblemCatalogButton['state'] = DGG.NORMAL
+
+ def modeLoyaltyCatalog(self):
+ self.backCatalogButton['state'] = DGG.NORMAL
+ self.newCatalogButton['state'] = DGG.NORMAL
+ self.loyaltyCatalogButton['state'] = DGG.DISABLED
+ self.emblemCatalogButton['state'] = DGG.NORMAL
+
+ def modeEmblemCatalog(self):
+ self.backCatalogButton['state'] = DGG.NORMAL
+ self.newCatalogButton['state'] = DGG.NORMAL
+ self.loyaltyCatalogButton['state'] = DGG.NORMAL
+ self.emblemCatalogButton['state'] = DGG.DISABLED
+
+ def showNewItems(self, index = None):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: CATALOG: New item')
+ taskMgr.remove('clarabelleHelpText1')
+ messenger.send('wakeup')
+ self.viewing = 'New'
+ self.modeNewCatalog()
+ self.setMaxPageIndex(self.numNewPages)
+ if self.numNewPages == 0:
+ self.setPageIndex(-1)
+ elif index is not None:
+ self.setPageIndex(index)
+ else:
+ self.setPageIndex(0)
+ self.showPageItems()
+ return
+
+ def showBackorderItems(self, index = None):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: CATALOG: Backorder item')
+ taskMgr.remove('clarabelleHelpText1')
+ messenger.send('wakeup')
+ self.viewing = 'Backorder'
+ self.modeBackorderCatalog()
+ self.setMaxPageIndex(self.numBackPages)
+ if self.numBackPages == 0:
+ self.setPageIndex(-1)
+ elif index is not None:
+ self.setPageIndex(index)
+ else:
+ self.setPageIndex(0)
+ self.showPageItems()
+ return
+
+ def showLoyaltyItems(self, index = None):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: CATALOG: Special item')
+ taskMgr.remove('clarabelleHelpText1')
+ messenger.send('wakeup')
+ self.viewing = 'Loyalty'
+ self.modeLoyaltyCatalog()
+ self.setMaxPageIndex(self.numLoyaltyPages)
+ if self.numLoyaltyPages == 0:
+ self.setPageIndex(-1)
+ elif index is not None:
+ self.setPageIndex(index)
+ else:
+ self.setPageIndex(0)
+ self.showPageItems()
+ return
+
+ def showEmblemItems(self, index = None):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: CATALOG: Emblem item')
+ taskMgr.remove('clarabelleHelpText1')
+ messenger.send('wakeup')
+ self.viewing = 'Emblem'
+ self.modeEmblemCatalog()
+ self.setMaxPageIndex(self.numEmblemPages)
+ if self.numEmblemPages == 0:
+ self.setPageIndex(-1)
+ elif index is not None:
+ self.setPageIndex(index)
+ else:
+ self.setPageIndex(0)
+ self.showPageItems()
+ return
+
+ def showNextPage(self):
+ taskMgr.remove('clarabelleHelpText1')
+ messenger.send('wakeup')
+ self.pageIndex = self.pageIndex + 1
+ if self.viewing == None:
+ self.modeNewCatalog()
+ self.viewing == 'New'
+ if self.viewing == 'New' and self.pageIndex > self.maxPageIndex and self.numBackPages > 0:
+ self.showBackorderItems()
+ if self.viewing == 'New' and self.pageIndex > self.maxPageIndex and self.numLoyaltyPages > 0:
+ self.showLoyaltyItems()
+ elif self.viewing == 'Backorder' and self.pageIndex > self.maxPageIndex and self.numLoyaltyPages > 0:
+ self.showLoyaltyItems()
+ elif self.viewing == 'Loyalty' and self.pageIndex > self.maxPageIndex and self.numEmblemPages > 0:
+ self.showEmblemItems()
+ else:
+ self.pageIndex = min(self.pageIndex, self.maxPageIndex)
+ self.showPageItems()
+ return
+
+ def showBackPage(self):
+ taskMgr.remove('clarabelleHelpText1')
+ messenger.send('wakeup')
+ self.pageIndex = self.pageIndex - 1
+ if self.viewing == 'Backorder' and self.pageIndex < 0 and self.numNewPages > 0:
+ self.showNewItems(self.numNewPages - 1)
+ elif self.viewing == 'Loyalty' and self.pageIndex < 0 and self.numBackPages > 0:
+ self.showBackorderItems(self.numBackPages - 1)
+ elif self.viewing == 'Emblem' and self.pageIndex < 0 and self.numLoyaltyPages > 0:
+ self.showLoyaltyItems(self.numLoyaltyPages - 1)
+ else:
+ self.pageIndex = max(self.pageIndex, -1)
+ self.showPageItems()
+
+ def showPageItems(self):
+ self.hidePages()
+ if self.viewing == None:
+ self.viewing = 'New'
+ if self.pageIndex < 0:
+ self.closeCover()
+ else:
+ if self.pageIndex == 0:
+ self.openCover()
+ if self.viewing == 'New':
+ page = self.pageList[self.pageIndex]
+ newOrBackOrLoyalty = 0
+ elif self.viewing == 'Backorder':
+ page = self.backPageList[self.pageIndex]
+ newOrBackOrLoyalty = 1
+ elif self.viewing == 'Loyalty':
+ page = self.loyaltyPageList[self.pageIndex]
+ newOrBackOrLoyalty = 2
+ elif self.viewing == 'Emblem':
+ page = self.emblemPageList[self.pageIndex]
+ newOrBackOrLoyalty = 3
+ page.show()
+ for panel in self.panelDict[page.id()]:
+ panel.load()
+ if panel.ival:
+ panel.ival.loop()
+ self.visiblePanels.append(panel)
+
+ pIndex = 0
+ randGen = random.Random()
+ randGen.seed(base.localAvatar.catalogScheduleCurrentWeek + (self.pageIndex << 8) + (newOrBackOrLoyalty << 16))
+ for i in range(NUM_CATALOG_ROWS):
+ for j in range(NUM_CATALOG_COLS):
+ if pIndex < len(self.visiblePanels):
+ type = self.visiblePanels[pIndex]['item'].getTypeCode()
+ self.squares[i][j].setColor(CatalogPanelColors.values()[randGen.randint(0, len(CatalogPanelColors) - 1)])
+ cs = 0.7 + 0.3 * randGen.random()
+ self.squares[i][j].setColorScale(0.7 + 0.3 * randGen.random(), 0.7 + 0.3 * randGen.random(), 0.7 + 0.3 * randGen.random(), 1)
+ else:
+ self.squares[i][j].setColor(CatalogPanelColors[CatalogItemTypes.CHAT_ITEM])
+ self.squares[i][j].clearColorScale()
+ pIndex += 1
+
+ if self.viewing == 'New':
+ text = TTLocalizer.CatalogNew
+ elif self.viewing == 'Loyalty':
+ text = TTLocalizer.CatalogLoyalty
+ elif self.viewing == 'Backorder':
+ text = TTLocalizer.CatalogBackorder
+ elif self.viewing == 'Emblem':
+ text = TTLocalizer.CatalogEmblem
+ self.pageLabel['text'] = text + ' - %d' % (self.pageIndex + 1)
+ if self.pageIndex < self.maxPageIndex:
+ self.nextPageButton.show()
+ elif self.viewing == 'New' and self.numBackPages == 0 and self.numLoyaltyPages == 0:
+ self.nextPageButton.hide()
+ elif self.viewing == 'Backorder' and self.numLoyaltyPages == 0:
+ self.nextPageButton.hide()
+ elif self.viewing == 'Loyalty' and self.numEmblemPages == 0:
+ self.nextPageButton.hide()
+ elif self.viewing == 'Loyalty' and self.numEmblemPages > 0:
+ self.nextPageButton.show()
+ elif self.viewing == 'Emblem':
+ self.nextPageButton.hide()
+ self.adjustForSound()
+ self.update()
+ return
+
+ def adjustForSound(self):
+ numEmoteItems = 0
+ emotePanels = []
+ for visIndex in xrange(len(self.visiblePanels)):
+ panel = self.visiblePanels[visIndex]
+ item = panel['item']
+ catalogType = item.getTypeCode()
+ if catalogType == CatalogItemTypes.EMOTE_ITEM:
+ numEmoteItems += 1
+ emotePanels.append(panel)
+ else:
+ panel.soundOnButton.hide()
+ panel.soundOffButton.hide()
+
+ if numEmoteItems == 1:
+ emotePanels[0].handleSoundOnButton()
+ elif numEmoteItems > 1:
+ for panel in emotePanels:
+ panel.handleSoundOffButton()
+
+ def hidePages(self):
+ for page in self.pageList:
+ page.hide()
+
+ for page in self.backPageList:
+ page.hide()
+
+ for page in self.loyaltyPageList:
+ page.hide()
+
+ for page in self.emblemPageList:
+ page.hide()
+
+ for panel in self.visiblePanels:
+ if panel.ival:
+ panel.ival.finish()
+
+ self.visiblePanels = []
+
+ def openCover(self):
+ self.cover.hide()
+ self.hideDummyTabs()
+ self.backPageButton.show()
+ self.pageLabel.show()
+
+ def closeCover(self):
+ self.cover.show()
+ self.showDummyTabs()
+ self.nextPageButton.show()
+ self.backPageButton.hide()
+ self.pageLabel.hide()
+ self.hidePages()
+
+ def showDummyTabs(self):
+ if self.numNewPages > 0:
+ self.newCatalogButton2.show()
+ if self.numBackPages > 0:
+ self.backCatalogButton2.show()
+ if self.numLoyaltyPages > 0:
+ self.loyaltyCatalogButton2.show()
+ if self.numEmblemPages > 0:
+ self.emblemCatalogButton2.show()
+ self.newCatalogButton.hide()
+ self.backCatalogButton.hide()
+ self.loyaltyCatalogButton.hide()
+ self.emblemCatalogButton.hide()
+
+ def hideDummyTabs(self):
+ self.newCatalogButton2.hide()
+ self.backCatalogButton2.hide()
+ self.loyaltyCatalogButton2.hide()
+ self.emblemCatalogButton2.hide()
+ if self.numNewPages > 0:
+ self.newCatalogButton.show()
+ if self.numBackPages > 0:
+ self.backCatalogButton.show()
+ if self.numLoyaltyPages > 0:
+ self.loyaltyCatalogButton.show()
+ if self.numEmblemPages > 0:
+ self.emblemCatalogButton.show()
+
+ def packPages(self, panelList, pageList, prefix):
+ i = 0
+ j = 0
+ numPages = 0
+ pageName = prefix + '_page%d' % numPages
+ for item in panelList:
+ if i == 0 and j == 0:
+ numPages += 1
+ pageName = prefix + '_page%d' % numPages
+ page = self.base.attachNewNode(pageName)
+ pageList.append(page)
+ item.reparentTo(page)
+ item.setPos(CatalogPanelCenters[i][j])
+ itemList = self.panelDict.get(page.id(), [])
+ itemList.append(item)
+ self.panelDict[page.id()] = itemList
+ j += 1
+ if j == NUM_CATALOG_COLS:
+ j = 0
+ i += 1
+ if i == NUM_CATALOG_ROWS:
+ i = 0
+
+ return numPages
+
+ def load(self, guiItems, guiButton, guiBack):
+ self.pageIndex = -1
+ self.maxPageIndex = 0
+ self.numNewPages = 0
+ self.numBackPages = 5
+ self.numLoyaltyPages = 0
+ self.viewing = 'New'
+ self.panelList = []
+ self.backPanelList = []
+ self.pageList = []
+ self.backPageList = []
+ self.loyaltyPanelList = []
+ self.loyaltyPageList = []
+ self.emblemPanelList = []
+ self.emblemPageList = []
+ self.panelDict = {}
+ self.visiblePanels = []
+ self.responseDialog = None
+ catalogBase = guiItems.find('**/catalog_base')
+ self.base = DirectLabel(self, relief=None, image=catalogBase)
+ newDown = guiItems.find('**/new1')
+ newUp = guiItems.find('**/new2')
+ backDown = guiItems.find('**/previous2')
+ backUp = guiItems.find('**/previous1')
+ giftToggleUp = guiItems.find('**/giftButtonUp')
+ giftToggleDown = guiItems.find('**/giftButtonDown')
+ giftFriends = guiItems.find('**/gift_names')
+ oldLift = 0.4
+ lift = 0.4
+ liftDiff = lift - oldLift
+ lift2 = 0.05
+ smash = 0.75
+ priceScale = 0.15
+ emblemIcon = loader.loadModel('phase_3.5/models/gui/tt_m_gui_gen_emblemIcons')
+ silverModel = emblemIcon.find('**/tt_t_gui_gen_emblemSilver')
+ goldModel = emblemIcon.find('**/tt_t_gui_gen_emblemGold')
+ self.silverLabel = DirectLabel(parent=self, relief=None, pos=(1.05, 0, -0.6), scale=priceScale, image=silverModel, image_pos=(-0.4, 0, 0.4), text=str(localAvatar.emblems[ToontownGlobals.EmblemTypes.Silver]), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft)
+ base.silverLabel = self.silverLabel
+ self.goldLabel = DirectLabel(parent=self, relief=None, pos=(1.05, 0, -0.8), scale=priceScale, image=goldModel, image_pos=(-0.4, 0, 0.4), text=str(localAvatar.emblems[ToontownGlobals.EmblemTypes.Gold]), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft)
+ base.goldLabel = self.goldLabel
+ if not base.cr.wantEmblems:
+ self.hideEmblems()
+ self.newCatalogButton = DirectButton(self.base, relief=None, pos=(0, 0, 0.17), frameSize=(-0.2,
+ 0.25,
+ 0.45,
+ 1.2), image=[newDown,
+ newDown,
+ newDown,
+ newUp], image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, lift), pressEffect=0, command=self.showNewItems, text=TTLocalizer.CatalogNew, text_font=ToontownGlobals.getSignFont(), text_pos=(-0.4 - lift, 0.13), text3_pos=(-0.4 - lift, 0.1), text_scale=0.08, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0))
+ self.newCatalogButton.hide()
+ self.newCatalogButton2 = DirectButton(self.base, relief=None, pos=(0, 0, 0.17), frameSize=(-0.2,
+ 0.25,
+ 0.45,
+ 1.2), image=newDown, image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, lift), pressEffect=0, command=self.showNewItems, text=TTLocalizer.CatalogNew, text_font=ToontownGlobals.getSignFont(), text_pos=(-0.4 - lift, 0.13), text_scale=0.08, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0))
+ self.newCatalogButton2.hide()
+ self.backCatalogButton = DirectButton(self.base, relief=None, pos=(0, 0, 0.269), frameSize=(-0.2,
+ 0.25,
+ -0.2,
+ 0.4), image=[backDown,
+ backDown,
+ backDown,
+ backUp], image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, lift), pressEffect=0, command=self.showBackorderItems, text=TTLocalizer.CatalogBackorder, text_font=ToontownGlobals.getSignFont(), text_pos=(0.25 - lift, 0.132), text3_pos=(0.25 - lift, 0.112), text_scale=TTLocalizer.CSbackCatalogButton, text_fg=(0.392, 0.549, 0.627, 1.0), text2_fg=(0.392, 0.349, 0.427, 1.0))
+ self.backCatalogButton.hide()
+ self.backCatalogButton2 = DirectButton(self.base, relief=None, pos=(0, 0, 0.269), frameSize=(-0.2,
+ 0.25,
+ -0.2,
+ 0.4), image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, lift), image=backDown, pressEffect=0, command=self.showBackorderItems, text=TTLocalizer.CatalogBackorder, text_font=ToontownGlobals.getSignFont(), text_pos=(0.25 - lift, 0.132), text_scale=TTLocalizer.CSbackCatalogButton, text_fg=(0.392, 0.549, 0.627, 1.0), text2_fg=(0.392, 0.349, 0.427, 1.0))
+ self.backCatalogButton2.hide()
+ self.loyaltyCatalogButton = DirectButton(self.base, relief=None, pos=(0, 0, 0.469), frameSize=(-0.2,
+ 0.25,
+ -0.85,
+ -0.3), image=[newDown,
+ newDown,
+ newDown,
+ newUp], image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, -1.4 + lift), pressEffect=0, command=self.showLoyaltyItems, text=TTLocalizer.CatalogLoyalty, text_font=ToontownGlobals.getSignFont(), text_pos=(1.0 - lift, 0.132), text3_pos=(1.0 - lift, 0.112), text_scale=0.065, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0))
+ self.loyaltyCatalogButton.hide()
+ self.loyaltyCatalogButton2 = DirectButton(self.base, relief=None, pos=(0, 0, 0.469), frameSize=(-0.2,
+ 0.25,
+ -0.85,
+ -0.3), image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, -1.4 + lift), image=newDown, pressEffect=0, command=self.showLoyaltyItems, text=TTLocalizer.CatalogLoyalty, text_font=ToontownGlobals.getSignFont(), text_pos=(1.0 - lift, 0.132), text_scale=0.065, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0))
+ self.loyaltyCatalogButton2.hide()
+ self.emblemCatalogButton = DirectButton(self.base, relief=None, pos=(0, 0, 1.05), frameSize=(-0.2,
+ 0.25,
+ -2.0,
+ -1.45), image=[backDown,
+ backDown,
+ backDown,
+ backUp], image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, -1.9 + lift), pressEffect=0, command=self.showEmblemItems, text=TTLocalizer.CatalogEmblem, text_font=ToontownGlobals.getSignFont(), text_pos=(1.75, 0.132), text3_pos=(1.75, 0.112), text_scale=0.065, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0))
+ self.emblemCatalogButton.hide()
+ self.emblemCatalogButton2 = DirectButton(self.base, relief=None, pos=(0, 0, 1.05), frameSize=(-0.2,
+ 0.25,
+ -2.0,
+ -1.45), image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, -1.9 + lift), image=backDown, pressEffect=0, command=self.showEmblemItems, text=TTLocalizer.CatalogEmblem, text_font=ToontownGlobals.getSignFont(), text_pos=(1.75, 0.132), text_scale=0.065, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0))
+ self.emblemCatalogButton2.hide()
+ self.__makeFFlist()
+ if len(self.ffList) > 0:
+ self.giftToggle = DirectButton(self.base, relief=None, pressEffect=0, image=(giftToggleUp, giftToggleDown, giftToggleUp), image_scale=(1.0, 1, 0.7), command=self.__giftToggle, text=TTLocalizer.CatalogGiftToggleOff, text_font=ToontownGlobals.getSignFont(), text_pos=TTLocalizer.CSgiftTogglePos, text_scale=TTLocalizer.CSgiftToggle, text_fg=(0.353, 0.627, 0.627, 1.0), text3_fg=(0.15, 0.3, 0.3, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0), image_color=Vec4(1.0, 1.0, 0.2, 1.0), image1_color=Vec4(0.9, 0.85, 0.2, 1.0), image2_color=Vec4(0.9, 0.85, 0.2, 1.0), image3_color=Vec4(0.5, 0.45, 0.2, 1.0))
+ self.giftToggle.setPos(0.0, 0, -0.035)
+ self.giftLabel = DirectLabel(self.base, relief=None, image=giftFriends, image_scale=(1.15, 1, 1.14), text=' ', text_font=ToontownGlobals.getSignFont(), text_pos=(1.2, -0.97), text_scale=0.07, text_fg=(0.392, 0.549, 0.627, 1.0), sortOrder=100, textMayChange=1)
+ self.giftLabel.setPos(-0.15, 0, 0.08)
+ self.giftLabel.hide()
+ self.friendLabel = DirectLabel(self.base, relief=None, text='Friend Name', text_font=ToontownGlobals.getSignFont(), text_pos=(-0.25, 0.132), text_scale=0.068, text_align=TextNode.ALeft, text_fg=(0.992, 0.949, 0.327, 1.0), sortOrder=100, textMayChange=1)
+ self.friendLabel.setPos(0.5, 0, -0.42)
+ self.friendLabel.hide()
+ gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui')
+ self.scrollList = DirectScrolledList(parent=self, relief=None, incButton_image=(gui.find('**/FndsLst_ScrollUp'),
+ gui.find('**/FndsLst_ScrollDN'),
+ gui.find('**/FndsLst_ScrollUp_Rllvr'),
+ gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_pos=(0.0, 0.0, -0.316), incButton_image1_color=Vec4(1.0, 0.9, 0.4, 1.0), incButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.5), incButton_scale=(1.0, 1.0, -1.0), decButton_image=(gui.find('**/FndsLst_ScrollUp'),
+ gui.find('**/FndsLst_ScrollDN'),
+ gui.find('**/FndsLst_ScrollUp_Rllvr'),
+ gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_pos=(0.0, 0.0, 0.117), decButton_image1_color=Vec4(1.0, 1.0, 0.6, 1.0), decButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.6), itemFrame_pos=(-0.17, 0.0, 0.06), itemFrame_relief=None, numItemsVisible=8, items=[])
+ self.scrollList.setPos(1.2, 0, -0.58)
+ self.scrollList.setScale(1.5)
+ self.scrollList.hide()
+ clipper = PlaneNode('clipper')
+ clipper.setPlane(Plane(Vec3(-1, 0, 0), Point3(0.17, 0, 0)))
+ clipNP = self.scrollList.attachNewNode(clipper)
+ self.scrollList.setClipPlane(clipNP)
+ self.__makeScrollList()
+ friendId = self.ffList[0]
+ self.__chooseFriend(self.ffList[0][0], self.ffList[0][1])
+ self.update()
+ self.createdGiftGui = 1
+ for i in range(4):
+ self.newCatalogButton.component('text%d' % i).setR(90)
+ self.newCatalogButton2.component('text%d' % i).setR(90)
+ self.backCatalogButton.component('text%d' % i).setR(90)
+ self.backCatalogButton2.component('text%d' % i).setR(90)
+ self.loyaltyCatalogButton.component('text%d' % i).setR(90)
+ self.loyaltyCatalogButton2.component('text%d' % i).setR(90)
+ self.emblemCatalogButton.component('text%d' % i).setR(90)
+ self.emblemCatalogButton2.component('text%d' % i).setR(90)
+
+ self.squares = [[],
+ [],
+ [],
+ []]
+ for i in range(NUM_CATALOG_ROWS):
+ for j in range(NUM_CATALOG_COLS):
+ square = guiItems.find('**/square%d%db' % (i + 1, j + 1))
+ label = DirectLabel(self.base, image=square, relief=None, state='normal')
+ self.squares[i].append(label)
+
+ def priceSort(a, b, type):
+ priceA = a.getPrice(type)
+ priceB = b.getPrice(type)
+ return priceB - priceA
+
+ itemList = base.localAvatar.monthlyCatalog + base.localAvatar.weeklyCatalog
+ itemList.sort(lambda a, b: priceSort(a, b, CatalogItem.CatalogTypeWeekly))
+ itemList.reverse()
+ allClosetItems = CatalogFurnitureItem.getAllClosets()
+ isMaxClosetOfferred = False
+ for item in itemList:
+ if item in allClosetItems and item.furnitureType in CatalogFurnitureItem.MaxClosetIds:
+ isMaxClosetOfferred = True
+ break
+
+ for item in itemList:
+ if isinstance(item, CatalogInvalidItem.CatalogInvalidItem):
+ self.notify.warning('skipping catalog invalid item %s' % item)
+ continue
+ if isMaxClosetOfferred and item in allClosetItems and item.furnitureType not in CatalogFurnitureItem.MaxClosetIds:
+ continue
+ if item.loyaltyRequirement() != 0:
+ self.loyaltyPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeLoyalty, parentCatalogScreen=self))
+ elif item.getEmblemPrices():
+ self.emblemPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeWeekly, parentCatalogScreen=self))
+ else:
+ self.panelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeWeekly, parentCatalogScreen=self))
+
+ itemList = base.localAvatar.backCatalog
+ itemList.sort(lambda a, b: priceSort(a, b, CatalogItem.CatalogTypeBackorder))
+ itemList.reverse()
+ for item in itemList:
+ if isinstance(item, CatalogInvalidItem.CatalogInvalidItem):
+ self.notify.warning('skipping catalog invalid item %s' % item)
+ continue
+ if isMaxClosetOfferred and item in allClosetItems and item.furnitureType not in CatalogFurnitureItem.MaxClosetIds:
+ continue
+ if item.loyaltyRequirement() != 0:
+ self.loyaltyPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeLoyalty, parentCatalogScreen=self))
+ elif item.getEmblemPrices():
+ self.emblemPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeBackOrder, parentCatalogScreen=self))
+ else:
+ self.backPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeBackorder, parentCatalogScreen=self))
+
+ numPages = self.packPages(self.panelList, self.pageList, 'new')
+ self.setNumNewPages(numPages)
+ numPages = self.packPages(self.backPanelList, self.backPageList, 'back')
+ self.setNumBackPages(numPages)
+ numPages = self.packPages(self.loyaltyPanelList, self.loyaltyPageList, 'loyalty')
+ self.setNumLoyaltyPages(numPages)
+ numPages = self.packPages(self.emblemPanelList, self.emblemPageList, 'emblem')
+ self.setNumEmblemPages(numPages)
+ currentWeek = base.localAvatar.catalogScheduleCurrentWeek - 1
+ if currentWeek < 57:
+ seriesNumber = currentWeek / ToontownGlobals.CatalogNumWeeksPerSeries + 1
+ weekNumber = currentWeek % ToontownGlobals.CatalogNumWeeksPerSeries + 1
+ elif currentWeek < 65:
+ seriesNumber = 6
+ weekNumber = currentWeek - 56
+ else:
+ seriesNumber = currentWeek / ToontownGlobals.CatalogNumWeeksPerSeries + 2
+ weekNumber = currentWeek % ToontownGlobals.CatalogNumWeeksPerSeries + 1
+ geom = NodePath('cover')
+ cover = guiItems.find('**/cover')
+ maxSeries = ToontownGlobals.CatalogNumWeeks / ToontownGlobals.CatalogNumWeeksPerSeries + 1
+ coverSeries = (seriesNumber - 1) % maxSeries + 1
+ coverPicture = cover.find('**/cover_picture%s' % coverSeries)
+ if not coverPicture.isEmpty():
+ coverPicture.reparentTo(geom)
+ bottomSquare = cover.find('**/cover_bottom')
+ topSquare = guiItems.find('**/square12b2')
+ if seriesNumber == 1:
+ topSquare.setColor(0.651, 0.404, 0.322, 1.0)
+ bottomSquare.setColor(0.655, 0.522, 0.263, 1.0)
+ else:
+ topSquare.setColor(0.651, 0.404, 0.322, 1.0)
+ bottomSquare.setColor(0.655, 0.522, 0.263, 1.0)
+ bottomSquare.reparentTo(geom)
+ topSquare.reparentTo(geom)
+ cover.find('**/clarabelle_text').reparentTo(geom)
+ cover.find('**/blue_circle').reparentTo(geom)
+ cover.find('**/clarabelle').reparentTo(geom)
+ cover.find('**/circle_green').reparentTo(geom)
+ self.cover = DirectLabel(self.base, relief=None, geom=geom)
+ self.catalogNumber = DirectLabel(self.cover, relief=None, scale=0.2, pos=(-0.22, 0, -0.33), text='#%d' % weekNumber, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont())
+ self.catalogSeries = DirectLabel(self.cover, relief=None, scale=(0.22, 1, 0.18), pos=(-0.76, 0, -0.9), text=TTLocalizer.CatalogSeriesLabel % seriesNumber, text_fg=(0.9, 0.9, 0.4, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont())
+ self.catalogSeries.setShxz(0.4)
+ self.rings = DirectLabel(self.base, relief=None, geom=guiItems.find('**/rings'))
+ self.clarabelleFrame = DirectLabel(self, relief=None, image=guiItems.find('**/clarabelle_frame'))
+ hangupGui = guiItems.find('**/hangup')
+ hangupRolloverGui = guiItems.find('**/hangup_rollover')
+ self.hangup = DirectButton(self, relief=None, pos=(1.78, 0, -1.3), image=[hangupGui,
+ hangupRolloverGui,
+ hangupRolloverGui,
+ hangupGui], text=['', TTLocalizer.CatalogHangUp, TTLocalizer.CatalogHangUp], text_fg=Vec4(1), text_scale=0.07, text_pos=(0.0, 0.14), command=self.hangUp)
+ self.beanBank = DirectLabel(self, relief=None, image=guiItems.find('**/bean_bank'), text=str(base.localAvatar.getMoney() + base.localAvatar.getBankMoney()), text_align=TextNode.ARight, text_scale=0.11, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0.75, -0.81), text_font=ToontownGlobals.getSignFont())
+ nextUp = guiItems.find('**/arrow_up')
+ nextRollover = guiItems.find('**/arrow_Rollover')
+ nextDown = guiItems.find('**/arrow_Down')
+ prevUp = guiItems.find('**/arrowUp')
+ prevDown = guiItems.find('**/arrowDown1')
+ prevRollover = guiItems.find('**/arrowRollover')
+ self.nextPageButton = DirectButton(self, relief=None, pos=(-0.1, 0, -0.9), image=[nextUp,
+ nextDown,
+ nextRollover,
+ nextUp], image_color=(0.9, 0.9, 0.9, 1), image2_color=(1, 1, 1, 1), command=self.showNextPage)
+ self.backPageButton = DirectButton(self, relief=None, pos=(-0.1, 0, -0.9), image=[prevUp,
+ prevDown,
+ prevRollover,
+ prevUp], image_color=(0.9, 0.9, 0.9, 1), image2_color=(1, 1, 1, 1), command=self.showBackPage)
+ self.backPageButton.hide()
+ self.pageLabel = DirectLabel(self.base, relief=None, pos=(-1.33, 0, -0.9), scale=0.06, text=TTLocalizer.CatalogPagePrefix, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft)
+ self.loadClarabelle()
+ return
+
+ def loadClarabelle(self):
+ self.cRender = NodePath('cRender')
+ self.cCamera = self.cRender.attachNewNode('cCamera')
+ self.cCamNode = Camera('cCam')
+ self.cLens = PerspectiveLens()
+ self.cLens.setFov(40, 40)
+ self.cLens.setNear(0.1)
+ self.cLens.setFar(100.0)
+ self.cCamNode.setLens(self.cLens)
+ self.cCamNode.setScene(self.cRender)
+ self.cCam = self.cCamera.attachNewNode(self.cCamNode)
+ self.cDr = base.win.makeDisplayRegion(0.58, 0.82, 0.53, 0.85)
+ self.cDr.setSort(1)
+ self.cDr.setClearDepthActive(1)
+ self.cDr.setClearColorActive(1)
+ self.cDr.setClearColor(Vec4(0.3, 0.3, 0.3, 1))
+ self.cDr.setCamera(self.cCam)
+ self.clarabelle = Actor.Actor('phase_5.5/models/char/Clarabelle-zero', {'listen': 'phase_5.5/models/char/Clarabelle-listens'})
+ self.clarabelle.loop('listen')
+ self.clarabelle.find('**/eyes').setBin('fixed', 0)
+ self.clarabelle.find('**/pupilL').setBin('fixed', 1)
+ self.clarabelle.find('**/pupilR').setBin('fixed', 1)
+ self.clarabelle.find('**/glassL').setBin('fixed', 2)
+ self.clarabelle.find('**/glassR').setBin('fixed', 2)
+ switchboard = loader.loadModel('phase_5.5/models/estate/switchboard')
+ switchboard.reparentTo(self.clarabelle)
+ switchboard.setPos(0, -2, 0)
+ self.clarabelle.reparentTo(self.cRender)
+ self.clarabelle.setPosHprScale(-0.56, 6.43, -3.81, 121.61, 0.0, 0.0, 1.0, 1.0, 1.0)
+ self.clarabelleFrame.setPosHprScale(-0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0)
+
+ def reload(self):
+ for panel in self.panelList + self.backPanelList + self.loyaltyPanelList + self.emblemPanelList:
+ panel.destroy()
+
+ def priceSort(a, b, type):
+ priceA = a.getPrice(type)
+ priceB = b.getPrice(type)
+ return priceB - priceA
+
+ self.pageIndex = -1
+ self.maxPageIndex = 0
+ self.numNewPages = 0
+ self.numBackPages = 5
+ self.numLoyaltyPages = 0
+ self.viewing = 'New'
+ self.panelList = []
+ self.backPanelList = []
+ self.loyaltyList = []
+ self.pageList = []
+ self.backPageList = []
+ self.loyaltyPanelList = []
+ self.loyaltyPageList = []
+ self.panelDict = {}
+ self.visiblePanels = []
+ itemList = base.localAvatar.monthlyCatalog + base.localAvatar.weeklyCatalog
+ itemList.sort(lambda a, b: priceSort(a, b, CatalogItem.CatalogTypeWeekly))
+ itemList.reverse()
+ for item in itemList:
+ if item.loyaltyRequirement() != 0:
+ self.loyaltyPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeLoyalty, parentCatalogScreen=self))
+ else:
+ self.panelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeWeekly))
+
+ itemList = base.localAvatar.backCatalog
+ itemList.sort(lambda a, b: priceSort(a, b, CatalogItem.CatalogTypeBackorder))
+ itemList.reverse()
+ for item in itemList:
+ if item.loyaltyRequirement() != 0:
+ self.loyaltyPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeLoyalty, parentCatalogScreen=self))
+ else:
+ self.backPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeBackorder))
+
+ numPages = self.packPages(self.panelList, self.pageList, 'new')
+ self.setNumNewPages(numPages)
+ numPages = self.packPages(self.backPanelList, self.backPageList, 'back')
+ self.setNumBackPages(numPages)
+ numPages = self.packPages(self.loyaltyPanelList, self.loyaltyPageList, 'loyalty')
+ self.setNumLoyaltyPages(numPages)
+ seriesNumber = (base.localAvatar.catalogScheduleCurrentWeek - 1) / ToontownGlobals.CatalogNumWeeksPerSeries + 1
+ self.catalogSeries['text'] = Localizer.CatalogSeriesLabel % seriesNumber
+ weekNumber = (base.localAvatar.catalogScheduleCurrentWeek - 1) % ToontownGlobals.CatalogNumWeeksPerSeries + 1
+ self.catalogNumber['text'] = '#%d' % weekNumber
+ self.enableBackorderCatalogButton()
+ self.setMaxPageIndex(self.numNewPages)
+ self.setPageIndex(-1)
+ self.showPageItems()
+
+ def unload(self):
+ taskMgr.remove('clearClarabelleChat')
+ taskMgr.remove('postGoodbyeHangUp')
+ taskMgr.remove('clarabelleGreeting')
+ taskMgr.remove('clarabelleHelpText1')
+ taskMgr.remove('clarabelleAskAnythingElse')
+ if self.giftAvatar:
+ base.cr.cancelAvatarDetailsRequest(self.giftAvatar)
+ self.hide()
+ self.destroy()
+ del self.base
+ del self.squares
+ for panel in self.panelList + self.backPanelList + self.loyaltyPanelList + self.emblemPanelList:
+ panel.destroy()
+
+ del self.panelList
+ del self.backPanelList
+ del self.cover
+ del self.rings
+ del self.clarabelleFrame
+ del self.hangup
+ del self.beanBank
+ del self.silverLabel
+ del self.goldLabel
+ del self.nextPageButton
+ del self.backPageButton
+ del self.newCatalogButton
+ del self.newCatalogButton2
+ del self.backCatalogButton
+ del self.backCatalogButton2
+ del self.loyaltyCatalogButton
+ del self.loyaltyCatalogButton2
+ del self.pageLabel
+ if self.createdGiftGui:
+ del self.giftToggle
+ del self.giftLabel
+ del self.friendLabel
+ del self.scrollList
+ self.unloadClarabelle()
+ if self.responseDialog:
+ self.responseDialog.cleanup()
+ self.responseDialog = None
+ if self.giftAvatar:
+ if hasattr(self.giftAvatar, 'doId'):
+ self.giftAvatar.delete()
+ else:
+ self.giftAvatar = None
+ return
+
+ def unloadClarabelle(self):
+ base.win.removeDisplayRegion(self.cDr)
+ del self.cRender
+ del self.cCamera
+ del self.cCamNode
+ del self.cLens
+ del self.cCam
+ del self.cDr
+ self.clarabelle.cleanup()
+ del self.clarabelle
+
+ def hangUp(self):
+ if hasattr(self, 'giftAvatar') and self.giftAvatar:
+ self.giftAvatar.disable()
+ self.setClarabelleChat(random.choice(TTLocalizer.CatalogGoodbyeList))
+ self.setPageIndex(-1)
+ self.showPageItems()
+ self.nextPageButton.hide()
+ self.backPageButton.hide()
+ self.newCatalogButton.hide()
+ self.newCatalogButton2.hide()
+ self.backCatalogButton.hide()
+ self.backCatalogButton2.hide()
+ self.loyaltyCatalogButton.hide()
+ self.loyaltyCatalogButton2.hide()
+ self.emblemCatalogButton.hide()
+ self.emblemCatalogButton2.hide()
+ self.hangup.hide()
+ taskMgr.remove('clarabelleGreeting')
+ taskMgr.remove('clarabelleHelpText1')
+ taskMgr.remove('clarabelleAskAnythingElse')
+
+ def postGoodbyeHangUp(task):
+ messenger.send(self['doneEvent'])
+ self.unload()
+
+ taskMgr.doMethodLater(1.5, postGoodbyeHangUp, 'postGoodbyeHangUp')
+
+ def remoteUpdate(self):
+ self.update()
+
+ def update(self, lock = 0):
+ if not hasattr(self.giftAvatar, 'doId'):
+ if self.gifting == 1:
+ self.__giftToggle()
+ if hasattr(self, 'beanBank'):
+ self.beanBank['text'] = str(base.localAvatar.getTotalMoney())
+ if lock == 0:
+ for item in self.panelList + self.backPanelList + self.loyaltyPanelList + self.emblemPanelList:
+ if type(item) != type(''):
+ item.updateButtons(self.gifting)
+
+ def __handlePurchaseRequest(self, item):
+ item.requestPurchase(self['phone'], self.__handlePurchaseResponse)
+ taskMgr.remove('clarabelleAskAnythingElse')
+
+ def __handleGiftPurchaseRequest(self, item):
+ item.requestGiftPurchase(self['phone'], self.frienddoId, self.__handleGiftPurchaseResponse)
+ taskMgr.remove('clarabelleAskAnythingElse')
+
+ def __handlePurchaseResponse(self, retCode, item):
+ if retCode == ToontownGlobals.P_UserCancelled:
+ self.update()
+ return
+ self.setClarabelleChat(item.getRequestPurchaseErrorText(retCode), item.getRequestPurchaseErrorTextTimeout())
+
+ def __handleGiftPurchaseResponse(self, retCode, item):
+ if retCode == ToontownGlobals.P_UserCancelled:
+ return
+ if self.isEmpty() or self.isHidden():
+ return
+ self.setClarabelleChat(item.getRequestGiftPurchaseErrorText(retCode) % self.receiverName)
+ self.__loadFriend()
+
+ def askAnythingElse(task):
+ self.setClarabelleChat(TTLocalizer.CatalogAnythingElse)
+
+ if retCode >= 0:
+ taskMgr.doMethodLater(8, askAnythingElse, 'clarabelleAskAnythingElse')
+
+ def __clearDialog(self, event):
+ self.responseDialog.cleanup()
+ self.responseDialog = None
+ return
+
+ def setClarabelleChat(self, str, timeout = 6):
+ self.clearClarabelleChat()
+ if not self.clarabelleChatBalloon:
+ self.clarabelleChatBalloon = loader.loadModel('phase_3/models/props/chatbox.bam')
+ self.clarabelleChat = ChatBalloon(self.clarabelleChatBalloon.node())
+ chatNode = self.clarabelleChat.generate(str, ToontownGlobals.getInterfaceFont(), 10, Vec4(0, 0, 0, 1), Vec4(1, 1, 1, 1), 0, 0, 0, NodePath(), 0, 0, NodePath())
+ self.clarabelleChatNP = self.attachNewNode(chatNode, 1000)
+ self.clarabelleChatNP.setScale(0.08)
+ self.clarabelleChatNP.setPos(0.7, 0, 0.6)
+ if timeout:
+ taskMgr.doMethodLater(timeout, self.clearClarabelleChat, 'clearClarabelleChat')
+
+ def clearClarabelleChat(self, task = None):
+ taskMgr.remove('clearClarabelleChat')
+ if self.clarabelleChatNP:
+ self.clarabelleChatNP.removeNode()
+ self.clarabelleChatNP = None
+ del self.clarabelleChat
+ return
+
+ def __moneyChange(self, money):
+ if self.gifting > 0:
+ self.update(1)
+ else:
+ self.update(0)
+
+ def __bankMoneyChange(self, bankMoney):
+ if self.gifting > 0:
+ self.update(1)
+ else:
+ self.update(0)
+
+ def __emblemChange(self, newEmblems):
+ self.silverLabel['text'] = str(newEmblems[0])
+ self.goldLabel['text'] = str(newEmblems[1])
+
+ def showEmblems(self):
+ if base.cr.wantEmblems:
+ self.silverLabel.show()
+ self.goldLabel.show()
+
+ def hideEmblems(self):
+ self.silverLabel.hide()
+ self.goldLabel.hide()
+
+ def checkFamily(self, doId):
+ test = 0
+ for familyMember in base.cr.avList:
+ if familyMember.id == doId:
+ test = 1
+
+ return test
+
+ def __makeFFlist(self):
+ for familyMember in base.cr.avList:
+ if familyMember.id != base.localAvatar.doId:
+ newFF = (familyMember.id, familyMember.name, NametagGroup.CCNonPlayer)
+ self.ffList.append(newFF)
+
+ for friendPair in base.localAvatar.friendsList:
+ friendId, flags = friendPair
+ handle = base.cr.identifyFriend(friendId)
+ if handle and not self.checkFamily(friendId):
+ if hasattr(handle, 'getName'):
+ colorCode = NametagGroup.CCSpeedChat
+ if flags & ToontownGlobals.FriendChat:
+ colorCode = NametagGroup.CCFreeChat
+ newFF = (friendPair[0], handle.getName(), colorCode)
+ self.ffList.append(newFF)
+ else:
+ self.notify.warning('Bad Handle for getName in makeFFlist')
+
+ hasManager = hasattr(base.cr, 'playerFriendsManager')
+ if hasManager:
+ for avatarId in base.cr.playerFriendsManager.getAllOnlinePlayerAvatars():
+ handle = base.cr.playerFriendsManager.getAvHandleFromId(avatarId)
+ playerId = base.cr.playerFriendsManager.findPlayerIdFromAvId(avatarId)
+ playerInfo = base.cr.playerFriendsManager.getFriendInfo(playerId)
+ freeChat = playerInfo.understandableYesNo
+ if handle and not self.checkFamily(avatarId):
+ if hasattr(handle, 'getName'):
+ colorCode = NametagGroup.CCSpeedChat
+ if freeChat:
+ colorCode = NametagGroup.CCFreeChat
+ newFF = (avatarId, handle.getName(), colorCode)
+ self.ffList.append(newFF)
+ else:
+ self.notify.warning('Bad Handle for getName in makeFFlist')
+
+ def __makeScrollList(self):
+ for ff in self.ffList:
+ ffbutton = self.makeFamilyButton(ff[0], ff[1], ff[2])
+ if ffbutton:
+ self.scrollList.addItem(ffbutton, refresh=0)
+ self.friends[ff] = ffbutton
+
+ self.scrollList.refresh()
+
+ def makeFamilyButton(self, familyId, familyName, colorCode):
+ fg = NametagGlobals.getNameFg(colorCode, PGButton.SInactive)
+ return DirectButton(relief=None, text=familyName, text_scale=0.04, text_align=TextNode.ALeft, text_fg=fg, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, textMayChange=0, command=self.__chooseFriend, extraArgs=[familyId, familyName])
+
+ def __chooseFriend(self, friendId, friendName):
+ messenger.send('wakeup')
+ self.frienddoId = friendId
+ self.receiverName = friendName
+ self.friendLabel['text'] = TTLocalizer.CatalogGiftTo % self.receiverName
+ self.__loadFriend()
+
+ def __loadFriend(self):
+ if self.allowGetDetails == 0:
+ CatalogScreen.notify.warning('smashing requests')
+ if self.frienddoId and self.allowGetDetails:
+ if self.giftAvatar:
+ if hasattr(self.giftAvatar, 'doId'):
+ self.giftAvatar.disable()
+ self.giftAvatar.delete()
+ self.giftAvatar = None
+ self.giftAvatar = DistributedToon.DistributedToon(base.cr)
+ self.giftAvatar.doId = self.frienddoId
+ self.giftAvatar.forceAllowDelayDelete()
+ self.giftAvatar.generate()
+ base.cr.getAvatarDetails(self.giftAvatar, self.__handleAvatarDetails, 'DistributedToon')
+ self.gotAvatar = 0
+ self.allowGetDetails = 0
+ self.scrollList['state'] = DGG.DISABLED
+ return
+
+ def __handleAvatarDetails(self, gotData, avatar, dclass):
+ if self.giftAvatar.doId != avatar.doId or gotData == 0:
+ CatalogScreen.notify.error('Get Gift Avatar Failed')
+ self.gotAvatar = 0
+ return
+ else:
+ self.gotAvatar = 1
+ self.giftAvatar = avatar
+ self.scrollList['state'] = DGG.NORMAL
+ self.allowGetDetails = 1
+ self.update()
+
+ def __giftToggle(self):
+ messenger.send('wakeup')
+ if self.gifting == -1:
+ self.gifting = 1
+ self.giftLabel.show()
+ self.friendLabel.show()
+ self.scrollList.show()
+ self.hideEmblems()
+ self.giftToggle['text'] = TTLocalizer.CatalogGiftToggleOn
+ self.__loadFriend()
+ else:
+ self.gifting = -1
+ self.giftLabel.hide()
+ self.friendLabel.hide()
+ self.scrollList.hide()
+ self.showEmblems()
+ self.giftToggle['text'] = TTLocalizer.CatalogGiftToggleOff
+ self.update()
+
+ def __handleUDack(self, caller = None):
+ taskMgr.remove('ackTimeOut')
+ if hasattr(self, 'giftToggle') and self.giftToggle:
+ self.giftToggle['state'] = DGG.NORMAL
+ self.giftToggle['text'] = TTLocalizer.CatalogGiftToggleOff
+
+ def __handleNoAck(self, caller = None):
+ if hasattr(self, 'giftToggle') and self.giftToggle:
+ self.giftToggle['text'] = TTLocalizer.CatalogGiftToggleNoAck
diff --git a/toontown/catalog/CatalogSurfaceColors.py b/toontown/catalog/CatalogSurfaceColors.py
new file mode 100644
index 0000000..16378bc
--- /dev/null
+++ b/toontown/catalog/CatalogSurfaceColors.py
@@ -0,0 +1,75 @@
+CT_WHITE = (1.0, 1.0, 1.0, 1.0)
+CT_RED = (1.0, 0.5, 0.5, 1.0)
+CT_BROWN = (0.641, 0.355, 0.27, 1.0)
+CT_CANTELOPE = (0.839, 0.651, 0.549, 1.0)
+CT_TAN = (0.996, 0.695, 0.512, 1.0)
+CT_ORANGE = (0.992, 0.48, 0.168, 1.0)
+CT_CORAL = (0.832, 0.5, 0.297, 1.0)
+CT_PEACH = (1.0, 0.82, 0.7, 1.0)
+CT_BEIGE = (1.0, 0.8, 0.6, 1.0)
+CT_TAN2 = (0.808, 0.678, 0.51, 1.0)
+CT_SIENNA = (0.57, 0.449, 0.164, 1.0)
+CT_YELLOW = (0.996, 0.898, 0.32, 1.0)
+CT_CREAM = (0.996, 0.957, 0.598, 1.0)
+CT_BEIGE2 = (1.0, 1.0, 0.6, 1.0)
+CT_YELLOW2 = (1.0, 1.0, 0.7, 1.0)
+CT_CITRINE = (0.855, 0.934, 0.492, 1.0)
+CT_FOREST_GREEN = (0.5, 0.586, 0.4, 1.0)
+CT_LINE = (0.551, 0.824, 0.324, 1.0)
+CT_PALE_GREEN = (0.789, 1.0, 0.7, 1.0)
+CT_GREEN = (0.305, 0.969, 0.402, 1.0)
+CT_TEAL = (0.6, 1.0, 0.8, 1.0)
+CT_SEA_GREEN = (0.242, 0.742, 0.516, 1.0)
+CT_LIGHT_BLUE = (0.434, 0.906, 0.836, 1.0)
+CT_AQUA = (0.348, 0.82, 0.953, 1.0)
+CT_BLUE = (0.191, 0.563, 0.773, 1.0)
+CT_LIGHT_BLUE2 = (0.875, 0.937, 1.0, 1.0)
+CT_PERIWINKLE = (0.559, 0.59, 0.875, 1.0)
+CT_ROYAL_BLUE = (0.285, 0.328, 0.727, 1.0)
+CT_GREY = (0.7, 0.7, 0.8, 1.0)
+CT_BLUE2 = (0.6, 0.6, 1.0, 1.0)
+CT_SLATE_BLUE = (0.461, 0.379, 0.824, 1.0)
+CT_PURPLE = (0.547, 0.281, 0.75, 1.0)
+CT_LAVENDER = (0.727, 0.473, 0.859, 1.0)
+CT_PINK = (0.898, 0.617, 0.906, 1.0)
+CT_PINK2 = (1.0, 0.6, 1.0, 1.0)
+CT_MAROON = (0.711, 0.234, 0.438, 1.0)
+CT_PEACH2 = (0.969, 0.691, 0.699, 1.0)
+CT_RED2 = (0.863, 0.406, 0.418, 1.0)
+CT_BRIGHT_RED = (0.934, 0.266, 0.281, 1.0)
+CT_DARK_WOOD = (0.69, 0.741, 0.71, 1.0)
+CT_DARK_WALNUT = (0.549, 0.412, 0.259, 1.0)
+CT_GENERIC_DARK = (0.443, 0.333, 0.176, 1.0)
+CT_PINE = (1.0, 0.812, 0.49, 1.0)
+CT_CHERRY = (0.71, 0.408, 0.267, 1.0)
+CT_BEECH = (0.961, 0.659, 0.4, 1.0)
+CTFlatColor = [CT_BEIGE,
+ CT_TEAL,
+ CT_BLUE2,
+ CT_PINK2,
+ CT_BEIGE2,
+ CT_RED]
+CTValentinesColors = [CT_PINK2, CT_RED]
+CTUnderwaterColors = [CT_WHITE,
+ CT_TEAL,
+ CT_SEA_GREEN,
+ CT_LIGHT_BLUE,
+ CT_PALE_GREEN,
+ CT_AQUA,
+ CT_CORAL,
+ CT_PEACH]
+CTFlatColorDark = []
+tint = 0.75
+for color in CTFlatColor:
+ CTFlatColorDark.append((color[0] * tint,
+ color[1] * tint,
+ color[2] * tint,
+ 1.0))
+
+CTFlatColorAll = CTFlatColor + CTFlatColorDark
+CTBasicWoodColorOnWhite = [CT_DARK_WALNUT,
+ CT_GENERIC_DARK,
+ CT_PINE,
+ CT_CHERRY,
+ CT_BEECH]
+CTWhite = [CT_WHITE]
diff --git a/toontown/catalog/CatalogSurfaceItem.py b/toontown/catalog/CatalogSurfaceItem.py
new file mode 100644
index 0000000..8984e12
--- /dev/null
+++ b/toontown/catalog/CatalogSurfaceItem.py
@@ -0,0 +1,34 @@
+import CatalogItem
+import CatalogAtticItem
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from CatalogSurfaceColors import *
+STWallpaper = 0
+STMoulding = 1
+STFlooring = 2
+STWainscoting = 3
+NUM_ST_TYPES = 4
+
+class CatalogSurfaceItem(CatalogAtticItem.CatalogAtticItem):
+
+ def makeNewItem(self):
+ CatalogAtticItem.CatalogAtticItem.makeNewItem(self)
+
+ def setPatternIndex(self, patternIndex):
+ self.patternIndex = patternIndex
+
+ def setColorIndex(self, colorIndex):
+ self.colorIndex = colorIndex
+
+ def saveHistory(self):
+ return 1
+
+ def recordPurchase(self, avatar, optional):
+ self.giftTag = None
+ house, retcode = self.getHouseInfo(avatar)
+ if retcode >= 0:
+ house.addWallpaper(self)
+ return retcode
+
+ def getDeliveryTime(self):
+ return 60
diff --git a/toontown/catalog/CatalogToonStatueItem.py b/toontown/catalog/CatalogToonStatueItem.py
new file mode 100644
index 0000000..0353753
--- /dev/null
+++ b/toontown/catalog/CatalogToonStatueItem.py
@@ -0,0 +1,63 @@
+import CatalogGardenItem
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from otp.otpbase import OTPLocalizer
+from direct.interval.IntervalGlobal import *
+from toontown.estate import GardenGlobals
+
+class CatalogToonStatueItem(CatalogGardenItem.CatalogGardenItem):
+ pictureToonStatue = None
+
+ def makeNewItem(self, itemIndex = 105, count = 1, tagCode = 1, endPoseIndex = 108):
+ self.startPoseIndex = itemIndex
+ self.endPoseIndex = endPoseIndex
+ CatalogGardenItem.CatalogGardenItem.makeNewItem(self, itemIndex, count, tagCode)
+
+ def needsCustomize(self):
+ return self.endPoseIndex - self.startPoseIndex > 0
+
+ def getPicture(self, avatar):
+ from toontown.estate import DistributedToonStatuary
+ toonStatuary = DistributedToonStatuary.DistributedToonStatuary(None)
+ toonStatuary.setupStoneToon(base.localAvatar.style)
+ toonStatuary.poseToonFromSpecialsIndex(self.gardenIndex)
+ toonStatuary.toon.setZ(0)
+ model, ival = self.makeFrameModel(toonStatuary.toon, 1)
+ self.pictureToonStatue = toonStatuary
+ self.hasPicture = True
+ return (model, ival)
+
+ def cleanupPicture(self):
+ self.pictureToonStatue.deleteToon()
+ self.pictureToonStatue = None
+ CatalogGardenItem.CatalogGardenItem.cleanupPicture(self)
+ return
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogGardenItem.CatalogGardenItem.decodeDatagram(self, di, versionNumber, store)
+ self.startPoseIndex = di.getUint8()
+ self.endPoseIndex = di.getUint8()
+
+ def encodeDatagram(self, dg, store):
+ CatalogGardenItem.CatalogGardenItem.encodeDatagram(self, dg, store)
+ dg.addUint8(self.startPoseIndex)
+ dg.addUint8(self.endPoseIndex)
+
+ def compareTo(self, other):
+ if self.gardenIndex >= self.startPoseIndex and self.gardenIndex <= self.endPoseIndex:
+ return 0
+ return 1
+
+ def getAllToonStatues(self):
+ self.statueList = []
+ for index in range(self.startPoseIndex, self.endPoseIndex + 1):
+ self.statueList.append(CatalogToonStatueItem(index, 1, endPoseIndex=index))
+
+ return self.statueList
+
+ def deleteAllToonStatues(self):
+ while len(self.statueList):
+ item = self.statueList[0]
+ if item.pictureToonStatue:
+ item.pictureToonStatue.deleteToon()
+ self.statueList.remove(item)
diff --git a/toontown/catalog/CatalogWainscotingItem.py b/toontown/catalog/CatalogWainscotingItem.py
new file mode 100644
index 0000000..accd395
--- /dev/null
+++ b/toontown/catalog/CatalogWainscotingItem.py
@@ -0,0 +1,143 @@
+from CatalogSurfaceItem import *
+WSTTextureName = 0
+WSTColor = 1
+WSTBasePrice = 2
+WainscotingTypes = {1000: ('phase_3.5/maps/wall_paper_b3.jpg', CTFlatColorDark, 200),
+ 1010: ('phase_5.5/maps/wall_paper_b4_greyscale.jpg', CTBasicWoodColorOnWhite, 200),
+ 1020: ('phase_5.5/maps/wainscotings_neutral.jpg', CTBasicWoodColorOnWhite, 200),
+ 1030: ('phase_3.5/maps/wall_paper_b3.jpg', CTValentinesColors, 200),
+ 1040: ('phase_3.5/maps/wall_paper_b3.jpg', CTUnderwaterColors, 200)}
+
+class CatalogWainscotingItem(CatalogSurfaceItem):
+
+ def makeNewItem(self, patternIndex, colorIndex):
+ self.patternIndex = patternIndex
+ self.colorIndex = colorIndex
+ CatalogSurfaceItem.makeNewItem(self)
+
+ def getTypeName(self):
+ return TTLocalizer.SurfaceNames[STWainscoting]
+
+ def getName(self):
+ name = TTLocalizer.WainscotingNames.get(self.patternIndex)
+ if name:
+ return name
+ return self.getTypeName()
+
+ def getSurfaceType(self):
+ return STWainscoting
+
+ def getPicture(self, avatar):
+ frame = self.makeFrame()
+ sample = loader.loadModel('phase_5.5/models/estate/wallpaper_sample')
+ a = sample.find('**/a')
+ b = sample.find('**/b')
+ c = sample.find('**/c')
+ a.hide()
+ b.hide()
+ c.setTexture(self.loadTexture(), 1)
+ c.setColorScale(*self.getColor())
+ sample.reparentTo(frame)
+ self.hasPicture = True
+ return (frame, None)
+
+ def output(self, store = -1):
+ return 'CatalogWainscotingItem(%s, %s%s)' % (self.patternIndex, self.colorIndex, self.formatOptionalData(store))
+
+ def getFilename(self):
+ return WainscotingTypes[self.patternIndex][WSTTextureName]
+
+ def compareTo(self, other):
+ if self.patternIndex != other.patternIndex:
+ return self.patternIndex - other.patternIndex
+ return self.colorIndex - other.colorIndex
+
+ def getHashContents(self):
+ return (self.patternIndex, self.colorIndex)
+
+ def getBasePrice(self):
+ return WainscotingTypes[self.patternIndex][WSTBasePrice]
+
+ def loadTexture(self):
+ from pandac.PandaModules import Texture
+ filename = WainscotingTypes[self.patternIndex][WSTTextureName]
+ texture = loader.loadTexture(filename)
+ texture.setMinfilter(Texture.FTLinearMipmapLinear)
+ texture.setMagfilter(Texture.FTLinear)
+ return texture
+
+ def getColor(self):
+ if self.colorIndex == None:
+ colorIndex = 0
+ else:
+ colorIndex = self.colorIndex
+ colors = WainscotingTypes[self.patternIndex][WSTColor]
+ if colors:
+ if colorIndex < len(colors):
+ return colors[colorIndex]
+ else:
+ print 'Warning: colorIndex not in colors. Returning white.'
+ return CT_WHITE
+ else:
+ return CT_WHITE
+ return
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store)
+ if versionNumber < 3:
+ self.patternIndex = di.getUint8()
+ else:
+ self.patternIndex = di.getUint16()
+ self.colorIndex = di.getUint8()
+ wtype = WainscotingTypes[self.patternIndex]
+
+ def encodeDatagram(self, dg, store):
+ CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store)
+ dg.addUint16(self.patternIndex)
+ dg.addUint8(self.colorIndex)
+
+
+def getWainscotings(*indexList):
+ list = []
+ for index in indexList:
+ list.append(CatalogWainscotingItem(index))
+
+ return list
+
+
+def getAllWainscotings(*indexList):
+ list = []
+ for index in indexList:
+ colors = WainscotingTypes[index][WSTColor]
+ if colors:
+ for n in range(len(colors)):
+ list.append(CatalogWainscotingItem(index, n))
+
+ else:
+ list.append(CatalogWainscotingItem(index, 0))
+
+ return list
+
+
+def getWainscotingRange(fromIndex, toIndex, *otherRanges):
+ list = []
+ froms = [fromIndex]
+ tos = [toIndex]
+ i = 0
+ while i < len(otherRanges):
+ froms.append(otherRanges[i])
+ tos.append(otherRanges[i + 1])
+ i += 2
+
+ for patternIndex in WainscotingTypes.keys():
+ for fromIndex, toIndex in zip(froms, tos):
+ if patternIndex >= fromIndex and patternIndex <= toIndex:
+ colors = WainscotingTypes[patternIndex][WSTColor]
+ if colors:
+ for n in range(len(colors)):
+ list.append(CatalogWainscotingItem(patternIndex, n))
+
+ else:
+ list.append(CatalogWainscotingItem(patternIndex, 0))
+
+ return list
diff --git a/toontown/catalog/CatalogWallpaperItem.py b/toontown/catalog/CatalogWallpaperItem.py
new file mode 100644
index 0000000..ec5bc70
--- /dev/null
+++ b/toontown/catalog/CatalogWallpaperItem.py
@@ -0,0 +1,750 @@
+from CatalogSurfaceItem import *
+WTTextureName = 0
+WTColor = 1
+WTBorderList = 2
+WTBasePrice = 3
+BDTextureName = 0
+BDColor = 1
+All = (1000,
+ 1010,
+ 1020,
+ 1030,
+ 1040,
+ 1050,
+ 1060,
+ 1070)
+WallpaperTypes = {1000: ('phase_5.5/maps/flat_wallpaper1.jpg',
+ CTFlatColor,
+ (0, 1000),
+ 180),
+ 1100: ('phase_5.5/maps/big_stripes1.jpg',
+ CTWhite,
+ (0, 1010),
+ 180),
+ 1110: ('phase_5.5/maps/big_stripes2.jpg',
+ CTWhite,
+ (0, 1040),
+ 180),
+ 1120: ('phase_5.5/maps/big_stripes3.jpg',
+ CTWhite,
+ (0, 1030),
+ 180),
+ 1130: ('phase_5.5/maps/big_stripes4.jpg',
+ CTWhite,
+ (0, 1010),
+ 180),
+ 1140: ('phase_5.5/maps/big_stripes5.jpg',
+ CTWhite,
+ (0, 1020),
+ 180),
+ 1150: ('phase_5.5/maps/big_stripes6.jpg',
+ CTWhite,
+ (0, 1020),
+ 180),
+ 1200: ('phase_5.5/maps/stripeB1.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1210: ('phase_5.5/maps/stripeB2.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1220: ('phase_5.5/maps/stripeB3.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1230: ('phase_5.5/maps/stripeB4.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1240: ('phase_3.5/maps/stripeB5.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1250: ('phase_5.5/maps/stripeB6.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1260: ('phase_5.5/maps/stripeB7.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1300: ('phase_5.5/maps/squiggle1.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1310: ('phase_5.5/maps/squiggle2.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1320: ('phase_5.5/maps/squiggle3.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1330: ('phase_5.5/maps/squiggle4.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1340: ('phase_5.5/maps/squiggle5.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1350: ('phase_5.5/maps/squiggle6.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1400: ('phase_5.5/maps/stripes_cyan.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1410: ('phase_5.5/maps/stripes_green.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1420: ('phase_5.5/maps/stripes_magenta.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1430: ('phase_5.5/maps/two_stripes1.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1440: ('phase_5.5/maps/two_stripes2.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1450: ('phase_5.5/maps/two_stripes3.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1500: ('phase_5.5/maps/leaves1.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1510: ('phase_5.5/maps/leaves2.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1520: ('phase_5.5/maps/leaves3.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1600: ('phase_5.5/maps/diamonds2_cherries.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1610: ('phase_5.5/maps/diamonds3_cherries.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1620: ('phase_5.5/maps/diamonds3_cherry.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1630: ('phase_5.5/maps/diamonds4_cherries.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1640: ('phase_5.5/maps/diamonds4_cherry.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1650: ('phase_5.5/maps/diamonds5_cherries.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1660: ('phase_5.5/maps/diamonds6_cherry.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1700: ('phase_5.5/maps/moon1.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1710: ('phase_5.5/maps/moon2.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1720: ('phase_5.5/maps/moon3.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1730: ('phase_5.5/maps/moon4.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1740: ('phase_5.5/maps/moon5.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1750: ('phase_5.5/maps/moon6.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1760: ('phase_5.5/maps/moon7.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1800: ('phase_5.5/maps/stars1.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1810: ('phase_5.5/maps/stars2.jpg',
+ (CT_BLUE2, CT_PINK2, CT_RED),
+ (0,),
+ 180),
+ 1820: ('phase_5.5/maps/stars3.jpg',
+ (CT_BLUE2,
+ CT_PINK2,
+ CT_RED,
+ CT_WHITE),
+ (0,),
+ 180),
+ 1830: ('phase_5.5/maps/stars4.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1840: ('phase_5.5/maps/stars5.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1850: ('phase_5.5/maps/stars6.jpg',
+ CTWhite,
+ (0,),
+ 180),
+ 1860: ('phase_5.5/maps/stars7.jpg',
+ (CT_BEIGE2, CT_WHITE),
+ (0,),
+ 180),
+ 1900: ('phase_5.5/maps/wall_paper_flower1.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1910: ('phase_5.5/maps/wall_paper_flower2.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1920: ('phase_5.5/maps/wall_paper_flower3.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1930: ('phase_5.5/maps/wall_paper_flower4.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1940: ('phase_5.5/maps/wall_paper_flower5.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 1950: ('phase_5.5/maps/wall_paper_flower6.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 2000: ('phase_5.5/maps/flat_wallpaper1.jpg',
+ (CT_BEIGE, CT_BEIGE2, CT_RED),
+ (1050,),
+ 180),
+ 2010: ('phase_5.5/maps/flat_wallpaper1.jpg',
+ (CT_BLUE2, CT_PINK2),
+ (1060,),
+ 180),
+ 2020: ('phase_5.5/maps/flat_wallpaper1.jpg',
+ (CT_BEIGE2,
+ CT_BLUE2,
+ CT_PINK2,
+ CT_BEIGE,
+ CT_RED),
+ (1070,),
+ 180),
+ 2100: ('phase_5.5/maps/big_stripes1.jpg',
+ CTWhite,
+ (1050,),
+ 180),
+ 2110: ('phase_5.5/maps/big_stripes2.jpg',
+ CTWhite,
+ (1050,),
+ 180),
+ 2120: ('phase_5.5/maps/big_stripes3.jpg',
+ CTWhite,
+ (1060,),
+ 180),
+ 2130: ('phase_5.5/maps/big_stripes3.jpg',
+ CTWhite,
+ (1070,),
+ 180),
+ 2140: ('phase_5.5/maps/big_stripes6.jpg',
+ CTWhite,
+ (1070,),
+ 180),
+ 2200: ('phase_5.5/maps/wall_paper_car.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 2210: ('phase_5.5/maps/wall_paper_car_neutral.jpg',
+ CTFlatColor,
+ (0, 1000),
+ 180),
+ 2300: ('phase_5.5/maps/wall_paper_football_neutral.jpg',
+ CTFlatColor,
+ (0, 1080),
+ 180),
+ 2400: ('phase_5.5/maps/wall_paper_clouds.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 2500: ('phase_5.5/maps/wall_paper_vine_neutral.jpg',
+ CTFlatColorAll,
+ (0, 1090),
+ 180),
+ 2600: ('phase_5.5/maps/basket.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 2610: ('phase_5.5/maps/basket_neutral.jpg',
+ CTFlatColor,
+ (0, 1000),
+ 180),
+ 2700: ('phase_5.5/maps/doll.jpg',
+ CTWhite,
+ (0, 1000, 1110),
+ 180),
+ 2710: ('phase_5.5/maps/doll_neutral.jpg',
+ CTFlatColor,
+ (0, 1100, 1110),
+ 180),
+ 2800: ('phase_5.5/maps/littleFlowers.jpg',
+ CTWhite,
+ (0, 1000),
+ 180),
+ 2810: ('phase_5.5/maps/littleFlowers_neutral.jpg',
+ CTFlatColor,
+ (0, 1000),
+ 180),
+ 2900: ('phase_5.5/maps/UWwallPaperAngelFish.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 2910: ('phase_5.5/maps/UWwallPaperAngelFishColor.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 2920: ('phase_5.5/maps/UWwallPaperBubbles.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 2930: ('phase_5.5/maps/UWwallPaperBubbles2.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 2940: ('phase_5.5/maps/UWwallPaperGreenFish.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 2950: ('phase_5.5/maps/UWwallPaperRedFish.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 2960: ('phase_5.5/maps/UWwallPaperSea_horse.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 2970: ('phase_5.5/maps/UWwallPaperShells.jpg',
+ CTWhite,
+ (0, 1140, 1150),
+ 180),
+ 2980: ('phase_5.5/maps/UWwaterFloor1.jpg',
+ (CT_WHITE, CT_PALE_GREEN, CT_LIGHT_BLUE),
+ (0,),
+ 180),
+ 3000: ('phase_5.5/maps/UWwallPaperBubbles.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 3100: ('phase_5.5/maps/UWwallPaperBubbles2.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 3200: ('phase_5.5/maps/UWwallPaperGreenFish.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 3300: ('phase_5.5/maps/UWwallPaperRedFish.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 3400: ('phase_5.5/maps/UWwallPaperSea_horse.jpg',
+ CTWhite,
+ (0, 1120, 1160),
+ 180),
+ 3500: ('phase_5.5/maps/UWwallPaperShells.jpg',
+ (CT_WHITE, CT_SEA_GREEN, CT_LIGHT_BLUE),
+ (0, 1140, 1150),
+ 180),
+ 3600: ('phase_5.5/maps/UWwaterFloor1.jpg',
+ (CT_WHITE, CT_PALE_GREEN, CT_LIGHT_BLUE),
+ (0,),
+ 180),
+ 3700: ('phase_5.5/maps/WesternBootWallpaper1.jpg',
+ CTWhite,
+ (0, 1170, 1180),
+ 180),
+ 3800: ('phase_5.5/maps/WesternCactusWallpaper1.jpg',
+ CTWhite,
+ (0, 1170, 1180),
+ 180),
+ 3900: ('phase_5.5/maps/WesternHatWallpaper1.jpg',
+ CTWhite,
+ (0, 1170, 1180),
+ 180),
+ 10100: ('phase_5.5/maps/cats1.jpg',
+ CTWhite,
+ (0, 10010, 10020),
+ 400),
+ 10200: ('phase_5.5/maps/bats2.jpg',
+ CTWhite,
+ (0, 10010, 10020),
+ 400),
+ 11000: ('phase_5.5/maps/wall_paper_snowflakes.jpg',
+ CTWhite,
+ (0, 11000, 11010),
+ 400),
+ 11100: ('phase_5.5/maps/wall_paper_hollyleaf.jpg',
+ CTWhite,
+ (0, 11000, 11010),
+ 400),
+ 11200: ('phase_5.5/maps/wall_paper_snowman.jpg',
+ CTWhite,
+ (0, 11000, 11010),
+ 400),
+ 12000: ('phase_5.5/maps/VdayWall1.jpg',
+ CTWhite,
+ (0,
+ 12000,
+ 12010,
+ 12020),
+ 400),
+ 12100: ('phase_5.5/maps/VdayWall2.jpg',
+ CTWhite,
+ (0,
+ 12000,
+ 12010,
+ 12020),
+ 400),
+ 12200: ('phase_5.5/maps/VdayWall3.jpg',
+ CTWhite,
+ (0,
+ 12000,
+ 12010,
+ 12020),
+ 400),
+ 12300: ('phase_5.5/maps/VdayWall4.jpg',
+ CTWhite,
+ (0,
+ 12000,
+ 12010,
+ 12020),
+ 400),
+ 13000: ('phase_5.5/maps/StPatWallpaper1.jpg',
+ CTWhite,
+ (0, 13000),
+ 400),
+ 13100: ('phase_5.5/maps/StPatWallpaper2.jpg',
+ CTWhite,
+ (0, 13000),
+ 400),
+ 13200: ('phase_5.5/maps/StPatWallpaper3.jpg',
+ CTWhite,
+ (0, 13000),
+ 400),
+ 13300: ('phase_5.5/maps/StPatWallpaper4.jpg',
+ CTWhite,
+ (0, 13000),
+ 400)}
+WallpaperGroups = {1100: (1100,
+ 1110,
+ 1120,
+ 1130,
+ 1140,
+ 1150),
+ 1200: (1200,
+ 1210,
+ 1220,
+ 1230,
+ 1240,
+ 1250,
+ 1260),
+ 1300: (1300,
+ 1310,
+ 1320,
+ 1330,
+ 1340,
+ 1350),
+ 1400: (1400,
+ 1410,
+ 1420,
+ 1430,
+ 1440,
+ 1450),
+ 1500: (1500, 1510, 1520),
+ 1600: (1600,
+ 1610,
+ 1620,
+ 1630,
+ 1640,
+ 1650,
+ 1660),
+ 1700: (1700,
+ 1710,
+ 1720,
+ 1730,
+ 1740,
+ 1750,
+ 1760),
+ 1800: (1800,
+ 1810,
+ 1820,
+ 1830,
+ 1840,
+ 1850,
+ 1860),
+ 1900: (1900,
+ 1910,
+ 1920,
+ 1930,
+ 1940,
+ 1950),
+ 2000: (2000, 2010, 2020),
+ 2100: (2100,
+ 2110,
+ 2120,
+ 2130,
+ 2140),
+ 2200: (2200, 2210),
+ 2600: (2600, 2610),
+ 2700: (2700, 2710),
+ 2800: (2800, 2810),
+ 2900: (2900, 2910)}
+BorderTypes = {1000: ('phase_5.5/maps/bd_grey_border1.jpg', CTFlatColorDark),
+ 1010: ('phase_5.5/maps/diamonds_border2.jpg', CTWhite),
+ 1020: ('phase_5.5/maps/diamonds_border2ch.jpg', CTWhite),
+ 1030: ('phase_5.5/maps/diamonds_border3ch.jpg', CTWhite),
+ 1040: ('phase_5.5/maps/diamonds_border4ch.jpg', CTWhite),
+ 1050: ('phase_5.5/maps/flower_border2.jpg', CTWhite),
+ 1060: ('phase_5.5/maps/flower_border5.jpg', CTWhite),
+ 1070: ('phase_5.5/maps/flower_border6.jpg', CTWhite),
+ 1080: ('phase_5.5/maps/football_border_neutral.jpg', CTFlatColorDark),
+ 1090: ('phase_5.5/maps/vine_border1.jpg', CTFlatColorDark),
+ 1100: ('phase_5.5/maps/doll_board.jpg', CTWhite),
+ 1110: ('phase_5.5/maps/doll_board_neutral.jpg', CTFlatColorDark),
+ 1120: ('phase_5.5/maps/UWwallPaperPlantBorder.jpg', CTWhite),
+ 1130: ('phase_5.5/maps/UWwallPaperSea_horseBorder.jpg', CTWhite),
+ 1140: ('phase_5.5/maps/UWwallPaperShellBorder1.jpg', CTWhite),
+ 1150: ('phase_5.5/maps/UWwallPaperShellBorder2.jpg', CTWhite),
+ 1160: ('phase_5.5/maps/UWwallPaperWaveBorder.jpg', CTWhite),
+ 1170: ('phase_5.5/maps/WesternSkullBorder.jpg', CTWhite),
+ 1180: ('phase_5.5/maps/WesternStarBorder.jpg', CTWhite),
+ 10010: ('phase_5.5/maps/border_ScarryMoon1.jpg', CTWhite),
+ 10020: ('phase_5.5/maps/border_candy1.jpg', CTWhite),
+ 11000: ('phase_5.5/maps/flakes_border.jpg', CTWhite),
+ 11010: ('phase_5.5/maps/hollyleaf_border.jpg', CTWhite),
+ 12000: ('phase_5.5/maps/Vborder1a.jpg', CTWhite),
+ 12010: ('phase_5.5/maps/Vborder1b.jpg', CTWhite),
+ 12020: ('phase_5.5/maps/Vborder2b.jpg', CTWhite),
+ 13000: ('phase_5.5/maps/StPatBorder1.jpg', CTWhite)}
+
+class CatalogWallpaperItem(CatalogSurfaceItem):
+
+ def makeNewItem(self, patternIndex, colorIndex = None, borderIndex = 0, borderColorIndex = 0):
+ self.patternIndex = patternIndex
+ self.colorIndex = colorIndex
+ self.borderIndex = borderIndex
+ self.borderColorIndex = borderColorIndex
+ CatalogSurfaceItem.makeNewItem(self)
+
+ def needsCustomize(self):
+ return self.colorIndex == None or self.borderIndex == None
+
+ def getTypeName(self):
+ return TTLocalizer.SurfaceNames[STWallpaper]
+
+ def getName(self):
+ name = TTLocalizer.WallpaperNames.get(self.patternIndex)
+ if name == None:
+ century = self.patternIndex - self.patternIndex % 100
+ name = TTLocalizer.WallpaperNames.get(century)
+ if name:
+ return name
+ return self.getTypeName()
+
+ def getSurfaceType(self):
+ return STWallpaper
+
+ def getPicture(self, avatar):
+ frame = self.makeFrame()
+ sample = loader.loadModel('phase_5.5/models/estate/wallpaper_sample')
+ a = sample.find('**/a')
+ b = sample.find('**/b')
+ c = sample.find('**/c')
+ a.setTexture(self.loadTexture(), 1)
+ a.setColorScale(*self.getColor())
+ b.setTexture(self.loadTexture(), 1)
+ b.setColorScale(*self.getColor())
+ c.setTexture(self.loadBorderTexture(), 1)
+ c.setColorScale(*self.getBorderColor())
+ sample.reparentTo(frame)
+ self.hasPicture = True
+ return (frame, None)
+
+ def output(self, store = -1):
+ return 'CatalogWallpaperItem(%s, %s, %s, %s%s)' % (self.patternIndex,
+ self.colorIndex,
+ self.borderIndex,
+ self.borderColorIndex,
+ self.formatOptionalData(store))
+
+ def getFilename(self):
+ return WallpaperTypes[self.patternIndex][WTTextureName]
+
+ def compareTo(self, other):
+ if self.patternIndex != other.patternIndex:
+ century = self.patternIndex - self.patternIndex % 100
+ otherCentury = other.patternIndex - other.patternIndex % 100
+ return century - otherCentury
+ return 0
+
+ def getHashContents(self):
+ century = self.patternIndex - self.patternIndex % 100
+ return century
+
+ def getBasePrice(self):
+ return WallpaperTypes[self.patternIndex][WTBasePrice]
+
+ def loadTexture(self):
+ from pandac.PandaModules import Texture
+ filename = WallpaperTypes[self.patternIndex][WTTextureName]
+ texture = loader.loadTexture(filename)
+ texture.setMinfilter(Texture.FTLinearMipmapLinear)
+ texture.setMagfilter(Texture.FTLinear)
+ return texture
+
+ def getColor(self):
+ if self.colorIndex == None:
+ colorIndex = 0
+ else:
+ colorIndex = self.colorIndex
+ colors = WallpaperTypes[self.patternIndex][WTColor]
+ if colorIndex < len(colors):
+ return colors[colorIndex]
+ else:
+ print 'Warning: colorIndex > len(colors). Returning white.'
+ return CT_WHITE
+ return
+
+ def loadBorderTexture(self):
+ from pandac.PandaModules import Texture
+ if self.borderIndex == None or self.borderIndex == 0:
+ return self.loadTexture()
+ borderInfo = BorderTypes[self.borderIndex]
+ filename = borderInfo[BDTextureName]
+ texture = loader.loadTexture(filename)
+ texture.setMinfilter(Texture.FTLinearMipmapLinear)
+ texture.setMagfilter(Texture.FTLinear)
+ return texture
+
+ def getBorderColor(self):
+ if self.borderIndex == None or self.borderIndex == 0:
+ return self.getColor()
+ else:
+ colors = BorderTypes[self.borderIndex][BDColor]
+ if self.borderColorIndex < len(colors):
+ return colors[self.borderColorIndex]
+ else:
+ return CT_WHITE
+ return
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store)
+ self.colorIndex = None
+ if store & CatalogItem.Customization:
+ self.borderIndex = 0
+ else:
+ self.borderIndex = None
+ self.borderColorIndex = 0
+ if versionNumber < 3:
+ self.patternIndex = di.getUint8()
+ self.colorIndex = di.getUint8()
+ elif versionNumber == 3:
+ self.patternIndex = di.getUint16()
+ self.colorIndex = di.getUint8()
+ else:
+ self.patternIndex = di.getUint16()
+ if store & CatalogItem.Customization:
+ self.colorIndex = di.getUint8()
+ self.borderIndex = di.getUint16()
+ self.borderColorIndex = di.getUint8()
+ wtype = WallpaperTypes[self.patternIndex]
+ return
+
+ def encodeDatagram(self, dg, store):
+ CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store)
+ dg.addUint16(self.patternIndex)
+ if store & CatalogItem.Customization:
+ dg.addUint8(self.colorIndex)
+ dg.addUint16(self.borderIndex)
+ dg.addUint8(self.borderColorIndex)
+
+
+def getWallpapers(*typeList):
+ list = []
+ for type in typeList:
+ list.append(CatalogWallpaperItem(type))
+
+ return list
+
+
+def getAllWallpapers(*typeList):
+ list = []
+ for type in typeList:
+ group = WallpaperGroups.get(type, [type])
+ for index in group:
+ borderKeys = WallpaperTypes[index][WTBorderList]
+ for borderKey in borderKeys:
+ borderData = BorderTypes.get(borderKey)
+ if borderData:
+ numBorderColors = len(borderData[BDColor])
+ else:
+ numBorderColors = 1
+ for borderColorIndex in range(numBorderColors):
+ colors = WallpaperTypes[index][WTColor]
+ for n in range(len(colors)):
+ list.append(CatalogWallpaperItem(index, n, borderKey, borderColorIndex))
+
+ return list
+
+
+def getWallpaperRange(fromIndex, toIndex, *otherRanges):
+ list = []
+ froms = [fromIndex]
+ tos = [toIndex]
+ i = 0
+ while i < len(otherRanges):
+ froms.append(otherRanges[i])
+ tos.append(otherRanges[i + 1])
+ i += 2
+
+ for patternIndex in WallpaperTypes.keys():
+ for fromIndex, toIndex in zip(froms, tos):
+ if patternIndex >= fromIndex and patternIndex <= toIndex:
+ borderKeys = WallpaperTypes[patternIndex][WTBorderList]
+ for borderKey in borderKeys:
+ borderData = BorderTypes.get(borderKey)
+ if borderData:
+ numBorderColors = len(borderData[BDColor])
+ else:
+ numBorderColors = 1
+ for borderColorIndex in range(numBorderColors):
+ colors = WallpaperTypes[patternIndex][WTColor]
+ for n in range(len(colors)):
+ list.append(CatalogWallpaperItem(patternIndex, n, borderKey, borderColorIndex))
+
+ return list
diff --git a/toontown/catalog/CatalogWindowItem.py b/toontown/catalog/CatalogWindowItem.py
new file mode 100644
index 0000000..48858b7
--- /dev/null
+++ b/toontown/catalog/CatalogWindowItem.py
@@ -0,0 +1,120 @@
+from pandac.PandaModules import *
+import CatalogAtticItem
+import CatalogItem
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+WVTModelName = 0
+WVTBasePrice = 1
+WVTSkyName = 2
+WindowViewTypes = {10: ('phase_5.5/models/estate/Garden1', 900, None),
+ 20: ('phase_5.5/models/estate/GardenA', 900, None),
+ 30: ('phase_5.5/models/estate/GardenB', 900, None),
+ 40: ('phase_5.5/models/estate/cityView', 900, None),
+ 50: ('phase_5.5/models/estate/westernView', 900, None),
+ 60: ('phase_5.5/models/estate/underwaterView', 900, None),
+ 70: ('phase_5.5/models/estate/tropicView', 900, None),
+ 80: ('phase_5.5/models/estate/spaceView', 900, None),
+ 90: ('phase_5.5/models/estate/PoolView', 900, None),
+ 100: ('phase_5.5/models/estate/SnowView', 900, None),
+ 110: ('phase_5.5/models/estate/FarmView', 900, None),
+ 120: ('phase_5.5/models/estate/IndianView', 900, None),
+ 130: ('phase_5.5/models/estate/WesternMainStreetView', 900, None)}
+
+class CatalogWindowItem(CatalogAtticItem.CatalogAtticItem):
+
+ def makeNewItem(self, windowType, placement = None):
+ self.windowType = windowType
+ self.placement = placement
+ CatalogAtticItem.CatalogAtticItem.makeNewItem(self)
+
+ def saveHistory(self):
+ return 1
+
+ def getTypeName(self):
+ return TTLocalizer.WindowViewTypeName
+
+ def getName(self):
+ return TTLocalizer.WindowViewNames.get(self.windowType)
+
+ def recordPurchase(self, avatar, optional):
+ self.giftTag = None
+ house, retcode = self.getHouseInfo(avatar)
+ if retcode >= 0:
+ house.addWindow(self)
+ return retcode
+
+ def getDeliveryTime(self):
+ return 4 * 60
+
+ def getPicture(self, avatar):
+ frame = self.makeFrame()
+ model = self.loadModel()
+ model.setDepthTest(1)
+ model.setDepthWrite(1)
+ clipperLeft = PlaneNode('clipper')
+ clipperRight = PlaneNode('clipper')
+ clipperTop = PlaneNode('clipper')
+ clipperBottom = PlaneNode('clipper')
+ clipperLeft.setPlane(Plane(Vec3(1, 0, 0), Point3(-1, 0, 0)))
+ clipperRight.setPlane(Plane(Vec3(-1, 0, 0), Point3(1, 0, 0)))
+ clipperTop.setPlane(Plane(Vec3(0, 0, -1), Point3(0, 0, 1)))
+ clipperBottom.setPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, -1)))
+ model.setClipPlane(frame.attachNewNode(clipperLeft))
+ model.setClipPlane(frame.attachNewNode(clipperRight))
+ model.setClipPlane(frame.attachNewNode(clipperTop))
+ model.setClipPlane(frame.attachNewNode(clipperBottom))
+ bgName = WindowViewTypes[self.windowType][WVTSkyName]
+ if bgName:
+ bgNodePath = model.find('**/' + bgName)
+ if not bgNodePath.isEmpty():
+ bgNodePath.reparentTo(model, -1)
+ windowFrame = model.find('**/frame')
+ if not windowFrame.isEmpty():
+ windowFrame.removeNode()
+ model.setPos(0, 2, 0)
+ model.setScale(0.4)
+ model.reparentTo(frame)
+ self.hasPicture = True
+ return (frame, None)
+
+ def output(self, store = -1):
+ return 'CatalogWindowItem(%s%s)' % (self.windowType, self.formatOptionalData(store))
+
+ def getFilename(self):
+ type = WindowViewTypes[self.windowType]
+ return type[WVTModelName]
+
+ def formatOptionalData(self, store = -1):
+ result = CatalogAtticItem.CatalogAtticItem.formatOptionalData(self, store)
+ if store & CatalogItem.WindowPlacement and self.placement != None:
+ result += ', placement = %s' % self.placement
+ return result
+
+ def compareTo(self, other):
+ return self.windowType - other.windowType
+
+ def getHashContents(self):
+ return self.windowType
+
+ def getBasePrice(self):
+ return WindowViewTypes[self.windowType][WVTBasePrice]
+
+ def loadModel(self):
+ type = WindowViewTypes[self.windowType]
+ model = loader.loadModel(type[WVTModelName])
+ return model
+
+ def decodeDatagram(self, di, versionNumber, store):
+ CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store)
+ self.placement = None
+ if store & CatalogItem.WindowPlacement:
+ self.placement = di.getUint8()
+ self.windowType = di.getUint8()
+ wvtype = WindowViewTypes[self.windowType]
+ return
+
+ def encodeDatagram(self, dg, store):
+ CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store)
+ if store & CatalogItem.WindowPlacement:
+ dg.addUint8(self.placement)
+ dg.addUint8(self.windowType)
diff --git a/toontown/catalog/MailboxScreen.py b/toontown/catalog/MailboxScreen.py
new file mode 100644
index 0000000..97b00c3
--- /dev/null
+++ b/toontown/catalog/MailboxScreen.py
@@ -0,0 +1,542 @@
+from direct.directnotify.DirectNotifyGlobal import *
+from direct.gui.DirectGui import *
+from direct.showbase import DirectObject, PythonUtil
+from pandac.PandaModules import *
+from toontown.parties import PartyGlobals
+from toontown.parties.InviteInfo import InviteInfoBase
+from toontown.parties.PartyGlobals import InviteStatus
+from toontown.parties.SimpleMailBase import SimpleMailBase
+from toontown.toonbase import TTLocalizer, ToontownGlobals
+from toontown.toontowngui import TTDialog
+from toontown.toontowngui.TeaserPanel import TeaserPanel
+from toontown.parties.InviteVisual import InviteVisual
+import CatalogItem
+from direct.showbase.PythonUtil import StackTrace
+
+class MailboxScreen(DirectObject.DirectObject):
+ notify = directNotify.newCategory('MailboxScreen')
+
+ def __init__(self, mailbox, avatar, doneEvent = None):
+ self.mailbox = mailbox
+ self.avatar = avatar
+ self.items = self.getItems()
+ self.doneEvent = doneEvent
+ self.itemIndex = 0
+ self.itemPanel = None
+ self.itemPicture = None
+ self.ival = None
+ self.itemText = None
+ self.giftTag = None
+ self.acceptingIndex = None
+ self.numAtticAccepted = 0
+ self.dialogBox = None
+ self.load()
+ self.hide()
+ return
+
+ def show(self):
+ self.frame.show()
+ self.__showCurrentItem()
+
+ def hide(self):
+ self.ignore('friendsListChanged')
+ if hasattr(self, 'frame'):
+ self.frame.hide()
+ else:
+ self.notify.warning('hide called, but frame is deleted, self.frame deleted in:')
+ if hasattr(self, 'frameDelStackTrace'):
+ print self.frameDelStackTrace
+ self.notify.warning('current stackTrace =')
+ print StackTrace()
+ self.notify.warning('crash averted, but root cause unknown')
+
+ def load(self):
+ self.accept('setMailboxContents-%s' % base.localAvatar.doId, self.__refreshItems)
+ self.accept('setAwardMailboxContents-%s' % base.localAvatar.doId, self.__refreshItems)
+ model = loader.loadModel('phase_5.5/models/gui/package_delivery_panel')
+ background = model.find('**/bg')
+ itemBoard = model.find('**/item_board')
+ self.frame = DirectFrame(scale=1.1, relief=DGG.FLAT, frameSize=(-0.5,
+ 0.5,
+ -0.45,
+ -0.05), frameColor=(0.737, 0.573, 0.345, 1.0))
+ self.background = DirectFrame(self.frame, image=background, image_scale=0.05, relief=None, pos=(0, 1, 0))
+ self.itemBoard = DirectFrame(parent=self.frame, image=itemBoard, image_scale=0.05, image_color=(0.922, 0.922, 0.753, 1), relief=None, pos=(0, 1, 0))
+ self.itemCountLabel = DirectLabel(parent=self.frame, relief=None, text=self.__getNumberOfItemsText(), text_wordwrap=16, pos=(0.0, 0.0, 0.7), scale=0.09)
+ exitUp = model.find('**/bu_return_rollover')
+ exitDown = model.find('**/bu_return_rollover')
+ exitRollover = model.find('**/bu_return_rollover')
+ exitUp.setP(-90)
+ exitDown.setP(-90)
+ exitRollover.setP(-90)
+ self.DiscardButton = DirectButton(parent=self.frame, relief=None, image=(exitUp,
+ exitDown,
+ exitRollover,
+ exitUp), pos=(-0.01, 1.0, -0.36), scale=0.048, text=('',
+ TTLocalizer.MailBoxDiscard,
+ TTLocalizer.MailBoxDiscard,
+ ''), text_scale=1.0, text_pos=(0, -0.08), textMayChange=1, command=self.__makeDiscardInterface)
+ gui2 = loader.loadModel('phase_3/models/gui/quit_button')
+ self.quitButton = DirectButton(parent=self.frame, relief=None, image=(gui2.find('**/QuitBtn_UP'), gui2.find('**/QuitBtn_DN'), gui2.find('**/QuitBtn_RLVR')), pos=(0.5, 1.0, -0.42), scale=0.9, text=TTLocalizer.MailboxExitButton, text_font=ToontownGlobals.getSignFont(), text0_fg=(0.152, 0.75, 0.258, 1), text1_fg=(0.152, 0.75, 0.258, 1), text2_fg=(0.977, 0.816, 0.133, 1), text_scale=0.045, text_pos=(0, -0.01), command=self.__handleExit)
+ self.gettingText = DirectLabel(parent=self.frame, relief=None, text='', text_wordwrap=10, pos=(0.0, 0.0, 0.32), scale=0.09)
+ self.gettingText.hide()
+ self.giftTagPanel = DirectLabel(parent=self.frame, relief=None, text='Gift TAG!!', text_wordwrap=16, pos=(0.0, 0.0, 0.01), scale=0.06)
+ self.giftTagPanel.hide()
+ self.itemText = DirectLabel(parent=self.frame, relief=None, text='', text_wordwrap=16, pos=(0.0, 0.0, -0.022), scale=0.07)
+ self.itemText.hide()
+ acceptUp = model.find('**/bu_check_up')
+ acceptDown = model.find('**/bu_check_down')
+ acceptRollover = model.find('**/bu_check_rollover')
+ acceptUp.setP(-90)
+ acceptDown.setP(-90)
+ acceptRollover.setP(-90)
+ self.acceptButton = DirectButton(parent=self.frame, relief=None, image=(acceptUp,
+ acceptDown,
+ acceptRollover,
+ acceptUp), image3_color=(0.8, 0.8, 0.8, 0.6), pos=(-0.01, 1.0, -0.16), scale=0.048, text=('',
+ TTLocalizer.MailboxAcceptButton,
+ TTLocalizer.MailboxAcceptButton,
+ ''), text_scale=1.0, text_pos=(0, -0.09), textMayChange=1, command=self.__handleAccept, state=DGG.DISABLED)
+ nextUp = model.find('**/bu_next_up')
+ nextUp.setP(-90)
+ nextDown = model.find('**/bu_next_down')
+ nextDown.setP(-90)
+ nextRollover = model.find('**/bu_next_rollover')
+ nextRollover.setP(-90)
+ self.nextButton = DirectButton(parent=self.frame, relief=None, image=(nextUp,
+ nextDown,
+ nextRollover,
+ nextUp), image3_color=(0.8, 0.8, 0.8, 0.6), pos=(0.31, 1.0, -0.26), scale=0.05, text=('',
+ TTLocalizer.MailboxItemNext,
+ TTLocalizer.MailboxItemNext,
+ ''), text_scale=1, text_pos=(-0.2, 0.3), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), textMayChange=0, command=self.__nextItem, state=DGG.DISABLED)
+ prevUp = model.find('**/bu_previous_up')
+ prevUp.setP(-90)
+ prevDown = model.find('**/bu_previous_down')
+ prevDown.setP(-90)
+ prevRollover = model.find('**/bu_previous_rollover')
+ prevRollover.setP(-90)
+ self.prevButton = DirectButton(parent=self.frame, relief=None, image=(prevUp,
+ prevDown,
+ prevRollover,
+ prevUp), pos=(-0.35, 1, -0.26), scale=0.05, image3_color=(0.8, 0.8, 0.8, 0.6), text=('',
+ TTLocalizer.MailboxItemPrev,
+ TTLocalizer.MailboxItemPrev,
+ ''), text_scale=1, text_pos=(0, 0.3), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), textMayChange=0, command=self.__prevItem, state=DGG.DISABLED)
+ self.currentItem = None
+ self.partyInviteVisual = InviteVisual(self.frame)
+ self.partyInviteVisual.setScale(0.73)
+ self.partyInviteVisual.setPos(0.0, 0.0, 0.48)
+ self.partyInviteVisual.stash()
+ if self.avatar:
+ self.avatar.applyCheesyEffect(ToontownGlobals.CENormal)
+ return
+
+ def unload(self):
+ if self.avatar:
+ self.avatar.reconsiderCheesyEffect()
+ self.__clearCurrentItem()
+ if hasattr(self, 'frame'):
+ self.frame.destroy()
+ del self.frame
+ self.frameDelStackTrace = StackTrace()
+ else:
+ self.notify.warning('unload, no self.frame')
+ if hasattr(self, 'mailbox'):
+ del self.mailbox
+ else:
+ self.notify.warning('unload, no self.mailbox')
+ if self.dialogBox:
+ self.dialogBox.cleanup()
+ self.dialogBox = None
+ for item in self.items:
+ if isinstance(item, CatalogItem.CatalogItem):
+ item.acceptItemCleanup()
+
+ self.ignoreAll()
+ return
+
+ def justExit(self):
+ self.__acceptExit()
+
+ def __handleExit(self):
+ if self.numAtticAccepted == 0:
+ self.__acceptExit()
+ elif self.numAtticAccepted == 1:
+ self.dialogBox = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.CatalogAcceptInAttic, text_wordwrap=15, command=self.__acceptExit)
+ self.dialogBox.show()
+ else:
+ self.dialogBox = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.CatalogAcceptInAtticP, text_wordwrap=15, command=self.__acceptExit)
+ self.dialogBox.show()
+
+ def __acceptExit(self, buttonValue = None):
+ if hasattr(self, 'frame'):
+ self.hide()
+ self.unload()
+ messenger.send(self.doneEvent)
+
+ def __handleAccept(self):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: MAILBOX: Accept item')
+ if self.acceptingIndex != None:
+ return
+ item = self.items[self.itemIndex]
+ isAward = False
+ if isinstance(item, CatalogItem.CatalogItem):
+ isAward = item.isAward()
+ if not base.cr.isPaid() and not (isinstance(item, InviteInfoBase) or isAward):
+ TeaserPanel(pageName='clothing')
+ else:
+ self.acceptingIndex = self.itemIndex
+ self.acceptButton['state'] = DGG.DISABLED
+ self.__showCurrentItem()
+ item = self.items[self.itemIndex]
+ item.acceptItem(self.mailbox, self.acceptingIndex, self.__acceptItemCallback)
+ return
+
+ def __handleDiscard(self, buttonValue = None):
+ if self.acceptingIndex != None:
+ return
+ elif buttonValue == -1:
+ if self.dialogBox:
+ self.dialogBox.cleanup()
+ self.dialogBox = None
+ self.__showCurrentItem()
+ else:
+ self.acceptingIndex = self.itemIndex
+ self.acceptButton['state'] = DGG.DISABLED
+ self.__showCurrentItem()
+ item = self.items[self.itemIndex]
+ item.discardItem(self.mailbox, self.acceptingIndex, self.__discardItemCallback)
+ return
+
+ def __discardItemCallback(self, retcode, item, index):
+ if not hasattr(self, 'frame'):
+ return
+ if self.dialogBox:
+ self.dialogBox.cleanup()
+ self.dialogBox = None
+ self.acceptingIndex = None
+ self.__updateItems()
+ if isinstance(item, InviteInfoBase):
+ callback = self.__incIndexRemoveDialog
+ self.dialogBox = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=item.getDiscardItemErrorText(retcode), text_wordwrap=15, command=callback)
+ self.dialogBox.show()
+ return
+
+ def __makeDiscardInterface(self):
+ if self.itemIndex >= 0 and self.itemIndex < len(self.items):
+ item = self.items[self.itemIndex]
+ if isinstance(item, InviteInfoBase):
+ itemText = TTLocalizer.MailBoxRejectVerify % self.getItemName(item)
+ yesText = TTLocalizer.MailboxReject
+ else:
+ itemText = TTLocalizer.MailBoxDiscardVerify % self.getItemName(item)
+ yesText = TTLocalizer.MailboxDiscard
+ self.dialogBox = TTDialog.TTDialog(style=TTDialog.TwoChoiceCustom, text=itemText, text_wordwrap=15, command=self.__handleDiscard, buttonText=[yesText, TTLocalizer.MailboxLeave])
+ self.dialogBox.show()
+
+ def __acceptItemCallback(self, retcode, item, index):
+ needtoUpdate = 0
+ if self.acceptingIndex == None:
+ needtoUpdate = 1
+ if not hasattr(self, 'frame'):
+ return
+ if retcode == ToontownGlobals.P_UserCancelled:
+ print 'mailbox screen user canceled'
+ self.acceptingIndex = None
+ self.__updateItems()
+ return
+ if self.acceptingIndex != index:
+ self.notify.warning('Got unexpected callback for index %s, expected %s.' % (index, self.acceptingIndex))
+ return
+ self.acceptingIndex = None
+ if retcode < 0:
+ self.notify.info('Could not take item %s: retcode %s' % (item, retcode))
+ if retcode == ToontownGlobals.P_NoTrunk:
+ self.dialogBox = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.CatalogAcceptNoTrunk, text_wordwrap=15, command=self.__acceptError)
+ else:
+ self.dialogBox = TTDialog.TTDialog(style=TTDialog.TwoChoiceCustom, text=item.getAcceptItemErrorText(retcode), text_wordwrap=15, command=self.__handleDiscard, buttonText=[TTLocalizer.MailboxDiscard, TTLocalizer.MailboxLeave])
+ self.dialogBox.show()
+ elif hasattr(item, 'storedInAttic') and item.storedInAttic():
+ self.numAtticAccepted += 1
+ self.itemIndex += 1
+ if needtoUpdate == 1:
+ self.__updateItems()
+ else:
+ if isinstance(item, InviteInfoBase):
+ self.__updateItems()
+ callback = self.__incIndexRemoveDialog
+ self.dialogBox = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=item.getAcceptItemErrorText(retcode), text_wordwrap=15, command=callback)
+ self.dialogBox.show()
+ return
+
+ def __acceptError(self, buttonValue = None):
+ self.dialogBox.cleanup()
+ self.dialogBox = None
+ self.__showCurrentItem()
+ return
+
+ def __incIndexRemoveDialog(self, junk = 0):
+ self.__incIndex()
+ self.dialogBox.cleanup()
+ self.dialogBox = None
+ self.__showCurrentItem()
+ return
+
+ def __incIndex(self, junk = 0):
+ self.itemIndex += 1
+
+ def __acceptOk(self, index, buttonValue = None):
+ self.acceptingIndex = None
+ if self.dialogBox:
+ self.dialogBox.cleanup()
+ self.dialogBox = None
+ self.items = self.getItems()
+ if self.itemIndex > index or self.itemIndex >= len(self.items):
+ print 'adjusting item index -1'
+ self.itemIndex -= 1
+ if len(self.items) < 1:
+ self.__handleExit()
+ return
+ self.itemCountLabel['text'] = (self.__getNumberOfItemsText(),)
+ self.__showCurrentItem()
+ return
+
+ def __refreshItems(self):
+ self.acceptingIndex = None
+ self.__updateItems()
+ return
+
+ def __updateItems(self):
+ if self.dialogBox:
+ self.dialogBox.cleanup()
+ self.dialogBox = None
+ self.items = self.getItems()
+ if self.itemIndex >= len(self.items):
+ print 'adjusting item index -1'
+ self.itemIndex = len(self.items) - 1
+ if len(self.items) == 0:
+ print 'exiting due to lack of items'
+ self.__handleExit()
+ return
+ self.itemCountLabel['text'] = (self.__getNumberOfItemsText(),)
+ self.__showCurrentItem()
+ return
+
+ def __getNumberOfItemsText(self):
+ if len(self.items) == 1:
+ return TTLocalizer.MailboxOneItem
+ else:
+ return TTLocalizer.MailboxNumberOfItems % len(self.items)
+
+ def __clearCurrentItem(self):
+ if self.itemPanel:
+ self.itemPanel.destroy()
+ self.itemPanel = None
+ if self.ival:
+ self.ival.finish()
+ self.ival = None
+ if not self.gettingText.isEmpty():
+ self.gettingText.hide()
+ if not self.itemText.isEmpty():
+ self.itemText.hide()
+ if not self.giftTagPanel.isEmpty():
+ self.giftTagPanel.hide()
+ if not self.acceptButton.isEmpty():
+ self.acceptButton['state'] = DGG.DISABLED
+ if self.currentItem:
+ if isinstance(self.currentItem, CatalogItem.CatalogItem):
+ self.currentItem.cleanupPicture()
+ self.currentItem = None
+ return
+
+ def checkFamily(self, doId):
+ for familyMember in base.cr.avList:
+ if familyMember.id == doId:
+ return familyMember
+
+ return None
+
+ def __showCurrentItem(self):
+ self.__clearCurrentItem()
+ if len(self.items) < 1:
+ self.__handleExit()
+ return
+ self.partyInviteVisual.stash()
+ if self.itemIndex + 1 > len(self.items):
+ self.itemIndex = len(self.items) - 1
+ item = self.items[self.itemIndex]
+ if self.itemIndex == self.acceptingIndex:
+ self.gettingText['text'] = TTLocalizer.MailboxGettingItem % self.getItemName(item)
+ self.gettingText.show()
+ return
+ self.itemText['text'] = self.getItemName(item)
+ self.currentItem = item
+ if isinstance(item, CatalogItem.CatalogItem):
+ self.acceptButton['text'] = ('',
+ TTLocalizer.MailboxAcceptButton,
+ TTLocalizer.MailboxAcceptButton,
+ '')
+ self.DiscardButton['text'] = ('',
+ TTLocalizer.MailBoxDiscard,
+ TTLocalizer.MailBoxDiscard,
+ '')
+ if item.isAward():
+ self.giftTagPanel['text'] = TTLocalizer.SpecialEventMailboxStrings[item.specialEventId]
+ elif item.giftTag != None:
+ nameOfSender = self.getSenderName(item.giftTag)
+ if item.giftCode == ToontownGlobals.GIFT_RAT:
+ self.giftTagPanel['text'] = TTLocalizer.CatalogAcceptRATBeans
+ elif item.giftCode == ToontownGlobals.GIFT_partyrefund:
+ self.giftTagPanel['text'] = TTLocalizer.CatalogAcceptPartyRefund
+ else:
+ self.giftTagPanel['text'] = TTLocalizer.MailboxGiftTag % nameOfSender
+ else:
+ self.giftTagPanel['text'] = ''
+ self.itemPanel, self.ival = item.getPicture(base.localAvatar)
+ elif isinstance(item, SimpleMailBase):
+ self.acceptButton['text'] = ('',
+ TTLocalizer.MailboxAcceptButton,
+ TTLocalizer.MailboxAcceptButton,
+ '')
+ self.DiscardButton['text'] = ('',
+ TTLocalizer.MailBoxDiscard,
+ TTLocalizer.MailBoxDiscard,
+ '')
+ senderId = item.senderId
+ nameOfSender = self.getSenderName(senderId)
+ self.giftTagPanel['text'] = TTLocalizer.MailFromTag % nameOfSender
+ self.itemText['text'] = item.body
+ elif isinstance(item, InviteInfoBase):
+ self.acceptButton['text'] = ('',
+ TTLocalizer.MailboxAcceptInvite,
+ TTLocalizer.MailboxAcceptInvite,
+ '')
+ self.DiscardButton['text'] = ('',
+ TTLocalizer.MailBoxRejectInvite,
+ TTLocalizer.MailBoxRejectInvite,
+ '')
+ partyInfo = None
+ for party in self.avatar.partiesInvitedTo:
+ if party.partyId == item.partyId:
+ partyInfo = party
+ break
+ else:
+ MailboxScreen.notify.error('Unable to find party with id %d to match invitation %s' % (item.partyId, item))
+
+ if self.mailbox:
+ if item.status == PartyGlobals.InviteStatus.NotRead:
+ self.mailbox.sendInviteReadButNotReplied(item.inviteKey)
+ senderId = partyInfo.hostId
+ nameOfSender = self.getSenderName(senderId)
+ self.giftTagPanel['text'] = ''
+ self.itemText['text'] = ''
+ self.partyInviteVisual.updateInvitation(nameOfSender, partyInfo)
+ self.partyInviteVisual.unstash()
+ self.itemPanel = None
+ self.ival = None
+ else:
+ self.acceptButton['text'] = ('',
+ TTLocalizer.MailboxAcceptButton,
+ TTLocalizer.MailboxAcceptButton,
+ '')
+ self.DiscardButton['text'] = ('',
+ TTLocalizer.MailBoxDiscard,
+ TTLocalizer.MailBoxDiscard,
+ '')
+ self.giftTagPanel['text'] = ' '
+ self.itemPanel = None
+ self.ival = None
+ self.itemText.show()
+ self.giftTagPanel.show()
+ if self.itemPanel and item.getTypeName() != TTLocalizer.ChatTypeName:
+ self.itemPanel.reparentTo(self.itemBoard, -1)
+ self.itemPanel.setPos(0, 0, 0.4)
+ self.itemPanel.setScale(0.21)
+ self.itemText['text_wordwrap'] = 16
+ self.itemText.setPos(0.0, 0.0, 0.075)
+ elif isinstance(item, CatalogItem.CatalogItem) and item.getTypeName() == TTLocalizer.ChatTypeName:
+ self.itemPanel.reparentTo(self.itemBoard, -1)
+ self.itemPanel.setPos(0, 0, 0.35)
+ self.itemPanel.setScale(0.21)
+ self.itemText['text_wordwrap'] = 10
+ self.itemText.setPos(0, 0, 0.3)
+ else:
+ self.itemText.setPos(0, 0, 0.3)
+ if self.ival:
+ self.ival.loop()
+ if self.acceptingIndex == None:
+ self.acceptButton['state'] = DGG.NORMAL
+ if self.itemIndex > 0:
+ self.prevButton['state'] = DGG.NORMAL
+ else:
+ self.prevButton['state'] = DGG.DISABLED
+ if self.itemIndex + 1 < len(self.items):
+ self.nextButton['state'] = DGG.NORMAL
+ else:
+ self.nextButton['state'] = DGG.DISABLED
+ return
+
+ def __nextItem(self):
+ messenger.send('wakeup')
+ if self.itemIndex + 1 < len(self.items):
+ self.itemIndex += 1
+ self.__showCurrentItem()
+
+ def __prevItem(self):
+ messenger.send('wakeup')
+ if self.itemIndex > 0:
+ self.itemIndex -= 1
+ self.__showCurrentItem()
+
+ def getItemName(self, item):
+ if isinstance(item, CatalogItem.CatalogItem):
+ return item.getName()
+ elif isinstance(item, str):
+ return TTLocalizer.MailSimpleMail
+ elif isinstance(item, InviteInfoBase):
+ return TTLocalizer.InviteInvitation
+ else:
+ return ''
+
+ def getItems(self):
+ result = []
+ result = self.avatar.awardMailboxContents[:]
+ result += self.avatar.mailboxContents[:]
+ if self.avatar.mail:
+ result += self.avatar.mail
+ mailboxInvites = self.avatar.getInvitesToShowInMailbox()
+ if mailboxInvites:
+ result += mailboxInvites
+ return result
+
+ def getNumberOfAwardItems(self):
+ result = 0
+ for item in self.items:
+ if isinstance(item, CatalogItem.CatalogItem) and item.specialEventId > 0:
+ result += 1
+ else:
+ break
+
+ return result
+
+ def getSenderName(self, avId):
+ sender = base.cr.identifyFriend(avId)
+ nameOfSender = ''
+ if sender:
+ nameOfSender = sender.getName()
+ else:
+ sender = self.checkFamily(avId)
+ if sender:
+ nameOfSender = sender.name
+ elif hasattr(base.cr, 'playerFriendsManager'):
+ sender = base.cr.playerFriendsManager.getAvHandleFromId(avId)
+ if sender:
+ nameOfSender = sender.getName()
+ if not sender:
+ nameOfSender = TTLocalizer.MailboxGiftTagAnonymous
+ if hasattr(base.cr, 'playerFriendsManager'):
+ base.cr.playerFriendsManager.requestAvatarInfo(avId)
+ self.accept('friendsListChanged', self.__showCurrentItem)
+ return nameOfSender
diff --git a/toontown/catalog/__init__.py b/toontown/catalog/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/toontown/char/Char.py b/toontown/char/Char.py
new file mode 100644
index 0000000..03a9fca
--- /dev/null
+++ b/toontown/char/Char.py
@@ -0,0 +1,631 @@
+from otp.avatar import Avatar
+from pandac.PandaModules import *
+from direct.task import Task
+import random
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+AnimDict = {'mk': (('walk', 'walk', 3),
+ ('run', 'run', 3),
+ ('neutral', 'wait', 3),
+ ('left-point-start', 'left-start', 3.5),
+ ('left-point', 'left', 3.5),
+ ('right-point-start', 'right-start', 3.5),
+ ('right-point', 'right', 3.5)),
+ 'vmk': (('walk', 'sneak', 3),
+ ('run', 'run', 3),
+ ('neutral', 'idle', 3),
+ ('sneak', 'sneak', 3),
+ ('into_sneak', 'into_sneak', 3),
+ ('chat', 'run', 3),
+ ('into_idle', 'into_idle', 3)),
+ 'wmn': (('walk', 'walkHalloween3', 3), ('neutral', 'neutral2', 3)),
+ 'mn': (('walk', 'walk', 3),
+ ('run', 'run', 3),
+ ('neutral', 'wait', 3),
+ ('left-point-start', 'start-Lpoint', 3.5),
+ ('left-point', 'Lpoint', 3.5),
+ ('right-point-start', 'start-Rpoint', 3.5),
+ ('right-point', 'Rpoint', 3.5),
+ ('up', 'up', 4),
+ ('down', 'down', 4),
+ ('left', 'left', 4),
+ ('right', 'right', 4)),
+ 'g': (('walk', 'Walk', 6), ('run', 'Run', 6), ('neutral', 'Wait', 6)),
+ 'sg': (('walk', 'walkStrut2', 6), ('neutral', 'neutral', 6)),
+ 'd': (('walk', 'walk', 6),
+ ('trans', 'transition', 6),
+ ('neutral', 'neutral', 6),
+ ('trans-back', 'transBack', 6)),
+ 'fd': (('walk', 'walk', 6), ('neutral', 'idle', 6)),
+ 'dw': (('wheel', 'wheel', 6), ('neutral', 'wheel', 6)),
+ 'p': (('walk', 'walk', 6),
+ ('sit', 'sit', 6),
+ ('neutral', 'neutral', 6),
+ ('stand', 'stand', 6)),
+ 'wp': (('walk', 'walk', 6),
+ ('sit', 'sitStart', 6),
+ ('neutral', 'sitLoop', 6),
+ ('stand', 'sitStop', 6)),
+ 'cl': (),
+ 'dd': (('walk', 'walk', 4), ('neutral', 'idle', 4)),
+ 'shdd': (('walk', 'walk', 4), ('neutral', 'idle', 4)),
+ 'ch': (('walk', 'walk', 6), ('neutral', 'idle', 6)),
+ 'pch': (('walk', 'walk', 6), ('neutral', 'idle', 6)),
+ 'da': (('walk', 'walk', 6), ('neutral', 'idle', 6)),
+ 'jda': (('walk', 'walk', 6), ('neutral', 'idle', 6))}
+ModelDict = {'mk': 'phase_3/models/char/mickey-',
+ 'vmk': 'phase_3.5/models/char/tt_a_chr_csc_mickey_vampire_',
+ 'mn': 'phase_3/models/char/minnie-',
+ 'wmn': 'phase_3.5/models/char/tt_a_chr_csc_witchMinnie_',
+ 'g': 'phase_6/models/char/TT_G',
+ 'sg': 'phase_6/models/char/tt_a_chr_csc_goofyCostume_',
+ 'd': 'phase_6/models/char/DL_donald-',
+ 'fd': 'phase_6/models/char/tt_a_chr_csc_donaldCostume_',
+ 'dw': 'phase_6/models/char/donald-wheel-',
+ 'p': 'phase_6/models/char/pluto-',
+ 'wp': 'phase_6/models/char/tt_a_chr_csc_plutoCostume_',
+ 'cl': 'phase_5.5/models/estate/Clara_pose2-',
+ 'dd': 'phase_4/models/char/daisyduck_',
+ 'shdd': 'phase_4/models/char/tt_a_chr_csc_daisyCostume_',
+ 'ch': 'phase_6/models/char/chip_',
+ 'pch': 'phase_6/models/char/tt_a_chr_csc_chipCostume_',
+ 'da': 'phase_6/models/char/dale_',
+ 'jda': 'phase_6/models/char/tt_a_chr_csc_daleCostume_'}
+LODModelDict = {'mk': [1200, 800, 400],
+ 'vmk': [1200, 800, 400],
+ 'wmn': [1200, 800, 400],
+ 'mn': [1200, 800, 400],
+ 'g': [1500, 1000, 500],
+ 'sg': [1200, 800, 400],
+ 'd': [1000, 500, 250],
+ 'fd': ['default'],
+ 'dw': [1000],
+ 'p': [1000, 500, 300],
+ 'wp': [1200, 800, 400],
+ 'cl': [],
+ 'dd': [1600, 800, 400],
+ 'shdd': ['default'],
+ 'ch': [1000, 500, 250],
+ 'pch': ['default'],
+ 'da': [1000, 500, 250],
+ 'jda': ['default']}
+
+class Char(Avatar.Avatar):
+ notify = DirectNotifyGlobal.directNotify.newCategory('Char')
+
+ def __init__(self):
+ try:
+ self.Char_initialized
+ except:
+ self.Char_initialized = 1
+ Avatar.Avatar.__init__(self)
+ self.setPickable(0)
+ self.setPlayerType(NametagGroup.CCNonPlayer)
+ self.dialogueArray = []
+ self.chatterArray = [[], [], []]
+
+ def delete(self):
+ try:
+ self.Char_deleted
+ except:
+ self.Char_deleted = 1
+ self.unloadDialogue()
+ Avatar.Avatar.delete(self)
+
+ def updateCharDNA(self, newDNA):
+ if newDNA.name != self.style.name:
+ self.swapCharModel(newDNA)
+
+ def setDNAString(self, dnaString):
+ newDNA = CharDNA.CharDNA()
+ newDNA.makeFromNetString(dnaString)
+ self.setDNA(newDNA)
+
+ def setDNA(self, dna):
+ if self.style:
+ self.updateCharDNA(dna)
+ else:
+ self.style = dna
+ self.generateChar()
+ self.initializeDropShadow()
+ self.initializeNametag3d()
+ self.nametag3d.setBin('fixed', 0)
+ if self.name == 'chip' or self.name == 'dale' or self.name == 'police_chip' or self.name == 'jailbird_dale':
+ self.find('**/drop-shadow').setScale(0.33)
+
+ def setLODs(self):
+ self.setLODNode()
+ levelOneIn = base.config.GetInt('lod1-in', 50)
+ levelOneOut = base.config.GetInt('lod1-out', 1)
+ levelTwoIn = base.config.GetInt('lod2-in', 100)
+ levelTwoOut = base.config.GetInt('lod2-out', 50)
+ levelThreeIn = base.config.GetInt('lod3-in', 280)
+ levelThreeOut = base.config.GetInt('lod3-out', 100)
+ self.addLOD(LODModelDict[self.style.name][0], levelOneIn, levelOneOut)
+ self.addLOD(LODModelDict[self.style.name][1], levelTwoIn, levelTwoOut)
+ self.addLOD(LODModelDict[self.style.name][2], levelThreeIn, levelThreeOut)
+
+ def generateChar(self):
+ dna = self.style
+ self.name = dna.getCharName()
+ self.geoEyes = 0
+ if len(LODModelDict[dna.name]) > 1:
+ self.setLODs()
+ filePrefix = ModelDict[dna.name]
+ if self.name == 'mickey':
+ height = 3.0
+ elif self.name == 'vampire_mickey':
+ height = 3.0
+ elif self.name == 'minnie':
+ height = 3.0
+ elif self.name == 'witch_minnie':
+ height = 3.0
+ elif self.name == 'goofy':
+ height = 4.8
+ elif self.name == 'super_goofy':
+ height = 4.8
+ elif self.name == 'donald' or self.name == 'donald-wheel' or self.name == 'franken_donald':
+ height = 4.5
+ elif self.name == 'daisy' or self.name == 'sockHop_daisy':
+ height = 4.5
+ elif self.name == 'pluto':
+ height = 3.0
+ elif self.name == 'western_pluto':
+ height = 4.5
+ elif self.name == 'clarabelle':
+ height = 3.0
+ elif self.name == 'chip':
+ height = 2.0
+ elif self.name == 'dale':
+ height = 2.0
+ elif self.name == 'police_chip':
+ height = 2.0
+ elif self.name == 'jailbird_dale':
+ height = 2.0
+ self.lodStrings = []
+ for lod in LODModelDict[self.style.name]:
+ self.lodStrings.append(str(lod))
+
+ if self.lodStrings:
+ for lodStr in self.lodStrings:
+ if len(self.lodStrings) > 1:
+ lodName = lodStr
+ else:
+ lodName = 'lodRoot'
+ if self.name == 'goofy':
+ self.loadModel(filePrefix + '-' + lodStr, lodName=lodName)
+ else:
+ self.loadModel(filePrefix + lodStr, lodName=lodName)
+
+ else:
+ self.loadModel(filePrefix)
+ animDict = {}
+ animList = AnimDict[self.style.name]
+ for anim in animList:
+ animFilePrefix = filePrefix[:6] + str(anim[2]) + filePrefix[7:]
+ animDict[anim[0]] = animFilePrefix + anim[1]
+
+ for lodStr in self.lodStrings:
+ if len(self.lodStrings) > 1:
+ lodName = lodStr
+ else:
+ lodName = 'lodRoot'
+ self.loadAnims(animDict, lodName=lodName)
+
+ self.setHeight(height)
+ self.loadDialogue(dna.name)
+ self.ears = []
+ if self.name == 'mickey' or self.name == 'vampire_mickey' or self.name == 'minnie':
+ for bundle in self.getPartBundleDict().values():
+ bundle = bundle['modelRoot'].getBundle()
+ earNull = bundle.findChild('sphere3')
+ if not earNull:
+ earNull = bundle.findChild('*sphere3')
+ earNull.clearNetTransforms()
+
+ for bundle in self.getPartBundleDict().values():
+ charNodepath = bundle['modelRoot'].partBundleNP
+ bundle = bundle['modelRoot'].getBundle()
+ earNull = bundle.findChild('sphere3')
+ if not earNull:
+ earNull = bundle.findChild('*sphere3')
+ ears = charNodepath.find('**/sphere3')
+ if ears.isEmpty():
+ ears = charNodepath.find('**/*sphere3')
+ ears.clearEffect(CharacterJointEffect.getClassType())
+ earRoot = charNodepath.attachNewNode('earRoot')
+ earPitch = earRoot.attachNewNode('earPitch')
+ earPitch.setP(40.0)
+ ears.reparentTo(earPitch)
+ earNull.addNetTransform(earRoot.node())
+ ears.clearMat()
+ ears.node().setPreserveTransform(ModelNode.PTNone)
+ ears.setP(-40.0)
+ ears.flattenMedium()
+ self.ears.append(ears)
+ ears.setBillboardAxis()
+
+ self.eyes = None
+ self.lpupil = None
+ self.rpupil = None
+ self.eyesOpen = None
+ self.eyesClosed = None
+ if self.name == 'mickey' or self.name == 'minnie':
+ self.eyesOpen = loader.loadTexture('phase_3/maps/eyes1.jpg', 'phase_3/maps/eyes1_a.rgb')
+ self.eyesClosed = loader.loadTexture('phase_3/maps/mickey_eyes_closed.jpg', 'phase_3/maps/mickey_eyes_closed_a.rgb')
+ self.eyes = self.find('**/1200/**/eyes')
+ self.eyes.setBin('transparent', 0)
+ self.lpupil = self.find('**/1200/**/joint_pupilL')
+ self.rpupil = self.find('**/1200/**/joint_pupilR')
+ for lodName in self.getLODNames():
+ self.drawInFront('joint_pupil?', 'eyes*', -3, lodName=lodName)
+
+ elif (self.name == 'witch_minnie' or
+ self.name == 'vampire_mickey' or
+ self.name == 'super_goofy' or
+ self.name == 'western_pluto' or
+ self.name == 'police_chip' or
+ self.name == 'jailbird_dale' or
+ self.name == 'franken_donald' or
+ self.name == 'sockHop_daisy'):
+ self.geoEyes = 1
+ self.eyeOpenList = []
+ self.eyeCloseList = []
+ if self.find('**/1200/**/eyesOpen').isEmpty():
+ self.eyeCloseList.append(self.find('**/eyesClosed'))
+ self.eyeOpenList.append(self.find('**/eyesOpen'))
+ else:
+ self.eyeCloseList.append(self.find('**/1200/**/eyesClosed'))
+ self.eyeOpenList.append(self.find('**/1200/**/eyesOpen'))
+ for part in self.eyeOpenList:
+ part.show()
+
+ for part in self.eyeCloseList:
+ part.hide()
+
+ elif self.name == 'pluto':
+ self.eyesOpen = loader.loadTexture('phase_6/maps/plutoEyesOpen.jpg', 'phase_6/maps/plutoEyesOpen_a.rgb')
+ self.eyesClosed = loader.loadTexture('phase_6/maps/plutoEyesClosed.jpg', 'phase_6/maps/plutoEyesClosed_a.rgb')
+ self.eyes = self.find('**/1000/**/eyes')
+ self.lpupil = self.find('**/1000/**/joint_pupilL')
+ self.rpupil = self.find('**/1000/**/joint_pupilR')
+ for lodName in self.getLODNames():
+ self.drawInFront('joint_pupil?', 'eyes*', -3, lodName=lodName)
+
+ elif self.name == 'daisy':
+ self.geoEyes = 1
+ self.eyeOpenList = []
+ self.eyeCloseList = []
+ self.eyeCloseList.append(self.find('**/1600/**/eyesclose'))
+ self.eyeCloseList.append(self.find('**/800/**/eyesclose'))
+ self.eyeOpenList.append(self.find('**/1600/**/eyesclose'))
+ self.eyeOpenList.append(self.find('**/800/**/eyesclose'))
+ self.eyeOpenList.append(self.find('**/1600/**/eyespupil'))
+ self.eyeOpenList.append(self.find('**/800/**/eyespupil'))
+ self.eyeOpenList.append(self.find('**/1600/**/eyesopen'))
+ self.eyeOpenList.append(self.find('**/800/**/eyesopen'))
+ for part in self.eyeOpenList:
+ part.show()
+
+ for part in self.eyeCloseList:
+ part.hide()
+
+ elif self.name == 'donald-wheel':
+ self.eyes = self.find('**/eyes')
+ self.lpupil = self.find('**/joint_pupilL')
+ self.rpupil = self.find('**/joint_pupilR')
+ self.drawInFront('joint_pupil?', 'eyes*', -3)
+ elif self.name == 'chip' or self.name == 'dale':
+ self.eyesOpen = loader.loadTexture('phase_6/maps/dale_eye1.jpg', 'phase_6/maps/dale_eye1_a.rgb')
+ self.eyesClosed = loader.loadTexture('phase_6/maps/chip_dale_eye1_blink.jpg', 'phase_6/maps/chip_dale_eye1_blink_a.rgb')
+ self.eyes = self.find('**/eyes')
+ self.lpupil = self.find('**/pupil_left')
+ self.rpupil = self.find('**/pupil_right')
+ self.find('**/blink').hide()
+ if self.lpupil != None:
+ self.lpupil.adjustAllPriorities(1)
+ self.rpupil.adjustAllPriorities(1)
+ if self.eyesOpen:
+ self.eyesOpen.setMinfilter(Texture.FTLinear)
+ self.eyesOpen.setMagfilter(Texture.FTLinear)
+ if self.eyesClosed:
+ self.eyesClosed.setMinfilter(Texture.FTLinear)
+ self.eyesClosed.setMagfilter(Texture.FTLinear)
+ if self.name == 'mickey':
+ pupilParent = self.rpupil.getParent()
+ pupilOffsetNode = pupilParent.attachNewNode('pupilOffsetNode')
+ pupilOffsetNode.setPos(0, 0.025, 0)
+ self.rpupil.reparentTo(pupilOffsetNode)
+ self.__blinkName = 'blink-' + self.name
+ return
+
+ def swapCharModel(self, charStyle):
+ for lodStr in self.lodStrings:
+ if len(self.lodStrings) > 1:
+ lodName = lodStr
+ else:
+ lodName = 'lodRoot'
+ self.removePart('modelRoot', lodName=lodName)
+
+ self.setStyle(charStyle)
+ self.generateChar()
+
+ def getDialogue(self, type, length):
+ sfxIndex = None
+ if type == 'statementA' or type == 'statementB':
+ if length == 1:
+ sfxIndex = 0
+ elif length == 2:
+ sfxIndex = 1
+ elif length >= 3:
+ sfxIndex = 2
+ elif type == 'question':
+ sfxIndex = 3
+ elif type == 'exclamation':
+ sfxIndex = 4
+ elif type == 'special':
+ sfxIndex = 5
+ else:
+ self.notify.error('unrecognized dialogue type: ', type)
+ if sfxIndex != None and sfxIndex < len(self.dialogueArray) and self.dialogueArray[sfxIndex] != None:
+ return self.dialogueArray[sfxIndex]
+ else:
+ return
+ return
+
+ def playDialogue(self, type, length, delay = None):
+ dialogue = self.getDialogue(type, length)
+ base.playSfx(dialogue)
+
+ def getChatterDialogue(self, category, msg):
+ try:
+ return self.chatterArray[category][msg]
+ except IndexError:
+ return None
+
+ return None
+
+ def getShadowJoint(self):
+ return self.getGeomNode()
+
+ def getNametagJoints(self):
+ return []
+
+ def loadChatterDialogue(self, name, audioIndexArray, loadPath, language):
+ chatterTypes = ['greetings', 'comments', 'goodbyes']
+ for categoryIndex in range(len(audioIndexArray)):
+ chatterType = chatterTypes[categoryIndex]
+ for fileIndex in audioIndexArray[categoryIndex]:
+ if fileIndex:
+ self.chatterArray[categoryIndex].append(base.loadSfx('%s/CC_%s_chatter_%s%02d.mp3' % (loadPath,
+ name,
+ chatterType,
+ fileIndex)))
+ else:
+ self.chatterArray[categoryIndex].append(None)
+
+ return
+
+ def loadDialogue(self, char):
+ if self.dialogueArray:
+ self.notify.warning('loadDialogue() called twice.')
+ self.unloadDialogue()
+ language = base.config.GetString('language', 'english')
+ if char == 'mk':
+ dialogueFile = base.loadSfx('phase_3/audio/dial/mickey.wav')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ if language == 'japanese':
+ chatterIndexArray = ([1, 2], [1,
+ 2,
+ 3,
+ 4], [1,
+ 2,
+ 3,
+ 4,
+ 5])
+ self.loadChatterDialogue('mickey', chatterIndexArray, 'phase_3/audio/dial', language)
+ elif char == 'vmk':
+ dialogueFile = base.loadSfx('phase_3/audio/dial/mickey.wav')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ if language == 'japanese':
+ chatterIndexArray = ([1, 2], [1,
+ 2,
+ 3,
+ 4], [1,
+ 2,
+ 3,
+ 4,
+ 5])
+ self.loadChatterDialogue('mickey', chatterIndexArray, 'phase_3/audio/dial', language)
+ elif char == 'mn' or char == 'wmn':
+ dialogueFile = base.loadSfx('phase_3/audio/dial/minnie.wav')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ if language == 'japanese':
+ chatterIndexArray = ([1, 2], [1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17], [1, 2, 3])
+ self.loadChatterDialogue('minnie', chatterIndexArray, 'phase_3/audio/dial', language)
+ elif char == 'dd' or char == 'shdd':
+ dialogueFile = base.loadSfx('phase_4/audio/dial/daisy.wav')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ if language == 'japanese':
+ chatterIndexArray = ([1, 2, 3], [1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12], [1,
+ 2,
+ 3,
+ 4])
+ self.loadChatterDialogue('daisy', chatterIndexArray, 'phase_8/audio/dial', language)
+ elif char == 'g' or char == 'sg':
+ dialogueFile = base.loadSfx('phase_6/audio/dial/goofy.wav')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ if language == 'japanese':
+ chatterIndexArray = ([1, 2, 3], [1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12], [1,
+ 2,
+ 3,
+ 4])
+ self.loadChatterDialogue('goofy', chatterIndexArray, 'phase_6/audio/dial', language)
+ elif char == 'd' or char == 'dw' or char == 'fd':
+ dialogueFile = base.loadSfx('phase_6/audio/dial/donald.wav')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ if char == 'd':
+ if language == 'japanese':
+ chatterIndexArray = ([1, 2], [1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11], [1,
+ 2,
+ 3,
+ 4])
+ self.loadChatterDialogue('donald', chatterIndexArray, 'phase_6/audio/dial', language)
+ elif char == 'p' or char == 'wp':
+ dialogueFile = base.loadSfx('phase_3.5/audio/dial/AV_dog_med.mp3')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ elif char == 'cl':
+ dialogueFile = base.loadSfx('phase_3.5/audio/dial/AV_dog_med.mp3')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ elif char == 'ch':
+ dialogueFile = base.loadSfx('phase_6/audio/dial/chip.wav')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ elif char == 'da':
+ dialogueFile = base.loadSfx('phase_6/audio/dial/dale.wav')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ elif char == 'pch':
+ dialogueFile = base.loadSfx('phase_6/audio/dial/chip.wav')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ elif char == 'jda':
+ dialogueFile = base.loadSfx('phase_6/audio/dial/dale.wav')
+ for i in range(0, 6):
+ self.dialogueArray.append(dialogueFile)
+
+ else:
+ self.notify.error('unknown character %s' % char)
+
+ def unloadDialogue(self):
+ self.dialogueArray = []
+ self.chatterArray = [[], [], []]
+
+ def __blinkOpenEyes(self, task):
+ self.openEyes()
+ r = random.random()
+ if r < 0.1:
+ t = 0.2
+ else:
+ t = r * 4.0 + 1.0
+ taskMgr.doMethodLater(t, self.__blinkCloseEyes, self.__blinkName)
+ return Task.done
+
+ def __blinkCloseEyes(self, task):
+ self.closeEyes()
+ taskMgr.doMethodLater(0.125, self.__blinkOpenEyes, self.__blinkName)
+ return Task.done
+
+ def openEyes(self):
+ if self.geoEyes:
+ for part in self.eyeOpenList:
+ part.show()
+
+ for part in self.eyeCloseList:
+ part.hide()
+
+ else:
+ if self.eyes:
+ self.eyes.setTexture(self.eyesOpen, 1)
+ self.lpupil.show()
+ self.rpupil.show()
+
+ def closeEyes(self):
+ if self.geoEyes:
+ for part in self.eyeOpenList:
+ part.hide()
+
+ for part in self.eyeCloseList:
+ part.show()
+
+ else:
+ if self.eyes:
+ self.eyes.setTexture(self.eyesClosed, 1)
+ self.lpupil.hide()
+ self.rpupil.hide()
+
+ def startBlink(self):
+ if self.eyesOpen or self.geoEyes:
+ taskMgr.remove(self.__blinkName)
+ taskMgr.doMethodLater(random.random() * 4 + 1, self.__blinkCloseEyes, self.__blinkName)
+
+ def stopBlink(self):
+ if self.eyesOpen or self.geoEyes:
+ taskMgr.remove(self.__blinkName)
+ self.openEyes()
+
+ def startEarTask(self):
+ pass
+
+ def stopEarTask(self):
+ pass
+
+ def uniqueName(self, idString):
+ return idString + '-' + str(self.this)
diff --git a/toontown/char/CharDNA.py b/toontown/char/CharDNA.py
new file mode 100644
index 0000000..f48e2bf
--- /dev/null
+++ b/toontown/char/CharDNA.py
@@ -0,0 +1,127 @@
+import random
+from pandac.PandaModules import *
+from direct.directnotify.DirectNotifyGlobal import *
+import random
+from direct.distributed.PyDatagram import PyDatagram
+from direct.distributed.PyDatagramIterator import PyDatagramIterator
+from otp.avatar import AvatarDNA
+notify = directNotify.newCategory('CharDNA')
+charTypes = ['mk',
+ 'vmk',
+ 'mn',
+ 'wmn',
+ 'g',
+ 'sg',
+ 'd',
+ 'fd',
+ 'dw',
+ 'p',
+ 'wp',
+ 'cl',
+ 'dd',
+ 'shdd',
+ 'ch',
+ 'da',
+ 'pch',
+ 'jda']
+
+class CharDNA(AvatarDNA.AvatarDNA):
+
+ def __init__(self, str = None, type = None, dna = None, r = None, b = None, g = None):
+ if str != None:
+ self.makeFromNetString(str)
+ elif type != None:
+ if type == 'c':
+ self.newChar(dna)
+ else:
+ self.type = 'u'
+ return
+
+ def __str__(self):
+ if self.type == 'c':
+ return 'type = char, name = %s' % self.name
+ else:
+ return 'type undefined'
+
+ def makeNetString(self):
+ dg = PyDatagram()
+ dg.addFixedString(self.type, 1)
+ if self.type == 'c':
+ dg.addFixedString(self.name, 2)
+ elif self.type == 'u':
+ notify.error('undefined avatar')
+ else:
+ notify.error('unknown avatar type: ', self.type)
+ return dg.getMessage()
+
+ def makeFromNetString(self, string):
+ dg = PyDatagram(string)
+ dgi = PyDatagramIterator(dg)
+ self.type = dgi.getFixedString(1)
+ if self.type == 'c':
+ self.name = sgi.getFixedString(2)
+ else:
+ notify.error('unknown avatar type: ', self.type)
+ return None
+
+ def __defaultChar(self):
+ self.type = 'c'
+ self.name = charTypes[0]
+
+ def newChar(self, name = None):
+ if name == None:
+ self.__defaultChar()
+ else:
+ self.type = 'c'
+ if name in charTypes:
+ self.name = name
+ else:
+ notify.error('unknown avatar type: %s' % name)
+ return
+
+ def getType(self):
+ if self.type == 'c':
+ type = self.getCharName()
+ else:
+ notify.error('Invalid DNA type: ', self.type)
+ return type
+
+ def getCharName(self):
+ if self.name == 'mk':
+ return 'mickey'
+ elif self.name == 'vmk':
+ return 'vampire_mickey'
+ elif self.name == 'mn':
+ return 'minnie'
+ elif self.name == 'wmn':
+ return 'witch_minnie'
+ elif self.name == 'g':
+ return 'goofy'
+ elif self.name == 'sg':
+ return 'super_goofy'
+ elif self.name == 'd':
+ return 'donald'
+ elif self.name == 'dw':
+ return 'donald-wheel'
+ elif self.name == 'fd':
+ return 'franken_donald'
+ elif self.name == 'dd':
+ return 'daisy'
+ elif self.name == 'shdd':
+ return 'sockHop_daisy'
+ elif self.name == 'p':
+ return 'pluto'
+ elif self.name == 'wp':
+ return 'western_pluto'
+ elif self.name == 'cl':
+ return 'clarabelle'
+ elif self.name == 'ch':
+ return 'chip'
+ elif self.name == 'da':
+ return 'dale'
+ elif self.name == 'pch':
+ return 'police_chip'
+ elif self.name == 'jda':
+ return 'jailbird_dale'
+ else:
+ notify.error('unknown char type: ', self.name)
diff --git a/toontown/char/DistributedChar.py b/toontown/char/DistributedChar.py
new file mode 100644
index 0000000..e397e7a
--- /dev/null
+++ b/toontown/char/DistributedChar.py
@@ -0,0 +1,32 @@
+from otp.avatar import DistributedAvatar
+import Char
+
+class DistributedChar(DistributedAvatar.DistributedAvatar, Char.Char):
+
+ def __init__(self, cr):
+ try:
+ self.DistributedChar_initialized
+ except:
+ self.DistributedChar_initialized = 1
+ DistributedAvatar.DistributedAvatar.__init__(self, cr)
+ Char.Char.__init__(self)
+
+ def delete(self):
+ try:
+ self.DistributedChar_deleted
+ except:
+ self.DistributedChar_deleted = 1
+ Char.Char.delete(self)
+ DistributedAvatar.DistributedAvatar.delete(self)
+
+ def setDNAString(self, dnaString):
+ Char.Char.setDNAString(self, dnaString)
+
+ def setDNA(self, dna):
+ Char.Char.setDNA(self, dna)
+
+ def playDialogue(self, *args):
+ Char.Char.playDialogue(self, *args)
+
+ def setHp(self, hp):
+ self.hp = hp
diff --git a/toontown/char/__init__.py b/toontown/char/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/toontown/chat/ResistanceChat.py b/toontown/chat/ResistanceChat.py
new file mode 100644
index 0000000..0bb7283
--- /dev/null
+++ b/toontown/chat/ResistanceChat.py
@@ -0,0 +1,190 @@
+from direct.interval.IntervalGlobal import *
+from pandac.PandaModules import *
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase import ToontownBattleGlobals
+from toontown.battle import BattleParticles
+from direct.particles import Particles
+import random
+TTBG = ToontownBattleGlobals
+TTL = TTLocalizer
+EFFECT_RADIUS = 30
+RESISTANCE_TOONUP = 0
+RESISTANCE_RESTOCK = 1
+RESISTANCE_MONEY = 2
+resistanceMenu = [RESISTANCE_TOONUP, RESISTANCE_RESTOCK, RESISTANCE_MONEY]
+resistanceDict = {RESISTANCE_TOONUP: {'menuName': TTL.ResistanceToonupMenu,
+ 'itemText': TTL.ResistanceToonupItem,
+ 'chatText': TTL.ResistanceToonupChat,
+ 'values': [10,
+ 20,
+ 40,
+ 80,
+ -1],
+ 'items': [0,
+ 1,
+ 2,
+ 3,
+ 4]},
+ RESISTANCE_MONEY: {'menuName': TTL.ResistanceMoneyMenu,
+ 'itemText': TTL.ResistanceMoneyItem,
+ 'chatText': TTL.ResistanceMoneyChat,
+ 'values': [100,
+ 200,
+ 350,
+ 600],
+ 'items': [0,
+ 1,
+ 2,
+ 3]},
+ RESISTANCE_RESTOCK: {'menuName': TTL.ResistanceRestockMenu,
+ 'itemText': TTL.ResistanceRestockItem,
+ 'chatText': TTL.ResistanceRestockChat,
+ 'values': [TTBG.HEAL_TRACK,
+ TTBG.TRAP_TRACK,
+ TTBG.LURE_TRACK,
+ TTBG.SOUND_TRACK,
+ TTBG.THROW_TRACK,
+ TTBG.SQUIRT_TRACK,
+ TTBG.DROP_TRACK,
+ -1],
+ 'extra': [TTL.MovieNPCSOSHeal,
+ TTL.MovieNPCSOSTrap,
+ TTL.MovieNPCSOSLure,
+ TTL.MovieNPCSOSSound,
+ TTL.MovieNPCSOSThrow,
+ TTL.MovieNPCSOSSquirt,
+ TTL.MovieNPCSOSDrop,
+ TTL.MovieNPCSOSAll],
+ 'items': [0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7]}}
+
+def encodeId(menuIndex, itemIndex):
+ textId = menuIndex * 100
+ textId += resistanceDict[menuIndex]['items'][itemIndex]
+ return textId
+
+
+def decodeId(textId):
+ menuIndex = int(textId / 100)
+ itemIndex = textId % 100
+ return (menuIndex, itemIndex)
+
+
+def validateId(textId):
+ if textId < 0:
+ return 0
+ menuIndex, itemIndex = decodeId(textId)
+ if not resistanceDict.has_key(menuIndex):
+ return 0
+ if itemIndex >= len(resistanceDict[menuIndex]['values']):
+ return 0
+ return 1
+
+
+def getItems(menuIndex):
+ return resistanceDict[menuIndex]['items']
+
+
+def getMenuName(textId):
+ menuIndex, itemIndex = decodeId(textId)
+ return resistanceDict[menuIndex]['menuName']
+
+
+def getItemText(textId):
+ menuIndex, itemIndex = decodeId(textId)
+ value = resistanceDict[menuIndex]['values'][itemIndex]
+ text = resistanceDict[menuIndex]['itemText']
+ if menuIndex is RESISTANCE_TOONUP:
+ if value is -1:
+ value = TTL.ResistanceToonupItemMax
+ elif menuIndex is RESISTANCE_RESTOCK:
+ value = resistanceDict[menuIndex]['extra'][itemIndex]
+ return text % str(value)
+
+
+def getChatText(textId):
+ menuIndex, itemIndex = decodeId(textId)
+ return resistanceDict[menuIndex]['chatText']
+
+
+def getItemValue(textId):
+ menuIndex, itemIndex = decodeId(textId)
+ return resistanceDict[menuIndex]['values'][itemIndex]
+
+
+def getRandomId():
+ menuIndex = random.choice(resistanceMenu)
+ itemIndex = random.choice(getItems(menuIndex))
+ return encodeId(menuIndex, itemIndex)
+
+
+def doEffect(textId, speakingToon, nearbyToons):
+ menuIndex, itemIndex = decodeId(textId)
+ itemValue = getItemValue(textId)
+ if menuIndex == RESISTANCE_TOONUP:
+ effect = BattleParticles.loadParticleFile('resistanceEffectSparkle.ptf')
+ fadeColor = VBase4(1, 0.5, 1, 1)
+ elif menuIndex == RESISTANCE_MONEY:
+ effect = BattleParticles.loadParticleFile('resistanceEffectBean.ptf')
+ bean = loader.loadModel('phase_4/models/props/jellybean4.bam')
+ bean = bean.find('**/jellybean')
+ colors = {'particles-1': (1, 1, 0, 1),
+ 'particles-2': (1, 0, 0, 1),
+ 'particles-3': (0, 1, 0, 1),
+ 'particles-4': (0, 0, 1, 1),
+ 'particles-5': (1, 0, 1, 1)}
+ for name, color in colors.items():
+ node = bean.copyTo(NodePath())
+ node.setColorScale(*color)
+ p = effect.getParticlesNamed(name)
+ p.renderer.setGeomNode(node.node())
+
+ fadeColor = VBase4(0, 1, 0, 1)
+ elif menuIndex == RESISTANCE_RESTOCK:
+ effect = BattleParticles.loadParticleFile('resistanceEffectSprite.ptf')
+ invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons')
+ invModel.setScale(4)
+ invModel.flattenLight()
+ icons = []
+ if itemValue != -1:
+ for item in range(6):
+ iconName = ToontownBattleGlobals.AvPropsNew[itemValue][item]
+ icons.append(invModel.find('**/%s' % iconName))
+
+ else:
+ tracks = range(7)
+ random.shuffle(tracks)
+ for i in range(6):
+ track = tracks[i]
+ item = random.randint(0, 5)
+ iconName = ToontownBattleGlobals.AvPropsNew[track][item]
+ icons.append(invModel.find('**/%s' % iconName))
+
+ iconDict = {'particles-1': icons[0],
+ 'particles-2': icons[1],
+ 'particles-3': icons[2],
+ 'particles-4': icons[3],
+ 'particles-5': icons[4],
+ 'particles-6': icons[5]}
+ for name, icon in iconDict.items():
+ p = effect.getParticlesNamed(name)
+ p.renderer.setFromNode(icon)
+
+ fadeColor = VBase4(0, 0, 1, 1)
+ else:
+ return
+ recolorToons = Parallel()
+ for toonId in nearbyToons:
+ toon = base.cr.doId2do.get(toonId)
+ if toon and not toon.ghostMode:
+ i = Sequence(toon.doToonColorScale(fadeColor, 0.3), toon.doToonColorScale(toon.defaultColorScale, 0.3), Func(toon.restoreDefaultColorScale))
+ recolorToons.append(i)
+
+ i = Parallel(ParticleInterval(effect, speakingToon, worldRelative=0, duration=3, cleanup=True), Sequence(Wait(0.2), recolorToons), autoFinish=1)
+ i.start()
diff --git a/toontown/chat/TTChatInputNormal.py b/toontown/chat/TTChatInputNormal.py
new file mode 100644
index 0000000..0e8765d
--- /dev/null
+++ b/toontown/chat/TTChatInputNormal.py
@@ -0,0 +1,47 @@
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from otp.chat import ChatInputNormal
+from otp.otpbase import OTPLocalizer
+from otp.otpbase import OTPGlobals
+
+class TTChatInputNormal(ChatInputNormal.ChatInputNormal):
+
+ def __init__(self, chatMgr):
+ ChatInputNormal.ChatInputNormal.__init__(self, chatMgr)
+ gui = loader.loadModel('phase_3.5/models/gui/chat_input_gui')
+ self.chatFrame = DirectFrame(parent=aspect2dp, image=gui.find('**/Chat_Bx_FNL'), relief=None, pos=(-1.083, 0, 0.804), state=DGG.NORMAL, sortOrder=DGG.FOREGROUND_SORT_INDEX)
+ self.chatFrame.hide()
+ self.chatButton = DirectButton(parent=self.chatFrame, image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(0.182, 0, -0.088), relief=None, text=('', OTPLocalizer.ChatInputNormalSayIt, OTPLocalizer.ChatInputNormalSayIt), text_scale=0.06, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.chatButtonPressed)
+ self.cancelButton = DirectButton(parent=self.chatFrame, image=(gui.find('**/CloseBtn_UP'), gui.find('**/CloseBtn_DN'), gui.find('**/CloseBtn_Rllvr')), pos=(-0.151, 0, -0.088), relief=None, text=('', OTPLocalizer.ChatInputNormalCancel, OTPLocalizer.ChatInputNormalCancel), text_scale=0.06, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.cancelButtonPressed)
+ self.whisperLabel = DirectLabel(parent=self.chatFrame, pos=(0.02, 0, 0.23), relief=DGG.FLAT, frameColor=(1, 1, 0.5, 1), frameSize=(-0.23,
+ 0.23,
+ -0.07,
+ 0.05), text=OTPLocalizer.ChatInputNormalWhisper, text_scale=0.04, text_fg=Vec4(0, 0, 0, 1), text_wordwrap=9.5, textMayChange=1)
+ self.whisperLabel.hide()
+ self.chatEntry = DirectEntry(parent=self.chatFrame, relief=None, scale=0.05, pos=(-0.2, 0, 0.11), entryFont=OTPGlobals.getInterfaceFont(), width=8.6, numLines=3, cursorKeys=0, backgroundFocus=0, command=self.sendChat)
+ self.chatEntry.bind(DGG.OVERFLOW, self.chatOverflow)
+ self.chatEntry.bind(DGG.TYPE, self.typeCallback)
+ return
+
+ def delete(self):
+ self.chatEntry.destroy()
+ self.chatButton.destroy()
+ self.cancelButton.destroy()
+ ChatInputNormal.ChatInputNormal.delete(self)
+ loader.unloadModel('phase_3.5/models/gui/chat_input_gui')
+
+ def importExecNamespace(self):
+ ChatInputNormal.ChatInputNormal.importExecNamespace(self)
+ exec 'from toontown.toonbase.ToonBaseGlobal import *' in globals(), self.ExecNamespace
+
+ def typeCallback(self, extraArgs):
+ if localAvatar.chatMgr.chatInputWhiteList.isActive():
+ return
+ else:
+ messenger.send('enterNormalChat')
+
+ def checkForOverRide(self):
+ return False
+ if localAvatar.chatMgr.chatInputWhiteList.isActive():
+ return True
+ return False
diff --git a/toontown/chat/TTChatInputSpeedChat.py b/toontown/chat/TTChatInputSpeedChat.py
new file mode 100644
index 0000000..8e74c32
--- /dev/null
+++ b/toontown/chat/TTChatInputSpeedChat.py
@@ -0,0 +1,963 @@
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from otp.speedchat.SpeedChatTypes import *
+from toontown.speedchat.TTSpeedChatTypes import *
+from otp.speedchat.SpeedChat import SpeedChat
+from otp.speedchat import SpeedChatGlobals
+from toontown.speedchat import TTSpeedChatGlobals
+from toontown.speedchat import TTSCSingingTerminal
+from toontown.speedchat import TTSCIndexedTerminal
+from direct.showbase import DirectObject
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+import string
+from otp.otpbase import OTPLocalizer
+from otp.otpbase import OTPGlobals
+from toontown.shtiker.OptionsPage import speedChatStyles
+from toontown.toonbase import TTLocalizer
+from toontown.parties.PartyGlobals import ActivityIds, DecorationIds
+from toontown.toonbase import ToontownGlobals
+scStructure = [[OTPLocalizer.SCMenuHello,
+ {100: 0},
+ {101: 0},
+ {102: 0},
+ {103: 0},
+ {104: 0},
+ {105: 0},
+ 106,
+ 107,
+ 108,
+ 109],
+ [OTPLocalizer.SCMenuBye,
+ {200: 0},
+ {201: 0},
+ {202: 0},
+ 203,
+ 204,
+ 205,
+ 206,
+ 208,
+ 209,
+ 207],
+ [OTPLocalizer.SCMenuHappy,
+ {300: 1},
+ {301: 1},
+ {302: 1},
+ 303,
+ {304: 1},
+ 305,
+ 306,
+ 307,
+ 308,
+ 309,
+ 310,
+ 311,
+ {312: 1},
+ {313: 1},
+ {314: 1},
+ 315],
+ [OTPLocalizer.SCMenuSad,
+ {400: 2},
+ {401: 2},
+ {402: 2},
+ 403,
+ 404,
+ 405,
+ 406,
+ 407,
+ 408,
+ 409,
+ 410],
+ [OTPLocalizer.SCMenuFriendly,
+ [OTPLocalizer.SCMenuFriendlyYou,
+ 600,
+ 601,
+ 602,
+ 603],
+ [OTPLocalizer.SCMenuFriendlyILike,
+ 700,
+ 701,
+ 702,
+ 703,
+ 704,
+ 705],
+ 500,
+ 501,
+ 502,
+ 503,
+ 504,
+ 505,
+ 506,
+ 507,
+ 508,
+ 509,
+ 510,
+ 515,
+ 511,
+ 512,
+ 513,
+ 514],
+ [OTPLocalizer.SCMenuSorry,
+ 801,
+ 800,
+ 802,
+ 803,
+ 804,
+ 811,
+ 814,
+ 815,
+ 817,
+ 812,
+ 813,
+ 818,
+ 805,
+ 806,
+ 807,
+ 816,
+ 808,
+ {809: 5},
+ 810],
+ [OTPLocalizer.SCMenuStinky,
+ {900: 3},
+ {901: 3},
+ {902: 3},
+ {903: 3},
+ 904,
+ {905: 3},
+ 907],
+ [OTPLocalizer.SCMenuPlaces,
+ [OTPLocalizer.SCMenuPlacesPlayground,
+ 1100,
+ 1101,
+ 1105,
+ 1106,
+ 1107,
+ 1108,
+ 1109,
+ 1110,
+ 1111,
+ 1117,
+ 1125,
+ 1126],
+ [OTPLocalizer.SCMenuPlacesCogs,
+ 1102,
+ 1103,
+ 1104,
+ 1114,
+ 1115,
+ 1116,
+ 1119,
+ 1120,
+ 1121,
+ 1122,
+ 1123,
+ 1124,
+ 1127,
+ 1128,
+ 1129,
+ 1130],
+ [OTPLocalizer.SCMenuPlacesEstate,
+ 1112,
+ 1113,
+ 1013,
+ 1118,
+ 1016],
+ [OTPLocalizer.SCMenuParties,
+ 5300,
+ 5301,
+ 5302,
+ 5303],
+ [OTPLocalizer.SCMenuPlacesWait,
+ 1015,
+ 1007,
+ 1008,
+ 1010,
+ 1011,
+ 1014,
+ 1017],
+ 1000,
+ 1001,
+ 1002,
+ 1003,
+ 1004,
+ 1005,
+ 1006,
+ 1009,
+ 1012],
+ [OTPLocalizer.SCMenuToontasks,
+ [TTSCToontaskMenu, OTPLocalizer.SCMenuToontasksMyTasks],
+ [OTPLocalizer.SCMenuToontasksYouShouldChoose,
+ 1300,
+ 1301,
+ 1302,
+ 1303,
+ 1304],
+ [OTPLocalizer.SCMenuToontasksINeedMore,
+ 1206,
+ 1210,
+ 1211,
+ 1212,
+ 1207,
+ 1213,
+ 1214,
+ 1215],
+ 1200,
+ 1201,
+ 1202,
+ 1208,
+ 1203,
+ 1209,
+ 1204,
+ 1205],
+ [OTPLocalizer.SCMenuBattle,
+ [OTPLocalizer.SCMenuBattleGags,
+ 1500,
+ 1501,
+ 1502,
+ 1503,
+ 1504,
+ 1505,
+ 1506,
+ 1401,
+ 1402,
+ 1413],
+ [OTPLocalizer.SCMenuBattleTaunts,
+ 1403,
+ 1406,
+ 1520,
+ 1521,
+ 1522,
+ 1523,
+ 1524,
+ 1525,
+ 1526,
+ 1407,
+ 1408],
+ [OTPLocalizer.SCMenuBattleStrategy,
+ 1414,
+ 1550,
+ 1551,
+ 1552,
+ 1415,
+ 1553,
+ 1554,
+ 1555,
+ 1556,
+ 1557,
+ 1558,
+ 1559],
+ 1400,
+ 1416,
+ 1404,
+ 1405,
+ 1409,
+ 1410,
+ 1411,
+ 1412],
+ [OTPLocalizer.SCMenuGagShop,
+ 1600,
+ 1601,
+ 1602,
+ 1603,
+ 1604,
+ 1605,
+ 1606],
+ {1: 17},
+ {2: 18},
+ 3]
+if hasattr(base, 'wantPets') and base.wantPets:
+ scPetMenuStructure = [[OTPLocalizer.SCMenuPets,
+ [TTSCPetTrickMenu, OTPLocalizer.SCMenuPetTricks],
+ 21000,
+ 21001,
+ 21002,
+ 21003,
+ 21004,
+ 21005,
+ 21006]]
+cfoMenuStructure = [[OTPLocalizer.SCMenuCFOBattleCranes,
+ 2100,
+ 2101,
+ 2102,
+ 2103,
+ 2104,
+ 2105,
+ 2106,
+ 2107,
+ 2108,
+ 2109,
+ 2110],
+ [OTPLocalizer.SCMenuCFOBattleGoons,
+ 2120,
+ 2121,
+ 2122,
+ 2123,
+ 2124,
+ 2125,
+ 2126],
+ 2130,
+ 2131,
+ 2132,
+ 2133,
+ 1410]
+cjMenuStructure = [2200,
+ 2201,
+ 2202,
+ 2203,
+ 2204,
+ 2205,
+ 2206,
+ 2207,
+ 2208,
+ 2209,
+ 2210]
+ceoMenuStructure = [2300,
+ 2301,
+ 2302,
+ 2303,
+ 2304,
+ 2305,
+ 2306,
+ 2307,
+ 2312,
+ 2313,
+ 2314,
+ 2315,
+ 2308,
+ 2309,
+ 2310,
+ 2311,
+ 2316,
+ 2317]
+
+class TTChatInputSpeedChat(DirectObject.DirectObject):
+ DefaultSCColorScheme = SCColorScheme()
+
+ def __init__(self, chatMgr):
+ self.chatMgr = chatMgr
+ self.whisperAvatarId = None
+ self.toPlayer = 0
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ self.emoteNoAccessPanel = DirectFrame(parent=hidden, relief=None, state='normal', text=OTPLocalizer.SCEmoteNoAccessMsg, frameSize=(-1, 1, -1, 1), geom=DGG.getDefaultDialogGeom(), geom_color=OTPGlobals.GlobalDialogColor, geom_scale=(0.92, 1, 0.6), geom_pos=(0, 0, -.08), text_scale=0.08)
+ self.okButton = DirectButton(parent=self.emoteNoAccessPanel, image=okButtonImage, relief=None, text=OTPLocalizer.SCEmoteNoAccessOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.2), command=self.handleEmoteNoAccessDone)
+ self.insidePartiesMenu = None
+ self.createSpeedChat()
+ self.whiteList = None
+ self.allowWhiteListSpeedChat = base.config.GetBool('white-list-speed-chat', 0)
+ if self.allowWhiteListSpeedChat:
+ self.addWhiteList()
+ self.factoryMenu = None
+ self.kartRacingMenu = None
+ self.cogMenu = None
+ self.cfoMenu = None
+ self.cjMenu = None
+ self.ceoMenu = None
+ self.golfMenu = None
+ self.boardingGroupMenu = None
+ self.singingGroupMenu = None
+ self.aprilToonsMenu = None
+ self.victoryPartiesMenu = None
+ self.sillyPhaseOneMenu = None
+ self.sillyPhaseTwoMenu = None
+ self.sillyPhaseThreeMenu = None
+ self.sillyPhaseFourMenu = None
+ self.sillyPhaseFiveMenu = None
+ self.sellbotNerfMenu = None
+ self.jellybeanJamMenu = None
+ self.halloweenMenu = None
+ self.winterMenu = None
+ self.sellbotInvasionMenu = None
+ self.sellbotFieldOfficeMenu = None
+ self.idesOfMarchMenu = None
+
+ def listenForSCEvent(eventBaseName, handler, self = self):
+ eventName = self.speedChat.getEventName(eventBaseName)
+ self.accept(eventName, handler)
+
+ listenForSCEvent(SpeedChatGlobals.SCTerminalLinkedEmoteEvent, self.handleLinkedEmote)
+ listenForSCEvent(SpeedChatGlobals.SCStaticTextMsgEvent, self.handleStaticTextMsg)
+ listenForSCEvent(SpeedChatGlobals.SCCustomMsgEvent, self.handleCustomMsg)
+ listenForSCEvent(SpeedChatGlobals.SCEmoteMsgEvent, self.handleEmoteMsg)
+ listenForSCEvent(SpeedChatGlobals.SCEmoteNoAccessEvent, self.handleEmoteNoAccess)
+ listenForSCEvent(TTSpeedChatGlobals.TTSCToontaskMsgEvent, self.handleToontaskMsg)
+ listenForSCEvent(TTSpeedChatGlobals.TTSCResistanceMsgEvent, self.handleResistanceMsg)
+ listenForSCEvent(TTSCSingingTerminal.TTSCSingingMsgEvent, self.handleSingingMsg)
+ listenForSCEvent('SpeedChatStyleChange', self.handleSpeedChatStyleChange)
+ listenForSCEvent(TTSCIndexedTerminal.TTSCIndexedMsgEvent, self.handleStaticTextMsg)
+ self.fsm = ClassicFSM.ClassicFSM('SpeedChat', [State.State('off', self.enterOff, self.exitOff, ['active']), State.State('active', self.enterActive, self.exitActive, ['off'])], 'off', 'off')
+ self.fsm.enterInitialState()
+ return
+
+ def delete(self):
+ self.ignoreAll()
+ self.removeWhiteList()
+ self.okButton.destroy()
+ self.emoteNoAccessPanel.destroy()
+ del self.emoteNoAccessPanel
+ self.speedChat.destroy()
+ del self.speedChat
+ del self.fsm
+ del self.chatMgr
+
+ def show(self, whisperAvatarId = None, toPlayer = 0):
+ self.whisperAvatarId = whisperAvatarId
+ self.toPlayer = toPlayer
+ self.fsm.request('active')
+
+ def hide(self):
+ self.fsm.request('off')
+
+ def createSpeedChat(self):
+ structure = []
+ if launcher and not launcher.isTestServer() or __dev__:
+ structure.append([TTSCPromotionalMenu, OTPLocalizer.SCMenuPromotion])
+ structure.append([SCEmoteMenu, OTPLocalizer.SCMenuEmotions])
+ structure.append([SCCustomMenu, OTPLocalizer.SCMenuCustom])
+ structure.append([TTSCResistanceMenu, OTPLocalizer.SCMenuResistance])
+ if hasattr(base, 'wantPets') and base.wantPets:
+ structure += scPetMenuStructure
+ structure += scStructure
+ self.createSpeedChatObject(structure)
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterActive(self):
+
+ def handleCancel(self = self):
+ self.chatMgr.fsm.request('mainMenu')
+
+ self.accept('mouse1', handleCancel)
+
+ def selectionMade(self = self):
+ self.chatMgr.fsm.request('mainMenu')
+
+ self.terminalSelectedEvent = self.speedChat.getEventName(SpeedChatGlobals.SCTerminalSelectedEvent)
+ if base.config.GetBool('want-sc-auto-hide', 1):
+ self.accept(self.terminalSelectedEvent, selectionMade)
+ self.speedChat.reparentTo(aspect2dp, DGG.FOREGROUND_SORT_INDEX)
+ scZ = 0.96
+ self.speedChat.setPos(-1.05, 0, scZ)
+ self.speedChat.setWhisperMode(self.whisperAvatarId != None)
+ self.speedChat.enter()
+ return
+
+ def exitActive(self):
+ self.ignore('mouse1')
+ self.ignore(self.terminalSelectedEvent)
+ self.speedChat.exit()
+ self.speedChat.reparentTo(hidden)
+ self.emoteNoAccessPanel.reparentTo(hidden)
+
+ def handleLinkedEmote(self, emoteId):
+ if self.whisperAvatarId is None:
+ lt = base.localAvatar
+ lt.b_setEmoteState(emoteId, animMultiplier=lt.animMultiplier)
+ return
+
+ def handleStaticTextMsg(self, textId):
+ if self.whisperAvatarId is None:
+ self.chatMgr.sendSCChatMessage(textId)
+ else:
+ self.chatMgr.sendSCWhisperMessage(textId, self.whisperAvatarId, self.toPlayer)
+ self.toPlayer = 0
+ return
+
+ def handleSingingMsg(self, textId):
+ if self.whisperAvatarId is None:
+ self.chatMgr.sendSCSingingChatMessage(textId)
+ else:
+ self.chatMgr.sendSCSingingWhisperMessage(textId)
+ self.toPlayer = 0
+ return
+
+ def handleCustomMsg(self, textId):
+ if self.whisperAvatarId is None:
+ self.chatMgr.sendSCCustomChatMessage(textId)
+ else:
+ self.chatMgr.sendSCCustomWhisperMessage(textId, self.whisperAvatarId, self.toPlayer)
+ self.toPlayer = 0
+ return
+
+ def handleEmoteMsg(self, emoteId):
+ if self.whisperAvatarId is None:
+ self.chatMgr.sendSCEmoteChatMessage(emoteId)
+ else:
+ self.chatMgr.sendSCEmoteWhisperMessage(emoteId, self.whisperAvatarId, self.toPlayer)
+ self.toPlayer = 0
+ return
+
+ def handleEmoteNoAccess(self):
+ if self.whisperAvatarId is None:
+ self.emoteNoAccessPanel.setPos(0, 0, 0)
+ else:
+ self.emoteNoAccessPanel.setPos(0.37, 0, 0)
+ self.emoteNoAccessPanel.reparentTo(aspect2d)
+ return
+
+ def handleEmoteNoAccessDone(self):
+ self.emoteNoAccessPanel.reparentTo(hidden)
+
+ def handleToontaskMsg(self, taskId, toNpcId, toonProgress, msgIndex):
+ if self.whisperAvatarId is None:
+ self.chatMgr.sendSCToontaskChatMessage(taskId, toNpcId, toonProgress, msgIndex)
+ else:
+ self.chatMgr.sendSCToontaskWhisperMessage(taskId, toNpcId, toonProgress, msgIndex, self.whisperAvatarId, self.toPlayer)
+ self.toPlayer = 0
+ return
+
+ def handleResistanceMsg(self, textId):
+ self.chatMgr.sendSCResistanceChatMessage(textId)
+
+ def handleSpeedChatStyleChange(self):
+ nameKey, arrowColor, rolloverColor, frameColor = speedChatStyles[base.localAvatar.getSpeedChatStyleIndex()]
+ newSCColorScheme = SCColorScheme(arrowColor=arrowColor, rolloverColor=rolloverColor, frameColor=frameColor)
+ self.speedChat.setColorScheme(newSCColorScheme)
+
+ def createSpeedChatObject(self, structure):
+ if hasattr(self, 'speedChat'):
+ self.speedChat.exit()
+ self.speedChat.destroy()
+ del self.speedChat
+ self.speedChat = SpeedChat(structure=structure, backgroundModelName='phase_3/models/gui/ChatPanel', guiModelName='phase_3.5/models/gui/speedChatGui')
+ self.speedChat.setScale(TTLocalizer.TTCISCspeedChat)
+ self.speedChat.setBin('gui-popup', 0)
+ self.speedChat.setTopLevelOverlap(TTLocalizer.TTCISCtopLevelOverlap)
+ self.speedChat.setColorScheme(self.DefaultSCColorScheme)
+ self.speedChat.finalizeAll()
+
+ def addFactoryMenu(self):
+ if self.factoryMenu == None:
+ menu = TTSCFactoryMenu()
+ self.factoryMenu = SCMenuHolder(OTPLocalizer.SCMenuFactory, menu=menu)
+ self.speedChat[2:2] = [self.factoryMenu]
+ return
+
+ def removeFactoryMenu(self):
+ if self.factoryMenu:
+ i = self.speedChat.index(self.factoryMenu)
+ del self.speedChat[i]
+ self.factoryMenu.destroy()
+ self.factoryMenu = None
+ return
+
+ def addKartRacingMenu(self):
+ if self.kartRacingMenu == None:
+ menu = TTSCKartRacingMenu()
+ self.kartRacingMenu = SCMenuHolder(OTPLocalizer.SCMenuKartRacing, menu=menu)
+ self.speedChat[2:2] = [self.kartRacingMenu]
+ return
+
+ def removeKartRacingMenu(self):
+ if self.kartRacingMenu:
+ i = self.speedChat.index(self.kartRacingMenu)
+ del self.speedChat[i]
+ self.kartRacingMenu.destroy()
+ self.kartRacingMenu = None
+ return
+
+ def addCogMenu(self, indices):
+ if self.cogMenu == None:
+ menu = TTSCCogMenu(indices)
+ self.cogMenu = SCMenuHolder(OTPLocalizer.SCMenuCog, menu=menu)
+ self.speedChat[2:2] = [self.cogMenu]
+ return
+
+ def removeCogMenu(self):
+ if self.cogMenu:
+ i = self.speedChat.index(self.cogMenu)
+ del self.speedChat[i]
+ self.cogMenu.destroy()
+ self.cogMenu = None
+ return
+
+ def addCFOMenu(self):
+ if self.cfoMenu == None:
+ menu = SCMenu()
+ menu.rebuildFromStructure(cfoMenuStructure)
+ self.cfoMenu = SCMenuHolder(OTPLocalizer.SCMenuCFOBattle, menu=menu)
+ self.speedChat[2:2] = [self.cfoMenu]
+ return
+
+ def removeCFOMenu(self):
+ if self.cfoMenu:
+ i = self.speedChat.index(self.cfoMenu)
+ del self.speedChat[i]
+ self.cfoMenu.destroy()
+ self.cfoMenu = None
+ return
+
+ def addCJMenu(self, bonusWeight = -1):
+ if self.cjMenu == None:
+ menu = SCMenu()
+ myMenuCopy = cjMenuStructure[:]
+ if bonusWeight >= 0:
+ myMenuCopy.append(2211 + bonusWeight)
+ menu.rebuildFromStructure(myMenuCopy)
+ self.cjMenu = SCMenuHolder(OTPLocalizer.SCMenuCJBattle, menu=menu)
+ self.speedChat[2:2] = [self.cjMenu]
+ return
+
+ def removeCJMenu(self):
+ if self.cjMenu:
+ i = self.speedChat.index(self.cjMenu)
+ del self.speedChat[i]
+ self.cjMenu.destroy()
+ self.cjMenu = None
+ return
+
+ def addCEOMenu(self):
+ if self.ceoMenu == None:
+ menu = SCMenu()
+ menu.rebuildFromStructure(ceoMenuStructure)
+ self.ceoMenu = SCMenuHolder(OTPLocalizer.SCMenuCEOBattle, menu=menu)
+ self.speedChat[2:2] = [self.ceoMenu]
+ return
+
+ def removeCEOMenu(self):
+ if self.ceoMenu:
+ i = self.speedChat.index(self.ceoMenu)
+ del self.speedChat[i]
+ self.ceoMenu.destroy()
+ self.ceoMenu = None
+ return
+
+ def addInsidePartiesMenu(self):
+
+ def isActivityInParty(activityId):
+ activityList = base.distributedParty.partyInfo.activityList
+ for activity in activityList:
+ if activity.activityId == activityId:
+ return True
+
+ return False
+
+ def isDecorInParty(decorId):
+ decorList = base.distributedParty.partyInfo.decors
+ for decor in decorList:
+ if decor.decorId == decorId:
+ return True
+
+ return False
+
+ insidePartiesMenuStructure = [5305,
+ 5306,
+ 5307,
+ 5308,
+ 5309]
+ if self.insidePartiesMenu == None:
+ menu = SCMenu()
+ if hasattr(base, 'distributedParty') and base.distributedParty:
+ if base.distributedParty.partyInfo.hostId == localAvatar.doId:
+ insidePartiesMenuStructure.insert(0, 5304)
+ if isActivityInParty(0):
+ insidePartiesMenuStructure.extend([5310, 5311])
+ if isActivityInParty(1):
+ insidePartiesMenuStructure.append(5312)
+ if isActivityInParty(2):
+ insidePartiesMenuStructure.extend([5313, 5314])
+ if isActivityInParty(3):
+ insidePartiesMenuStructure.append(5315)
+ if isActivityInParty(4):
+ insidePartiesMenuStructure.extend([5316, 5317])
+ if isActivityInParty(5):
+ insidePartiesMenuStructure.append(5318)
+ if isActivityInParty(6):
+ insidePartiesMenuStructure.extend([5319, 5320])
+ if len(base.distributedParty.partyInfo.decors):
+ insidePartiesMenuStructure.append(5321)
+ if isDecorInParty(3):
+ insidePartiesMenuStructure.append(5322)
+ menu.rebuildFromStructure(insidePartiesMenuStructure)
+ self.insidePartiesMenu = SCMenuHolder(OTPLocalizer.SCMenuParties, menu=menu)
+ self.speedChat[2:2] = [self.insidePartiesMenu]
+ return
+
+ def removeInsidePartiesMenu(self):
+ if self.insidePartiesMenu:
+ i = self.speedChat.index(self.insidePartiesMenu)
+ del self.speedChat[i]
+ self.insidePartiesMenu.destroy()
+ self.insidePartiesMenu = None
+ return
+
+ def addGolfMenu(self):
+ if self.golfMenu == None:
+ menu = TTSCGolfMenu()
+ self.golfMenu = SCMenuHolder(OTPLocalizer.SCMenuGolf, menu=menu)
+ self.speedChat[2:2] = [self.golfMenu]
+ return
+
+ def removeGolfMenu(self):
+ if self.golfMenu:
+ i = self.speedChat.index(self.golfMenu)
+ del self.speedChat[i]
+ self.golfMenu.destroy()
+ self.golfMenu = None
+ return
+
+ def addBoardingGroupMenu(self, zoneId):
+ if self.boardingGroupMenu == None:
+ menu = TTSCBoardingMenu(zoneId)
+ self.boardingGroupMenu = SCMenuHolder(OTPLocalizer.SCMenuBoardingGroup, menu=menu)
+ self.speedChat[2:2] = [self.boardingGroupMenu]
+ return
+
+ def removeBoardingGroupMenu(self):
+ if self.boardingGroupMenu:
+ i = self.speedChat.index(self.boardingGroupMenu)
+ del self.speedChat[i]
+ self.boardingGroupMenu.destroy()
+ self.boardingGroupMenu = None
+ return
+
+ def addSingingGroupMenu(self):
+ if self.singingGroupMenu == None:
+ menu = TTSCSingingMenu()
+ self.singingGroupMenu = SCMenuHolder(OTPLocalizer.SCMenuSingingGroup, menu=menu)
+ self.speedChat[2:2] = [self.singingGroupMenu]
+ return
+
+ def removeSingingMenu(self):
+ if self.singingGroupMenu:
+ i = self.speedChat.index(self.singingGroupMenu)
+ del self.speedChat[i]
+ self.singingGroupMenu.destroy()
+ self.singingGroupMenu = None
+ return
+
+ def addAprilToonsMenu(self):
+ if self.aprilToonsMenu == None:
+ menu = TTSCAprilToonsMenu()
+ self.aprilToonsMenu = SCMenuHolder(OTPLocalizer.SCMenuAprilToons, menu=menu)
+ self.speedChat[3:3] = [self.aprilToonsMenu]
+ return
+
+ def removeAprilToonsMenu(self):
+ if self.aprilToonsMenu:
+ i = self.speedChat.index(self.aprilToonsMenu)
+ del self.speedChat[i]
+ self.aprilToonsMenu.destroy()
+ self.aprilToonsMenu = None
+ return
+
+ def addSillyPhaseOneMenu(self):
+ if self.sillyPhaseOneMenu == None:
+ menu = TTSCSillyPhaseOneMenu()
+ self.sillyPhaseOneMenu = SCMenuHolder(OTPLocalizer.SCMenuSillyHoliday, menu=menu)
+ self.speedChat[3:3] = [self.sillyPhaseOneMenu]
+ return
+
+ def removeSillyPhaseOneMenu(self):
+ if self.sillyPhaseOneMenu:
+ i = self.speedChat.index(self.sillyPhaseOneMenu)
+ del self.speedChat[i]
+ self.sillyPhaseOneMenu.destroy()
+ self.sillyPhaseOneMenu = None
+ return
+
+ def addSillyPhaseTwoMenu(self):
+ if self.sillyPhaseTwoMenu == None:
+ menu = TTSCSillyPhaseTwoMenu()
+ self.sillyPhaseTwoMenu = SCMenuHolder(OTPLocalizer.SCMenuSillyHoliday, menu=menu)
+ self.speedChat[3:3] = [self.sillyPhaseTwoMenu]
+ return
+
+ def removeSillyPhaseTwoMenu(self):
+ if self.sillyPhaseTwoMenu:
+ i = self.speedChat.index(self.sillyPhaseTwoMenu)
+ del self.speedChat[i]
+ self.sillyPhaseTwoMenu.destroy()
+ self.sillyPhaseTwoMenu = None
+ return
+
+ def addSillyPhaseThreeMenu(self):
+ if self.sillyPhaseThreeMenu == None:
+ menu = TTSCSillyPhaseThreeMenu()
+ self.sillyPhaseThreeMenu = SCMenuHolder(OTPLocalizer.SCMenuSillyHoliday, menu=menu)
+ self.speedChat[3:3] = [self.sillyPhaseThreeMenu]
+ return
+
+ def removeSillyPhaseThreeMenu(self):
+ if self.sillyPhaseThreeMenu:
+ i = self.speedChat.index(self.sillyPhaseThreeMenu)
+ del self.speedChat[i]
+ self.sillyPhaseThreeMenu.destroy()
+ self.sillyPhaseThreeMenu = None
+ return
+
+ def addSillyPhaseFourMenu(self):
+ if self.sillyPhaseFourMenu == None:
+ menu = TTSCSillyPhaseFourMenu()
+ self.sillyPhaseFourMenu = SCMenuHolder(OTPLocalizer.SCMenuSillyHoliday, menu=menu)
+ self.speedChat[3:3] = [self.sillyPhaseFourMenu]
+ return
+
+ def removeSillyPhaseFourMenu(self):
+ if self.sillyPhaseFourMenu:
+ i = self.speedChat.index(self.sillyPhaseFourMenu)
+ del self.speedChat[i]
+ self.sillyPhaseFourMenu.destroy()
+ self.sillyPhaseFourMenu = None
+ return
+
+ def addSillyPhaseFiveMenu(self):
+ if self.sillyPhaseFiveMenu == None:
+ menu = TTSCSillyPhaseFiveMenu()
+ self.sillyPhaseFiveMenu = SCMenuHolder(OTPLocalizer.SCMenuSillyHoliday, menu=menu)
+ self.speedChat[3:3] = [self.sillyPhaseFiveMenu]
+ return
+
+ def removeSillyPhaseFiveMenu(self):
+ if self.sillyPhaseFiveMenu:
+ i = self.speedChat.index(self.sillyPhaseFiveMenu)
+ del self.speedChat[i]
+ self.sillyPhaseFiveMenu.destroy()
+ self.sillyPhaseFiveMenu = None
+ return
+
+ def addVictoryPartiesMenu(self):
+ if self.victoryPartiesMenu == None:
+ menu = TTSCVictoryPartiesMenu()
+ self.victoryPartiesMenu = SCMenuHolder(OTPLocalizer.SCMenuVictoryParties, menu=menu)
+ self.speedChat[3:3] = [self.victoryPartiesMenu]
+ return
+
+ def removeVictoryPartiesMenu(self):
+ if self.victoryPartiesMenu:
+ i = self.speedChat.index(self.victoryPartiesMenu)
+ del self.speedChat[i]
+ self.victoryPartiesMenu.destroy()
+ self.victoryPartiesMenu = None
+ return
+
+ def addSellbotNerfMenu(self):
+ if self.sellbotNerfMenu == None:
+ menu = TTSCSellbotNerfMenu()
+ self.sellbotNerfMenu = SCMenuHolder(OTPLocalizer.SCMenuSellbotNerf, menu=menu)
+ self.speedChat[2:2] = [self.sellbotNerfMenu]
+ return
+
+ def removeSellbotNerfMenu(self):
+ if self.sellbotNerfMenu:
+ i = self.speedChat.index(self.sellbotNerfMenu)
+ del self.speedChat[i]
+ self.sellbotNerfMenu.destroy()
+ self.sellbotNerfMenu = None
+ return
+
+ def addJellybeanJamMenu(self, phase):
+ if self.jellybeanJamMenu == None:
+ menu = TTSCJellybeanJamMenu(phase)
+ self.jellybeanJamMenu = SCMenuHolder(OTPLocalizer.SCMenuJellybeanJam, menu=menu)
+ self.speedChat[2:2] = [self.jellybeanJamMenu]
+ return
+
+ def removeJellybeanJamMenu(self):
+ if self.jellybeanJamMenu:
+ i = self.speedChat.index(self.jellybeanJamMenu)
+ del self.speedChat[i]
+ self.jellybeanJamMenu.destroy()
+ self.jellybeanJamMenu = None
+ return
+
+ def addHalloweenMenu(self):
+ if self.halloweenMenu == None:
+ menu = TTSCHalloweenMenu()
+ self.halloweenMenu = SCMenuHolder(OTPLocalizer.SCMenuHalloween, menu=menu)
+ self.speedChat[2:2] = [self.halloweenMenu]
+ return
+
+ def removeHalloweenMenu(self):
+ if self.halloweenMenu:
+ i = self.speedChat.index(self.halloweenMenu)
+ del self.speedChat[i]
+ self.halloweenMenu.destroy()
+ self.halloweenMenu = None
+ return
+
+ def addWinterMenu(self, carol = False):
+ if self.winterMenu == None:
+ menu = TTSCWinterMenu(carol)
+ self.winterMenu = SCMenuHolder(OTPLocalizer.SCMenuWinter, menu=menu)
+ self.speedChat[2:2] = [self.winterMenu]
+ return
+
+ def removeWinterMenu(self):
+ if self.winterMenu:
+ i = self.speedChat.index(self.winterMenu)
+ del self.speedChat[i]
+ self.winterMenu.destroy()
+ self.winterMenu = None
+ return
+
+ def addCarolMenu(self):
+ self.removeWinterMenu()
+ self.addWinterMenu(carol=True)
+
+ def removeCarolMenu(self):
+ pass
+
+ def addWhiteList(self):
+ if self.whiteList == None:
+ from toontown.chat.TTSCWhiteListTerminal import TTSCWhiteListTerminal
+ self.whiteList = TTSCWhiteListTerminal(4, self)
+ self.speedChat[1:1] = [self.whiteList]
+ return
+
+ def removeWhiteList(self):
+ if self.whiteList:
+ i = self.speedChat.index(self.whiteList)
+ del self.speedChat[i]
+ self.whiteList.destroy()
+ self.whiteList = None
+ return
+
+ def addSellbotInvasionMenu(self):
+ if self.sellbotInvasionMenu == None:
+ menu = TTSCSellbotInvasionMenu()
+ self.sellbotInvasionMenu = SCMenuHolder(OTPLocalizer.SCMenuSellbotInvasion, menu=menu)
+ self.speedChat[2:2] = [self.sellbotInvasionMenu]
+ return
+
+ def removeSellbotInvasionMenu(self):
+ if self.sellbotInvasionMenu:
+ i = self.speedChat.index(self.sellbotInvasionMenu)
+ del self.speedChat[i]
+ self.sellbotInvasionMenu.destroy()
+ self.sellbotInvasionMenu = None
+ return
+
+ def addSellbotFieldOfficeMenu(self):
+ if self.sellbotFieldOfficeMenu == None:
+ menu = TTSCSellbotFieldOfficeMenu()
+ self.sellbotFieldOfficeMenu = SCMenuHolder(OTPLocalizer.SCMenuFieldOffice, menu=menu)
+ self.speedChat[2:2] = [self.sellbotFieldOfficeMenu]
+ return
+
+ def removeSellbotFieldOfficeMenu(self):
+ if self.sellbotFieldOfficeMenu:
+ i = self.speedChat.index(self.sellbotFieldOfficeMenu)
+ del self.speedChat[i]
+ self.sellbotFieldOfficeMenu.destroy()
+ self.sellbotFieldOfficeMenu = None
+ return
+
+ def addIdesOfMarchMenu(self):
+ if self.idesOfMarchMenu == None:
+ menu = TTSCIdesOfMarchMenu()
+ self.idesOfMarchMenu = SCMenuHolder(OTPLocalizer.SCMenuIdesOfMarch, menu=menu)
+ self.speedChat[2:2] = [self.idesOfMarchMenu]
+ return
+
+ def removeIdesOfMarchMenu(self):
+ if self.idesOfMarchMenu:
+ i = self.speedChat.index(self.idesOfMarchMenu)
+ del self.speedChat[i]
+ self.idesOfMarchMenu.destroy()
+ self.idesOfMarchMenu = None
+ return
diff --git a/toontown/chat/TTChatInputWhiteList.py b/toontown/chat/TTChatInputWhiteList.py
new file mode 100644
index 0000000..6911b52
--- /dev/null
+++ b/toontown/chat/TTChatInputWhiteList.py
@@ -0,0 +1,212 @@
+from otp.chat.ChatInputWhiteListFrame import ChatInputWhiteListFrame
+from toontown.chat.TTWhiteList import TTWhiteList
+from direct.showbase import DirectObject
+from otp.otpbase import OTPGlobals
+import sys
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from otp.otpbase import OTPLocalizer
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownGlobals
+
+class TTChatInputWhiteList(ChatInputWhiteListFrame):
+ notify = DirectNotifyGlobal.directNotify.newCategory('TTChatInputWhiteList')
+ TFToggleKey = base.config.GetString('true-friend-toggle-key', 'alt')
+ TFToggleKeyUp = TFToggleKey + '-up'
+
+ def __init__(self, parent = None, **kw):
+ entryOptions = {'parent': self,
+ 'relief': DGG.SUNKEN,
+ 'scale': 0.05,
+ 'frameColor': (0.9, 0.9, 0.85, 0.0),
+ 'pos': (-0.2, 0, 0.11),
+ 'entryFont': OTPGlobals.getInterfaceFont(),
+ 'width': 8.6,
+ 'numLines': 3,
+ 'cursorKeys': 0,
+ 'backgroundFocus': 0,
+ 'suppressKeys': 0,
+ 'suppressMouse': 1,
+ 'command': self.sendChat,
+ 'failedCommand': self.sendFailed,
+ 'focus': 0,
+ 'text': '',
+ 'sortOrder': DGG.FOREGROUND_SORT_INDEX}
+ ChatInputWhiteListFrame.__init__(self, entryOptions, parent, **kw)
+ self.whiteList = TTWhiteList()
+ base.whiteList = self.whiteList
+ base.ttwl = self
+ self.autoOff = 1
+ self.sendBy = 'Data'
+ self.prefilter = 0
+ self.promoteWhiteList = 1
+ self.typeGrabbed = 0
+ self.deactivate()
+ gui = loader.loadModel('phase_3.5/models/gui/chat_input_gui')
+ self.chatFrame = DirectFrame(parent=self, image=gui.find('**/Chat_Bx_FNL'), relief=None, pos=(0.0, 0, 0.0), state=DGG.NORMAL)
+ self.chatButton = DirectButton(parent=self.chatFrame, image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(0.182, 0, -0.088), relief=None, text=('', OTPLocalizer.ChatInputNormalSayIt, OTPLocalizer.ChatInputNormalSayIt), text_scale=0.06, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.chatButtonPressed)
+ self.cancelButton = DirectButton(parent=self.chatFrame, image=(gui.find('**/CloseBtn_UP'), gui.find('**/CloseBtn_DN'), gui.find('**/CloseBtn_Rllvr')), pos=(-0.151, 0, -0.088), relief=None, text=('', OTPLocalizer.ChatInputNormalCancel, OTPLocalizer.ChatInputNormalCancel), text_scale=0.06, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.cancelButtonPressed)
+ self.whisperLabel = DirectLabel(parent=self.chatFrame, pos=(0.02, 0, 0.23), relief=DGG.FLAT, frameColor=(1, 1, 0.5, 1), frameSize=(-0.23,
+ 0.23,
+ -0.07,
+ 0.05), text=OTPLocalizer.ChatInputNormalWhisper, text_scale=0.04, text_fg=Vec4(0, 0, 0, 1), text_wordwrap=9.5, textMayChange=1)
+ self.chatEntry.bind(DGG.OVERFLOW, self.chatOverflow)
+ self.chatEntry.bind(DGG.TYPE, self.typeCallback)
+ self.trueFriendChat = 0
+ if base.config.GetBool('whisper-to-nearby-true-friends', 1):
+ self.accept(self.TFToggleKey, self.shiftPressed)
+ return
+
+ def shiftPressed(self):
+ self.ignore(self.TFToggleKey)
+ self.trueFriendChat = 1
+ self.accept(self.TFToggleKeyUp, self.shiftReleased)
+
+ def shiftReleased(self):
+ self.ignore(self.TFToggleKeyUp)
+ self.trueFriendChat = 0
+ self.accept(self.TFToggleKey, self.shiftPressed)
+
+ def handleTypeGrab(self):
+ self.ignore('typeEntryGrab')
+ self.accept('typeEntryRelease', self.handleTypeRelease)
+ self.typeGrabbed = 1
+
+ def handleTypeRelease(self):
+ self.ignore('typeEntryRelease')
+ self.accept('typeEntryGrab', self.handleTypeGrab)
+ self.typeGrabbed = 0
+
+ def typeCallback(self, extraArgs):
+ if self.typeGrabbed:
+ return
+ self.applyFilter(extraArgs)
+ if localAvatar.chatMgr.chatInputWhiteList.isActive():
+ return
+ else:
+ messenger.send('wakeup')
+ messenger.send('enterNormalChat')
+
+ def destroy(self):
+ self.chatEntry.destroy()
+ self.chatFrame.destroy()
+ self.ignoreAll()
+ ChatInputWhiteListFrame.destroy(self)
+
+ def delete(self):
+ base.whiteList = None
+ ChatInputWhiteListFrame.delete(self)
+ return
+
+ def sendChat(self, text, overflow = False):
+ if self.typeGrabbed:
+ return
+ else:
+ ChatInputWhiteListFrame.sendChat(self, self.chatEntry.get())
+
+ def sendChatByData(self, text):
+ if self.trueFriendChat:
+ for friendId, flags in base.localAvatar.friendsList:
+ if flags & ToontownGlobals.FriendChat:
+ self.sendWhisperByFriend(friendId, text)
+
+ elif not self.receiverId:
+ base.talkAssistant.sendOpenTalk(text)
+ elif self.receiverId and not self.toPlayer:
+ base.talkAssistant.sendWhisperTalk(text, self.receiverId)
+ elif self.receiverId and self.toPlayer:
+ base.talkAssistant.sendAccountTalk(text, self.receiverId)
+
+ def sendWhisperByFriend(self, avatarId, text):
+ online = 0
+ if base.cr.doId2do.has_key(avatarId):
+ online = 1
+ avatarUnderstandable = 0
+ av = None
+ if avatarId:
+ av = base.cr.identifyAvatar(avatarId)
+ if av != None:
+ avatarUnderstandable = av.isUnderstandable()
+ if avatarUnderstandable and online:
+ base.talkAssistant.sendWhisperTalk(text, avatarId)
+ return
+
+ def chatButtonPressed(self):
+ print 'chatButtonPressed'
+ if self.okayToSubmit:
+ self.sendChat(self.chatEntry.get())
+ else:
+ self.sendFailed(self.chatEntry.get())
+
+ def cancelButtonPressed(self):
+ self.requestMode('Off')
+ localAvatar.chatMgr.fsm.request('mainMenu')
+
+ def enterAllChat(self):
+ ChatInputWhiteListFrame.enterAllChat(self)
+ self.whisperLabel.hide()
+
+ def exitAllChat(self):
+ ChatInputWhiteListFrame.exitAllChat(self)
+
+ def enterPlayerWhisper(self):
+ ChatInputWhiteListFrame.enterPlayerWhisper(self)
+ self.labelWhisper()
+
+ def exitPlayerWhisper(self):
+ ChatInputWhiteListFrame.exitPlayerWhisper(self)
+ self.whisperLabel.hide()
+
+ def enterAvatarWhisper(self):
+ ChatInputWhiteListFrame.enterAvatarWhisper(self)
+ self.labelWhisper()
+
+ def exitAvatarWhisper(self):
+ ChatInputWhiteListFrame.exitAvatarWhisper(self)
+ self.whisperLabel.hide()
+
+ def labelWhisper(self):
+ if self.receiverId:
+ self.whisperName = base.talkAssistant.findName(self.receiverId, self.toPlayer)
+ self.whisperLabel['text'] = OTPLocalizer.ChatInputWhisperLabel % self.whisperName
+ self.whisperLabel.show()
+ else:
+ self.whisperLabel.hide()
+
+ def applyFilter(self, keyArgs, strict = False):
+ text = self.chatEntry.get(plain=True)
+ if len(text) > 0 and text[0] in ['~', '>']:
+ self.okayToSubmit = True
+ else:
+ words = text.split(' ')
+ newwords = []
+ self.okayToSubmit = True
+ flag = 0
+ for friendId, flags in base.localAvatar.friendsList:
+ if flags & ToontownGlobals.FriendChat:
+ flag = 1
+
+ for word in words:
+ if word == '' or self.whiteList.isWord(word) or not base.cr.whiteListChatEnabled:
+ newwords.append(word)
+ else:
+ if self.checkBeforeSend:
+ self.okayToSubmit = False
+ else:
+ self.okayToSubmit = True
+ if flag:
+ newwords.append('\x01WLDisplay\x01' + word + '\x02')
+ else:
+ newwords.append('\x01WLEnter\x01' + word + '\x02')
+
+ if not strict:
+ lastword = words[-1]
+ if lastword == '' or self.whiteList.isPrefix(lastword) or not base.cr.whiteListChatEnabled:
+ newwords[-1] = lastword
+ elif flag:
+ newwords[-1] = '\x01WLDisplay\x01' + lastword + '\x02'
+ else:
+ newwords[-1] = '\x01WLEnter\x01' + lastword + '\x02'
+ newtext = ' '.join(newwords)
+ self.chatEntry.set(newtext)
+ self.chatEntry.guiItem.setAcceptEnabled(self.okayToSubmit)
diff --git a/toontown/chat/TTSCWhiteListTerminal.py b/toontown/chat/TTSCWhiteListTerminal.py
new file mode 100644
index 0000000..dfff6c7
--- /dev/null
+++ b/toontown/chat/TTSCWhiteListTerminal.py
@@ -0,0 +1,25 @@
+from otp.speedchat.SCTerminal import SCTerminal
+from otp.otpbase.OTPLocalizer import SpeedChatStaticText
+SCStaticTextMsgEvent = 'SCStaticTextMsg'
+
+def decodeSCStaticTextMsg(textId):
+ return SpeedChatStaticText.get(textId, None)
+
+
+class TTSCWhiteListTerminal(SCTerminal):
+
+ def __init__(self, textId, parentMenu = None):
+ SCTerminal.__init__(self)
+ self.parentClass = parentMenu
+ self.textId = textId
+ self.text = SpeedChatStaticText[self.textId]
+ print 'SpeedText %s %s' % (self.textId, self.text)
+
+ def handleSelect(self):
+ SCTerminal.handleSelect(self)
+ if not self.parentClass.whisperAvatarId:
+ base.localAvatar.chatMgr.fsm.request('whiteListOpenChat')
+ elif self.parentClass.toPlayer:
+ base.localAvatar.chatMgr.fsm.request('whiteListPlayerChat', [self.parentClass.whisperAvatarId])
+ else:
+ base.localAvatar.chatMgr.fsm.request('whiteListAvatarChat', [self.parentClass.whisperAvatarId])
diff --git a/toontown/chat/TTTalkAssistant.py b/toontown/chat/TTTalkAssistant.py
new file mode 100644
index 0000000..09b89b6
--- /dev/null
+++ b/toontown/chat/TTTalkAssistant.py
@@ -0,0 +1,47 @@
+import string
+import sys
+from direct.showbase import DirectObject
+from otp.otpbase import OTPLocalizer
+from toontown.toonbase import TTLocalizer
+from direct.directnotify import DirectNotifyGlobal
+from otp.otpbase import OTPGlobals
+from otp.speedchat import SCDecoders
+from pandac.PandaModules import *
+from otp.chat.ChatGlobals import *
+from otp.chat.TalkGlobals import *
+from otp.speedchat import SpeedChatGlobals
+from otp.chat.TalkMessage import TalkMessage
+from otp.chat.TalkAssistant import TalkAssistant
+from toontown.speedchat import TTSCDecoders
+import time
+
+class TTTalkAssistant(TalkAssistant):
+ notify = DirectNotifyGlobal.directNotify.newCategory('TTTalkAssistant')
+
+ def __init__(self):
+ TalkAssistant.__init__(self)
+
+ def clearHistory(self):
+ TalkAssistant.clearHistory(self)
+
+ def sendPlayerWhisperToonTaskSpeedChat(self, taskId, toNpcId, toonProgress, msgIndex, receiverId):
+ error = None
+ base.cr.speedchatRelay.sendSpeedchatToonTask(receiverId, taskId, toNpcId, toonProgress, msgIndex)
+ message = TTSCDecoders.decodeTTSCToontaskMsg(taskId, toNpcId, toonProgress, msgIndex)
+ if self.logWhispers:
+ receiverName = self.findName(receiverId, 1)
+ newMessage = TalkMessage(self.countMessage(), self.stampTime(), message, localAvatar.doId, localAvatar.getName(), localAvatar.DISLid, localAvatar.DISLname, None, None, receiverId, receiverName, TALK_ACCOUNT, None)
+ self.historyComplete.append(newMessage)
+ self.addToHistoryDoId(newMessage, localAvatar.doId)
+ self.addToHistoryDISLId(newMessage, base.cr.accountDetailRecord.playerAccountId)
+ messenger.send('NewOpenMessage', [newMessage])
+ return error
+
+ def sendToonTaskSpeedChat(self, taskId, toNpcId, toonProgress, msgIndex):
+ error = None
+ messenger.send(SCChatEvent)
+ messenger.send('chatUpdateSCToontask', [taskId,
+ toNpcId,
+ toonProgress,
+ msgIndex])
+ return error
diff --git a/toontown/chat/TTWhiteList.py b/toontown/chat/TTWhiteList.py
new file mode 100644
index 0000000..21d7085
--- /dev/null
+++ b/toontown/chat/TTWhiteList.py
@@ -0,0 +1,165 @@
+import os
+import datetime
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from direct.showbase import AppRunnerGlobal
+from otp.chat.WhiteList import WhiteList
+from toontown.toonbase import TTLocalizer
+
+class TTWhiteList(WhiteList, DistributedObject.DistributedObject):
+ RedownloadTaskName = 'RedownloadWhitelistTask'
+ WhitelistBaseDir = config.GetString('whitelist-base-dir', '')
+ WhitelistStageDir = config.GetString('whitelist-stage-dir', 'whitelist')
+ WhitelistOverHttp = config.GetBool('whitelist-over-http', True)
+ WhitelistFileName = config.GetString('whitelist-filename', 'twhitelist.dat')
+
+ def __init__(self):
+ self.redownloadingWhitelist = False
+ self.startRedownload = datetime.datetime.now()
+ self.endRedownload = datetime.datetime.now()
+ self.percentDownloaded = 0.0
+ self.notify = DirectNotifyGlobal.directNotify.newCategory('TTWhiteList')
+ vfs = VirtualFileSystem.getGlobalPtr()
+ filename = Filename('twhitelist.dat')
+ searchPath = DSearchPath()
+ if AppRunnerGlobal.appRunner:
+ searchPath.appendDirectory(Filename.expandFrom('$TT_3_ROOT/phase_3/etc'))
+ else:
+ searchPath.appendDirectory(Filename('.'))
+ searchPath.appendDirectory(Filename('etc'))
+ searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('$TOONTOWN/src/chat')))
+ searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('toontown/src/chat')))
+ searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('toontown/chat')))
+ found = vfs.resolveFilename(filename, searchPath)
+ if not found:
+ self.notify.info("Couldn't find whitelist data file!")
+ data = vfs.readFile(filename, 1)
+ lines = data.split('\n')
+ WhiteList.__init__(self, lines)
+ self.redownloadWhitelist()
+ self.defaultWord = TTLocalizer.ChatGarblerDefault[0]
+ self.accept('updateWhitelist', self.handleNewWhitelist)
+
+ def unload(self):
+ self.ignore('updateWhitelist')
+ self.removeDownloadingTextTask()
+
+ def redownloadWhitelist(self):
+ self.percentDownload = 0.0
+ self.notify.info('starting redownloadWhitelist')
+ self.startRedownload = datetime.datetime.now()
+ self.redownloadingWhitelist = True
+ self.addDownloadingTextTask()
+ self.whitelistUrl = self.getWhitelistUrl()
+ self.whitelistDir = Filename(self.findWhitelistDir())
+ Filename(self.whitelistDir + '/.').makeDir()
+ http = HTTPClient.getGlobalPtr()
+ self.url = self.whitelistUrl + self.WhitelistFileName
+ self.ch = http.makeChannel(True)
+ localFilename = Filename(self.whitelistDir, 'twhitelist.dat')
+ self.ch.getHeader(DocumentSpec(self.url))
+ size = self.ch.getFileSize()
+ doc = self.ch.getDocumentSpec()
+ localSize = localFilename.getFileSize()
+ outOfDate = True
+ if size == localSize:
+ if doc.hasDate():
+ date = doc.getDate()
+ localDate = HTTPDate(localFilename.getTimestamp())
+ if localDate.compareTo(date) > 0:
+ outOfDate = False
+ self.notify.info('Whitelist is up to date')
+ taskMgr.remove(self.RedownloadTaskName)
+ if outOfDate and self.ch.isValid():
+ self.ch.beginGetDocument(doc)
+ self.ch.downloadToFile(localFilename)
+ taskMgr.add(self.downloadWhitelistTask, self.RedownloadTaskName)
+ else:
+ self.updateWhitelist()
+
+ def getWhitelistUrl(self):
+ result = base.config.GetString('fallback-whitelist-url', 'http://cdn.toontown.disney.go.com/toontown/en/')
+ override = base.config.GetString('whitelist-url', '')
+ if override:
+ self.notify.info('got an override url, using %s for the whitelist' % override)
+ result = override
+ else:
+ try:
+ launcherUrl = base.launcher.getValue('GAME_WHITELIST_URL', '')
+ if launcherUrl:
+ result = launcherUrl
+ self.notify.info('got GAME_WHITELIST_URL from launcher using %s' % result)
+ else:
+ self.notify.info('blank GAME_WHITELIST_URL from launcher, using %s' % result)
+ except:
+ self.notify.warning('got exception getting GAME_WHITELIST_URL from launcher, using %s' % result)
+
+ return result
+
+ def addDownloadingTextTask(self):
+ self.removeDownloadingTextTask()
+ task = taskMgr.doMethodLater(1, self.loadingTextTask, 'WhitelistDownloadingTextTask')
+ task.startTime = globalClock.getFrameTime()
+ self.loadingTextTask(task)
+
+ def removeDownloadingTextTask(self):
+ taskMgr.remove('WhitelistDownloadingTextTask')
+
+ def loadingTextTask(self, task):
+ timeIndex = int(globalClock.getFrameTime() - task.startTime) % 3
+ timeStrs = (TTLocalizer.NewsPageDownloadingNews0, TTLocalizer.NewsPageDownloadingNews1, TTLocalizer.NewsPageDownloadingNews2)
+ textToDisplay = timeStrs[timeIndex] % int(self.percentDownloaded * 100)
+ return task.again
+
+ def findWhitelistDir(self):
+ if self.WhitelistOverHttp:
+ return self.WhitelistStageDir
+ searchPath = DSearchPath()
+ if AppRunnerGlobal.appRunner:
+ searchPath.appendDirectory(Filename.expandFrom('$TT_3_5_ROOT/phase_3.5/models/news'))
+ else:
+ basePath = os.path.expandvars('$TTMODELS') or './ttmodels'
+ searchPath.appendDirectory(Filename.fromOsSpecific(basePath + '/built/' + self.NewsBaseDir))
+ searchPath.appendDirectory(Filename(self.NewsBaseDir))
+ pfile = Filename(self.WhitelistFileName)
+ found = vfs.resolveFilename(pfile, searchPath)
+ if not found:
+ self.notify.warning('findWhitelistDir - no path: %s' % self.WhitelistFileName)
+ self.setErrorMessage(TTLocalizer.NewsPageErrorDownloadingFile % self.WhitelistFileName)
+ return None
+ self.notify.debug('found whitelist file %s' % pfile)
+ realDir = pfile.getDirname()
+ return realDir
+
+ def downloadWhitelistTask(self, task):
+ if self.ch.run():
+ return task.cont
+ doc = self.ch.getDocumentSpec()
+ date = ''
+ if doc.hasDate():
+ date = doc.getDate().getString()
+ if not self.ch.isValid():
+ self.notify.warning('Unable to download %s' % self.url)
+ self.redownloadingWhitelist = False
+ return task.done
+ self.notify.info('Done downloading whitelist file')
+ self.updateWhitelist()
+ return task.done
+
+ def updateWhitelist(self):
+ localFilename = Filename(self.whitelistDir, 'twhitelist.dat')
+ if not localFilename.exists():
+ return
+ data = vfs.readFile(localFilename, 1)
+ lines = data.split('\n')
+ self.words = []
+ for line in lines:
+ self.words.append(line.strip('\n\r').lower())
+
+ self.words.sort()
+ self.numWords = len(self.words)
+ self.defaultWord = TTLocalizer.ChatGarblerDefault[0]
+
+ def handleNewWhitelist(self):
+ self.redownloadWhitelist()
diff --git a/toontown/chat/ToonChatGarbler.py b/toontown/chat/ToonChatGarbler.py
new file mode 100644
index 0000000..71394dc
--- /dev/null
+++ b/toontown/chat/ToonChatGarbler.py
@@ -0,0 +1,49 @@
+import string
+import random
+from toontown.toonbase import TTLocalizer
+from otp.otpbase import OTPLocalizer
+from otp.chat import ChatGarbler
+
+class ToonChatGarbler(ChatGarbler.ChatGarbler):
+ animalSounds = {'dog': TTLocalizer.ChatGarblerDog,
+ 'cat': TTLocalizer.ChatGarblerCat,
+ 'mouse': TTLocalizer.ChatGarblerMouse,
+ 'horse': TTLocalizer.ChatGarblerHorse,
+ 'rabbit': TTLocalizer.ChatGarblerRabbit,
+ 'duck': TTLocalizer.ChatGarblerDuck,
+ 'monkey': TTLocalizer.ChatGarblerMonkey,
+ 'bear': TTLocalizer.ChatGarblerBear,
+ 'pig': TTLocalizer.ChatGarblerPig,
+ 'default': OTPLocalizer.ChatGarblerDefault}
+
+ def garble(self, toon, message):
+ newMessage = ''
+ animalType = toon.getStyle().getType()
+ if ToonChatGarbler.animalSounds.has_key(animalType):
+ wordlist = ToonChatGarbler.animalSounds[animalType]
+ else:
+ wordlist = ToonChatGarbler.animalSounds['default']
+ numWords = random.randint(1, 7)
+ for i in range(1, numWords + 1):
+ wordIndex = random.randint(0, len(wordlist) - 1)
+ newMessage = newMessage + wordlist[wordIndex]
+ if i < numWords:
+ newMessage = newMessage + ' '
+
+ return newMessage
+
+ def garbleSingle(self, toon, message):
+ newMessage = ''
+ animalType = toon.getStyle().getType()
+ if ToonChatGarbler.animalSounds.has_key(animalType):
+ wordlist = ToonChatGarbler.animalSounds[animalType]
+ else:
+ wordlist = ToonChatGarbler.animalSounds['default']
+ numWords = 1
+ for i in range(1, numWords + 1):
+ wordIndex = random.randint(0, len(wordlist) - 1)
+ newMessage = newMessage + wordlist[wordIndex]
+ if i < numWords:
+ newMessage = newMessage + ' '
+
+ return newMessage
diff --git a/toontown/chat/ToontownChatManager.py b/toontown/chat/ToontownChatManager.py
new file mode 100644
index 0000000..e807797
--- /dev/null
+++ b/toontown/chat/ToontownChatManager.py
@@ -0,0 +1,601 @@
+import sys
+from direct.showbase import DirectObject
+from direct.showbase.PythonUtil import traceFunctionCall
+from otp.otpbase import OTPGlobals
+from otp.otpbase import OTPLocalizer
+from toontown.toonbase import TTLocalizer
+from toontown.toontowngui import TeaserPanel
+from direct.directnotify import DirectNotifyGlobal
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from otp.chat import ChatManager
+from TTChatInputSpeedChat import TTChatInputSpeedChat
+from TTChatInputNormal import TTChatInputNormal
+from TTChatInputWhiteList import TTChatInputWhiteList
+
+class HackedDirectRadioButton(DirectCheckButton):
+
+ def __init__(self, parent = None, **kw):
+ optiondefs = ()
+ self.defineoptions(kw, optiondefs)
+ DirectCheckButton.__init__(self, parent)
+ self.initialiseoptions(HackedDirectRadioButton)
+
+ def commandFunc(self, event):
+ if self['indicatorValue']:
+ self['indicatorValue'] = 0
+ DirectCheckButton.commandFunc(self, event)
+
+
+class ToontownChatManager(ChatManager.ChatManager):
+ notify = DirectNotifyGlobal.directNotify.newCategory('ToontownChatManager')
+
+ def __init__(self, cr, localAvatar):
+ gui = loader.loadModel('phase_3.5/models/gui/chat_input_gui')
+ self.normalButton = DirectButton(image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(-1.2647, 0, 0.928), scale=1.179, relief=None, image_color=Vec4(1, 1, 1, 1), text=('', OTPLocalizer.ChatManagerChat, OTPLocalizer.ChatManagerChat), text_align=TextNode.ALeft, text_scale=TTLocalizer.TCMnormalButton, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(-0.0525, -0.09), textMayChange=0, sortOrder=DGG.FOREGROUND_SORT_INDEX, command=self.__normalButtonPressed)
+ self.normalButton.hide()
+ self.openScSfx = loader.loadSfx('phase_3.5/audio/sfx/GUI_quicktalker.mp3')
+ self.openScSfx.setVolume(0.6)
+ self.scButton = DirectButton(image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=TTLocalizer.TCMscButtonPos, scale=1.179, relief=None, image_color=Vec4(0.75, 1, 0.6, 1), text=('', OTPLocalizer.GlobalSpeedChatName, OTPLocalizer.GlobalSpeedChatName), text_scale=TTLocalizer.TCMscButton, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, sortOrder=DGG.FOREGROUND_SORT_INDEX, command=self.__scButtonPressed, clickSound=self.openScSfx)
+ self.scButton.hide()
+ self.whisperFrame = DirectFrame(parent=aspect2dp, relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(0.45, 0.45, 0.45), image_color=OTPGlobals.GlobalDialogColor, pos=(-0.4, 0, 0.754), text=OTPLocalizer.ChatManagerWhisperTo, text_wordwrap=7.0, text_scale=TTLocalizer.TCMwhisperFrame, text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0.14), textMayChange=1, sortOrder=DGG.FOREGROUND_SORT_INDEX)
+ self.whisperFrame.hide()
+ self.whisperButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(-0.125, 0, -0.1), scale=1.179, relief=None, image_color=Vec4(1, 1, 1, 1), text=('',
+ OTPLocalizer.ChatManagerChat,
+ OTPLocalizer.ChatManagerChat,
+ ''), image3_color=Vec4(0.6, 0.6, 0.6, 0.6), text_scale=TTLocalizer.TCMwhisperButton, text_fg=(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.__whisperButtonPressed)
+ self.whisperScButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(0.0, 0, -0.1), scale=1.179, relief=None, image_color=Vec4(0.75, 1, 0.6, 1), text=('',
+ OTPLocalizer.GlobalSpeedChatName,
+ OTPLocalizer.GlobalSpeedChatName,
+ ''), image3_color=Vec4(0.6, 0.6, 0.6, 0.6), text_scale=TTLocalizer.TCMwhisperScButton, text_fg=(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.__whisperScButtonPressed)
+ self.whisperCancelButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/CloseBtn_UP'), gui.find('**/CloseBtn_DN'), gui.find('**/CloseBtn_Rllvr')), pos=(0.125, 0, -0.1), scale=1.179, relief=None, text=('', OTPLocalizer.ChatManagerCancel, OTPLocalizer.ChatManagerCancel), text_scale=0.05, text_fg=(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.__whisperCancelPressed)
+ gui.removeNode()
+ ChatManager.ChatManager.__init__(self, cr, localAvatar)
+ self.defaultToWhiteList = base.config.GetBool('white-list-is-default', 1)
+ self.chatInputSpeedChat = TTChatInputSpeedChat(self)
+ self.normalPos = Vec3(-1.083, 0, 0.804)
+ self.whisperPos = Vec3(0.0, 0, 0.71)
+ self.speedChatPlusPos = Vec3(-0.35, 0, 0.71)
+ self.chatInputWhiteList = TTChatInputWhiteList()
+ if self.defaultToWhiteList:
+ self.chatInputNormal = self.chatInputWhiteList
+ self.chatInputNormal.setPos(self.normalPos)
+ self.chatInputNormal.desc = 'chatInputNormal'
+ else:
+ self.chatInputNormal = TTChatInputNormal(self)
+ self.chatInputWhiteList.setPos(self.speedChatPlusPos)
+ self.chatInputWhiteList.desc = 'chatInputWhiteList'
+ return
+
+ def delete(self):
+ ChatManager.ChatManager.delete(self)
+ loader.unloadModel('phase_3.5/models/gui/chat_input_gui')
+ self.normalButton.destroy()
+ del self.normalButton
+ self.scButton.destroy()
+ del self.scButton
+ del self.openScSfx
+ self.whisperFrame.destroy()
+ del self.whisperFrame
+ self.whisperButton.destroy()
+ del self.whisperButton
+ self.whisperScButton.destroy()
+ del self.whisperScButton
+ self.whisperCancelButton.destroy()
+ del self.whisperCancelButton
+ self.chatInputWhiteList.destroy()
+ del self.chatInputWhiteList
+
+ def sendSCResistanceChatMessage(self, textId):
+ messenger.send('chatUpdateSCResistance', [textId])
+ self.announceSCChat()
+
+ def sendSCSingingChatMessage(self, textId):
+ messenger.send('chatUpdateSCSinging', [textId])
+ self.announceSCChat()
+
+ def sendSCSingingWhisperMessage(self, textId):
+ pass
+
+ def sendSCToontaskChatMessage(self, taskId, toNpcId, toonProgress, msgIndex):
+ messenger.send('chatUpdateSCToontask', [taskId,
+ toNpcId,
+ toonProgress,
+ msgIndex])
+ self.announceSCChat()
+
+ def sendSCToontaskWhisperMessage(self, taskId, toNpcId, toonProgress, msgIndex, whisperAvatarId, toPlayer):
+ if toPlayer:
+ base.talkAssistant.sendPlayerWhisperToonTaskSpeedChat(taskId, toNpcId, toonProgress, msgIndex, whisperAvatarId)
+ else:
+ messenger.send('whisperUpdateSCToontask', [taskId,
+ toNpcId,
+ toonProgress,
+ msgIndex,
+ whisperAvatarId])
+
+ def enterOpenChatWarning(self):
+ if self.openChatWarning == None:
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ buttonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ self.openChatWarning = DirectFrame(parent=aspect2dp, pos=(0.0, 0.1, 0.2), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(1.2, 1.0, 1.4), text=OTPLocalizer.OpenChatWarning, text_wordwrap=19, text_scale=TTLocalizer.TCMopenChatWarning, text_pos=(0.0, 0.575), textMayChange=0)
+ DirectButton(self.openChatWarning, image=buttonImage, relief=None, text=OTPLocalizer.OpenChatWarningOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.55), command=self.__handleOpenChatWarningOK)
+ buttons.removeNode()
+ self.openChatWarning.show()
+ normObs, scObs = self.isObscured()
+ if not scObs:
+ self.scButton.show()
+ if not normObs:
+ self.normalButton.show()
+ return
+
+ def enterMainMenu(self):
+ self.chatInputNormal.setPos(self.normalPos)
+ if self.chatInputWhiteList.isActive():
+ self.notify.debug('enterMainMenu calling checkObscured')
+ ChatManager.ChatManager.checkObscurred(self)
+ else:
+ ChatManager.ChatManager.enterMainMenu(self)
+
+ def exitOpenChatWarning(self):
+ self.openChatWarning.hide()
+ self.scButton.hide()
+
+ def enterUnpaidChatWarning(self):
+ self.forceHidePayButton = False
+ if base.cr.productName in ['DisneyOnline-UK',
+ 'JP',
+ 'DE',
+ 'BR',
+ 'FR']:
+ directFrameText = OTPLocalizer.PaidParentPasswordUKWarning
+ payButtonText = OTPLocalizer.PaidParentPasswordUKWarningSet
+ directButtonText = OTPLocalizer.PaidParentPasswordUKWarningContinue
+ else:
+ directFrameText = OTPLocalizer.PaidNoParentPasswordWarning
+ payButtonText = OTPLocalizer.PaidNoParentPasswordWarningSet
+ directButtonText = OTPLocalizer.PaidNoParentPasswordWarningContinue
+ if 'QuickLauncher' not in str(base.cr.launcher.__class__) and not base.cr.isPaid():
+ directFrameText = OTPLocalizer.UnpaidNoParentPasswordWarning
+ self.forceHidePayButton = True
+ if self.unpaidChatWarning == None:
+ guiButton = loader.loadModel('phase_3/models/gui/quit_button')
+ buttonImage = (guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR'))
+ self.unpaidChatWarning = DirectFrame(parent=aspect2dp, pos=(0.0, 0.1, 0.4), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(1.2, 1.0, 0.8), text=directFrameText, text_wordwrap=TTLocalizer.TCMunpaidChatWarningWordwrap, text_scale=TTLocalizer.TCMunpaidChatWarning, text_pos=TTLocalizer.TCMunpaidChatWarningPos, textMayChange=0)
+ self.payButton = DirectButton(self.unpaidChatWarning, image=buttonImage, relief=None, text=payButtonText, image_scale=(1.75, 1, 1.15), text_scale=TTLocalizer.TCMpayButton, text_pos=(0, -0.02), textMayChange=0, pos=TTLocalizer.TCMpayButtonPos, command=self.__handleUnpaidChatWarningPay)
+ DirectButton(self.unpaidChatWarning, image=buttonImage, relief=None, text=directButtonText, textMayChange=0, image_scale=(1.75, 1, 1.15), text_scale=0.06, text_pos=(0, -0.02), pos=TTLocalizer.TCMdirectButtonTextPos, command=self.__handleUnpaidChatWarningContinue)
+ guiButton.removeNode()
+ if base.localAvatar.cantLeaveGame or self.forceHidePayButton:
+ self.payButton.hide()
+ else:
+ self.payButton.show()
+ if base.cr.productName not in ['ES',
+ 'JP',
+ 'DE',
+ 'BR',
+ 'FR']:
+ self.unpaidChatWarning.show()
+ else:
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.fsm.request('stopped')
+ self.teaser = TeaserPanel.TeaserPanel('secretChat', self.__handleUnpaidChatWarningDone)
+ if base.localAvatar.inTutorial:
+ self.teaser.hidePay()
+ normObs, scObs = self.isObscured()
+ if not scObs:
+ self.scButton.show()
+ if not normObs:
+ self.normalButton.show()
+ return
+
+ def exitUnpaidChatWarning(self):
+ if self.unpaidChatWarning:
+ self.unpaidChatWarning.hide()
+ self.scButton.hide()
+
+ def enterNoSecretChatAtAll(self):
+ if self.noSecretChatAtAll == None:
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ self.noSecretChatAtAll = DirectFrame(parent=aspect2dp, pos=(0.0, 0.1, 0.2), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(1.4, 1.0, 1.1), text=OTPLocalizer.NoSecretChatAtAll, text_wordwrap=20, textMayChange=0, text_scale=0.06, text_pos=(0, 0.3))
+ DirectLabel(parent=self.noSecretChatAtAll, relief=None, pos=(0, 0, 0.4), text=OTPLocalizer.NoSecretChatAtAllTitle, textMayChange=0, text_scale=0.08)
+ DirectButton(self.noSecretChatAtAll, image=okButtonImage, relief=None, text=OTPLocalizer.NoSecretChatAtAllOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.4), command=self.__handleNoSecretChatAtAllOK)
+ buttons.removeNode()
+ self.noSecretChatAtAll.show()
+ return
+
+ def exitNoSecretChatAtAll(self):
+ self.noSecretChatAtAll.hide()
+
+ def enterNoSecretChatWarning(self, passwordOnly = 0):
+ if not passwordOnly:
+ warningText = OTPLocalizer.NoSecretChatWarning
+ else:
+ warningText = OTPLocalizer.ChangeSecretFriendsOptionsWarning
+ if self.noSecretChatWarning == None:
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ nameBalloon = loader.loadModel('phase_3/models/props/chatbox_input')
+ okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ cancelButtonImage = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr'))
+ if base.cr.productName != 'Terra-DMC':
+ okPos = (-0.22, 0.0, -0.35)
+ textPos = (0, 0.25)
+ okCommand = self.__handleNoSecretChatWarningOK
+ else:
+ self.passwordEntry = None
+ okPos = (0, 0, -0.35)
+ textPos = (0, 0.125)
+ okCommand = self.__handleNoSecretChatWarningCancel
+ self.noSecretChatWarning = DirectFrame(parent=aspect2dp, pos=(0.0, 0.1, 0.2), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(1.4, 1.0, 1.0), text=warningText, text_wordwrap=20, text_scale=0.055, text_pos=textPos, textMayChange=1)
+ DirectButton(self.noSecretChatWarning, image=okButtonImage, relief=None, text=OTPLocalizer.NoSecretChatWarningOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=okPos, command=okCommand)
+ DirectLabel(parent=self.noSecretChatWarning, relief=None, pos=(0, 0, 0.35), text=OTPLocalizer.NoSecretChatWarningTitle, textMayChange=0, text_scale=0.08)
+ if base.cr.productName != 'Terra-DMC':
+ self.passwordLabel = DirectLabel(parent=self.noSecretChatWarning, relief=None, pos=(-0.07, 0.0, -0.2), text=OTPLocalizer.ParentPassword, text_scale=0.06, text_align=TextNode.ARight, textMayChange=0)
+ self.passwordEntry = DirectEntry(parent=self.noSecretChatWarning, relief=None, image=nameBalloon, image1_color=(0.8, 0.8, 0.8, 1.0), scale=0.064, pos=(0.0, 0.0, -0.2), width=OTPGlobals.maxLoginWidth, numLines=1, focus=1, cursorKeys=1, obscured=1, command=self.__handleNoSecretChatWarningOK)
+ DirectButton(self.noSecretChatWarning, image=cancelButtonImage, relief=None, text=OTPLocalizer.NoSecretChatWarningCancel, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=1, pos=(0.2, 0.0, -0.35), command=self.__handleNoSecretChatWarningCancel)
+ buttons.removeNode()
+ nameBalloon.removeNode()
+ else:
+ self.noSecretChatWarning['text'] = warningText
+ if self.passwordEntry:
+ self.passwordEntry['focus'] = 1
+ self.passwordEntry.enterText('')
+ self.noSecretChatWarning.show()
+ return
+
+ def exitNoSecretChatWarning(self):
+ self.noSecretChatWarning.hide()
+
+ def enterActivateChat(self):
+ if self.activateChatGui == None:
+ guiButton = loader.loadModel('phase_3/models/gui/quit_button')
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ cancelButtonImage = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr'))
+ moreButtonImage = (guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR'))
+ nameShopGui = loader.loadModel('phase_3/models/gui/nameshop_gui')
+ circle = nameShopGui.find('**/namePanelCircle')
+ self.activateChatGui = DirectFrame(parent=aspect2dp, pos=(0.0, 0.1, 0.2), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(1.8, 1.0, 1.6), text=OTPLocalizer.ActivateChat, text_align=TextNode.ALeft, text_wordwrap=33, text_scale=TTLocalizer.TCMactivateChatGui, text_pos=(-0.82, 0.58), textMayChange=0)
+ innerCircle = circle.copyTo(hidden)
+ innerCircle.setPos(0, 0, 0.2)
+ self.c1b = circle.copyTo(self.activateChatGui, -1)
+ self.c1b.setColor(0, 0, 0, 1)
+ self.c1b.setPos(-0.8, 0, 0.29)
+ self.c1b.setScale(0.4)
+ c1f = circle.copyTo(self.c1b)
+ c1f.setColor(1, 1, 1, 1)
+ c1f.setScale(0.8)
+ self.c2b = circle.copyTo(self.activateChatGui, -2)
+ self.c2b.setColor(0, 0, 0, 1)
+ self.c2b.setPos(-0.8, 0, 0.14)
+ self.c2b.setScale(0.4)
+ c2f = circle.copyTo(self.c2b)
+ c2f.setColor(1, 1, 1, 1)
+ c2f.setScale(0.8)
+ self.c3b = circle.copyTo(self.activateChatGui, -2)
+ self.c3b.setColor(0, 0, 0, 1)
+ self.c3b.setPos(-0.8, 0, -0.01)
+ self.c3b.setScale(0.4)
+ c3f = circle.copyTo(self.c3b)
+ c3f.setColor(1, 1, 1, 1)
+ c3f.setScale(0.8)
+ DirectLabel(self.activateChatGui, relief=None, text=OTPLocalizer.ActivateChatTitle, text_align=TextNode.ACenter, text_scale=0.07, text_pos=(0, 0.7), textMayChange=0)
+ if base.cr.productName != 'JP':
+ DirectButton(self.activateChatGui, image=moreButtonImage, image_scale=(1.25, 1.0, 1.0), relief=None, text=OTPLocalizer.ActivateChatMoreInfo, text_scale=0.06, text_pos=(0, -0.02), textMayChange=0, pos=(0.0, 0.0, -0.7), command=self.__handleActivateChatMoreInfo)
+ self.dcb1 = HackedDirectRadioButton(parent=self.activateChatGui, relief=None, scale=0.1, boxImage=innerCircle, boxImageScale=2.5, boxImageColor=VBase4(0, 0.25, 0.5, 1), boxRelief=None, pos=(-0.745, 0, 0.297), command=self.__updateCheckBoxen, extraArgs=[1])
+ self.dcb2 = HackedDirectRadioButton(parent=self.activateChatGui, relief=None, scale=0.1, boxImage=innerCircle, boxImageScale=2.5, boxImageColor=VBase4(0, 0.25, 0.5, 1), boxRelief=None, pos=(-0.745, 0, 0.147), command=self.__updateCheckBoxen, extraArgs=[2])
+ self.dcb3 = HackedDirectRadioButton(parent=self.activateChatGui, relief=None, scale=0.1, boxImage=innerCircle, boxImageScale=2.5, boxImageColor=VBase4(0, 0.25, 0.5, 1), boxRelief=None, pos=(-0.745, 0, -0.003), command=self.__updateCheckBoxen, extraArgs=[3])
+ DirectButton(self.activateChatGui, image=okButtonImage, relief=None, text=OTPLocalizer.ActivateChatYes, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.35, 0.0, -0.27), command=self.__handleActivateChatYes)
+ DirectButton(self.activateChatGui, image=cancelButtonImage, relief=None, text=OTPLocalizer.ActivateChatNo, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.35, 0.0, -0.27), command=self.__handleActivateChatNo)
+ guiButton.removeNode()
+ buttons.removeNode()
+ nameShopGui.removeNode()
+ innerCircle.removeNode()
+ self.__initializeCheckBoxen()
+ self.activateChatGui.show()
+ return
+
+ def __initializeCheckBoxen(self):
+ if base.cr.secretChatAllowed and not base.cr.secretChatNeedsParentPassword:
+ self.dcb1['indicatorValue'] = 0
+ self.dcb2['indicatorValue'] = 0
+ self.dcb3['indicatorValue'] = 1
+ elif base.cr.secretChatAllowed and base.cr.secretChatNeedsParentPassword:
+ self.dcb1['indicatorValue'] = 0
+ self.dcb2['indicatorValue'] = 1
+ self.dcb3['indicatorValue'] = 0
+ else:
+ self.dcb1['indicatorValue'] = 1
+ self.dcb2['indicatorValue'] = 0
+ self.dcb3['indicatorValue'] = 0
+
+ def __updateCheckBoxen(self, value, checkBox):
+ if value == 0:
+ return
+ if checkBox == 1:
+ self.dcb2['indicatorValue'] = 0
+ self.dcb3['indicatorValue'] = 0
+ elif checkBox == 2:
+ self.dcb1['indicatorValue'] = 0
+ self.dcb3['indicatorValue'] = 0
+ else:
+ self.dcb1['indicatorValue'] = 0
+ self.dcb2['indicatorValue'] = 0
+
+ def exitActivateChat(self):
+ self.activateChatGui.hide()
+
+ def enterSecretChatActivated(self, mode = 2):
+ if mode == 0:
+ modeText = OTPLocalizer.SecretChatDeactivated
+ elif mode == 1:
+ modeText = OTPLocalizer.RestrictedSecretChatActivated
+ else:
+ modeText = OTPLocalizer.SecretChatActivated
+ if self.secretChatActivated == None:
+ guiButton = loader.loadModel('phase_3/models/gui/quit_button')
+ optionsButtonImage = (guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR'))
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ buttonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ self.secretChatActivated = DirectFrame(parent=aspect2dp, pos=(0.0, 0.1, 0.4), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.8), text=modeText, text_align=TextNode.ACenter, text_wordwrap=14, text_scale=TTLocalizer.TCMsecretChatActivated, text_pos=(0, 0.25))
+ DirectButton(self.secretChatActivated, image=buttonImage, relief=None, text=OTPLocalizer.SecretChatActivatedOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.1), command=self.__handleSecretChatActivatedOK)
+ buttons.removeNode()
+ guiButton.removeNode()
+ else:
+ self.secretChatActivated['text'] = modeText
+ self.secretChatActivated.show()
+ return
+
+ def exitSecretChatActivated(self):
+ self.secretChatActivated.hide()
+
+ def enterProblemActivatingChat(self):
+ if self.problemActivatingChat == None:
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ buttonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ self.problemActivatingChat = DirectFrame(parent=aspect2dp, pos=(0.0, 0.1, 0.4), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(1.2, 1.0, 0.9), text='', text_align=TextNode.ALeft, text_wordwrap=18, text_scale=0.06, text_pos=(-0.5, 0.28), textMayChange=1)
+ DirectButton(self.problemActivatingChat, image=buttonImage, relief=None, text=OTPLocalizer.ProblemActivatingChatOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.28), command=self.__handleProblemActivatingChatOK)
+ buttons.removeNode()
+ self.problemActivatingChat.show()
+ return
+
+ def exitProblemActivatingChat(self):
+ self.problemActivatingChat.hide()
+
+ def __normalButtonPressed(self):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: CHAT: Speedchat Plus')
+ messenger.send('wakeup')
+ if base.cr.productName in ['DisneyOnline-US', 'ES']:
+ if base.cr.whiteListChatEnabled:
+ self.fsm.request('normalChat')
+ elif not base.cr.isParentPasswordSet():
+ self.paidNoParentPassword = 1
+ self.fsm.request('unpaidChatWarning')
+ elif not base.cr.allowSecretChat():
+ self.fsm.request('noSecretChatAtAllAndNoWhitelist')
+ elif not base.localAvatar.canChat():
+ self.fsm.request('openChatWarning')
+ else:
+ self.fsm.request('normalChat')
+ elif base.cr.productName == 'Terra-DMC':
+ if not base.cr.allowSecretChat():
+ self.fsm.request('noSecretChatWarning')
+ elif not base.localAvatar.canChat():
+ self.fsm.request('openChatWarning')
+ else:
+ self.fsm.request('normalChat')
+ elif base.cr.productName in ['DisneyOnline-UK',
+ 'DisneyOnline-AP',
+ 'JP',
+ 'BR',
+ 'FR']:
+ if base.cr.whiteListChatEnabled:
+ self.fsm.request('normalChat')
+ elif not base.cr.isParentPasswordSet():
+ self.paidNoParentPassword = 1
+ self.fsm.request('unpaidChatWarning')
+ elif not base.cr.allowSecretChat():
+ self.paidNoParentPassword = 1
+ self.fsm.request('unpaidChatWarning')
+ elif not base.localAvatar.canChat():
+ self.fsm.request('openChatWarning')
+ else:
+ self.fsm.request('normalChat')
+ else:
+ print 'ChatManager: productName: %s not recognized' % base.cr.productName
+
+ def __scButtonPressed(self):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: CHAT: Speedchat')
+ messenger.send('wakeup')
+ if self.fsm.getCurrentState().getName() == 'speedChat':
+ self.fsm.request('mainMenu')
+ else:
+ self.fsm.request('speedChat')
+
+ def __whisperButtonPressed(self, avatarName, avatarId, playerId):
+ messenger.send('wakeup')
+ playerInfo = None
+ if playerId:
+ playerInfo = base.cr.playerFriendsManager.getFriendInfo(playerId)
+ if playerInfo:
+ if playerInfo.understandableYesNo:
+ self.fsm.request('whisperChatPlayer', [avatarName, playerId])
+ return
+ if avatarId:
+ self.fsm.request('whisperChat', [avatarName, avatarId])
+ return
+
+ def enterNormalChat(self):
+ result = ChatManager.ChatManager.enterNormalChat(self)
+ if result == None:
+ self.notify.warning('something went wrong in enterNormalChat, falling back to main menu')
+ self.fsm.request('mainMenu')
+ return
+
+ def enterWhisperChatPlayer(self, avatarName, playerId):
+ result = ChatManager.ChatManager.enterWhisperChatPlayer(self, avatarName, playerId)
+ self.chatInputNormal.setPos(self.whisperPos)
+ if result == None:
+ self.notify.warning('something went wrong in enterWhisperChatPlayer, falling back to main menu')
+ self.fsm.request('mainMenu')
+ return
+
+ def enterWhisperChat(self, avatarName, avatarId):
+ result = ChatManager.ChatManager.enterWhisperChat(self, avatarName, avatarId)
+ self.chatInputNormal.setPos(self.whisperPos)
+ if result == None:
+ self.notify.warning('something went wrong in enterWhisperChat, falling back to main menu')
+ self.fsm.request('mainMenu')
+ return
+
+ def enterNoSecretChatAtAllAndNoWhitelist(self):
+ if self.noSecretChatAtAllAndNoWhitelist == None:
+ buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui')
+ okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr'))
+ self.noSecretChatAtAllAndNoWhitelist = DirectFrame(parent=aspect2dp, pos=(0.0, 0.1, 0.05), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(1.4, 1.0, 1.58), text=OTPLocalizer.NoSecretChatAtAllAndNoWhitelist, text_wordwrap=20, textMayChange=0, text_scale=0.06, text_pos=(0, 0.55))
+ DirectLabel(parent=self.noSecretChatAtAllAndNoWhitelist, relief=None, pos=(0, 0, 0.67), text=OTPLocalizer.NoSecretChatAtAllAndNoWhitelistTitle, textMayChange=0, text_scale=0.08)
+ DirectButton(self.noSecretChatAtAllAndNoWhitelist, image=okButtonImage, relief=None, text=OTPLocalizer.NoSecretChatAtAllOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.64), command=self.__handleNoSecretChatAtAllOK)
+ buttons.removeNode()
+ self.noSecretChatAtAllAndNoWhitelist.show()
+ return
+
+ def exitNoSecretChatAtAllAndNoWhitelist(self):
+ self.noSecretChatAtAllAndNoWhitelist.hide()
+
+ def enterTrueFriendTeaserPanel(self):
+ self.previousStateBeforeTeaser = None
+ place = base.cr.playGame.getPlace()
+ if place:
+ if place.fsm.hasStateNamed('stopped'):
+ self.previousStateBeforeTeaser = place.fsm.getCurrentState().getName()
+ place.fsm.request('stopped')
+ else:
+ self.notify.warning("Enter: %s has no 'stopped' state." % place)
+ self.teaser = TeaserPanel.TeaserPanel(pageName='secretChat', doneFunc=self.handleOkTeaser)
+ return
+
+ def exitTrueFriendTeaserPanel(self):
+ self.teaser.destroy()
+ place = base.cr.playGame.getPlace()
+ if place:
+ if place.fsm.hasStateNamed('stopped'):
+ if self.previousStateBeforeTeaser:
+ place.fsm.request(self.previousStateBeforeTeaser, force=1)
+ else:
+ place.fsm.request('walk')
+ else:
+ self.notify.warning("Exit: %s has no 'stopped' state." % place)
+
+ def handleOkTeaser(self):
+ self.fsm.request('mainMenu')
+
+ def __whisperScButtonPressed(self, avatarName, avatarId, playerId):
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: CHAT: Whisper')
+ messenger.send('wakeup')
+ hasManager = hasattr(base.cr, 'playerFriendsManager')
+ transientFriend = 0
+ if hasManager:
+ transientFriend = base.cr.playerFriendsManager.askTransientFriend(avatarId)
+ if transientFriend:
+ playerId = base.cr.playerFriendsManager.findPlayerIdFromAvId(avatarId)
+ if avatarId and not transientFriend:
+ if self.fsm.getCurrentState().getName() == 'whisperSpeedChat':
+ self.fsm.request('whisper', [avatarName, avatarId, playerId])
+ else:
+ self.fsm.request('whisperSpeedChat', [avatarId])
+ elif playerId:
+ if self.fsm.getCurrentState().getName() == 'whisperSpeedChatPlayer':
+ self.fsm.request('whisper', [avatarName, avatarId, playerId])
+ else:
+ self.fsm.request('whisperSpeedChatPlayer', [playerId])
+
+ def __whisperCancelPressed(self):
+ self.fsm.request('mainMenu')
+
+ def __handleOpenChatWarningOK(self):
+ self.fsm.request('mainMenu')
+
+ def __handleUnpaidChatWarningDone(self):
+ place = base.cr.playGame.getPlace()
+ if place:
+ place.handleBookClose()
+ self.fsm.request('mainMenu')
+
+ def __handleUnpaidChatWarningContinue(self):
+ self.fsm.request('mainMenu')
+
+ def __handleUnpaidChatWarningPay(self):
+ if base.cr.isWebPlayToken():
+ self.fsm.request('leaveToPayDialog')
+ else:
+ self.fsm.request('mainMenu')
+
+ def __handleNoSecretChatAtAllOK(self):
+ self.fsm.request('mainMenu')
+
+ def __handleNoSecretChatWarningOK(self, *args):
+ password = self.passwordEntry.get()
+ tt = base.cr.loginInterface
+ okflag, message = tt.authenticateParentPassword(base.cr.userName, base.cr.password, password)
+ if okflag:
+ self.fsm.request('activateChat')
+ elif message:
+ self.fsm.request('problemActivatingChat')
+ self.problemActivatingChat['text'] = OTPLocalizer.ProblemActivatingChat % message
+ else:
+ self.noSecretChatWarning['text'] = OTPLocalizer.NoSecretChatWarningWrongPassword
+ self.passwordEntry['focus'] = 1
+ self.passwordEntry.enterText('')
+
+ def __handleNoSecretChatWarningCancel(self):
+ self.fsm.request('mainMenu')
+
+ def __handleActivateChatYes(self):
+ password = self.passwordEntry.get()
+ tt = base.cr.loginInterface
+ if self.dcb1['indicatorValue']:
+ base.cr.secretChatAllowed = 0
+ mode = 0
+ elif self.dcb2['indicatorValue']:
+ base.cr.secretChatAllowed = 1
+ base.cr.secretChatNeedsParentPassword = 1
+ mode = 1
+ else:
+ base.cr.secretChatAllowed = 1
+ base.cr.secretChatNeedsParentPassword = 0
+ mode = 2
+ okflag, message = tt.enableSecretFriends(base.cr.userName, base.cr.password, password)
+ if okflag:
+ tt.resendPlayToken()
+ self.fsm.request('secretChatActivated', [mode])
+ else:
+ if message == None:
+ message = 'Parent Password was invalid.'
+ self.fsm.request('problemActivatingChat')
+ self.problemActivatingChat['text'] = OTPLocalizer.ProblemActivatingChat % message
+ return
+
+ def __handleActivateChatMoreInfo(self):
+ self.fsm.request('chatMoreInfo')
+
+ def __handleActivateChatNo(self):
+ self.fsm.request('mainMenu')
+
+ def __handleSecretChatActivatedOK(self):
+ self.fsm.request('mainMenu')
+
+ def __handleSecretChatActivatedChangeOptions(self):
+ self.fsm.request('activateChat')
+
+ def __handleProblemActivatingChatOK(self):
+ self.fsm.request('mainMenu')
+
+ def messageSent(self):
+ pass
+
+ def deactivateChat(self):
+ pass
diff --git a/toontown/chat/__init__.py b/toontown/chat/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/toontown/classicchars/CCharChatter.py b/toontown/classicchars/CCharChatter.py
new file mode 100644
index 0000000..df31d8b
--- /dev/null
+++ b/toontown/classicchars/CCharChatter.py
@@ -0,0 +1,266 @@
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase import ToontownGlobals
+GREETING = 0
+COMMENT = 1
+GOODBYE = 2
+DaisyChatter = TTLocalizer.DaisyChatter
+MickeyChatter = TTLocalizer.MickeyChatter
+VampireMickeyChatter = TTLocalizer.VampireMickeyChatter
+MinnieChatter = TTLocalizer.MinnieChatter
+GoofyChatter = TTLocalizer.GoofyChatter
+GoofySpeedwayChatter = TTLocalizer.GoofySpeedwayChatter
+DonaldChatter = TTLocalizer.DonaldChatter
+ChipChatter = TTLocalizer.ChipChatter
+DaleChatter = TTLocalizer.DaleChatter
+
+def getExtendedChat(chatset, extendedChat):
+ newChat = []
+ for chatList in chatset:
+ newChat.append(list(chatList))
+
+ newChat[1] += extendedChat
+ return newChat
+
+
+def getChatter(charName, chatterType):
+ if charName == TTLocalizer.Mickey:
+ if chatterType == ToontownGlobals.APRIL_FOOLS_COSTUMES:
+ return TTLocalizer.AFMickeyChatter
+ elif chatterType == ToontownGlobals.WINTER_CAROLING:
+ return TTLocalizer.WinterMickeyCChatter
+ elif chatterType == ToontownGlobals.WINTER_DECORATIONS:
+ return TTLocalizer.WinterMickeyDChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_CAROLING:
+ return TTLocalizer.WinterMickeyCChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_DECORATIONS:
+ return TTLocalizer.WinterMickeyDChatter
+ elif chatterType == ToontownGlobals.VALENTINES_DAY:
+ return TTLocalizer.ValentinesMickeyChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_ONE:
+ SillyMickeyChatter = getExtendedChat(MickeyChatter, TTLocalizer.SillyPhase1Chatter)
+ return SillyMickeyChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_TWO:
+ SillyMickeyChatter = getExtendedChat(MickeyChatter, TTLocalizer.SillyPhase2Chatter)
+ return SillyMickeyChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_THREE:
+ SillyMickeyChatter = getExtendedChat(MickeyChatter, TTLocalizer.SillyPhase3Chatter)
+ return SillyMickeyChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_FOUR:
+ SillyMickeyChatter = getExtendedChat(MickeyChatter, TTLocalizer.SillyPhase4Chatter)
+ return SillyMickeyChatter
+ elif chatterType == ToontownGlobals.SELLBOT_FIELD_OFFICE:
+ fieldOfficeMickeyChatter = getExtendedChat(MickeyChatter, TTLocalizer.FieldOfficeMickeyChatter)
+ return fieldOfficeMickeyChatter
+ else:
+ return MickeyChatter
+ elif charName == TTLocalizer.VampireMickey:
+ return VampireMickeyChatter
+ elif charName == TTLocalizer.Minnie:
+ if chatterType == ToontownGlobals.APRIL_FOOLS_COSTUMES:
+ return TTLocalizer.AFMinnieChatter
+ elif chatterType == ToontownGlobals.WINTER_CAROLING:
+ return TTLocalizer.WinterMinnieCChatter
+ elif chatterType == ToontownGlobals.WINTER_DECORATIONS:
+ return TTLocalizer.WinterMinnieDChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_CAROLING:
+ return TTLocalizer.WinterMinnieCChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_DECORATIONS:
+ return TTLocalizer.WinterMinnieDChatter
+ elif chatterType == ToontownGlobals.VALENTINES_DAY:
+ return TTLocalizer.ValentinesMinnieChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_ONE:
+ SillyMinnieChatter = getExtendedChat(MinnieChatter, TTLocalizer.SillyPhase1Chatter)
+ return SillyMinnieChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_TWO:
+ SillyMinnieChatter = getExtendedChat(MinnieChatter, TTLocalizer.SillyPhase2Chatter)
+ return SillyMinnieChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_THREE:
+ SillyMinnieChatter = getExtendedChat(MinnieChatter, TTLocalizer.SillyPhase3Chatter)
+ return SillyMinnieChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_FOUR:
+ SillyMinnieChatter = getExtendedChat(MinnieChatter, TTLocalizer.SillyPhase4Chatter)
+ return SillyMinnieChatter
+ elif chatterType == ToontownGlobals.SELLBOT_FIELD_OFFICE:
+ fieldOfficeMinnieChatter = getExtendedChat(MinnieChatter, TTLocalizer.FieldOfficeMinnieChatter)
+ return fieldOfficeMinnieChatter
+ else:
+ return MinnieChatter
+ elif charName == TTLocalizer.WitchMinnie:
+ return TTLocalizer.WitchMinnieChatter
+ elif charName == TTLocalizer.Daisy or charName == TTLocalizer.SockHopDaisy:
+ if chatterType == ToontownGlobals.APRIL_FOOLS_COSTUMES:
+ return TTLocalizer.AFDaisyChatter
+ elif chatterType == ToontownGlobals.HALLOWEEN_COSTUMES:
+ return TTLocalizer.HalloweenDaisyChatter
+ elif chatterType == ToontownGlobals.SPOOKY_COSTUMES:
+ return TTLocalizer.HalloweenDaisyChatter
+ elif chatterType == ToontownGlobals.WINTER_CAROLING:
+ return TTLocalizer.WinterDaisyCChatter
+ elif chatterType == ToontownGlobals.WINTER_DECORATIONS:
+ return TTLocalizer.WinterDaisyDChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_CAROLING:
+ return TTLocalizer.WinterDaisyCChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_DECORATIONS:
+ return TTLocalizer.WinterDaisyDChatter
+ elif chatterType == ToontownGlobals.VALENTINES_DAY:
+ return TTLocalizer.ValentinesDaisyChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_ONE:
+ SillyDaisyChatter = getExtendedChat(DaisyChatter, TTLocalizer.SillyPhase1Chatter)
+ return SillyDaisyChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_TWO:
+ SillyDaisyChatter = getExtendedChat(DaisyChatter, TTLocalizer.SillyPhase2Chatter)
+ return SillyDaisyChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_THREE:
+ SillyDaisyChatter = getExtendedChat(DaisyChatter, TTLocalizer.SillyPhase3Chatter)
+ return SillyDaisyChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_FOUR:
+ SillyDaisyChatter = getExtendedChat(DaisyChatter, TTLocalizer.SillyPhase4Chatter)
+ return SillyDaisyChatter
+ elif chatterType == ToontownGlobals.SELLBOT_FIELD_OFFICE:
+ fieldOfficeDaisyChatter = getExtendedChat(DaisyChatter, TTLocalizer.FieldOfficeDaisyChatter)
+ return fieldOfficeDaisyChatter
+ else:
+ return DaisyChatter
+ elif charName == TTLocalizer.Goofy:
+ if chatterType == ToontownGlobals.APRIL_FOOLS_COSTUMES:
+ return TTLocalizer.AFGoofySpeedwayChatter
+ elif chatterType == ToontownGlobals.CRASHED_LEADERBOARD:
+ return TTLocalizer.CLGoofySpeedwayChatter
+ elif chatterType == ToontownGlobals.CIRCUIT_RACING_EVENT:
+ return TTLocalizer.GPGoofySpeedwayChatter
+ elif chatterType == ToontownGlobals.WINTER_DECORATIONS or chatterType == ToontownGlobals.WINTER_CAROLING or chatterType == ToontownGlobals.WACKY_WINTER_DECORATIONS or chatterType == ToontownGlobals.WACKY_WINTER_CAROLING:
+ return TTLocalizer.WinterGoofyChatter
+ elif chatterType == ToontownGlobals.VALENTINES_DAY:
+ return TTLocalizer.ValentinesGoofyChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_ONE:
+ SillyGoofySpeedwayChatter = getExtendedChat(GoofySpeedwayChatter, TTLocalizer.SillyPhase1Chatter)
+ return SillyGoofySpeedwayChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_TWO:
+ SillyGoofySpeedwayChatter = getExtendedChat(GoofySpeedwayChatter, TTLocalizer.SillyPhase2Chatter)
+ return SillyGoofySpeedwayChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_THREE:
+ SillyGoofySpeedwayChatter = getExtendedChat(GoofySpeedwayChatter, TTLocalizer.SillyPhase3Chatter)
+ return SillyGoofySpeedwayChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_FOUR:
+ SillyGoofySpeedwayChatter = getExtendedChat(GoofySpeedwayChatter, TTLocalizer.SillyPhase4Chatter)
+ return SillyGoofySpeedwayChatter
+ else:
+ return GoofySpeedwayChatter
+ elif charName == TTLocalizer.SuperGoofy:
+ return TTLocalizer.SuperGoofyChatter
+ elif charName == TTLocalizer.Donald or charName == TTLocalizer.FrankenDonald:
+ if chatterType == ToontownGlobals.APRIL_FOOLS_COSTUMES:
+ return TTLocalizer.AFDonaldChatter
+ elif chatterType == ToontownGlobals.HALLOWEEN_COSTUMES:
+ return TTLocalizer.HalloweenDreamlandChatter
+ elif chatterType == ToontownGlobals.SPOOKY_COSTUMES:
+ return TTLocalizer.HalloweenDreamlandChatter
+ elif chatterType == ToontownGlobals.WINTER_CAROLING:
+ return TTLocalizer.WinterDreamlandCChatter
+ elif chatterType == ToontownGlobals.WINTER_DECORATIONS:
+ return TTLocalizer.WinterDreamlandDChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_CAROLING:
+ return TTLocalizer.WinterDreamlandCChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_DECORATIONS:
+ return TTLocalizer.WinterDreamlandDChatter
+ elif chatterType == ToontownGlobals.VALENTINES_DAY:
+ return TTLocalizer.ValentinesDreamlandChatter
+ elif chatterType == ToontownGlobals.SELLBOT_FIELD_OFFICE:
+ fieldOfficeDreamlandChatter = getExtendedChat(DonaldChatter, TTLocalizer.FieldOfficeDreamlandChatter)
+ return fieldOfficeDreamlandChatter
+ else:
+ return DonaldChatter
+ elif charName == TTLocalizer.DonaldDock:
+ if chatterType == ToontownGlobals.APRIL_FOOLS_COSTUMES:
+ return TTLocalizer.AFDonaldDockChatter
+ elif chatterType == ToontownGlobals.HALLOWEEN_COSTUMES:
+ return TTLocalizer.HalloweenDonaldChatter
+ elif chatterType == ToontownGlobals.SPOOKY_COSTUMES:
+ return TTLocalizer.HalloweenDonaldChatter
+ elif chatterType == ToontownGlobals.WINTER_CAROLING:
+ return TTLocalizer.WinterDonaldCChatter
+ elif chatterType == ToontownGlobals.WINTER_DECORATIONS:
+ return TTLocalizer.WinterDonaldDChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_CAROLING:
+ return TTLocalizer.WinterDonaldCChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_DECORATIONS:
+ return TTLocalizer.WinterDonaldDChatter
+ elif chatterType == ToontownGlobals.VALENTINES_DAY:
+ return TTLocalizer.ValentinesDonaldChatter
+ else:
+ return None
+ elif charName == TTLocalizer.Pluto:
+ if chatterType == ToontownGlobals.APRIL_FOOLS_COSTUMES:
+ return TTLocalizer.AFPlutoChatter
+ elif chatterType == ToontownGlobals.HALLOWEEN_COSTUMES:
+ return TTLocalizer.WesternPlutoChatter
+ elif chatterType == ToontownGlobals.SPOOKY_COSTUMES:
+ return TTLocalizer.WesternPlutoChatter
+ elif chatterType == ToontownGlobals.WINTER_CAROLING:
+ return TTLocalizer.WinterPlutoCChatter
+ elif chatterType == ToontownGlobals.WINTER_DECORATIONS:
+ return TTLocalizer.WinterPlutoDChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_CAROLING:
+ return TTLocalizer.WinterPlutoCChatter
+ elif chatterType == ToontownGlobals.WACKY_WINTER_DECORATIONS:
+ return TTLocalizer.WinterPlutoDChatter
+ else:
+ return None
+ elif charName == TTLocalizer.WesternPluto:
+ if chatterType == ToontownGlobals.HALLOWEEN_COSTUMES:
+ return TTLocalizer.WesternPlutoChatter
+ elif chatterType == ToontownGlobals.SPOOKY_COSTUMES:
+ return TTLocalizer.WesternPlutoChatter
+ else:
+ return None
+ elif charName == TTLocalizer.Chip or charName == TTLocalizer.PoliceChip:
+ if chatterType == ToontownGlobals.APRIL_FOOLS_COSTUMES:
+ return TTLocalizer.AFChipChatter
+ elif chatterType == ToontownGlobals.HALLOWEEN_COSTUMES:
+ return TTLocalizer.HalloweenChipChatter
+ elif chatterType == ToontownGlobals.SPOOKY_COSTUMES:
+ return TTLocalizer.HalloweenChipChatter
+ elif chatterType == ToontownGlobals.WINTER_DECORATIONS or chatterType == ToontownGlobals.WINTER_CAROLING or chatterType == ToontownGlobals.WACKY_WINTER_DECORATIONS or chatterType == ToontownGlobals.WACKY_WINTER_CAROLING:
+ return TTLocalizer.WinterChipChatter
+ elif chatterType == ToontownGlobals.VALENTINES_DAY:
+ return TTLocalizer.ValentinesChipChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_ONE:
+ SillyChipChatter = getExtendedChat(ChipChatter, TTLocalizer.SillyPhase1Chatter)
+ return SillyChipChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_TWO:
+ SillyChipChatter = getExtendedChat(ChipChatter, TTLocalizer.SillyPhase2Chatter)
+ return SillyChipChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_THREE:
+ SillyChipChatter = getExtendedChat(ChipChatter, TTLocalizer.SillyPhase3Chatter)
+ return SillyChipChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_FOUR:
+ SillyChipChatter = getExtendedChat(ChipChatter, TTLocalizer.SillyPhase4Chatter)
+ return SillyChipChatter
+ else:
+ return ChipChatter
+ elif charName == TTLocalizer.Dale or TTLocalizer.JailbirdDale:
+ if chatterType == ToontownGlobals.APRIL_FOOLS_COSTUMES:
+ return TTLocalizer.AFDaleChatter
+ elif chatterType == ToontownGlobals.HALLOWEEN_COSTUMES:
+ return TTLocalizer.HalloweenDaleChatter
+ elif chatterType == ToontownGlobals.SPOOKY_COSTUMES:
+ return TTLocalizer.HalloweenDaleChatter
+ elif chatterType == ToontownGlobals.WINTER_DECORATIONS or chatterType == ToontownGlobals.WINTER_CAROLING or chatterType == ToontownGlobals.WACKY_WINTER_DECORATIONS or chatterType == ToontownGlobals.WACKY_WINTER_CAROLING:
+ return TTLocalizer.WinterDaleChatter
+ elif chatterType == ToontownGlobals.VALENTINES_DAY:
+ return TTLocalizer.ValentinesDaleChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_ONE:
+ SillyDaleChatter = getExtendedChat(DaleChatter, TTLocalizer.SillyPhase1Chatter)
+ return SillyDaleChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_TWO:
+ SillyDaleChatter = getExtendedChat(DaleChatter, TTLocalizer.SillyPhase2Chatter)
+ return SillyDaleChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_THREE:
+ SillyDaleChatter = getExtendedChat(DaleChatter, TTLocalizer.SillyPhase3Chatter)
+ return SillyDaleChatter
+ elif chatterType == ToontownGlobals.SILLY_CHATTER_FOUR:
+ SillyDaleChatter = getExtendedChat(DaleChatter, TTLocalizer.SillyPhase4Chatter)
+ return SillyDaleChatter
+ else:
+ return DaleChatter
+ return None
diff --git a/toontown/classicchars/CCharPaths.py b/toontown/classicchars/CCharPaths.py
new file mode 100644
index 0000000..f69d6ae
--- /dev/null
+++ b/toontown/classicchars/CCharPaths.py
@@ -0,0 +1,459 @@
+from pandac.PandaModules import Point3
+from pandac.PandaModules import Vec3
+import copy
+from toontown.toonbase import TTLocalizer
+__mickeyPaths = {'a': (Point3(17, -17, 4.025), ('b', 'e')),
+ 'b': (Point3(17.5, 7.6, 4.025), ('c', 'e')),
+ 'c': (Point3(85, 11.5, 4.025), ('d',)),
+ 'd': (Point3(85, -13, 4.025), ('a',)),
+ 'e': (Point3(-27.5, -5.25, 0.0), ('a', 'b', 'f')),
+ 'f': (Point3(-106.15, -4.0, -2.5), ('e',
+ 'g',
+ 'h',
+ 'i')),
+ 'g': (Point3(-89.5, 93.5, 0.5), ('f', 'h')),
+ 'h': (Point3(-139.95, 1.69, 0.5), ('f', 'g', 'i')),
+ 'i': (Point3(-110.95, -68.57, 0.5), ('f', 'h'))}
+__mickeyWaypoints = (('a',
+ 'e',
+ 1,
+ []),
+ ('b',
+ 'e',
+ 1,
+ []),
+ ('e',
+ 'f',
+ 1,
+ [Point3(-76.87, -7.85, -1.85), Point3(-80.57, -4.0, -1.85)]),
+ ('f',
+ 'g',
+ 1,
+ [Point3(-106.62, 28.65, -1.5)]),
+ ('g',
+ 'h',
+ 1,
+ [Point3(-128.38, 60.27, 0.5)]),
+ ('h',
+ 'f',
+ 1,
+ []),
+ ('h',
+ 'i',
+ 1,
+ [Point3(-137.13, -42.79, 0.5)]),
+ ('i',
+ 'f',
+ 1,
+ []))
+__minniePaths = {'a': (Point3(53.334, 71.057, 6.525), ('b', 'r')),
+ 'b': (Point3(127.756, 58.665, -11.75), ('a', 's', 'c')),
+ 'c': (Point3(130.325, 15.174, -2.003), ('b', 'd')),
+ 'd': (Point3(126.173, 7.057, 0.522), ('c', 'e')),
+ 'e': (Point3(133.843, -6.618, 4.71), ('d',
+ 'f',
+ 'g',
+ 'h')),
+ 'f': (Point3(116.876, 1.119, 3.304), 'e'),
+ 'g': (Point3(116.271, -41.568, 3.304), ('e', 'h')),
+ 'h': (Point3(128.983, -49.656, -0.231), ('e',
+ 'g',
+ 'i',
+ 'j')),
+ 'i': (Point3(106.024, -75.249, -4.498), 'h'),
+ 'j': (Point3(135.016, -93.072, -13.376), ('h', 'k', 'z')),
+ 'k': (Point3(123.966, -100.242, -10.879), ('j', 'l')),
+ 'l': (Point3(52.859, -109.081, 6.525), ('k', 'm')),
+ 'm': (Point3(-32.071, -107.049, 6.525), ('l', 'n')),
+ 'n': (Point3(-40.519, -99.685, 6.525), ('m', 'o')),
+ 'o': (Point3(-40.245, -88.634, 6.525), ('n', 'p')),
+ 'p': (Point3(-66.3, -62.192, 6.525), ('o', 'q')),
+ 'q': (Point3(-66.212, 23.069, 6.525), ('p', 'r')),
+ 'r': (Point3(-18.344, 69.532, 6.525), ('q', 'a')),
+ 's': (Point3(91.357, 44.546, -13.475), ('b', 't')),
+ 't': (Point3(90.355, 6.279, -13.475), ('s', 'u')),
+ 'u': (Point3(-13.765, 42.362, -14.553), ('t', 'v')),
+ 'v': (Point3(-52.627, 7.428, -14.553), ('u', 'w')),
+ 'w': (Point3(-50.654, -54.879, -14.553), ('v', 'x')),
+ 'x': (Point3(-3.711, -81.819, -14.553), ('w', 'y')),
+ 'y': (Point3(90.777, -49.714, -13.475), ('z', 'x')),
+ 'z': (Point3(90.059, -79.426, -13.475), ('j', 'y'))}
+__minnieWaypoints = (('a',
+ 'b',
+ 1,
+ []),
+ ('k',
+ 'l',
+ 1,
+ []),
+ ('b',
+ 'c',
+ 1,
+ []),
+ ('c',
+ 'd',
+ 1,
+ []),
+ ('d',
+ 'e',
+ 1,
+ []),
+ ('e',
+ 'f',
+ 1,
+ []),
+ ('e',
+ 'g',
+ 1,
+ []),
+ ('e',
+ 'h',
+ 1,
+ []),
+ ('g',
+ 'h',
+ 1,
+ []),
+ ('h',
+ 'i',
+ 1,
+ []),
+ ('h',
+ 'j',
+ 1,
+ []),
+ ('s',
+ 'b',
+ 1,
+ []),
+ ('t',
+ 'u',
+ 1,
+ []),
+ ('x',
+ 'y',
+ 1,
+ []))
+__goofyPaths = {'a': (Point3(64.995, 169.665, 10.027), ('b', 'q')),
+ 'b': (Point3(48.893, 208.912, 10.027), ('a', 'c')),
+ 'c': (Point3(5.482, 210.479, 10.03), ('b', 'd')),
+ 'd': (Point3(-34.153, 203.284, 10.029), ('c', 'e')),
+ 'e': (Point3(-66.656, 174.334, 10.026), ('d', 'f')),
+ 'f': (Point3(-55.994, 162.33, 10.026), ('e', 'g')),
+ 'g': (Point3(-84.554, 142.099, 0.027), ('f', 'h')),
+ 'h': (Point3(-92.215, 96.446, 0.027), ('g', 'i')),
+ 'i': (Point3(-63.168, 60.055, 0.027), ('h', 'j')),
+ 'j': (Point3(-37.637, 69.974, 0.027), ('i', 'k')),
+ 'k': (Point3(-3.018, 26.157, 0.027), ('j', 'l', 'm')),
+ 'l': (Point3(-0.711, 46.843, 0.027), 'k'),
+ 'm': (Point3(26.071, 46.401, 0.027), ('k', 'n')),
+ 'n': (Point3(30.87, 67.432, 0.027), ('m', 'o')),
+ 'o': (Point3(93.903, 90.685, 0.027), ('n', 'p')),
+ 'p': (Point3(88.129, 140.575, 0.027), ('o', 'q')),
+ 'q': (Point3(53.988, 158.232, 10.027), ('p', 'a'))}
+__goofyWaypoints = (('f',
+ 'g',
+ 1,
+ []), ('p',
+ 'q',
+ 1,
+ []))
+__goofySpeedwayPaths = {'a': (Point3(-9.0, -19.517, -0.323), ('b', 'k')),
+ 'b': (Point3(-30.047, -1.578, -0.373), ('a', 'c')),
+ 'c': (Point3(-10.367, 49.042, -0.373), ('b', 'd')),
+ 'd': (Point3(38.439, 44.348, -0.373), ('c', 'e')),
+ 'e': (Point3(25.527, -2.395, -0.373), ('d', 'f')),
+ 'f': (Point3(-4.043, -59.865, -0.003), ('e', 'g')),
+ 'g': (Point3(0.39, -99.475, -0.009), ('f', 'h')),
+ 'h': (Point3(21.147, -109.127, -0.013), ('g', 'i')),
+ 'i': (Point3(5.981, -147.606, -0.013), ('h', 'j')),
+ 'j': (Point3(-24.898, -120.618, -0.013), ('i', 'k')),
+ 'k': (Point3(-2.71, -90.315, -0.011), ('j', 'a'))}
+__goofySpeedwayWaypoints = (('a',
+ 'k',
+ 1,
+ []), ('k',
+ 'a',
+ 1,
+ []))
+__donaldPaths = {'a': (Point3(-94.883, -94.024, 0.025), 'b'),
+ 'b': (Point3(-13.962, -92.233, 0.025), ('a', 'h')),
+ 'c': (Point3(68.417, -91.929, 0.025), ('m', 'g')),
+ 'd': (Point3(68.745, 91.227, 0.025), ('k', 'i')),
+ 'e': (Point3(4.047, 94.26, 0.025), ('i', 'j')),
+ 'f': (Point3(-91.271, 90.987, 0.025), 'j'),
+ 'g': (Point3(43.824, -94.129, 0.025), ('c', 'h')),
+ 'h': (Point3(13.905, -91.334, 0.025), ('b', 'g')),
+ 'i': (Point3(43.062, 88.152, 0.025), ('d', 'e')),
+ 'j': (Point3(-48.96, 88.565, 0.025), ('e', 'f')),
+ 'k': (Point3(75.118, 52.84, -16.62), ('d', 'l')),
+ 'l': (Point3(44.677, 27.091, -15.385), ('k', 'm')),
+ 'm': (Point3(77.009, -16.022, -14.975), ('l', 'c'))}
+__donaldWaypoints = (('d',
+ 'k',
+ 1,
+ []),
+ ('k',
+ 'l',
+ 1,
+ []),
+ ('l',
+ 'm',
+ 1,
+ []),
+ ('m',
+ 'c',
+ 1,
+ []),
+ ('b',
+ 'a',
+ 1,
+ [Point3(-55.883, -89.0, 0.025)]))
+__plutoPaths = {'a': (Point3(-110.0, -37.8, 8.6), ('b', 'c')),
+ 'b': (Point3(-11.9, -128.2, 6.2), ('a', 'c')),
+ 'c': (Point3(48.9, -14.4, 6.2), ('b', 'a', 'd')),
+ 'd': (Point3(0.25, 80.5, 6.2), ('c', 'e')),
+ 'e': (Point3(-83.3, 36.1, 6.2), ('d', 'a'))}
+__plutoWaypoints = (('a',
+ 'b',
+ 1,
+ [Point3(-90.4, -57.2, 3.0), Point3(-63.6, -79.8, 3.0), Point3(-50.1, -89.1, 6.2)]),
+ ('c',
+ 'a',
+ 1,
+ [Point3(-15.6, -25.6, 6.2),
+ Point3(-37.5, -38.5, 3.0),
+ Point3(-55.0, -55.0, 3.0),
+ Point3(-85.0, -46.4, 3.0)]),
+ ('d',
+ 'e',
+ 0,
+ [Point3(-25.8, 60.0, 6.2), Point3(-61.9, 64.5, 6.2)]),
+ ('e',
+ 'a',
+ 1,
+ [Point3(-77.2, 28.5, 6.2), Point3(-76.4, 12.0, 3.0), Point3(-93.2, -21.2, 3.0)]))
+__daisyPaths = {'a': (Point3(64.995, 169.665, 10.027), ('b', 'q')),
+ 'b': (Point3(48.893, 208.912, 10.027), ('a', 'c')),
+ 'c': (Point3(5.482, 210.479, 10.03), ('b', 'd')),
+ 'd': (Point3(-34.153, 203.284, 10.029), ('c', 'e')),
+ 'e': (Point3(-66.656, 174.334, 10.026), ('d', 'f')),
+ 'f': (Point3(-55.994, 162.33, 10.026), ('e', 'g')),
+ 'g': (Point3(-84.554, 142.099, 0.027), ('f', 'h')),
+ 'h': (Point3(-92.215, 96.446, 0.027), ('g', 'i')),
+ 'i': (Point3(-63.168, 60.055, 0.027), ('h', 'j')),
+ 'j': (Point3(-37.637, 69.974, 0.027), ('i', 'k')),
+ 'k': (Point3(-3.018, 26.157, 0.027), ('j', 'l', 'm')),
+ 'l': (Point3(-0.711, 46.843, 0.027), 'k'),
+ 'm': (Point3(26.071, 46.401, 0.027), ('k', 'n')),
+ 'n': (Point3(30.87, 67.432, 0.027), ('m', 'o')),
+ 'o': (Point3(93.903, 90.685, 0.027), ('n', 'p')),
+ 'p': (Point3(88.129, 140.575, 0.027), ('o', 'q')),
+ 'q': (Point3(53.988, 158.232, 10.027), ('p', 'a'))}
+__daisyWaypoints = (('f',
+ 'g',
+ 1,
+ []), ('p',
+ 'q',
+ 1,
+ []))
+__chipPaths = {'a': (Point3(50.004, 102.725, 0.6), ('b', 'k')),
+ 'b': (Point3(-29.552, 112.531, 0.6), ('c', 'a')),
+ 'c': (Point3(-51.941, 146.155, 0.025), ('d', 'b')),
+ 'd': (Point3(-212.334, -3.639, 0.025), ('e', 'c')),
+ 'e': (Point3(-143.466, -67.526, 0.025), ('f', 'd', 'i')),
+ 'f': (Point3(-107.556, -62.257, 0.025), ('g', 'e', 'j')),
+ 'g': (Point3(-43.103, -71.518, 0.2734), ('h', 'f', 'j')),
+ 'h': (Point3(-40.605, -125.124, 0.025), ('i', 'g')),
+ 'i': (Point3(-123.05, -124.542, 0.025), ('h', 'e')),
+ 'j': (Point3(-40.092, 2.784, 1.268), ('k',
+ 'b',
+ 'f',
+ 'g')),
+ 'k': (Point3(75.295, 26.715, 1.4), ('a', 'j'))}
+__chipWaypoints = (('a',
+ 'b',
+ 1,
+ []),
+ ('a',
+ 'k',
+ 1,
+ []),
+ ('b',
+ 'c',
+ 1,
+ []),
+ ('b',
+ 'j',
+ 1,
+ []),
+ ('c',
+ 'd',
+ 1,
+ []),
+ ('d',
+ 'e',
+ 1,
+ []),
+ ('e',
+ 'f',
+ 1,
+ []),
+ ('e',
+ 'i',
+ 1,
+ []),
+ ('f',
+ 'g',
+ 1,
+ []),
+ ('f',
+ 'j',
+ 1,
+ []),
+ ('g',
+ 'h',
+ 1,
+ []),
+ ('g',
+ 'j',
+ 1,
+ []),
+ ('h',
+ 'i',
+ 1,
+ []),
+ ('j',
+ 'k',
+ 1,
+ []))
+DaleOrbitDistanceOverride = {('b', 'c'): 2.5,
+ ('e', 'f'): 2.5}
+startNode = 'a'
+
+def getPaths(charName, location = 0):
+ if charName == TTLocalizer.Mickey:
+ return __mickeyPaths
+ elif charName == TTLocalizer.VampireMickey:
+ return __mickeyPaths
+ elif charName == TTLocalizer.Minnie:
+ return __minniePaths
+ elif charName == TTLocalizer.WitchMinnie:
+ return __minniePaths
+ elif charName == TTLocalizer.Daisy or charName == TTLocalizer.SockHopDaisy:
+ return __daisyPaths
+ elif charName == TTLocalizer.Goofy:
+ if location == 0:
+ return __goofyPaths
+ else:
+ return __goofySpeedwayPaths
+ elif charName == TTLocalizer.SuperGoofy:
+ return __goofySpeedwayPaths
+ elif charName == TTLocalizer.Donald or charName == TTLocalizer.FrankenDonald:
+ return __donaldPaths
+ elif charName == TTLocalizer.Pluto:
+ return __plutoPaths
+ elif charName == TTLocalizer.WesternPluto:
+ return __plutoPaths
+ elif charName == TTLocalizer.Chip or charName == TTLocalizer.PoliceChip:
+ return __chipPaths
+ elif charName == TTLocalizer.Dale or charName == TTLocalizer.JailbirdDale:
+ return __chipPaths
+ elif charName == TTLocalizer.DonaldDock:
+ return {'a': (Point3(0, 0, 0), 'a')}
+
+
+def __getWaypointList(paths):
+ if paths == __mickeyPaths:
+ return __mickeyWaypoints
+ elif paths == __minniePaths:
+ return __minnieWaypoints
+ elif paths == __daisyPaths:
+ return __daisyWaypoints
+ elif paths == __goofyPaths:
+ return __goofyWaypoints
+ elif paths == __goofySpeedwayPaths:
+ return __goofySpeedwayWaypoints
+ elif paths == __donaldPaths:
+ return __donaldWaypoints
+ elif paths == __plutoPaths:
+ return __plutoWaypoints
+ elif paths == __chipPaths:
+ return __chipWaypoints
+ elif paths == __dalePaths:
+ return __chipWaypoints
+
+
+def getNodePos(node, paths):
+ return paths[node][0]
+
+
+def getAdjacentNodes(node, paths):
+ return paths[node][1]
+
+
+def getWayPoints(fromNode, toNode, paths, wpts = None):
+ list = []
+ if fromNode != toNode:
+ if wpts == None:
+ wpts = __getWaypointList(paths)
+ for path in wpts:
+ if path[0] == fromNode and path[1] == toNode:
+ for point in path[3]:
+ list.append(Point3(point))
+
+ break
+ elif path[0] == toNode and path[1] == fromNode:
+ for point in path[3]:
+ list = [Point3(point)] + list
+
+ break
+
+ return list
+
+
+def getRaycastFlag(fromNode, toNode, paths):
+ result = 0
+ if fromNode != toNode:
+ wpts = __getWaypointList(paths)
+ for path in wpts:
+ if path[0] == fromNode and path[1] == toNode:
+ if path[2]:
+ result = 1
+ break
+ elif path[0] == toNode and path[1] == fromNode:
+ if path[2]:
+ result = 1
+ break
+
+ return result
+
+
+def getPointsFromTo(fromNode, toNode, paths):
+ startPoint = Point3(getNodePos(fromNode, paths))
+ endPoint = Point3(getNodePos(toNode, paths))
+ return [startPoint] + getWayPoints(fromNode, toNode, paths) + [endPoint]
+
+
+def getWalkDuration(fromNode, toNode, velocity, paths):
+ posPoints = getPointsFromTo(fromNode, toNode, paths)
+ duration = 0
+ for pointIndex in range(len(posPoints) - 1):
+ startPoint = posPoints[pointIndex]
+ endPoint = posPoints[pointIndex + 1]
+ distance = Vec3(endPoint - startPoint).length()
+ duration += distance / velocity
+
+ return duration
+
+
+def getWalkDistance(fromNode, toNode, velocity, paths):
+ posPoints = getPointsFromTo(fromNode, toNode, paths)
+ retval = 0
+ for pointIndex in range(len(posPoints) - 1):
+ startPoint = posPoints[pointIndex]
+ endPoint = posPoints[pointIndex + 1]
+ distance = Vec3(endPoint - startPoint).length()
+ retval += distance
+
+ return retval
diff --git a/toontown/classicchars/CharStateDatas.py b/toontown/classicchars/CharStateDatas.py
new file mode 100644
index 0000000..7c446ec
--- /dev/null
+++ b/toontown/classicchars/CharStateDatas.py
@@ -0,0 +1,225 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from direct.distributed.ClockDelta import *
+from direct.fsm import StateData
+from direct.directnotify import DirectNotifyGlobal
+from direct.showbase.PythonUtil import *
+from direct.task import Task
+import CCharPaths
+from toontown.toonbase import ToontownGlobals
+
+class CharNeutralState(StateData.StateData):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CharNeutralState')
+
+ def __init__(self, doneEvent, character):
+ StateData.StateData.__init__(self, doneEvent)
+ self.__doneEvent = doneEvent
+ self.character = character
+
+ def enter(self, startTrack = None, playRate = None):
+ StateData.StateData.enter(self)
+ self.notify.debug('Neutral ' + self.character.getName() + '...')
+ self.__neutralTrack = Sequence(name=self.character.getName() + '-neutral')
+ if startTrack:
+ self.__neutralTrack.append(startTrack)
+ if playRate:
+ self.__neutralTrack.append(Func(self.character.setPlayRate, playRate, 'neutral'))
+ self.__neutralTrack.append(Func(self.character.loop, 'neutral'))
+ self.__neutralTrack.start()
+
+ def exit(self):
+ StateData.StateData.exit(self)
+ self.__neutralTrack.finish()
+
+ def __doneHandler(self):
+ doneStatus = {}
+ doneStatus['state'] = 'walk'
+ doneStatus['status'] = 'done'
+ messenger.send(self.__doneEvent, [doneStatus])
+ return Task.done
+
+
+class CharWalkState(StateData.StateData):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CharWalkState')
+
+ def __init__(self, doneEvent, character, diffPath = None):
+ StateData.StateData.__init__(self, doneEvent)
+ self.doneEvent = doneEvent
+ self.character = character
+ if diffPath == None:
+ self.paths = CCharPaths.getPaths(character.getName(), character.getCCLocation())
+ else:
+ self.paths = CCharPaths.getPaths(diffPath, character.getCCLocation())
+ self.speed = character.walkSpeed()
+ self.offsetX = 0
+ self.offsetY = 0
+ self.oldOffsetX = 0
+ self.olfOffsetY = 0
+ self.walkTrack = None
+ return
+
+ def enter(self, startTrack = None, playRate = None):
+ StateData.StateData.enter(self)
+ self.notify.debug('Walking ' + self.character.getName() + '... from ' + str(self.walkInfo[0]) + ' to ' + str(self.walkInfo[1]))
+ posPoints = CCharPaths.getPointsFromTo(self.walkInfo[0], self.walkInfo[1], self.paths)
+ lastPos = posPoints[-1]
+ newLastPos = Point3(lastPos[0] + self.offsetX, lastPos[1] + self.offsetY, lastPos[2])
+ posPoints[-1] = newLastPos
+ firstPos = posPoints[0]
+ newFirstPos = Point3(firstPos[0] + self.oldOffsetX, firstPos[1] + self.oldOffsetY, firstPos[2])
+ posPoints[0] = newFirstPos
+ self.walkTrack = Sequence(name=self.character.getName() + '-walk')
+ if startTrack:
+ self.walkTrack.append(startTrack)
+ self.character.setPos(posPoints[0])
+ raycast = CCharPaths.getRaycastFlag(self.walkInfo[0], self.walkInfo[1], self.paths)
+ moveTrack = self.makePathTrack(self.character, posPoints, self.speed, raycast)
+ if playRate:
+ self.walkTrack.append(Func(self.character.setPlayRate, playRate, 'walk'))
+ self.walkTrack.append(Func(self.character.loop, 'walk'))
+ self.walkTrack.append(moveTrack)
+ doneEventName = self.character.getName() + 'WalkDone'
+ self.walkTrack.append(Func(messenger.send, doneEventName))
+ ts = globalClockDelta.localElapsedTime(self.walkInfo[2])
+ self.accept(doneEventName, self.doneHandler)
+ self.notify.debug('walkTrack.start(%s)' % ts)
+ self.walkTrack.start(ts)
+
+ def makePathTrack(self, nodePath, posPoints, velocity, raycast = 0):
+ track = Sequence()
+ if raycast:
+ track.append(Func(nodePath.enableRaycast, 1))
+ startHpr = nodePath.getHpr()
+ for pointIndex in range(len(posPoints) - 1):
+ startPoint = posPoints[pointIndex]
+ endPoint = posPoints[pointIndex + 1]
+ track.append(Func(nodePath.setPos, startPoint))
+ distance = Vec3(endPoint - startPoint).length()
+ duration = distance / velocity
+ curHpr = nodePath.getHpr()
+ nodePath.headsUp(endPoint[0], endPoint[1], endPoint[2])
+ destHpr = nodePath.getHpr()
+ reducedCurH = reduceAngle(curHpr[0])
+ reducedCurHpr = Vec3(reducedCurH, curHpr[1], curHpr[2])
+ reducedDestH = reduceAngle(destHpr[0])
+ shortestAngle = closestDestAngle(reducedCurH, reducedDestH)
+ shortestHpr = Vec3(shortestAngle, destHpr[1], destHpr[2])
+ turnTime = abs(shortestAngle) / 270.0
+ nodePath.setHpr(shortestHpr)
+ if duration - turnTime > 0.01:
+ track.append(Parallel(Func(nodePath.loop, 'walk'), LerpHprInterval(nodePath, turnTime, shortestHpr, startHpr=reducedCurHpr, name='lerp' + nodePath.getName() + 'Hpr'), LerpPosInterval(nodePath, duration=duration - turnTime, pos=Point3(endPoint), startPos=Point3(startPoint), fluid=1)))
+
+ nodePath.setHpr(startHpr)
+ if raycast:
+ track.append(Func(nodePath.enableRaycast, 0))
+ return track
+
+ def doneHandler(self):
+ doneStatus = {}
+ doneStatus['state'] = 'walk'
+ doneStatus['status'] = 'done'
+ messenger.send(self.doneEvent, [doneStatus])
+ return Task.done
+
+ def exit(self):
+ StateData.StateData.exit(self)
+ self.ignore(self.character.getName() + 'WalkDone')
+ if self.walkTrack:
+ self.walkTrack.finish()
+ self.walkTrack = None
+ return
+
+ def setWalk(self, srcNode, destNode, timestamp, offsetX = 0, offsetY = 0):
+ self.oldOffsetX = self.offsetX
+ self.oldOffsetY = self.offsetY
+ self.walkInfo = (srcNode, destNode, timestamp)
+ self.offsetX = offsetX
+ self.offsetY = offsetY
+
+
+class CharFollowChipState(CharWalkState):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CharFollowChipState')
+ completeRevolutionDistance = 13
+
+ def __init__(self, doneEvent, character, chipId):
+ CharWalkState.__init__(self, doneEvent, character)
+ self.offsetDict = {'a': (ToontownGlobals.DaleOrbitDistance, 0)}
+ self.chipId = chipId
+
+ def setWalk(self, srcNode, destNode, timestamp, offsetX = 0, offsetY = 0):
+ self.offsetDict[destNode] = (offsetX, offsetY)
+ self.srcNode = srcNode
+ self.destNode = destNode
+ self.orbitDistance = ToontownGlobals.DaleOrbitDistance
+ if (srcNode, destNode) in CCharPaths.DaleOrbitDistanceOverride:
+ self.orbitDistance = CCharPaths.DaleOrbitDistanceOverride[srcNode, destNode]
+ elif (destNode, srcNode) in CCharPaths.DaleOrbitDistanceOverride:
+ self.orbitDistance = CCharPaths.DaleOrbitDistanceOverride[destNode, srcNode]
+ CharWalkState.setWalk(self, srcNode, destNode, timestamp, offsetX, offsetY)
+
+ def makePathTrack(self, nodePath, posPoints, velocity, raycast = 0):
+ retval = Sequence()
+ if raycast:
+ retval.append(Func(nodePath.enableRaycast, 1))
+ chip = base.cr.doId2do.get(self.chipId)
+ self.chipPaths = CCharPaths.getPaths(chip.getName(), chip.getCCLocation())
+ self.posPoints = posPoints
+ chipDuration = chip.walk.walkTrack.getDuration()
+ self.notify.debug('chipDuration = %f' % chipDuration)
+ chipDistance = CCharPaths.getWalkDistance(self.srcNode, self.destNode, ToontownGlobals.ChipSpeed, self.chipPaths)
+ self.revolutions = chipDistance / self.completeRevolutionDistance
+ srcOffset = (0, 0)
+ if self.srcNode in self.offsetDict:
+ srcOffset = self.offsetDict[self.srcNode]
+ srcTheta = math.atan2(srcOffset[1], srcOffset[0])
+ if srcTheta < 0:
+ srcTheta += 2 * math.pi
+ if srcTheta > 0:
+ srcRev = (2 * math.pi - srcTheta) / (2 * math.pi)
+ else:
+ srcRev = 0
+ self.srcTheta = srcTheta
+ destOffset = (0, 0)
+ if self.destNode in self.offsetDict:
+ destOffset = self.offsetDict[self.destNode]
+ destTheta = math.atan2(destOffset[1], destOffset[0])
+ if destTheta < 0:
+ destTheta += 2 * math.pi
+ self.destTheta = destTheta
+ self.revolutions += srcRev
+ endingTheta = srcTheta + self.revolutions % 1.0 * 2 * math.pi
+ diffTheta = destTheta - endingTheta
+ destRev = diffTheta / (2 * math.pi)
+ self.revolutions += destRev
+ while self.revolutions < 1:
+ self.revolutions += 1
+
+ def positionDale(t):
+ self.orbitChip(t)
+
+ retval.append(LerpFunctionInterval(positionDale, chipDuration))
+ if raycast:
+ retval.append(Func(nodePath.enableRaycast, 0))
+ return retval
+
+ def orbitChip(self, t):
+ srcOffset = (0, 0)
+ if self.srcNode in self.offsetDict:
+ srcOffset = self.offsetDict[self.srcNode]
+ chipSrcPos = Point3(self.posPoints[0][0] - srcOffset[0], self.posPoints[0][1] - srcOffset[1], self.posPoints[0][2])
+ destOffset = (0, 0)
+ if self.destNode in self.offsetDict:
+ destOffset = self.offsetDict[self.destNode]
+ chipDestPos = Point3(self.posPoints[-1][0] - destOffset[0], self.posPoints[-1][1] - destOffset[1], self.posPoints[-1][2])
+ displacement = chipDestPos - chipSrcPos
+ displacement *= t
+ chipPos = chipSrcPos + displacement
+ diffTheta = t * self.revolutions * 2 * math.pi
+ curTheta = self.srcTheta + diffTheta
+ newOffsetX = math.cos(curTheta) * self.orbitDistance
+ newOffsetY = math.sin(curTheta) * self.orbitDistance
+ dalePos = Point3(chipPos[0] + newOffsetX, chipPos[1] + newOffsetY, chipPos[2])
+ self.character.setPos(dalePos)
+ newHeading = rad2Deg(curTheta)
+ newHeading %= 360
+ self.character.setH(newHeading)
diff --git a/toontown/classicchars/CharStateDatasAI.py b/toontown/classicchars/CharStateDatasAI.py
new file mode 100644
index 0000000..508d474
--- /dev/null
+++ b/toontown/classicchars/CharStateDatasAI.py
@@ -0,0 +1,326 @@
+from otp.ai.AIBaseGlobal import *
+from direct.distributed.ClockDelta import *
+from direct.fsm import StateData
+from direct.directnotify import DirectNotifyGlobal
+import random
+from direct.task import Task
+from toontown.toonbase import ToontownGlobals
+import CCharChatter, CCharPaths
+CHATTY_DURATION = 120.0
+
+class CharLonelyStateAI(StateData.StateData):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CharLonelyStateAI')
+
+ def __init__(self, doneEvent, character):
+ StateData.StateData.__init__(self, doneEvent)
+ self.__doneEvent = doneEvent
+ self.character = character
+
+ def enter(self):
+ if hasattr(self.character, 'name'):
+ name = self.character.getName()
+ else:
+ name = 'character'
+ self.notify.debug('Lonely ' + self.character.getName() + '...')
+ StateData.StateData.enter(self)
+ duration = random.randint(3, 15)
+ taskMgr.doMethodLater(duration, self.__doneHandler, self.character.taskName('startWalking'))
+
+ def exit(self):
+ StateData.StateData.exit(self)
+ taskMgr.remove(self.character.taskName('startWalking'))
+
+ def __doneHandler(self, task):
+ doneStatus = {}
+ doneStatus['state'] = 'lonely'
+ doneStatus['status'] = 'done'
+ messenger.send(self.__doneEvent, [doneStatus])
+ return Task.done
+
+
+class CharChattyStateAI(StateData.StateData):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CharChattyStateAI')
+ notify.setDebug(True)
+
+ def __init__(self, doneEvent, character):
+ StateData.StateData.__init__(self, doneEvent)
+ self.__doneEvent = doneEvent
+ self.character = character
+ self.__chatTaskName = 'characterChat-' + str(character)
+ self.lastChatTarget = 0
+ self.nextChatTime = 0
+ self.lastMessage = [
+ -1, -1]
+
+ def enter(self):
+ if hasattr(self.character, 'name'):
+ name = self.character.getName()
+ else:
+ name = 'character'
+ self.notify.debug('Chatty ' + self.character.getName() + '...')
+ self.chatter = CCharChatter.getChatter(self.character.getName(), self.character.getCCChatter())
+ if self.chatter != None:
+ taskMgr.remove(self.__chatTaskName)
+ taskMgr.add(self.blather, self.__chatTaskName)
+ else:
+ self.notify.debug('Chatter is none.. sending done message')
+ self.leave(timeout=1)
+ StateData.StateData.enter(self)
+ return
+
+ def pickMsg(self, category):
+ self.getLatestChatter()
+ if self.chatter:
+ return random.randint(0, len(self.chatter[category]) - 1)
+ else:
+ return None
+ return None
+
+ def getLatestChatter(self):
+ self.chatter = CCharChatter.getChatter(self.character.getName(), self.character.getCCChatter())
+
+ def setCorrectChatter(self):
+ self.chatter = CCharChatter.getChatter(self.character.getName(), self.character.getCCChatter())
+
+ def blather(self, task):
+ now = globalClock.getFrameTime()
+ if now < self.nextChatTime:
+ return Task.cont
+ self.getLatestChatter()
+ if self.character.lostInterest():
+ self.leave()
+ return Task.done
+ if task.time > CHATTY_DURATION:
+ self.leave(timeout=1)
+ return Task.done
+ if not self.chatter:
+ self.notify.debug('Chatter doesnt exist')
+ self.leave(timeout=1)
+ return Task.done
+ if not self.character.getNearbyAvatars():
+ return Task.cont
+ target = self.character.getNearbyAvatars()[0]
+ if self.lastChatTarget != target:
+ self.lastChatTarget = target
+ category = CCharChatter.GREETING
+ else:
+ category = CCharChatter.COMMENT
+ self.setCorrectChatter()
+ if category == self.lastMessage[0] and len(self.chatter[category]) > 1:
+ msg = self.lastMessage[1]
+ lastMsgIndex = self.lastMessage[1]
+ if lastMsgIndex < len(self.chatter[category]) and lastMsgIndex >= 0:
+ while self.chatter[category][msg] == self.chatter[category][lastMsgIndex]:
+ msg = self.pickMsg(category)
+ if not msg:
+ break
+
+ else:
+ msg = self.pickMsg(category)
+ else:
+ msg = self.pickMsg(category)
+ if msg == None:
+ self.notify.debug('Cannot pick a message')
+ self.leave(timeout=1)
+ return Task.done
+ self.character.sendUpdate('setChat', [category, msg, target])
+ self.lastMessage = [
+ category, msg]
+ self.nextChatTime = now + 8.0 + random.random() * 4.0
+ return Task.cont
+
+ def leave(self, timeout=0):
+ if self.chatter != None and not timeout:
+ category = CCharChatter.GOODBYE
+ msg = random.randint(0, len(self.chatter[CCharChatter.GOODBYE]) - 1)
+ if len(self.character.getNearbyAvatars()) > 0:
+ target = self.character.getNearbyAvatars()[0]
+ self.character.sendUpdate('setChat', [category, msg, target])
+ else:
+ self.notify.warning('Nearby avatars left')
+ if timeout == 1:
+ self.notify.debug('We were stuck in the chatty state')
+ taskMgr.doMethodLater(1, self.doneHandler, self.character.taskName('waitToFinish'))
+ return
+
+ def exit(self):
+ StateData.StateData.exit(self)
+ taskMgr.remove(self.__chatTaskName)
+
+ def doneHandler(self, task):
+ doneStatus = {}
+ doneStatus['state'] = 'chatty'
+ doneStatus['status'] = 'done'
+ messenger.send(self.__doneEvent, [doneStatus])
+ return Task.done
+
+
+class CharWalkStateAI(StateData.StateData):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CharWalkStateAI')
+
+ def __init__(self, doneEvent, character, diffPath=None):
+ StateData.StateData.__init__(self, doneEvent)
+ self.__doneEvent = doneEvent
+ self.character = character
+ if diffPath == None:
+ self.paths = CCharPaths.getPaths(character.getName(), character.getCCLocation())
+ else:
+ self.paths = CCharPaths.getPaths(diffPath, character.getCCLocation())
+ self.speed = character.walkSpeed()
+ self.__lastWalkNode = CCharPaths.startNode
+ self.__curWalkNode = CCharPaths.startNode
+ return
+
+ def enter(self):
+ destNode = self.__lastWalkNode
+ choices = CCharPaths.getAdjacentNodes(self.__curWalkNode, self.paths)
+ if len(choices) == 1:
+ destNode = choices[0]
+ else:
+ while destNode == self.__lastWalkNode:
+ destNode = random.choice(CCharPaths.getAdjacentNodes(self.__curWalkNode, self.paths))
+
+ self.notify.debug('Walking ' + self.character.getName() + '... from ' + str(self.__curWalkNode) + '(' + str(CCharPaths.getNodePos(self.__curWalkNode, self.paths)) + ') to ' + str(destNode) + '(' + str(CCharPaths.getNodePos(destNode, self.paths)) + ')')
+ self.character.sendUpdate('setWalk', [self.__curWalkNode, destNode, globalClockDelta.getRealNetworkTime()])
+ duration = CCharPaths.getWalkDuration(self.__curWalkNode, destNode, self.speed, self.paths)
+ t = taskMgr.doMethodLater(duration, self.doneHandler, self.character.taskName(self.character.getName() + 'DoneWalking'))
+ t.newWalkNode = destNode
+ self.destNode = destNode
+
+ def exit(self):
+ StateData.StateData.exit(self)
+ taskMgr.remove(self.character.taskName(self.character.getName() + 'DoneWalking'))
+
+ def getDestNode(self):
+ if hasattr(self, 'destNode') and self.destNode:
+ return self.destNode
+ else:
+ return self.__curWalkNode
+
+ def setCurNode(self, curWalkNode):
+ self.__curWalkNode = curWalkNode
+
+ def doneHandler(self, task):
+ self.__lastWalkNode = self.__curWalkNode
+ self.__curWalkNode = task.newWalkNode
+ self.character.sendUpdate('setWalk', [self.__curWalkNode, self.__curWalkNode, globalClockDelta.getRealNetworkTime()])
+ doneStatus = {}
+ doneStatus['state'] = 'walk'
+ doneStatus['status'] = 'done'
+ messenger.send(self.__doneEvent, [doneStatus])
+ return Task.done
+
+
+class CharFollowChipStateAI(StateData.StateData):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CharFollowChipStateAI')
+
+ def __init__(self, doneEvent, character, followedChar):
+ StateData.StateData.__init__(self, doneEvent)
+ self.__doneEvent = doneEvent
+ self.character = character
+ self.followedChar = followedChar
+ self.paths = CCharPaths.getPaths(character.getName(), character.getCCLocation())
+ self.speed = character.walkSpeed()
+ self.__lastWalkNode = CCharPaths.startNode
+ self.__curWalkNode = CCharPaths.startNode
+
+ def enter(self, chipDestNode):
+ destNode = self.__lastWalkNode
+ choices = CCharPaths.getAdjacentNodes(self.__curWalkNode, self.paths)
+ if len(choices) == 1:
+ destNode = choices[0]
+ else:
+ while destNode == self.__lastWalkNode:
+ destNode = random.choice(CCharPaths.getAdjacentNodes(self.__curWalkNode, self.paths))
+
+ destNode = chipDestNode
+ self.notify.debug('Walking ' + self.character.getName() + '... from ' + str(self.__curWalkNode) + '(' + str(CCharPaths.getNodePos(self.__curWalkNode, self.paths)) + ') to ' + str(destNode) + '(' + str(CCharPaths.getNodePos(destNode, self.paths)) + ')')
+ self.offsetDistance = ToontownGlobals.DaleOrbitDistance
+ angle = random.randint(0, 359)
+ self.offsetX = math.cos(deg2Rad(angle)) * self.offsetDistance
+ self.offsetY = math.sin(deg2Rad(angle)) * self.offsetDistance
+ self.character.sendUpdate('setFollowChip', [self.__curWalkNode, destNode, globalClockDelta.getRealNetworkTime(), self.offsetX, self.offsetY])
+ duration = CCharPaths.getWalkDuration(self.__curWalkNode, destNode, self.speed, self.paths)
+ t = taskMgr.doMethodLater(duration, self.__doneHandler, self.character.taskName(self.character.getName() + 'DoneWalking'))
+ t.newWalkNode = destNode
+
+ def exit(self):
+ StateData.StateData.exit(self)
+ taskMgr.remove(self.character.taskName(self.character.getName() + 'DoneWalking'))
+
+ def __doneHandler(self, task):
+ self.__lastWalkNode = self.__curWalkNode
+ self.__curWalkNode = task.newWalkNode
+ self.character.sendUpdate('setFollowChip', [self.__curWalkNode, self.__curWalkNode, globalClockDelta.getRealNetworkTime(), self.offsetX, self.offsetY])
+ doneStatus = {}
+ doneStatus['state'] = 'walk'
+ doneStatus['status'] = 'done'
+ messenger.send(self.__doneEvent, [doneStatus])
+ return Task.done
+
+
+class ChipChattyStateAI(CharChattyStateAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('ChipChattyStateAI')
+
+ def setDaleId(self, daleId):
+ self.daleId = daleId
+ self.dale = simbase.air.doId2do.get(self.daleId)
+
+ def blather(self, task):
+ now = globalClock.getFrameTime()
+ if now < self.nextChatTime:
+ return Task.cont
+ self.getLatestChatter()
+ if self.character.lostInterest():
+ self.leave()
+ return Task.done
+ if task.time > CHATTY_DURATION:
+ self.leave(timeout=1)
+ return Task.done
+ if not self.chatter:
+ self.notify.debug('I do not want to talk')
+ return Task.done
+ if not self.character.getNearbyAvatars():
+ return Task.cont
+ target = self.character.getNearbyAvatars()[0]
+ if self.lastChatTarget != target:
+ self.lastChatTarget = target
+ category = CCharChatter.GREETING
+ else:
+ category = CCharChatter.COMMENT
+ if category == self.lastMessage[0] and len(self.chatter[category]) > 1:
+ msg = self.lastMessage[1]
+ lastMsgIndex = self.lastMessage[1]
+ if lastMsgIndex < len(self.chatter[category]) and lastMsgIndex >= 0:
+ while self.chatter[category][msg] == self.chatter[category][lastMsgIndex]:
+ msg = self.pickMsg(category)
+ if not msg:
+ break
+
+ else:
+ msg = self.pickMsg(category)
+ else:
+ msg = self.pickMsg(category)
+ if msg == None:
+ self.notify.debug('I do not want to talk')
+ return Task.done
+ self.character.sendUpdate('setChat', [category, msg, target])
+ if hasattr(self, 'dale') and self.dale:
+ self.dale.sendUpdate('setChat', [category, msg, target])
+ self.lastMessage = [category, msg]
+ self.nextChatTime = now + 8.0 + random.random() * 4.0
+ return Task.cont
+
+ def leave(self, timeout=0):
+ if self.chatter != None and not timeout:
+ category = CCharChatter.GOODBYE
+ msg = random.randint(0, len(self.chatter[CCharChatter.GOODBYE]) - 1)
+ if len(self.character.getNearbyAvatars()) > 0:
+ target = self.character.getNearbyAvatars()[0]
+ self.character.sendUpdate('setChat', [category, msg, target])
+ if hasattr(self, 'dale') and self.dale:
+ self.dale.sendUpdate('setChat', [category, msg, target])
+ else:
+ self.notify.warning('Nearby avatars left')
+ taskMgr.doMethodLater(1, self.doneHandler, self.character.taskName('waitToFinish'))
+ return
diff --git a/toontown/classicchars/DistributedCCharBase.py b/toontown/classicchars/DistributedCCharBase.py
new file mode 100644
index 0000000..f5cdd12
--- /dev/null
+++ b/toontown/classicchars/DistributedCCharBase.py
@@ -0,0 +1,293 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from otp.avatar import Avatar
+from libotp import CFQuicktalker
+from toontown.char import CharDNA
+from toontown.char import DistributedChar
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from direct.controls.ControlManager import CollisionHandlerRayStart
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase.TTLocalizer import Donald, DonaldDock, WesternPluto, Pluto
+from toontown.effects import DustCloud
+import CCharChatter
+import CCharPaths
+import string
+import copy
+
+class DistributedCCharBase(DistributedChar.DistributedChar):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCCharBase')
+
+ def __init__(self, cr, name, dnaName):
+ try:
+ self.DistributedCCharBase_initialized
+ return
+ except:
+ self.DistributedCCharBase_initialized = 1
+
+ DistributedChar.DistributedChar.__init__(self, cr)
+ dna = CharDNA.CharDNA()
+ dna.newChar(dnaName)
+ self.setDNA(dna)
+ self.setName(name)
+ self.setTransparency(TransparencyAttrib.MDual, 1)
+ fadeIn = self.colorScaleInterval(0.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0), blendType='easeInOut')
+ fadeIn.start()
+ self.diffPath = None
+ self.transitionToCostume = 0
+ self.__initCollisions()
+ return
+
+ def __initCollisions(self):
+ self.cSphere = CollisionSphere(0.0, 0.0, 0.0, 8.0)
+ self.cSphere.setTangible(0)
+ self.cSphereNode = CollisionNode(self.getName() + 'BlatherSphere')
+ self.cSphereNode.addSolid(self.cSphere)
+ self.cSphereNodePath = self.attachNewNode(self.cSphereNode)
+ self.cSphereNodePath.hide()
+ self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask)
+ self.acceptOnce('enter' + self.cSphereNode.getName(), self.__handleCollisionSphereEnter)
+ self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
+ self.cRayNode = CollisionNode(self.getName() + 'cRay')
+ self.cRayNode.addSolid(self.cRay)
+ self.cRayNodePath = self.attachNewNode(self.cRayNode)
+ self.cRayNodePath.hide()
+ self.cRayBitMask = ToontownGlobals.FloorBitmask
+ self.cRayNode.setFromCollideMask(self.cRayBitMask)
+ self.cRayNode.setIntoCollideMask(BitMask32.allOff())
+ self.lifter = CollisionHandlerFloor()
+ self.lifter.setOffset(ToontownGlobals.FloorOffset)
+ self.lifter.setReach(10.0)
+ self.lifter.setMaxVelocity(0.0)
+ self.lifter.addCollider(self.cRayNodePath, self)
+ self.cTrav = base.localAvatar.cTrav
+
+ def __deleteCollisions(self):
+ del self.cSphere
+ del self.cSphereNode
+ self.cSphereNodePath.removeNode()
+ del self.cSphereNodePath
+ self.cRay = None
+ self.cRayNode = None
+ self.cRayNodePath = None
+ self.lifter = None
+ self.cTrav = None
+ return
+
+ def disable(self):
+ self.stopBlink()
+ self.ignoreAll()
+ self.chatTrack.finish()
+ del self.chatTrack
+ if self.chatterDialogue:
+ self.chatterDialogue.stop()
+ del self.chatterDialogue
+ DistributedChar.DistributedChar.disable(self)
+ self.stopEarTask()
+
+ def delete(self):
+ try:
+ self.DistributedCCharBase_deleted
+ except:
+ self.setParent(NodePath('Temp'))
+ self.DistributedCCharBase_deleted = 1
+ self.__deleteCollisions()
+ DistributedChar.DistributedChar.delete(self)
+
+ def generate(self, diffPath = None):
+ DistributedChar.DistributedChar.generate(self)
+ if diffPath == None:
+ self.setPos(CCharPaths.getNodePos(CCharPaths.startNode, CCharPaths.getPaths(self.getName(), self.getCCLocation())))
+ else:
+ self.setPos(CCharPaths.getNodePos(CCharPaths.startNode, CCharPaths.getPaths(diffPath, self.getCCLocation())))
+ self.setHpr(0, 0, 0)
+ self.setParent(ToontownGlobals.SPRender)
+ self.startBlink()
+ self.startEarTask()
+ self.chatTrack = Sequence()
+ self.chatterDialogue = None
+ self.acceptOnce('enter' + self.cSphereNode.getName(), self.__handleCollisionSphereEnter)
+ self.accept('exitSafeZone', self.__handleExitSafeZone)
+ return
+
+ def __handleExitSafeZone(self):
+ self.__handleCollisionSphereExit(None)
+ return
+
+ def __handleCollisionSphereEnter(self, collEntry):
+ self.notify.debug('Entering collision sphere...')
+ self.sendUpdate('avatarEnter', [])
+ self.accept('chatUpdate', self.__handleChatUpdate)
+ self.accept('chatUpdateSC', self.__handleChatUpdateSC)
+ self.accept('chatUpdateSCCustom', self.__handleChatUpdateSCCustom)
+ self.accept('chatUpdateSCToontask', self.__handleChatUpdateSCToontask)
+ self.nametag3d.setBin('transparent', 100)
+ self.acceptOnce('exit' + self.cSphereNode.getName(), self.__handleCollisionSphereExit)
+
+ def __handleCollisionSphereExit(self, collEntry):
+ self.notify.debug('Exiting collision sphere...')
+ self.sendUpdate('avatarExit', [])
+ self.ignore('chatUpdate')
+ self.ignore('chatUpdateSC')
+ self.ignore('chatUpdateSCCustom')
+ self.ignore('chatUpdateSCToontask')
+ self.acceptOnce('enter' + self.cSphereNode.getName(), self.__handleCollisionSphereEnter)
+
+ def __handleChatUpdate(self, msg, chatFlags):
+ self.sendUpdate('setNearbyAvatarChat', [msg])
+
+ def __handleChatUpdateSC(self, msgIndex):
+ self.sendUpdate('setNearbyAvatarSC', [msgIndex])
+
+ def __handleChatUpdateSCCustom(self, msgIndex):
+ self.sendUpdate('setNearbyAvatarSCCustom', [msgIndex])
+
+ def __handleChatUpdateSCToontask(self, taskId, toNpcId, toonProgress, msgIndex):
+ self.sendUpdate('setNearbyAvatarSCToontask', [taskId,
+ toNpcId,
+ toonProgress,
+ msgIndex])
+
+ def makeTurnToHeadingTrack(self, heading):
+ curHpr = self.getHpr()
+ destHpr = self.getHpr()
+ destHpr.setX(heading)
+ if destHpr[0] - curHpr[0] > 180.0:
+ destHpr.setX(destHpr[0] - 360)
+ elif destHpr[0] - curHpr[0] < -180.0:
+ destHpr.setX(destHpr[0] + 360)
+ turnSpeed = 180.0
+ time = abs(destHpr[0] - curHpr[0]) / turnSpeed
+ turnTracks = Parallel()
+ if time > 0.2:
+ turnTracks.append(Sequence(Func(self.loop, 'walk'), Wait(time), Func(self.loop, 'neutral')))
+ turnTracks.append(LerpHprInterval(self, time, destHpr, name='lerp' + self.getName() + 'Hpr'))
+ return turnTracks
+
+ def setChat(self, category, msg, avId):
+ if self.cr.doId2do.has_key(avId):
+ avatar = self.cr.doId2do[avId]
+ chatter = CCharChatter.getChatter(self.getName(), self.getCCChatter())
+ if category >= len(chatter):
+ self.notify.debug("Chatter's changed")
+ return
+ elif len(chatter[category]) <= msg:
+ self.notify.debug("Chatter's changed")
+ return
+ str = chatter[category][msg]
+ if '%' in str:
+ str = copy.deepcopy(str)
+ avName = avatar.getName()
+ str = string.replace(str, '%', avName)
+ track = Sequence()
+ if category != CCharChatter.GOODBYE:
+ curHpr = self.getHpr()
+ self.headsUp(avatar)
+ destHpr = self.getHpr()
+ self.setHpr(curHpr)
+ track.append(self.makeTurnToHeadingTrack(destHpr[0]))
+ if self.getName() == Donald or self.getName() == WesternPluto or self.getName() == Pluto:
+ chatFlags = CFThought | CFTimeout
+ if hasattr(base.cr, 'newsManager') and base.cr.newsManager:
+ holidayIds = base.cr.newsManager.getHolidayIdList()
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds:
+ if self.getName() == Pluto:
+ chatFlags = CFTimeout | CFSpeech
+ elif self.getName() == DonaldDock:
+ chatFlags = CFTimeout | CFSpeech
+ self.nametag3d.hide()
+ else:
+ chatFlags = CFTimeout | CFSpeech
+ self.chatterDialogue = self.getChatterDialogue(category, msg)
+ track.append(Func(self.setChatAbsolute, str, chatFlags, self.chatterDialogue))
+ self.chatTrack.finish()
+ self.chatTrack = track
+ self.chatTrack.start()
+
+ def setWalk(self, srcNode, destNode, timestamp):
+ pass
+
+ def walkSpeed(self):
+ return 0.1
+
+ def enableRaycast(self, enable = 1):
+ if not self.cTrav or not hasattr(self, 'cRayNode') or not self.cRayNode:
+ self.notify.debug('raycast info not found for ' + self.getName())
+ return
+ self.cTrav.removeCollider(self.cRayNodePath)
+ if enable:
+ if self.notify.getDebug():
+ self.notify.debug('enabling raycast for ' + self.getName())
+ self.cTrav.addCollider(self.cRayNodePath, self.lifter)
+ elif self.notify.getDebug():
+ self.notify.debug('disabling raycast for ' + self.getName())
+
+ def getCCLocation(self):
+ return 0
+
+ def getCCChatter(self):
+ self.handleHolidays()
+ return self.CCChatter
+
+ def handleHolidays(self):
+ self.CCChatter = 0
+ if hasattr(base.cr, 'newsManager') and base.cr.newsManager:
+ holidayIds = base.cr.newsManager.getHolidayIdList()
+ if ToontownGlobals.CRASHED_LEADERBOARD in holidayIds:
+ self.CCChatter = ToontownGlobals.CRASHED_LEADERBOARD
+ elif ToontownGlobals.CIRCUIT_RACING_EVENT in holidayIds:
+ self.CCChatter = ToontownGlobals.CIRCUIT_RACING_EVENT
+ elif ToontownGlobals.WINTER_CAROLING in holidayIds:
+ self.CCChatter = ToontownGlobals.WINTER_CAROLING
+ elif ToontownGlobals.WINTER_DECORATIONS in holidayIds:
+ self.CCChatter = ToontownGlobals.WINTER_DECORATIONS
+ elif ToontownGlobals.WACKY_WINTER_CAROLING in holidayIds:
+ self.CCChatter = ToontownGlobals.WACKY_WINTER_CAROLING
+ elif ToontownGlobals.WACKY_WINTER_DECORATIONS in holidayIds:
+ self.CCChatter = ToontownGlobals.WACKY_WINTER_DECORATIONS
+ elif ToontownGlobals.VALENTINES_DAY in holidayIds:
+ self.CCChatter = ToontownGlobals.VALENTINES_DAY
+ elif ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds:
+ self.CCChatter = ToontownGlobals.APRIL_FOOLS_COSTUMES
+ elif ToontownGlobals.SILLY_CHATTER_ONE in holidayIds:
+ self.CCChatter = ToontownGlobals.SILLY_CHATTER_ONE
+ elif ToontownGlobals.SILLY_CHATTER_TWO in holidayIds:
+ self.CCChatter = ToontownGlobals.SILLY_CHATTER_TWO
+ elif ToontownGlobals.SILLY_CHATTER_THREE in holidayIds:
+ self.CCChatter = ToontownGlobals.SILLY_CHATTER_THREE
+ elif ToontownGlobals.SILLY_CHATTER_FOUR in holidayIds:
+ self.CCChatter = ToontownGlobals.SILLY_CHATTER_FOUR
+ elif ToontownGlobals.SILLY_CHATTER_FIVE in holidayIds:
+ self.CCChatter = ToontownGlobals.SILLY_CHATTER_FOUR
+ elif ToontownGlobals.HALLOWEEN_COSTUMES in holidayIds:
+ self.CCChatter = ToontownGlobals.HALLOWEEN_COSTUMES
+ elif ToontownGlobals.SPOOKY_COSTUMES in holidayIds:
+ self.CCChatter = ToontownGlobals.SPOOKY_COSTUMES
+ elif ToontownGlobals.SELLBOT_FIELD_OFFICE in holidayIds:
+ self.CCChatter = ToontownGlobals.SELLBOT_FIELD_OFFICE
+
+ def fadeAway(self):
+ fadeOut = self.colorScaleInterval(0.5, Vec4(1, 1, 1, 0.5), startColorScale=Vec4(1, 1, 1, 1), blendType='easeInOut')
+ fadeOut.start()
+ self.loop('neutral')
+ if self.fsm:
+ self.fsm.addState(State.State('TransitionToCostume', self.enterTransitionToCostume, self.exitTransitionToCostume, ['Off']))
+ self.fsm.request('TransitionToCostume', force=1)
+ self.ignoreAll()
+
+ def enterTransitionToCostume(self):
+
+ def getDustCloudIval():
+ dustCloud = DustCloud.DustCloud(fBillboard=0, wantSound=1)
+ dustCloud.setBillboardAxis(2.0)
+ dustCloud.setZ(4)
+ dustCloud.setScale(0.6)
+ dustCloud.createTrack()
+ return Sequence(Func(dustCloud.reparentTo, self), dustCloud.track, Func(dustCloud.destroy), name='dustCloadIval')
+
+ dust = getDustCloudIval()
+ dust.start()
+
+ def exitTransitionToCostume(self):
+ pass
diff --git a/toontown/classicchars/DistributedCCharBaseAI.py b/toontown/classicchars/DistributedCCharBaseAI.py
new file mode 100644
index 0000000..5d243d9
--- /dev/null
+++ b/toontown/classicchars/DistributedCCharBaseAI.py
@@ -0,0 +1,248 @@
+from otp.ai.AIBaseGlobal import *
+from direct.distributed.ClockDelta import *
+from otp.avatar import DistributedAvatarAI
+from otp.avatar.DistributedPlayerAI import DistributedPlayerAI
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownGlobals
+import random
+
+class DistributedCCharBaseAI(DistributedAvatarAI.DistributedAvatarAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCCharBaseAI')
+
+ def __init__(self, air, name):
+ DistributedAvatarAI.DistributedAvatarAI.__init__(self, air)
+ self.setName(name)
+ self.exitOff()
+ self.transitionToCostume = 0
+ self.diffPath = None
+ return
+
+ def generate(self):
+ DistributedAvatarAI.DistributedAvatarAI.generate(self)
+ if config.GetBool('classic-char-client-spam', 0):
+ self._ccharSpamTask = taskMgr.add(self._simSpam, 'cchar-spam-%s' % serialNum())
+
+ def _simSpam(self, task):
+ AvEnter = 'avatarEnter'
+ AvExit = 'avatarExit'
+ SetChat = 'setNearbyAvatarChat'
+ SetSC = 'setNearbyAvatarSC'
+ SetSCCustom = 'setNearbyAvatarSCCustom'
+ SetSCToontask = 'setNearbyAvatarSCToontask'
+ msgs = (AvEnter, AvExit, SetChat, SetSC, SetSCCustom, SetSCToontask)
+ msg = random.choice(msgs)
+ r = random.random()
+ fixedAvId = 1000000006
+ if r < 0.3:
+ avId = random.randrange(1L << 32)
+ else:
+ if r < 0.6:
+ players = self.air.doFindAllOfType('DistributedToonAI')[0]
+ if len(players):
+ player = random.choice(players)
+ avId = player.doId
+ else:
+ avId = fixedAvId
+ else:
+ avId = fixedAvId
+ savedIdFunc = self.air.getAvatarIdFromSender
+ self.air.getAvatarIdFromSender = lambda : avId
+ rrange = random.randrange
+ if msg is AvEnter:
+ self.avatarEnter()
+ elif msg is AvExit:
+ self.avatarExit()
+ else:
+ if msg is SetChat:
+ length = rrange(1024)
+ s = ''
+ for i in xrange(length):
+ s += chr(rrange(1 << 8))
+
+ self.setNearbyAvatarChat(s)
+ else:
+ if msg is SetSC:
+ self.setNearbyAvatarSC(rrange(1 << 16))
+ else:
+ if msg is SetSCCustom:
+ self.setNearbyAvatarSCCustom(rrange(1 << 16))
+ else:
+ if msg is SetSCToontask:
+ self.setNearbyAvatarSCToontask(rrange(1 << 32), rrange(1 << 32), rrange(1 << 32), rrange(1 << 8))
+ self.air.getAvatarIdFromSender = savedIdFunc
+ return task.cont
+
+ def delete(self):
+ self.ignoreAll()
+ if hasattr(self, '_ccharSpamTask'):
+ taskMgr.remove(self._ccharSpamTask)
+ self._ccharSpamTask = None
+ DistributedAvatarAI.DistributedAvatarAI.delete(self)
+ return
+
+ def exitOff(self):
+ self.__initAttentionSpan()
+ self.__clearNearbyAvatars()
+
+ def avatarEnter(self):
+ avId = self.air.getAvatarIdFromSender()
+ if avId not in self.air.doId2do:
+ self.air.writeServerEvent('suspicious', avId, 'CCharBaseAI.avatarEnter from unknown avId')
+ return
+ av = self.air.getDo(avId)
+ if not isinstance(av, DistributedPlayerAI):
+ self.air.writeServerEvent('suspicious', avId, 'CCharBaseAI.avatarEnter from non-player object to cchar %s' % self.doId)
+ return
+ if av.zoneId != self.zoneId:
+ self.air.writeServerEvent('suspicious', avId, 'CCharBaseAI.avatarEnter from av in zone %s, my zone is %s' % (av.zoneId, self.zoneId))
+ return
+ self.notify.debug('adding avatar ' + str(avId) + ' to the nearby avatar list')
+ if avId not in self.nearbyAvatars:
+ self.nearbyAvatars.append(avId)
+ else:
+ self.air.writeServerEvent('suspicious', avId, 'CCharBase.avatarEnter')
+ self.notify.warning('Avatar %s already in nearby avatars!' % avId)
+ self.nearbyAvatarInfoDict[avId] = {}
+ self.nearbyAvatarInfoDict[avId]['enterTime'] = globalClock.getRealTime()
+ self.nearbyAvatarInfoDict[avId]['lastChatTime'] = 0
+ self.sortNearbyAvatars()
+ self.__interestingAvatarEventOccured()
+ avExitEvent = self.air.getAvatarExitEvent(avId)
+ self.acceptOnce(avExitEvent, self.__handleExitedAvatar, [avId])
+ self.avatarEnterNextState()
+
+ def avatarExit(self):
+ avId = self.air.getAvatarIdFromSender()
+ self.__doAvatarExit(avId)
+
+ def __doAvatarExit(self, avId):
+ avId = self.air.getAvatarIdFromSender()
+ self.notify.debug('removing avatar ' + str(avId) + ' from the nearby avatar list')
+ if avId not in self.nearbyAvatars:
+ self.notify.debug('avatar ' + str(avId) + ' not in the nearby avatar list')
+ else:
+ avExitEvent = self.air.getAvatarExitEvent(avId)
+ self.ignore(avExitEvent)
+ del self.nearbyAvatarInfoDict[avId]
+ self.nearbyAvatars.remove(avId)
+
+ def avatarEnterNextState():
+ pass
+
+ def avatarExitNextState():
+ pass
+
+ def __clearNearbyAvatars(self):
+ self.nearbyAvatars = []
+ self.nearbyAvatarInfoDict = {}
+
+ def sortNearbyAvatars(self):
+
+ def nAv_compare(a, b, nAvIDict=self.nearbyAvatarInfoDict):
+ tsA = nAvIDict[a]['enterTime']
+ tsB = nAvIDict[b]['enterTime']
+ if tsA == tsB:
+ return 0
+ else:
+ if tsA < tsB:
+ return -1
+ else:
+ return 1
+
+ self.nearbyAvatars.sort(nAv_compare)
+
+ def getNearbyAvatars(self):
+ return self.nearbyAvatars
+
+ def __avatarSpoke(self, avId):
+ now = globalClock.getRealTime()
+ if self.nearbyAvatarInfoDict.has_key(avId):
+ self.nearbyAvatarInfoDict[avId]['lastChatTime'] = now
+ self.__interestingAvatarEventOccured()
+
+ def __initAttentionSpan(self):
+ self.__avatarTimeoutBase = 0
+
+ def __interestingAvatarEventOccured(self, t=None):
+ if t == None:
+ t = globalClock.getRealTime()
+ self.__avatarTimeoutBase = t
+ return
+
+ def lostInterest(self):
+ now = globalClock.getRealTime()
+ if now > self.__avatarTimeoutBase + 50.0:
+ return 1
+ return 0
+
+ def __handleExitedAvatar(self, avId):
+ self.__doAvatarExit(avId)
+
+ def setNearbyAvatarChat(self, msg):
+ avId = self.air.getAvatarIdFromSender()
+ self.notify.debug('setNearbyAvatarChat: avatar ' + str(avId) + ' said ' + str(msg))
+ self.__avatarSpoke(avId)
+
+ def setNearbyAvatarSC(self, msgIndex):
+ avId = self.air.getAvatarIdFromSender()
+ self.notify.debug('setNearbyAvatarSC: avatar %s said SpeedChat phrase %s' % (avId, msgIndex))
+ self.__avatarSpoke(avId)
+
+ def setNearbyAvatarSCCustom(self, msgIndex):
+ avId = self.air.getAvatarIdFromSender()
+ self.notify.debug('setNearbyAvatarSCCustom: avatar %s said custom SpeedChat phrase %s' % (avId, msgIndex))
+ self.__avatarSpoke(avId)
+
+ def setNearbyAvatarSCToontask(self, taskId, toNpcId, toonProgress, msgIndex):
+ avId = self.air.getAvatarIdFromSender()
+ self.notify.debug('setNearbyAvatarSCToontask: avatar %s said %s' % (avId, (taskId, toNpcId, toonProgress, msgIndex)))
+ self.__avatarSpoke(avId)
+
+ def getWalk(self):
+ return ('', '', 0)
+
+ def walkSpeed(self):
+ return 0.1
+
+ def handleHolidays(self):
+ self.CCChatter = 0
+ if hasattr(simbase.air, 'holidayManager'):
+ if ToontownGlobals.CRASHED_LEADERBOARD in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.CRASHED_LEADERBOARD
+ elif ToontownGlobals.CIRCUIT_RACING_EVENT in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.CIRCUIT_RACING_EVENT
+ elif ToontownGlobals.WINTER_CAROLING in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.WINTER_CAROLING
+ elif ToontownGlobals.WINTER_DECORATIONS in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.WINTER_DECORATIONS
+ elif ToontownGlobals.VALENTINES_DAY in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.VALENTINES_DAY
+ elif ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.APRIL_FOOLS_COSTUMES
+ elif ToontownGlobals.SILLY_CHATTER_ONE in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.SILLY_CHATTER_ONE
+ elif ToontownGlobals.SILLY_CHATTER_TWO in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.SILLY_CHATTER_TWO
+ elif ToontownGlobals.SILLY_CHATTER_THREE in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.SILLY_CHATTER_THREE
+ elif ToontownGlobals.SILLY_CHATTER_FOUR in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.SILLY_CHATTER_FOUR
+ elif ToontownGlobals.SILLY_CHATTER_FIVE in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.SILLY_CHATTER_FOUR
+ elif ToontownGlobals.HALLOWEEN_COSTUMES in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.HALLOWEEN_COSTUMES
+ elif ToontownGlobals.SELLBOT_FIELD_OFFICE in simbase.air.holidayManager.currentHolidays:
+ self.CCChatter = ToontownGlobals.SELLBOT_FIELD_OFFICE
+
+ def getCCLocation(self):
+ return 0
+
+ def getCCChatter(self):
+ self.handleHolidays()
+ return self.CCChatter
+
+ def fadeAway(self):
+ self.sendUpdate('fadeAway', [])
+
+ def transitionCostume(self):
+ self.transitionToCostume = 1
diff --git a/toontown/classicchars/DistributedChip.py b/toontown/classicchars/DistributedChip.py
new file mode 100644
index 0000000..f0a1d9d
--- /dev/null
+++ b/toontown/classicchars/DistributedChip.py
@@ -0,0 +1,80 @@
+from direct.showbase.ShowBaseGlobal import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+
+class DistributedChip(DistributedCCharBase.DistributedCCharBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedChip')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedChip_initialized
+ except:
+ self.DistributedChip_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.Chip, 'ch')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBase.DistributedCCharBase.disable(self)
+ del self.neutralDoneEvent
+ del self.neutral
+ del self.walkDoneEvent
+ del self.walk
+ self.fsm.requestFinalState()
+
+ def delete(self):
+ try:
+ self.DistributedChip_deleted
+ except:
+ del self.fsm
+ self.DistributedChip_deleted = 1
+ DistributedCCharBase.DistributedCCharBase.delete(self)
+
+ def generate(self):
+ DistributedCCharBase.DistributedCCharBase.generate(self)
+ name = self.getName()
+ self.neutralDoneEvent = self.taskName(name + '-neutral-done')
+ self.neutral = CharStateDatas.CharNeutralState(self.neutralDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self)
+ self.fsm.request('Neutral')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterNeutral(self):
+ self.neutral.enter()
+ self.acceptOnce(self.neutralDoneEvent, self.__decideNextState)
+
+ def exitNeutral(self):
+ self.ignore(self.neutralDoneEvent)
+ self.neutral.exit()
+
+ def enterWalk(self):
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def __decideNextState(self, doneStatus):
+ self.fsm.request('Neutral')
+
+ def setWalk(self, srcNode, destNode, timestamp):
+ if destNode and not destNode == srcNode:
+ self.walk.setWalk(srcNode, destNode, timestamp)
+ self.fsm.request('Walk')
+
+ def walkSpeed(self):
+ return ToontownGlobals.ChipSpeed
diff --git a/toontown/classicchars/DistributedChipAI.py b/toontown/classicchars/DistributedChipAI.py
new file mode 100644
index 0000000..3defa31
--- /dev/null
+++ b/toontown/classicchars/DistributedChipAI.py
@@ -0,0 +1,166 @@
+from otp.ai.AIBaseGlobal import *
+import DistributedCCharBaseAI
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from direct.task import Task
+import random
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import CharStateDatasAI
+
+class DistributedChipAI(DistributedCCharBaseAI.DistributedCCharBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedChipAI')
+
+ def __init__(self, air):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.__init__(self, air, TTLocalizer.Chip)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedChipAI', [
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'Lonely', 'TransitionToCostume']),
+ State.State('Lonely', self.enterLonely, self.exitLonely, [
+ 'Chatty', 'Walk', 'TransitionToCostume']),
+ State.State('Chatty', self.enterChatty, self.exitChatty, [
+ 'Lonely', 'Walk', 'TransitionToCostume']),
+ State.State('Walk', self.enterWalk, self.exitWalk, [
+ 'Lonely', 'Chatty', 'TransitionToCostume']),
+ State.State('TransitionToCostume', self.enterTransitionToCostume, self.exitTransitionToCostume, [
+ 'Off'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.dale = None
+ self.handleHolidays()
+ return
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBaseAI.DistributedCCharBaseAI.delete(self)
+ self.lonelyDoneEvent = None
+ self.lonely = None
+ self.chattyDoneEvent = None
+ self.chatty = None
+ self.walkDoneEvent = None
+ self.walk = None
+ return
+
+ def generate(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.generate(self)
+ name = self.getName()
+ self.lonelyDoneEvent = self.taskName(name + '-lonely-done')
+ self.lonely = CharStateDatasAI.CharLonelyStateAI(self.lonelyDoneEvent, self)
+ self.chattyDoneEvent = self.taskName(name + '-chatty-done')
+ self.chatty = CharStateDatasAI.ChipChattyStateAI(self.chattyDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self)
+
+ def walkSpeed(self):
+ return ToontownGlobals.ChipSpeed
+
+ def start(self):
+ self.fsm.request('Lonely')
+
+ def __decideNextState(self, doneStatus):
+ if self.transitionToCostume == 1:
+ curWalkNode = self.walk.getDestNode()
+ if simbase.air.holidayManager:
+ if ToontownGlobals.HALLOWEEN_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ else:
+ self.notify.warning('transitionToCostume == 1 but no costume holiday')
+ else:
+ self.notify.warning('transitionToCostume == 1 but no holiday Manager')
+ if doneStatus['state'] == 'lonely' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'chatty' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'walk' and doneStatus['status'] == 'done':
+ if len(self.nearbyAvatars) > 0:
+ self.fsm.request('Chatty')
+ else:
+ self.fsm.request('Lonely')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.exitOff(self)
+
+ def enterLonely(self):
+ self.lonely.enter()
+ self.acceptOnce(self.lonelyDoneEvent, self.__decideNextState)
+ if self.dale:
+ self.dale.chipEnteringState(self.fsm.getCurrentState().getName())
+
+ def exitLonely(self):
+ self.ignore(self.lonelyDoneEvent)
+ self.lonely.exit()
+ if self.dale:
+ self.dale.chipLeavingState(self.fsm.getCurrentState().getName())
+
+ def __goForAWalk(self, task):
+ self.notify.debug('going for a walk')
+ self.fsm.request('Walk')
+ return Task.done
+
+ def enterChatty(self):
+ self.chatty.enter()
+ self.acceptOnce(self.chattyDoneEvent, self.__decideNextState)
+ if self.dale:
+ self.dale.chipEnteringState(self.fsm.getCurrentState().getName())
+ taskMgr.doMethodLater(CharStateDatasAI.CHATTY_DURATION + 10, self.forceLeaveChatty, self.taskName('forceLeaveChatty'))
+
+ def forceLeaveChatty(self, task):
+ self.notify.warning('Had to force change of state from Chatty state')
+ doneStatus = {}
+ doneStatus['state'] = 'chatty'
+ doneStatus['status'] = 'done'
+ self.__decideNextState(doneStatus)
+ return Task.done
+
+ def cleanUpChattyTasks(self):
+ taskMgr.removeTasksMatching(self.taskName('forceLeaveChatty'))
+
+ def exitChatty(self):
+ self.ignore(self.chattyDoneEvent)
+ self.chatty.exit()
+ if self.dale:
+ self.dale.chipLeavingState(self.fsm.getCurrentState().getName())
+ self.cleanUpChattyTasks()
+
+ def enterWalk(self):
+ self.notify.debug('going for a walk')
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+ if self.dale:
+ self.dale.chipEnteringState(self.fsm.getCurrentState().getName())
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+ if self.dale:
+ self.dale.chipLeavingState(self.fsm.getCurrentState().getName())
+
+ def avatarEnterNextState(self):
+ if len(self.nearbyAvatars) == 1:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Chatty')
+ else:
+ self.notify.debug('avatarEnterNextState: in walk state')
+ else:
+ self.notify.debug('avatarEnterNextState: num avatars: ' + str(len(self.nearbyAvatars)))
+
+ def avatarExitNextState(self):
+ if len(self.nearbyAvatars) == 0:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Lonely')
+
+ def setDaleId(self, daleId):
+ self.daleId = daleId
+ self.dale = self.air.doId2do.get(daleId)
+ self.chatty.setDaleId(self.daleId)
+
+ def enterTransitionToCostume(self):
+ pass
+
+ def exitTransitionToCostume(self):
+ pass
diff --git a/toontown/classicchars/DistributedDaisy.py b/toontown/classicchars/DistributedDaisy.py
new file mode 100644
index 0000000..2cc7945
--- /dev/null
+++ b/toontown/classicchars/DistributedDaisy.py
@@ -0,0 +1,93 @@
+from direct.showbase.ShowBaseGlobal import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.hood import TTHood
+
+class DistributedDaisy(DistributedCCharBase.DistributedCCharBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedDaisy')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedDaisy_initialized
+ except:
+ self.DistributedDaisy_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.Daisy, 'dd')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+
+ self.handleHolidays()
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBase.DistributedCCharBase.disable(self)
+ del self.neutralDoneEvent
+ del self.neutral
+ del self.walkDoneEvent
+ del self.walk
+ self.fsm.requestFinalState()
+
+ def delete(self):
+ try:
+ self.DistributedDaisy_deleted
+ except:
+ del self.fsm
+ self.DistributedDaisy_deleted = 1
+ DistributedCCharBase.DistributedCCharBase.delete(self)
+
+ def generate(self):
+ DistributedCCharBase.DistributedCCharBase.generate(self, self.diffPath)
+ name = self.getName()
+ self.neutralDoneEvent = self.taskName(name + '-neutral-done')
+ self.neutral = CharStateDatas.CharNeutralState(self.neutralDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self, self.diffPath)
+ self.fsm.request('Neutral')
+ return
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterNeutral(self):
+ self.neutral.enter()
+ self.acceptOnce(self.neutralDoneEvent, self.__decideNextState)
+
+ def exitNeutral(self):
+ self.ignore(self.neutralDoneEvent)
+ self.neutral.exit()
+
+ def enterWalk(self):
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def __decideNextState(self, doneStatus):
+ self.fsm.request('Neutral')
+
+ def setWalk(self, srcNode, destNode, timestamp):
+ if destNode and not destNode == srcNode:
+ self.walk.setWalk(srcNode, destNode, timestamp)
+ self.fsm.request('Walk')
+
+ def walkSpeed(self):
+ return ToontownGlobals.DaisySpeed
+
+ def handleHolidays(self):
+ DistributedCCharBase.DistributedCCharBase.handleHolidays(self)
+ if hasattr(base.cr, 'newsManager') and base.cr.newsManager:
+ holidayIds = base.cr.newsManager.getHolidayIdList()
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds and isinstance(self.cr.playGame.hood, TTHood.TTHood):
+ self.diffPath = TTLocalizer.Mickey
diff --git a/toontown/classicchars/DistributedDaisyAI.py b/toontown/classicchars/DistributedDaisyAI.py
new file mode 100644
index 0000000..8d6c93f
--- /dev/null
+++ b/toontown/classicchars/DistributedDaisyAI.py
@@ -0,0 +1,163 @@
+from otp.ai.AIBaseGlobal import *
+import DistributedCCharBaseAI
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from direct.task import Task
+import random
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import CharStateDatasAI
+
+class DistributedDaisyAI(DistributedCCharBaseAI.DistributedCCharBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedDaisyAI')
+
+ def __init__(self, air):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.__init__(self, air, TTLocalizer.Daisy)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedDaisyAI', [
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'Lonely', 'TransitionToCostume', 'Walk']),
+ State.State('Lonely', self.enterLonely, self.exitLonely, [
+ 'Chatty', 'Walk', 'TransitionToCostume']),
+ State.State('Chatty', self.enterChatty, self.exitChatty, [
+ 'Lonely', 'Walk', 'TransitionToCostume']),
+ State.State('Walk', self.enterWalk, self.exitWalk, [
+ 'Lonely', 'Chatty', 'TransitionToCostume']),
+ State.State('TransitionToCostume', self.enterTransitionToCostume, self.exitTransitionToCostume, [
+ 'Off'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBaseAI.DistributedCCharBaseAI.delete(self)
+ self.lonelyDoneEvent = None
+ self.lonely = None
+ self.chattyDoneEvent = None
+ self.chatty = None
+ self.walkDoneEvent = None
+ self.walk = None
+ return
+
+ def generate(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.generate(self)
+ name = self.getName()
+ self.lonelyDoneEvent = self.taskName(name + '-lonely-done')
+ self.lonely = CharStateDatasAI.CharLonelyStateAI(self.lonelyDoneEvent, self)
+ self.chattyDoneEvent = self.taskName(name + '-chatty-done')
+ self.chatty = CharStateDatasAI.CharChattyStateAI(self.chattyDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self, self.diffPath)
+ return
+
+ def walkSpeed(self):
+ return ToontownGlobals.DaisySpeed
+
+ def start(self):
+ self.fsm.request('Lonely')
+
+ def __decideNextState(self, doneStatus):
+ if self.transitionToCostume == 1:
+ curWalkNode = self.walk.getDestNode()
+ if simbase.air.holidayManager:
+ if ToontownGlobals.HALLOWEEN_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ elif ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ else:
+ self.notify.warning('transitionToCostume == 1 but no costume holiday')
+ else:
+ self.notify.warning('transitionToCostume == 1 but no holiday Manager')
+ if doneStatus['state'] == 'lonely' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'chatty' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'walk' and doneStatus['status'] == 'done':
+ if len(self.nearbyAvatars) > 0:
+ self.fsm.request('Chatty')
+ else:
+ self.fsm.request('Lonely')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.exitOff(self)
+
+ def enterLonely(self):
+ self.lonely.enter()
+ self.acceptOnce(self.lonelyDoneEvent, self.__decideNextState)
+
+ def exitLonely(self):
+ self.ignore(self.lonelyDoneEvent)
+ self.lonely.exit()
+
+ def __goForAWalk(self, task):
+ self.notify.debug('going for a walk')
+ self.fsm.request('Walk')
+ return Task.done
+
+ def enterChatty(self):
+ self.chatty.enter()
+ self.acceptOnce(self.chattyDoneEvent, self.__decideNextState)
+ taskMgr.doMethodLater(CharStateDatasAI.CHATTY_DURATION + 10, self.forceLeaveChatty, self.taskName('forceLeaveChatty'))
+
+ def forceLeaveChatty(self, task):
+ self.notify.warning('Had to force change of state from Chatty state')
+ doneStatus = {}
+ doneStatus['state'] = 'chatty'
+ doneStatus['status'] = 'done'
+ self.__decideNextState(doneStatus)
+ return Task.done
+
+ def cleanUpChattyTasks(self):
+ taskMgr.removeTasksMatching(self.taskName('forceLeaveChatty'))
+
+ def exitChatty(self):
+ self.ignore(self.chattyDoneEvent)
+ self.chatty.exit()
+ self.cleanUpChattyTasks()
+
+ def enterWalk(self):
+ self.notify.debug('going for a walk')
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def avatarEnterNextState(self):
+ if len(self.nearbyAvatars) == 1:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Chatty')
+ else:
+ self.notify.debug('avatarEnterNextState: in walk state')
+ else:
+ self.notify.debug('avatarEnterNextState: num avatars: ' + str(len(self.nearbyAvatars)))
+
+ def avatarExitNextState(self):
+ if len(self.nearbyAvatars) == 0:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Lonely')
+
+ def enterTransitionToCostume(self):
+ pass
+
+ def exitTransitionToCostume(self):
+ pass
+
+ def handleHolidays(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.handleHolidays(self)
+ if hasattr(simbase.air, 'holidayManager'):
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays:
+ if simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES] != None and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].getRunningState():
+ self.diffPath = TTLocalizer.Mickey
+ return
diff --git a/toontown/classicchars/DistributedDale.py b/toontown/classicchars/DistributedDale.py
new file mode 100644
index 0000000..ec70726
--- /dev/null
+++ b/toontown/classicchars/DistributedDale.py
@@ -0,0 +1,94 @@
+from direct.showbase.ShowBaseGlobal import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+
+class DistributedDale(DistributedCCharBase.DistributedCCharBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedDale')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedDale_initialized
+ except:
+ self.DistributedDale_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.Dale, 'da')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBase.DistributedCCharBase.disable(self)
+ del self.neutralDoneEvent
+ del self.neutral
+ del self.walkDoneEvent
+ if self.walk:
+ self.walk.exit()
+ del self.walk
+ self.fsm.requestFinalState()
+
+ def delete(self):
+ try:
+ self.DistributedDale_deleted
+ except:
+ del self.fsm
+ self.DistributedDale_deleted = 1
+ DistributedCCharBase.DistributedCCharBase.delete(self)
+
+ def generate(self):
+ DistributedCCharBase.DistributedCCharBase.generate(self)
+ self.setX(self.getX() + ToontownGlobals.DaleOrbitDistance)
+ name = self.getName()
+ self.neutralDoneEvent = self.taskName(name + '-neutral-done')
+ self.neutral = CharStateDatas.CharNeutralState(self.neutralDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ self.fsm.request('Neutral')
+
+ def announceGenerate(self):
+ DistributedCCharBase.DistributedCCharBase.announceGenerate(self)
+ self.walk = CharStateDatas.CharFollowChipState(self.walkDoneEvent, self, self.chipId)
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterNeutral(self):
+ self.neutral.enter()
+ self.acceptOnce(self.neutralDoneEvent, self.__decideNextState)
+
+ def exitNeutral(self):
+ self.ignore(self.neutralDoneEvent)
+ self.neutral.exit()
+
+ def enterWalk(self):
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def __decideNextState(self, doneStatus):
+ self.fsm.request('Neutral')
+
+ def setWalk(self, srcNode, destNode, timestamp, offsetX = 0, offsetY = 0):
+ if destNode and not destNode == srcNode:
+ self.walk.setWalk(srcNode, destNode, timestamp, offsetX, offsetY)
+ self.fsm.request('Walk')
+
+ def walkSpeed(self):
+ return ToontownGlobals.DaleSpeed
+
+ def setFollowChip(self, srcNode, destNode, timestamp, offsetX, offsetY):
+ if destNode and not destNode == srcNode:
+ self.walk.setWalk(srcNode, destNode, timestamp, offsetX, offsetY)
+ self.fsm.request('Walk')
+
+ def setChipId(self, chipId):
+ self.chipId = chipId
diff --git a/toontown/classicchars/DistributedDaleAI.py b/toontown/classicchars/DistributedDaleAI.py
new file mode 100644
index 0000000..fed78c5
--- /dev/null
+++ b/toontown/classicchars/DistributedDaleAI.py
@@ -0,0 +1,146 @@
+from otp.ai.AIBaseGlobal import *
+import DistributedCCharBaseAI
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from direct.task import Task
+import random
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import CharStateDatasAI
+
+class DistributedDaleAI(DistributedCCharBaseAI.DistributedCCharBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedDaleAI')
+
+ def __init__(self, air, chipId):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.__init__(self, air, TTLocalizer.Dale)
+ self.chipId = chipId
+ self.chip = air.doId2do.get(chipId)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedDaleAI', [
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'Lonely']),
+ State.State('Lonely', self.enterLonely, self.exitLonely, [
+ 'Chatty', 'FollowChip', 'Walk']),
+ State.State('Chatty', self.enterChatty, self.exitChatty, [
+ 'Lonely', 'FollowChip', 'Walk']),
+ State.State('Walk', self.enterWalk, self.exitWalk, [
+ 'Lonely', 'Chatty']),
+ State.State('FollowChip', self.enterFollowChip, self.exitFollowChip, [
+ 'Lonely', 'Chatty', 'FollowChip'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBaseAI.DistributedCCharBaseAI.delete(self)
+ self.lonelyDoneEvent = None
+ self.lonely = None
+ self.chattyDoneEvent = None
+ self.chatty = None
+ self.walkDoneEvent = None
+ self.walk = None
+ return
+
+ def generate(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.generate(self)
+ self.lonely = CharStateDatasAI.CharLonelyStateAI(None, self)
+ self.chatty = CharStateDatasAI.CharChattyStateAI(None, self)
+ self.followChip = CharStateDatasAI.CharFollowChipStateAI(None, self, self.chip)
+ return
+
+ def walkSpeed(self):
+ return ToontownGlobals.DaleSpeed
+
+ def start(self):
+ self.fsm.request('Lonely')
+
+ def __decideNextState(self, doneStatus):
+ if doneStatus['state'] == 'lonely' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'chatty' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'walk' and doneStatus['status'] == 'done':
+ if len(self.nearbyAvatars) > 0:
+ self.fsm.request('Chatty')
+ else:
+ self.fsm.request('Lonely')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.exitOff(self)
+
+ def enterLonely(self):
+ self.lonely.enter()
+
+ def exitLonely(self):
+ self.lonely.exit()
+
+ def __goForAWalk(self, task):
+ self.notify.debug('going for a walk')
+ self.fsm.request('Walk')
+ return Task.done
+
+ def enterChatty(self):
+ self.chatty.enter()
+
+ def exitChatty(self):
+ self.chatty.exit()
+
+ def enterWalk(self):
+ self.notify.debug('going for a walk')
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def enterFollowChip(self):
+ self.notify.debug('enterFollowChip')
+ walkState = self.chip.walk
+ destNode = walkState.getDestNode()
+ self.followChip.enter(destNode)
+
+ def exitFollowChip(self):
+ self.notify.debug('exitFollowChip')
+ self.followChip.exit()
+
+ def avatarEnterNextState(self):
+ if len(self.nearbyAvatars) == 1:
+ if False:
+ self.fsm.request('Chatty')
+ else:
+ self.notify.debug('avatarEnterNextState: in walk state')
+ else:
+ self.notify.debug('avatarEnterNextState: num avatars: ' + str(len(self.nearbyAvatars)))
+
+ def avatarExitNextState(self):
+ if len(self.nearbyAvatars) == 0:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ pass
+
+ def chipEnteringState(self, newState):
+ if newState == 'Walk':
+ self.doFollowChip()
+
+ def chipLeavingState(self, oldState):
+ pass
+
+ def doFollowChip(self):
+ walkState = self.chip.walk
+ destNode = walkState.getDestNode()
+ self.fsm.request('FollowChip')
+
+ def doChatty(self):
+ pass
+
+ def getChipId(self):
+ return self.chipId
+
+ def enterTransitionToCostume(self):
+ pass
+
+ def exitTransitionToCostume(self):
+ pass
diff --git a/toontown/classicchars/DistributedDonald.py b/toontown/classicchars/DistributedDonald.py
new file mode 100644
index 0000000..8fa908c
--- /dev/null
+++ b/toontown/classicchars/DistributedDonald.py
@@ -0,0 +1,112 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+import CharStateDatas
+import CCharChatter
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.hood import GSHood
+
+class DistributedDonald(DistributedCCharBase.DistributedCCharBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedDonald')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedDonald_initialized
+ except:
+ self.DistributedDonald_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.Donald, 'd')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+
+ self.handleHolidays()
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBase.DistributedCCharBase.disable(self)
+ del self.neutralDoneEvent
+ del self.neutral
+ del self.walkDoneEvent
+ del self.walk
+ del self.walkStartTrack
+ del self.neutralStartTrack
+ self.fsm.requestFinalState()
+
+ def delete(self):
+ try:
+ self.DistributedDonald_deleted
+ except:
+ self.DistributedDonald_deleted = 1
+ del self.fsm
+ DistributedCCharBase.DistributedCCharBase.delete(self)
+
+ def generate(self):
+ DistributedCCharBase.DistributedCCharBase.generate(self, self.diffPath)
+ name = self.getName()
+ self.neutralDoneEvent = self.taskName(name + '-neutral-done')
+ self.neutral = CharStateDatas.CharNeutralState(self.neutralDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self, self.diffPath)
+ self.walkStartTrack = self.actorInterval('trans-back')
+ self.neutralStartTrack = self.actorInterval('trans')
+ self.fsm.request('Neutral')
+ return
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterNeutral(self):
+ self.notify.debug('Neutral ' + self.getName() + '...')
+ self.neutral.enter(startTrack=self.neutralStartTrack, playRate=0.5)
+ self.acceptOnce(self.neutralDoneEvent, self.__decideNextState)
+
+ def exitNeutral(self):
+ self.ignore(self.neutralDoneEvent)
+ self.neutral.exit()
+
+ def enterWalk(self):
+ self.notify.debug('Walking ' + self.getName() + '...')
+ self.walk.enter(startTrack=self.walkStartTrack)
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def __decideNextState(self, doneStatus):
+ self.fsm.request('Neutral')
+
+ def setWalk(self, srcNode, destNode, timestamp):
+ if destNode and not destNode == srcNode:
+ self.walk.setWalk(srcNode, destNode, timestamp)
+ self.fsm.request('Walk')
+
+ def walkSpeed(self):
+ return ToontownGlobals.DonaldSpeed
+
+ def handleHolidays(self):
+ DistributedCCharBase.DistributedCCharBase.handleHolidays(self)
+ if hasattr(base.cr, 'newsManager') and base.cr.newsManager:
+ holidayIds = base.cr.newsManager.getHolidayIdList()
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds and isinstance(self.cr.playGame.hood, GSHood.GSHood):
+ self.diffPath = TTLocalizer.Goofy
+
+ def getCCLocation(self):
+ if self.diffPath != None:
+ return 1
+ else:
+ return 0
+ return
+
+ def getCCChatter(self):
+ self.handleHolidays()
+ return self.CCChatter
diff --git a/toontown/classicchars/DistributedDonaldAI.py b/toontown/classicchars/DistributedDonaldAI.py
new file mode 100644
index 0000000..a5acbcb
--- /dev/null
+++ b/toontown/classicchars/DistributedDonaldAI.py
@@ -0,0 +1,169 @@
+from otp.ai.AIBaseGlobal import *
+import DistributedCCharBaseAI
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task import Task
+import random
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import CharStateDatasAI
+
+class DistributedDonaldAI(DistributedCCharBaseAI.DistributedCCharBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedDonaldAI')
+
+ def __init__(self, air):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.__init__(self, air, TTLocalizer.Donald)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedDonaldAI', [
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'Lonely', 'TransitionToCostume', 'Walk']),
+ State.State('Lonely', self.enterLonely, self.exitLonely, [
+ 'Chatty', 'Walk', 'TransitionToCostume']),
+ State.State('Chatty', self.enterChatty, self.exitChatty, [
+ 'Lonely', 'Walk', 'TransitionToCostume']),
+ State.State('Walk', self.enterWalk, self.exitWalk, [
+ 'Lonely', 'Chatty', 'TransitionToCostume']),
+ State.State('TransitionToCostume', self.enterTransitionToCostume, self.exitTransitionToCostume, [
+ 'Off'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBaseAI.DistributedCCharBaseAI.delete(self)
+ self.lonelyDoneEvent = None
+ self.lonely = None
+ self.chattyDoneEvent = None
+ self.chatty = None
+ self.walkDoneEvent = None
+ self.walk = None
+ return
+
+ def generate(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.generate(self)
+ name = self.getName()
+ self.lonelyDoneEvent = self.taskName(name + '-lonely-done')
+ self.lonely = CharStateDatasAI.CharLonelyStateAI(self.lonelyDoneEvent, self)
+ self.chattyDoneEvent = self.taskName(name + '-chatty-done')
+ self.chatty = CharStateDatasAI.CharChattyStateAI(self.chattyDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self, self.diffPath)
+ return
+
+ def walkSpeed(self):
+ return ToontownGlobals.DonaldSpeed
+
+ def start(self):
+ self.fsm.request('Lonely')
+
+ def __decideNextState(self, doneStatus):
+ if self.transitionToCostume == 1:
+ curWalkNode = self.walk.getDestNode()
+ if simbase.air.holidayManager:
+ if ToontownGlobals.HALLOWEEN_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ elif ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ else:
+ self.notify.warning('transitionToCostume == 1 but no costume holiday')
+ else:
+ self.notify.warning('transitionToCostume == 1 but no holiday Manager')
+ if doneStatus['state'] == 'lonely' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'chatty' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'walk' and doneStatus['status'] == 'done':
+ if len(self.nearbyAvatars) > 0:
+ self.fsm.request('Chatty')
+ else:
+ self.fsm.request('Lonely')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.exitOff(self)
+
+ def enterLonely(self):
+ self.lonely.enter()
+ self.acceptOnce(self.lonelyDoneEvent, self.__decideNextState)
+
+ def exitLonely(self):
+ self.ignore(self.lonelyDoneEvent)
+ self.lonely.exit()
+
+ def __goForAWalk(self, task):
+ self.notify.debug('going for a walk')
+ self.fsm.request('Walk')
+ return Task.done
+
+ def enterChatty(self):
+ self.chatty.enter()
+ self.acceptOnce(self.chattyDoneEvent, self.__decideNextState)
+ taskMgr.doMethodLater(CharStateDatasAI.CHATTY_DURATION + 10, self.forceLeaveChatty, self.taskName('forceLeaveChatty'))
+
+ def forceLeaveChatty(self, task):
+ self.notify.warning('Had to force change of state from Chatty state')
+ doneStatus = {}
+ doneStatus['state'] = 'chatty'
+ doneStatus['status'] = 'done'
+ self.__decideNextState(doneStatus)
+
+ def cleanUpChattyTasks(self):
+ taskMgr.removeTasksMatching(self.taskName('forceLeaveChatty'))
+
+ def exitChatty(self):
+ self.ignore(self.chattyDoneEvent)
+ self.chatty.exit()
+ self.cleanUpChattyTasks()
+
+ def enterWalk(self):
+ self.notify.debug('going for a walk')
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def avatarEnterNextState(self):
+ if len(self.nearbyAvatars) == 1:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Chatty')
+ else:
+ self.notify.debug('avatarEnterNextState: in walk state')
+ else:
+ self.notify.debug('avatarEnterNextState: num avatars: ' + str(len(self.nearbyAvatars)))
+
+ def avatarExitNextState(self):
+ if len(self.nearbyAvatars) == 0:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Lonely')
+
+ def handleHolidays(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.handleHolidays(self)
+ if hasattr(simbase.air, 'holidayManager'):
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays:
+ if simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES] != None and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].getRunningState():
+ self.diffPath = TTLocalizer.Goofy
+ return
+
+ def getCCLocation(self):
+ if self.diffPath != None:
+ return 1
+ else:
+ return 0
+ return
+
+ def enterTransitionToCostume(self):
+ pass
+
+ def exitTransitionToCostume(self):
+ pass
diff --git a/toontown/classicchars/DistributedDonaldDock.py b/toontown/classicchars/DistributedDonaldDock.py
new file mode 100644
index 0000000..523d358
--- /dev/null
+++ b/toontown/classicchars/DistributedDonaldDock.py
@@ -0,0 +1,68 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from toontown.toonbase import ToontownGlobals
+import CharStateDatas
+from direct.fsm import StateData
+from direct.task import Task
+from toontown.toonbase import TTLocalizer
+
+class DistributedDonaldDock(DistributedCCharBase.DistributedCCharBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedDonaldDock')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedDonaldDock_initialized
+ except:
+ self.DistributedDonaldDock_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.DonaldDock, 'dw')
+ self.fsm = ClassicFSM.ClassicFSM('DistributedDonaldDock', [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Off'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.nametag.setName(TTLocalizer.Donald)
+ self.handleHolidays()
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBase.DistributedCCharBase.disable(self)
+ taskMgr.remove('enterNeutralTask')
+ del self.neutralDoneEvent
+ del self.neutral
+ self.fsm.requestFinalState()
+
+ def delete(self):
+ try:
+ self.DistributedDonaldDock_deleted
+ except:
+ self.DistributedDonaldDock_deleted = 1
+ del self.fsm
+ DistributedCCharBase.DistributedCCharBase.delete(self)
+
+ def generate(self):
+ DistributedCCharBase.DistributedCCharBase.generate(self)
+ boat = base.cr.playGame.hood.loader.boat
+ self.setPos(0, -1, 3.95)
+ self.reparentTo(boat)
+ self.neutralDoneEvent = self.taskName('DonaldDock-neutral-done')
+ self.neutral = CharStateDatas.CharNeutralState(self.neutralDoneEvent, self)
+ self.fsm.request('Neutral')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterNeutral(self):
+ self.notify.debug('Neutral ' + self.getName() + '...')
+ self.neutral.enter()
+ self.acceptOnce(self.neutralDoneEvent, self.__decideNextState)
+
+ def exitNeutral(self):
+ self.ignore(self.neutralDoneEvent)
+ self.neutral.exit()
+
+ def __decideNextState(self, doneStatus):
+ self.fsm.request('Neutral')
diff --git a/toontown/classicchars/DistributedDonaldDockAI.py b/toontown/classicchars/DistributedDonaldDockAI.py
new file mode 100644
index 0000000..691f899
--- /dev/null
+++ b/toontown/classicchars/DistributedDonaldDockAI.py
@@ -0,0 +1,96 @@
+from otp.ai.AIBaseGlobal import *
+import DistributedCCharBaseAI
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task import Task
+import random, CharStateDatasAI
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+
+class DistributedDonaldDockAI(DistributedCCharBaseAI.DistributedCCharBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedDonaldDockAI')
+
+ def __init__(self, air):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.__init__(self, air, TTLocalizer.DonaldDock)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedDonaldDockAI', [
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'Lonely', 'TransitionToCostume']),
+ State.State('Lonely', self.enterLonely, self.exitLonely, [
+ 'Chatty', 'TransitionToCostume']),
+ State.State('Chatty', self.enterChatty, self.exitChatty, [
+ 'Lonely', 'TransitionToCostume']),
+ State.State('TransitionToCostume', self.enterTransitionToCostume, self.exitTransitionToCostume, [
+ 'Off'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBaseAI.DistributedCCharBaseAI.delete(self)
+ self.lonelyDoneEvent = None
+ self.lonely = None
+ self.chattyDoneEvent = None
+ self.chatty = None
+ return
+
+ def generate(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.generate(self)
+ self.lonelyDoneEvent = self.taskName('DonaldDock-lonely-done')
+ self.lonely = CharStateDatasAI.CharLonelyStateAI(self.lonelyDoneEvent, self)
+ self.chattyDoneEvent = self.taskName('DonaldDock-chatty-done')
+ self.chatty = CharStateDatasAI.CharChattyStateAI(self.chattyDoneEvent, self)
+
+ def start(self):
+ self.fsm.request('Lonely')
+
+ def __decideNextState(self, doneStatus):
+ if doneStatus['state'] == 'lonely' and doneStatus['status'] == 'done':
+ if len(self.nearbyAvatars) > 0:
+ self.fsm.request('Chatty')
+ else:
+ self.fsm.request('Lonely')
+ elif doneStatus['state'] == 'chatty' and doneStatus['status'] == 'done':
+ self.fsm.request('Lonely')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.exitOff(self)
+
+ def enterLonely(self):
+ self.notify.debug('Entering Lonely')
+ self.lonely.enter()
+ self.acceptOnce(self.lonelyDoneEvent, self.__decideNextState)
+
+ def exitLonely(self):
+ self.notify.debug('Exiting Lonely')
+ self.ignore(self.lonelyDoneEvent)
+ self.lonely.exit()
+
+ def enterChatty(self):
+ self.notify.debug('Entering Chatty')
+ self.chatty.enter()
+ self.acceptOnce(self.chattyDoneEvent, self.__decideNextState)
+
+ def exitChatty(self):
+ self.notify.debug('Exiting Chatty')
+ self.ignore(self.chattyDoneEvent)
+ self.chatty.exit()
+
+ def avatarEnterNextState(self):
+ if len(self.nearbyAvatars) == 1:
+ self.fsm.request('Chatty')
+ else:
+ self.notify.debug('avatarEnterNextState: num avatars: ' + str(len(self.nearbyAvatars)))
+
+ def avatarExitNextState(self):
+ if len(self.nearbyAvatars) == 0:
+ self.fsm.request('Lonely')
+
+ def enterTransitionToCostume(self):
+ pass
+
+ def exitTransitionToCostume(self):
+ pass
diff --git a/toontown/classicchars/DistributedFrankenDonald.py b/toontown/classicchars/DistributedFrankenDonald.py
new file mode 100644
index 0000000..e10ebc1
--- /dev/null
+++ b/toontown/classicchars/DistributedFrankenDonald.py
@@ -0,0 +1,64 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+import DistributedCCharBase
+import DistributedDonald
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+import CharStateDatas
+import CCharChatter
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.hood import GSHood
+
+class DistributedFrankenDonald(DistributedDonald.DistributedDonald):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFrankenDonald')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedFrankenDonald_initialized
+ except:
+ self.DistributedFrankenDonald_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.FrankenDonald, 'fd')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.nametag.setName(TTLocalizer.Donald)
+ self.handleHolidays()
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBase.DistributedCCharBase.disable(self)
+ del self.neutralDoneEvent
+ del self.neutral
+ del self.walkDoneEvent
+ del self.walk
+ self.fsm.requestFinalState()
+
+ def generate(self):
+ DistributedCCharBase.DistributedCCharBase.generate(self, self.diffPath)
+ name = self.getName()
+ self.neutralDoneEvent = self.taskName(name + '-neutral-done')
+ self.neutral = CharStateDatas.CharNeutralState(self.neutralDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self, self.diffPath)
+ self.fsm.request('Neutral')
+ return
+
+ def enterNeutral(self):
+ self.notify.debug('Neutral ' + self.getName() + '...')
+ self.neutral.enter()
+ self.acceptOnce(self.neutralDoneEvent, self.__decideNextState)
+
+ def enterWalk(self):
+ self.notify.debug('Walking ' + self.getName() + '...')
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def __decideNextState(self, doneStatus):
+ self.fsm.request('Neutral')
+
+ def walkSpeed(self):
+ return ToontownGlobals.FrankenDonaldSpeed
diff --git a/toontown/classicchars/DistributedGoofy.py b/toontown/classicchars/DistributedGoofy.py
new file mode 100644
index 0000000..d3a3296
--- /dev/null
+++ b/toontown/classicchars/DistributedGoofy.py
@@ -0,0 +1,79 @@
+from pandac.PandaModules import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+
+class DistributedGoofy(DistributedCCharBase.DistributedCCharBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoofy')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedGoofy_initialized
+ except:
+ self.DistributedGoofy_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.Goofy, 'g')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBase.DistributedCCharBase.disable(self)
+ del self.neutralDoneEvent
+ del self.neutral
+ del self.walkDoneEvent
+ del self.walk
+ self.fsm.requestFinalState()
+
+ def delete(self):
+ try:
+ self.DistributedGoofy_deleted
+ except:
+ del self.fsm
+ self.DistributedGoofy_deleted = 1
+ DistributedCCharBase.DistributedCCharBase.delete(self)
+
+ def generate(self):
+ DistributedCCharBase.DistributedCCharBase.generate(self)
+ name = self.getName()
+ self.neutralDoneEvent = self.taskName(name + '-neutral-done')
+ self.neutral = CharStateDatas.CharNeutralState(self.neutralDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self)
+ self.fsm.request('Neutral')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterNeutral(self):
+ self.neutral.enter()
+ self.acceptOnce(self.neutralDoneEvent, self.__decideNextState)
+
+ def exitNeutral(self):
+ self.ignore(self.neutralDoneEvent)
+ self.neutral.exit()
+
+ def enterWalk(self):
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def __decideNextState(self, doneStatus):
+ self.fsm.request('Neutral')
+
+ def setWalk(self, srcNode, destNode, timestamp):
+ if destNode and not destNode == srcNode:
+ self.walk.setWalk(srcNode, destNode, timestamp)
+ self.fsm.request('Walk')
+
+ def walkSpeed(self):
+ return ToontownGlobals.GoofySpeed
diff --git a/toontown/classicchars/DistributedGoofyAI.py b/toontown/classicchars/DistributedGoofyAI.py
new file mode 100644
index 0000000..894ada3
--- /dev/null
+++ b/toontown/classicchars/DistributedGoofyAI.py
@@ -0,0 +1,127 @@
+from otp.ai.AIBaseGlobal import *
+import DistributedCCharBaseAI
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task import Task
+import random
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import CharStateDatasAI
+
+class DistributedGoofyAI(DistributedCCharBaseAI.DistributedCCharBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoofyAI')
+
+ def __init__(self, air):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.__init__(self, air, TTLocalizer.Goofy)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedGoofyAI', [
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'Lonely']),
+ State.State('Lonely', self.enterLonely, self.exitLonely, [
+ 'Chatty', 'Walk']),
+ State.State('Chatty', self.enterChatty, self.exitChatty, [
+ 'Lonely', 'Walk']),
+ State.State('Walk', self.enterWalk, self.exitWalk, [
+ 'Lonely', 'Chatty'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBaseAI.DistributedCCharBaseAI.delete(self)
+ self.lonelyDoneEvent = None
+ self.lonely = None
+ self.chattyDoneEvent = None
+ self.chatty = None
+ self.walkDoneEvent = None
+ self.walk = None
+ return
+
+ def generate(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.generate(self)
+ name = self.getName()
+ self.lonelyDoneEvent = self.taskName(name + '-lonely-done')
+ self.lonely = CharStateDatasAI.CharLonelyStateAI(self.lonelyDoneEvent, self)
+ self.chattyDoneEvent = self.taskName(name + '-chatty-done')
+ self.chatty = CharStateDatasAI.CharChattyStateAI(self.chattyDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self)
+
+ def walkSpeed(self):
+ return ToontownGlobals.GoofySpeed
+
+ def start(self):
+ self.fsm.request('Lonely')
+
+ def __decideNextState(self, doneStatus):
+ if doneStatus['state'] == 'lonely' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'chatty' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'walk' and doneStatus['status'] == 'done':
+ if len(self.nearbyAvatars) > 0:
+ self.fsm.request('Chatty')
+ else:
+ self.fsm.request('Lonely')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.exitOff(self)
+
+ def enterLonely(self):
+ self.lonely.enter()
+ self.acceptOnce(self.lonelyDoneEvent, self.__decideNextState)
+
+ def exitLonely(self):
+ self.ignore(self.lonelyDoneEvent)
+ self.lonely.exit()
+
+ def __goForAWalk(self, task):
+ self.notify.debug('going for a walk')
+ self.fsm.request('Walk')
+ return Task.done
+
+ def enterChatty(self):
+ self.chatty.enter()
+ self.acceptOnce(self.chattyDoneEvent, self.__decideNextState)
+ taskMgr.doMethodLater(CharStateDatasAI.CHATTY_DURATION + 10, self.forceLeaveChatty, self.taskName('forceLeaveChatty'))
+
+ def forceLeaveChatty(self, task):
+ self.notify.warning('Had to force change of state from Chatty state')
+ doneStatus = {}
+ doneStatus['state'] = 'chatty'
+ doneStatus['status'] = 'done'
+ self.__decideNextState(doneStatus)
+ return Task.done
+
+ def cleanUpChattyTasks(self):
+ taskMgr.removeTasksMatching(self.taskName('forceLeaveChatty'))
+
+ def exitChatty(self):
+ self.ignore(self.chattyDoneEvent)
+ self.chatty.exit()
+ self.cleanUpChattyTasks()
+
+ def enterWalk(self):
+ self.notify.debug('going for a walk')
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def avatarEnterNextState(self):
+ if len(self.nearbyAvatars) == 1:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Chatty')
+ else:
+ self.notify.debug('avatarEnterNextState: in walk state')
+ else:
+ self.notify.debug('avatarEnterNextState: num avatars: ' + str(len(self.nearbyAvatars)))
+
+ def avatarExitNextState(self):
+ if len(self.nearbyAvatars) == 0:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Lonely')
diff --git a/toontown/classicchars/DistributedGoofySpeedway.py b/toontown/classicchars/DistributedGoofySpeedway.py
new file mode 100644
index 0000000..3512e17
--- /dev/null
+++ b/toontown/classicchars/DistributedGoofySpeedway.py
@@ -0,0 +1,100 @@
+from pandac.PandaModules import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.hood import DLHood
+
+class DistributedGoofySpeedway(DistributedCCharBase.DistributedCCharBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoofySpeedway')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedGoofySpeedway_initialized
+ except:
+ self.DistributedGoofySpeedway_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.Goofy, 'g')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+
+ self.handleHolidays()
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBase.DistributedCCharBase.disable(self)
+ del self.neutralDoneEvent
+ del self.neutral
+ del self.walkDoneEvent
+ del self.walk
+ self.fsm.requestFinalState()
+
+ def delete(self):
+ try:
+ self.DistributedGoofySpeedway_deleted
+ except:
+ del self.fsm
+ self.DistributedGoofySpeedway_deleted = 1
+ DistributedCCharBase.DistributedCCharBase.delete(self)
+
+ def generate(self):
+ DistributedCCharBase.DistributedCCharBase.generate(self, self.diffPath)
+ name = self.getName()
+ self.neutralDoneEvent = self.taskName(name + '-neutral-done')
+ self.neutral = CharStateDatas.CharNeutralState(self.neutralDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self, self.diffPath)
+ self.fsm.request('Neutral')
+ return
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterNeutral(self):
+ self.neutral.enter()
+ self.acceptOnce(self.neutralDoneEvent, self.__decideNextState)
+
+ def exitNeutral(self):
+ self.ignore(self.neutralDoneEvent)
+ self.neutral.exit()
+
+ def enterWalk(self):
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def __decideNextState(self, doneStatus):
+ self.fsm.request('Neutral')
+
+ def setWalk(self, srcNode, destNode, timestamp):
+ if destNode and not destNode == srcNode:
+ self.walk.setWalk(srcNode, destNode, timestamp)
+ self.fsm.request('Walk')
+
+ def walkSpeed(self):
+ return ToontownGlobals.GoofySpeed
+
+ def handleHolidays(self):
+ DistributedCCharBase.DistributedCCharBase.handleHolidays(self)
+ if hasattr(base.cr, 'newsManager') and base.cr.newsManager:
+ holidayIds = base.cr.newsManager.getHolidayIdList()
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds and isinstance(self.cr.playGame.hood, DLHood.DLHood):
+ self.diffPath = TTLocalizer.Donald
+
+ def getCCLocation(self):
+ if self.diffPath == None:
+ return 1
+ else:
+ return 0
+ return
diff --git a/toontown/classicchars/DistributedGoofySpeedwayAI.py b/toontown/classicchars/DistributedGoofySpeedwayAI.py
new file mode 100644
index 0000000..24d0ae1
--- /dev/null
+++ b/toontown/classicchars/DistributedGoofySpeedwayAI.py
@@ -0,0 +1,170 @@
+from otp.ai.AIBaseGlobal import *
+import DistributedCCharBaseAI
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task import Task
+import random
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import CharStateDatasAI
+
+class DistributedGoofySpeedwayAI(DistributedCCharBaseAI.DistributedCCharBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoofySpeedwayAI')
+
+ def __init__(self, air):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.__init__(self, air, TTLocalizer.Goofy)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedGoofySpeedwayAI', [
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'Lonely', 'TransitionToCostume', 'Walk']),
+ State.State('Lonely', self.enterLonely, self.exitLonely, [
+ 'Chatty', 'Walk', 'TransitionToCostume']),
+ State.State('Chatty', self.enterChatty, self.exitChatty, [
+ 'Lonely', 'Walk', 'TransitionToCostume']),
+ State.State('Walk', self.enterWalk, self.exitWalk, [
+ 'Lonely', 'Chatty', 'TransitionToCostume']),
+ State.State('TransitionToCostume', self.enterTransitionToCostume, self.exitTransitionToCostume, [
+ 'Off'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBaseAI.DistributedCCharBaseAI.delete(self)
+ self.lonelyDoneEvent = None
+ self.lonely = None
+ self.chattyDoneEvent = None
+ self.chatty = None
+ self.walkDoneEvent = None
+ self.walk = None
+ return
+
+ def generate(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.generate(self)
+ name = self.getName()
+ self.lonelyDoneEvent = self.taskName(name + '-lonely-done')
+ self.lonely = CharStateDatasAI.CharLonelyStateAI(self.lonelyDoneEvent, self)
+ self.chattyDoneEvent = self.taskName(name + '-chatty-done')
+ self.chatty = CharStateDatasAI.CharChattyStateAI(self.chattyDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self, self.diffPath)
+ return
+
+ def walkSpeed(self):
+ return ToontownGlobals.GoofySpeed
+
+ def start(self):
+ self.fsm.request('Lonely')
+
+ def __decideNextState(self, doneStatus):
+ if self.transitionToCostume == 1:
+ curWalkNode = self.walk.getDestNode()
+ if simbase.air.holidayManager:
+ if ToontownGlobals.HALLOWEEN_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ elif ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ else:
+ self.notify.warning('transitionToCostume == 1 but no costume holiday')
+ else:
+ self.notify.warning('transitionToCostume == 1 but no holiday Manager')
+ if doneStatus['state'] == 'lonely' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'chatty' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'walk' and doneStatus['status'] == 'done':
+ if len(self.nearbyAvatars) > 0:
+ self.fsm.request('Chatty')
+ else:
+ self.fsm.request('Lonely')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.exitOff(self)
+
+ def enterLonely(self):
+ self.lonely.enter()
+ self.acceptOnce(self.lonelyDoneEvent, self.__decideNextState)
+
+ def exitLonely(self):
+ self.ignore(self.lonelyDoneEvent)
+ self.lonely.exit()
+
+ def __goForAWalk(self, task):
+ self.notify.debug('going for a walk')
+ self.fsm.request('Walk')
+ return Task.done
+
+ def enterChatty(self):
+ self.chatty.enter()
+ self.acceptOnce(self.chattyDoneEvent, self.__decideNextState)
+ taskMgr.doMethodLater(CharStateDatasAI.CHATTY_DURATION + 10, self.forceLeaveChatty, self.taskName('forceLeaveChatty'))
+
+ def forceLeaveChatty(self, task):
+ self.notify.warning('Had to force change of state from Chatty state')
+ doneStatus = {}
+ doneStatus['state'] = 'chatty'
+ doneStatus['status'] = 'done'
+ self.__decideNextState(doneStatus)
+ return Task.done
+
+ def cleanUpChattyTasks(self):
+ taskMgr.removeTasksMatching(self.taskName('forceLeaveChatty'))
+
+ def exitChatty(self):
+ self.ignore(self.chattyDoneEvent)
+ self.chatty.exit()
+ self.cleanUpChattyTasks()
+
+ def enterWalk(self):
+ self.notify.debug('going for a walk')
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def avatarEnterNextState(self):
+ if len(self.nearbyAvatars) == 1:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Chatty')
+ else:
+ self.notify.debug('avatarEnterNextState: in walk state')
+ else:
+ self.notify.debug('avatarEnterNextState: num avatars: ' + str(len(self.nearbyAvatars)))
+
+ def avatarExitNextState(self):
+ if len(self.nearbyAvatars) == 0:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Lonely')
+
+ def handleHolidays(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.handleHolidays(self)
+ if hasattr(simbase.air, 'holidayManager'):
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays:
+ if simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES] != None and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].getRunningState():
+ self.diffPath = TTLocalizer.Donald
+ return
+
+ def getCCLocation(self):
+ if self.diffPath == None:
+ return 1
+ else:
+ return 0
+ return
+
+ def enterTransitionToCostume(self):
+ pass
+
+ def exitTransitionToCostume(self):
+ pass
diff --git a/toontown/classicchars/DistributedJailbirdDale.py b/toontown/classicchars/DistributedJailbirdDale.py
new file mode 100644
index 0000000..74375d3
--- /dev/null
+++ b/toontown/classicchars/DistributedJailbirdDale.py
@@ -0,0 +1,23 @@
+from direct.showbase.ShowBaseGlobal import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import DistributedDale
+
+class DistributedJailbirdDale(DistributedDale.DistributedDale):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedJailbirdDale')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedDale_initialized
+ except:
+ self.DistributedDale_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.JailbirdDale, 'jda')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+ self.nametag.setName(TTLocalizer.Dale)
diff --git a/toontown/classicchars/DistributedMickey.py b/toontown/classicchars/DistributedMickey.py
new file mode 100644
index 0000000..9aed6e6
--- /dev/null
+++ b/toontown/classicchars/DistributedMickey.py
@@ -0,0 +1,96 @@
+from pandac.PandaModules import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.hood import DGHood
+
+class DistributedMickey(DistributedCCharBase.DistributedCCharBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMickey')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedMickey_initialized
+ except:
+ self.DistributedMickey_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.Mickey, 'mk')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBase.DistributedCCharBase.disable(self)
+ self.neutralDoneEvent = None
+ self.neutral = None
+ self.walkDoneEvent = None
+ self.walk = None
+ self.fsm.requestFinalState()
+ self.notify.debug('Mickey Disbled')
+ return
+
+ def delete(self):
+ try:
+ self.DistributedMickey_deleted
+ except:
+ self.DistributedMickey_deleted = 1
+ del self.fsm
+ DistributedCCharBase.DistributedCCharBase.delete(self)
+
+ self.notify.debug('Mickey Deleted')
+
+ def generate(self):
+ DistributedCCharBase.DistributedCCharBase.generate(self, self.diffPath)
+ name = self.getName()
+ self.neutralDoneEvent = self.taskName(name + '-neutral-done')
+ self.neutral = CharStateDatas.CharNeutralState(self.neutralDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self, self.diffPath)
+ self.fsm.request('Neutral')
+ return
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterNeutral(self):
+ self.neutral.enter()
+ self.acceptOnce(self.neutralDoneEvent, self.__decideNextState)
+
+ def exitNeutral(self):
+ self.ignore(self.neutralDoneEvent)
+ self.neutral.exit()
+
+ def enterWalk(self):
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def __decideNextState(self, doneStatus):
+ self.fsm.request('Neutral')
+
+ def setWalk(self, srcNode, destNode, timestamp):
+ if destNode and not destNode == srcNode:
+ self.walk.setWalk(srcNode, destNode, timestamp)
+ self.fsm.request('Walk')
+
+ def walkSpeed(self):
+ return ToontownGlobals.MickeySpeed
+
+ def handleHolidays(self):
+ DistributedCCharBase.DistributedCCharBase.handleHolidays(self)
+ if hasattr(base.cr, 'newsManager') and base.cr.newsManager:
+ holidayIds = base.cr.newsManager.getHolidayIdList()
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds and isinstance(self.cr.playGame.hood, DGHood.DGHood):
+ self.diffPath = TTLocalizer.Daisy
diff --git a/toontown/classicchars/DistributedMickeyAI.py b/toontown/classicchars/DistributedMickeyAI.py
new file mode 100644
index 0000000..0b07d6b
--- /dev/null
+++ b/toontown/classicchars/DistributedMickeyAI.py
@@ -0,0 +1,164 @@
+from otp.ai.AIBaseGlobal import *
+import DistributedCCharBaseAI
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task import Task
+import random
+from toontown.toonbase import ToontownGlobals
+import CharStateDatasAI
+from toontown.toonbase import TTLocalizer
+
+class DistributedMickeyAI(DistributedCCharBaseAI.DistributedCCharBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMickeyAI')
+
+ def __init__(self, air):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.__init__(self, air, TTLocalizer.Mickey)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedMickeyAI', [
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'Lonely', 'TransitionToCostume', 'Walk']),
+ State.State('Lonely', self.enterLonely, self.exitLonely, [
+ 'Chatty', 'Walk', 'TransitionToCostume']),
+ State.State('Chatty', self.enterChatty, self.exitChatty, [
+ 'Lonely', 'Walk', 'TransitionToCostume']),
+ State.State('Walk', self.enterWalk, self.exitWalk, [
+ 'Lonely', 'Chatty', 'TransitionToCostume']),
+ State.State('TransitionToCostume', self.enterTransitionToCostume, self.exitTransitionToCostume, [
+ 'Off'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ del self.fsm
+ DistributedCCharBaseAI.DistributedCCharBaseAI.delete(self)
+ self.lonelyDoneEvent = None
+ self.lonely = None
+ self.chattyDoneEvent = None
+ self.chatty = None
+ self.walkDoneEvent = None
+ self.walk = None
+ self.notify.debug('MickeyAI Deleted')
+ return
+
+ def generate(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.generate(self)
+ name = self.getName()
+ self.lonelyDoneEvent = self.taskName(name + '-lonely-done')
+ self.lonely = CharStateDatasAI.CharLonelyStateAI(self.lonelyDoneEvent, self)
+ self.chattyDoneEvent = self.taskName(name + '-chatty-done')
+ self.chatty = CharStateDatasAI.CharChattyStateAI(self.chattyDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self, self.diffPath)
+ return
+
+ def walkSpeed(self):
+ return ToontownGlobals.MickeySpeed
+
+ def start(self):
+ self.fsm.request('Lonely')
+
+ def __decideNextState(self, doneStatus):
+ if self.transitionToCostume == 1:
+ curWalkNode = self.walk.getDestNode()
+ if simbase.air.holidayManager:
+ if ToontownGlobals.HALLOWEEN_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ elif ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ else:
+ self.notify.warning('transitionToCostume == 1 but no costume holiday')
+ else:
+ self.notify.warning('transitionToCostume == 1 but no holiday Manager')
+ if doneStatus['state'] == 'lonely' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'chatty' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'walk' and doneStatus['status'] == 'done':
+ if len(self.nearbyAvatars) > 0:
+ self.fsm.request('Chatty')
+ else:
+ self.fsm.request('Lonely')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.exitOff(self)
+
+ def enterLonely(self):
+ self.lonely.enter()
+ self.acceptOnce(self.lonelyDoneEvent, self.__decideNextState)
+
+ def exitLonely(self):
+ self.ignore(self.lonelyDoneEvent)
+ self.lonely.exit()
+
+ def __goForAWalk(self, task):
+ self.notify.debug('going for a walk')
+ self.fsm.request('Walk')
+ return Task.done
+
+ def enterChatty(self):
+ self.chatty.enter()
+ self.acceptOnce(self.chattyDoneEvent, self.__decideNextState)
+ taskMgr.doMethodLater(CharStateDatasAI.CHATTY_DURATION + 10, self.forceLeaveChatty, self.taskName('forceLeaveChatty'))
+
+ def forceLeaveChatty(self, task):
+ self.notify.warning('Had to force change of state from Chatty state')
+ doneStatus = {}
+ doneStatus['state'] = 'chatty'
+ doneStatus['status'] = 'done'
+ self.__decideNextState(doneStatus)
+ return Task.done
+
+ def cleanUpChattyTasks(self):
+ taskMgr.removeTasksMatching(self.taskName('forceLeaveChatty'))
+
+ def exitChatty(self):
+ self.ignore(self.chattyDoneEvent)
+ self.chatty.exit()
+ self.cleanUpChattyTasks()
+
+ def enterWalk(self):
+ self.notify.debug('going for a walk')
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def avatarEnterNextState(self):
+ if len(self.nearbyAvatars) == 1:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Chatty')
+ else:
+ self.notify.debug('avatarEnterNextState: in walk state')
+ else:
+ self.notify.debug('avatarEnterNextState: num avatars: ' + str(len(self.nearbyAvatars)))
+
+ def avatarExitNextState(self):
+ if len(self.nearbyAvatars) == 0:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Lonely')
+
+ def enterTransitionToCostume(self):
+ pass
+
+ def exitTransitionToCostume(self):
+ pass
+
+ def handleHolidays(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.handleHolidays(self)
+ if hasattr(simbase.air, 'holidayManager'):
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES] != None and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].getRunningState():
+ self.diffPath = TTLocalizer.Daisy
+ return
diff --git a/toontown/classicchars/DistributedMinnie.py b/toontown/classicchars/DistributedMinnie.py
new file mode 100644
index 0000000..6f01360
--- /dev/null
+++ b/toontown/classicchars/DistributedMinnie.py
@@ -0,0 +1,93 @@
+from pandac.PandaModules import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.hood import BRHood
+
+class DistributedMinnie(DistributedCCharBase.DistributedCCharBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinnie')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedMinnie_initialized
+ except:
+ self.DistributedMinnie_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.Minnie, 'mn')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+
+ self.handleHolidays()
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBase.DistributedCCharBase.disable(self)
+ self.neutralDoneEvent = None
+ self.neutral = None
+ self.walkDoneEvent = None
+ self.walk = None
+ self.fsm.requestFinalState()
+ return
+
+ def delete(self):
+ try:
+ self.DistributedMinnie_deleted
+ except:
+ self.DistributedMinnie_deleted = 1
+ del self.fsm
+ DistributedCCharBase.DistributedCCharBase.delete(self)
+
+ def generate(self):
+ DistributedCCharBase.DistributedCCharBase.generate(self, self.diffPath)
+ self.neutralDoneEvent = self.taskName('minnie-neutral-done')
+ self.neutral = CharStateDatas.CharNeutralState(self.neutralDoneEvent, self)
+ self.walkDoneEvent = self.taskName('minnie-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self, self.diffPath)
+ self.fsm.request('Neutral')
+ return
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterNeutral(self):
+ self.neutral.enter()
+ self.acceptOnce(self.neutralDoneEvent, self.__decideNextState)
+
+ def exitNeutral(self):
+ self.ignore(self.neutralDoneEvent)
+ self.neutral.exit()
+
+ def enterWalk(self):
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def __decideNextState(self, doneStatus):
+ self.fsm.request('Neutral')
+
+ def setWalk(self, srcNode, destNode, timestamp):
+ if destNode and not destNode == srcNode:
+ self.walk.setWalk(srcNode, destNode, timestamp)
+ self.fsm.request('Walk')
+
+ def walkSpeed(self):
+ return ToontownGlobals.MinnieSpeed
+
+ def handleHolidays(self):
+ DistributedCCharBase.DistributedCCharBase.handleHolidays(self)
+ if hasattr(base.cr, 'newsManager') and base.cr.newsManager:
+ holidayIds = base.cr.newsManager.getHolidayIdList()
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds and isinstance(self.cr.playGame.hood, BRHood.BRHood):
+ self.diffPath = TTLocalizer.Pluto
diff --git a/toontown/classicchars/DistributedMinnieAI.py b/toontown/classicchars/DistributedMinnieAI.py
new file mode 100644
index 0000000..ad1c43a
--- /dev/null
+++ b/toontown/classicchars/DistributedMinnieAI.py
@@ -0,0 +1,162 @@
+from otp.ai.AIBaseGlobal import *
+import DistributedCCharBaseAI
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task import Task
+import random
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import CharStateDatasAI
+
+class DistributedMinnieAI(DistributedCCharBaseAI.DistributedCCharBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinnieAI')
+
+ def __init__(self, air):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.__init__(self, air, TTLocalizer.Minnie)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedMinnieAI', [
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'Lonely', 'TransitionToCostume', 'Walk']),
+ State.State('Lonely', self.enterLonely, self.exitLonely, [
+ 'Chatty', 'Walk', 'TransitionToCostume']),
+ State.State('Chatty', self.enterChatty, self.exitChatty, [
+ 'Lonely', 'Walk', 'TransitionToCostume']),
+ State.State('Walk', self.enterWalk, self.exitWalk, [
+ 'Lonely', 'Chatty', 'TransitionToCostume']),
+ State.State('TransitionToCostume', self.enterTransitionToCostume, self.exitTransitionToCostume, [
+ 'Off'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBaseAI.DistributedCCharBaseAI.delete(self)
+ self.lonelyDoneEvent = None
+ self.lonely = None
+ self.chattyDoneEvent = None
+ self.chatty = None
+ self.walkDoneEvent = None
+ self.walk = None
+ return
+
+ def generate(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.generate(self)
+ name = self.getName()
+ self.lonelyDoneEvent = self.taskName(name + '-lonely-done')
+ self.lonely = CharStateDatasAI.CharLonelyStateAI(self.lonelyDoneEvent, self)
+ self.chattyDoneEvent = self.taskName(name + '-chatty-done')
+ self.chatty = CharStateDatasAI.CharChattyStateAI(self.chattyDoneEvent, self)
+ self.walkDoneEvent = self.taskName(name + '-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self, self.diffPath)
+ return
+
+ def walkSpeed(self):
+ return ToontownGlobals.MinnieSpeed
+
+ def start(self):
+ self.fsm.request('Lonely')
+
+ def __decideNextState(self, doneStatus):
+ if self.transitionToCostume == 1:
+ curWalkNode = self.walk.getDestNode()
+ if simbase.air.holidayManager:
+ if ToontownGlobals.HALLOWEEN_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ elif ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ else:
+ self.notify.warning('transitionToCostume == 1 but no costume holiday')
+ else:
+ self.notify.warning('transitionToCostume == 1 but no holiday Manager')
+ if doneStatus['state'] == 'lonely' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'chatty' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'walk' and doneStatus['status'] == 'done':
+ if len(self.nearbyAvatars) > 0:
+ self.fsm.request('Chatty')
+ else:
+ self.fsm.request('Lonely')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.exitOff(self)
+
+ def enterLonely(self):
+ self.lonely.enter()
+ self.acceptOnce(self.lonelyDoneEvent, self.__decideNextState)
+
+ def exitLonely(self):
+ self.ignore(self.lonelyDoneEvent)
+ self.lonely.exit()
+
+ def __goForAWalk(self, task):
+ self.notify.debug('going for a walk')
+ self.fsm.request('Walk')
+ return Task.done
+
+ def enterChatty(self):
+ self.chatty.enter()
+ self.acceptOnce(self.chattyDoneEvent, self.__decideNextState)
+ taskMgr.doMethodLater(CharStateDatasAI.CHATTY_DURATION + 10, self.forceLeaveChatty, self.taskName('forceLeaveChatty'))
+
+ def forceLeaveChatty(self, task):
+ self.notify.warning('Had to force change of state from Chatty state')
+ doneStatus = {}
+ doneStatus['state'] = 'chatty'
+ doneStatus['status'] = 'done'
+ self.__decideNextState(doneStatus)
+ return Task.done
+
+ def cleanUpChattyTasks(self):
+ taskMgr.removeTasksMatching(self.taskName('forceLeaveChatty'))
+
+ def exitChatty(self):
+ self.ignore(self.chattyDoneEvent)
+ self.chatty.exit()
+ self.cleanUpChattyTasks()
+
+ def enterWalk(self):
+ self.notify.debug('going for a walk')
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def avatarEnterNextState(self):
+ if len(self.nearbyAvatars) == 1:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Chatty')
+ else:
+ self.notify.debug('avatarEnterNextState: in walk state')
+ else:
+ self.notify.debug('avatarEnterNextState: num avatars: ' + str(len(self.nearbyAvatars)))
+
+ def avatarExitNextState(self):
+ if len(self.nearbyAvatars) == 0:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Lonely')
+
+ def handleHolidays(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.handleHolidays(self)
+ if hasattr(simbase.air, 'holidayManager'):
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES] != None and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].getRunningState():
+ self.diffPath = TTLocalizer.Pluto
+ return
+
+ def enterTransitionToCostume(self):
+ pass
+
+ def exitTransitionToCostume(self):
+ pass
diff --git a/toontown/classicchars/DistributedPluto.py b/toontown/classicchars/DistributedPluto.py
new file mode 100644
index 0000000..2bffb28
--- /dev/null
+++ b/toontown/classicchars/DistributedPluto.py
@@ -0,0 +1,119 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from toontown.toonbase import ToontownGlobals
+import CharStateDatas
+from direct.fsm import StateData
+from direct.task import Task
+from toontown.toonbase import TTLocalizer
+from toontown.hood import MMHood
+
+class DistributedPluto(DistributedCCharBase.DistributedCCharBase):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPluto')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedPluto_initialized
+ except:
+ self.DistributedPluto_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.Pluto, 'p')
+ self.fsm = ClassicFSM.ClassicFSM('DistributedPluto', [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBase.DistributedCCharBase.disable(self)
+ taskMgr.remove('enterNeutralTask')
+ taskMgr.remove('enterWalkTask')
+ del self.neutralDoneEvent
+ del self.neutral
+ del self.walkDoneEvent
+ del self.walk
+ del self.neutralStartTrack
+ del self.walkStartTrack
+ self.fsm.requestFinalState()
+
+ def delete(self):
+ try:
+ self.DistributedPluto_deleted
+ except:
+ self.DistributedPluto_deleted = 1
+ del self.fsm
+ DistributedCCharBase.DistributedCCharBase.delete(self)
+
+ def generate(self):
+ DistributedCCharBase.DistributedCCharBase.generate(self, self.diffPath)
+ self.neutralDoneEvent = self.taskName('pluto-neutral-done')
+ self.neutral = CharStateDatas.CharNeutralState(self.neutralDoneEvent, self)
+ self.walkDoneEvent = self.taskName('pluto-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatas.CharWalkState(self.walkDoneEvent, self, self.diffPath)
+ self.walkStartTrack = Sequence(self.actorInterval('stand'), Func(self.stand))
+ self.neutralStartTrack = Sequence(self.actorInterval('sit'), Func(self.sit))
+ self.fsm.request('Neutral')
+ return
+
+ def stand(self):
+ self.dropShadow.setScale(0.9, 1.35, 0.9)
+ if hasattr(self, 'collNodePath'):
+ self.collNodePath.setScale(1.0, 1.5, 1.0)
+
+ def sit(self):
+ self.dropShadow.setScale(0.9)
+ if hasattr(self, 'collNodePath'):
+ self.collNodePath.setScale(1.0)
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterNeutral(self):
+ self.notify.debug('Neutral ' + self.getName() + '...')
+ self.neutral.enter(self.neutralStartTrack)
+ self.acceptOnce(self.neutralDoneEvent, self.__decideNextState)
+
+ def exitNeutral(self):
+ self.ignore(self.neutralDoneEvent)
+ self.neutral.exit()
+
+ def enterWalk(self):
+ self.notify.debug('Walking ' + self.getName() + '...')
+ self.walk.enter(self.walkStartTrack)
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def __decideNextState(self, doneStatus):
+ self.fsm.request('Neutral')
+
+ def setWalk(self, srcNode, destNode, timestamp):
+ if destNode and not destNode == srcNode:
+ self.walk.setWalk(srcNode, destNode, timestamp)
+ self.fsm.request('Walk')
+
+ def walkSpeed(self):
+ return ToontownGlobals.PlutoSpeed
+
+ def handleHolidays(self):
+ DistributedCCharBase.DistributedCCharBase.handleHolidays(self)
+ if hasattr(base.cr, 'newsManager') and base.cr.newsManager:
+ holidayIds = base.cr.newsManager.getHolidayIdList()
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds and isinstance(self.cr.playGame.hood, MMHood.MMHood):
+ self.diffPath = TTLocalizer.Minnie
+
+ def getCCLocation(self):
+ if self.diffPath == None:
+ return 1
+ else:
+ return 0
+ return
diff --git a/toontown/classicchars/DistributedPlutoAI.py b/toontown/classicchars/DistributedPlutoAI.py
new file mode 100644
index 0000000..456c8f4
--- /dev/null
+++ b/toontown/classicchars/DistributedPlutoAI.py
@@ -0,0 +1,155 @@
+from otp.ai.AIBaseGlobal import *
+import DistributedCCharBaseAI
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from direct.task import Task
+import random, CharStateDatasAI, CCharChatter
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+
+class DistributedPlutoAI(DistributedCCharBaseAI.DistributedCCharBaseAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPlutoAI')
+
+ def __init__(self, air):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.__init__(self, air, TTLocalizer.Pluto)
+ self.fsm = ClassicFSM.ClassicFSM('DistributedPlutoAI', [
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'Lonely', 'TransitionToCostume', 'Walk']),
+ State.State('Lonely', self.enterLonely, self.exitLonely, [
+ 'Chatty', 'Walk', 'TransitionToCostume']),
+ State.State('Chatty', self.enterChatty, self.exitChatty, [
+ 'Lonely', 'Walk', 'TransitionToCostume']),
+ State.State('Walk', self.enterWalk, self.exitWalk, [
+ 'Lonely', 'Chatty', 'TransitionToCostume']),
+ State.State('TransitionToCostume', self.enterTransitionToCostume, self.exitTransitionToCostume, [
+ 'Off'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+
+ def delete(self):
+ self.fsm.requestFinalState()
+ DistributedCCharBaseAI.DistributedCCharBaseAI.delete(self)
+ self.lonelyDoneEvent = None
+ self.lonely = None
+ self.chattyDoneEvent = None
+ self.chatty = None
+ self.walkDoneEvent = None
+ self.walk = None
+ return
+
+ def generate(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.generate(self)
+ self.lonelyDoneEvent = self.taskName('pluto-lonely-done')
+ self.lonely = CharStateDatasAI.CharLonelyStateAI(self.lonelyDoneEvent, self)
+ self.chattyDoneEvent = self.taskName('pluto-chatty-done')
+ self.chatty = CharStateDatasAI.CharChattyStateAI(self.chattyDoneEvent, self)
+ self.walkDoneEvent = self.taskName('pluto-walk-done')
+ if self.diffPath == None:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self)
+ else:
+ self.walk = CharStateDatasAI.CharWalkStateAI(self.walkDoneEvent, self, self.diffPath)
+ return
+
+ def walkSpeed(self):
+ return ToontownGlobals.PlutoSpeed
+
+ def start(self):
+ self.fsm.request('Lonely')
+
+ def __decideNextState(self, doneStatus):
+ if self.transitionToCostume == 1:
+ curWalkNode = self.walk.getDestNode()
+ if simbase.air.holidayManager:
+ if ToontownGlobals.HALLOWEEN_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.HALLOWEEN_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ elif ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES]:
+ simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].triggerSwitch(curWalkNode, self)
+ self.fsm.request('TransitionToCostume')
+ return
+ else:
+ self.notify.warning('transitionToCostume == 1 but no costume holiday')
+ else:
+ self.notify.warning('transitionToCostume == 1 but no holiday Manager')
+ if doneStatus['state'] == 'lonely' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'chatty' and doneStatus['status'] == 'done':
+ self.fsm.request('Walk')
+ elif doneStatus['state'] == 'walk' and doneStatus['status'] == 'done':
+ if len(self.nearbyAvatars) > 0:
+ self.fsm.request('Chatty')
+ else:
+ self.fsm.request('Lonely')
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.exitOff(self)
+
+ def enterLonely(self):
+ self.lonely.enter()
+ self.acceptOnce(self.lonelyDoneEvent, self.__decideNextState)
+
+ def exitLonely(self):
+ self.ignore(self.lonelyDoneEvent)
+ self.lonely.exit()
+
+ def __goForAWalk(self, task):
+ self.notify.debug('going for a walk')
+ self.fsm.request('Walk')
+ return Task.done
+
+ def enterChatty(self):
+ chatter = CCharChatter.getChatter(self.getName(), self.getCCChatter())
+ if not chatter:
+ self.chatterExists = False
+ self.fsm.request('Lonely')
+ else:
+ self.chatterExists = True
+ self.chatty.enter()
+ self.acceptOnce(self.chattyDoneEvent, self.__decideNextState)
+
+ def exitChatty(self):
+ if self.chatterExists:
+ self.ignore(self.chattyDoneEvent)
+ self.chatty.exit()
+
+ def enterWalk(self):
+ self.notify.debug('going for a walk')
+ self.walk.enter()
+ self.acceptOnce(self.walkDoneEvent, self.__decideNextState)
+
+ def exitWalk(self):
+ self.ignore(self.walkDoneEvent)
+ self.walk.exit()
+
+ def avatarEnterNextState(self):
+ if len(self.nearbyAvatars) == 1:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Chatty')
+ else:
+ self.notify.debug('avatarEnterNextState: in walk state')
+ else:
+ self.notify.debug('avatarEnterNextState: num avatars: ' + str(len(self.nearbyAvatars)))
+
+ def avatarExitNextState(self):
+ if len(self.nearbyAvatars) == 0:
+ if self.fsm.getCurrentState().getName() != 'Walk':
+ self.fsm.request('Lonely')
+
+ def handleHolidays(self):
+ DistributedCCharBaseAI.DistributedCCharBaseAI.handleHolidays(self)
+ if hasattr(simbase.air, 'holidayManager'):
+ if ToontownGlobals.APRIL_FOOLS_COSTUMES in simbase.air.holidayManager.currentHolidays:
+ if simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES] != None and simbase.air.holidayManager.currentHolidays[ToontownGlobals.APRIL_FOOLS_COSTUMES].getRunningState():
+ self.diffPath = TTLocalizer.Minnie
+ return
+
+ def enterTransitionToCostume(self):
+ pass
+
+ def exitTransitionToCostume(self):
+ pass
diff --git a/toontown/classicchars/DistributedPoliceChip.py b/toontown/classicchars/DistributedPoliceChip.py
new file mode 100644
index 0000000..b064b96
--- /dev/null
+++ b/toontown/classicchars/DistributedPoliceChip.py
@@ -0,0 +1,23 @@
+from direct.showbase.ShowBaseGlobal import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import DistributedChip
+
+class DistributedPoliceChip(DistributedChip.DistributedChip):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPoliceChip')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedChip_initialized
+ except:
+ self.DistributedChip_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.PoliceChip, 'pch')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.handleHolidays()
+ self.nametag.setName(TTLocalizer.Chip)
diff --git a/toontown/classicchars/DistributedSockHopDaisy.py b/toontown/classicchars/DistributedSockHopDaisy.py
new file mode 100644
index 0000000..05ecc21
--- /dev/null
+++ b/toontown/classicchars/DistributedSockHopDaisy.py
@@ -0,0 +1,24 @@
+from direct.showbase.ShowBaseGlobal import *
+import DistributedCCharBase
+import DistributedDaisy
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.hood import TTHood
+
+class DistributedSockHopDaisy(DistributedDaisy.DistributedDaisy):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSockHopDaisy')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedSockHopDaisy_initialized
+ except:
+ self.DistributedSockHopDaisy_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.SockHopDaisy, 'shdd')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.nametag.setName(TTLocalizer.Daisy)
+ self.handleHolidays()
diff --git a/toontown/classicchars/DistributedSuperGoofy.py b/toontown/classicchars/DistributedSuperGoofy.py
new file mode 100644
index 0000000..d2c19b5
--- /dev/null
+++ b/toontown/classicchars/DistributedSuperGoofy.py
@@ -0,0 +1,26 @@
+from pandac.PandaModules import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from toontown.classicchars import DistributedGoofySpeedway
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import DistributedCCharBase
+
+class DistributedSuperGoofy(DistributedGoofySpeedway.DistributedGoofySpeedway):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSuperGoofy')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedGoofySpeedway_initialized
+ except:
+ self.DistributedGoofySpeedway_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.SuperGoofy, 'sg')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.nametag.setName(TTLocalizer.Goofy)
+
+ def walkSpeed(self):
+ return ToontownGlobals.SuperGoofySpeed
diff --git a/toontown/classicchars/DistributedVampireMickey.py b/toontown/classicchars/DistributedVampireMickey.py
new file mode 100644
index 0000000..0ca001c
--- /dev/null
+++ b/toontown/classicchars/DistributedVampireMickey.py
@@ -0,0 +1,26 @@
+from pandac.PandaModules import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from toontown.classicchars import DistributedMickey
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import DistributedCCharBase
+
+class DistributedVampireMickey(DistributedMickey.DistributedMickey):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVampireMickey')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedMickey_initialized
+ except:
+ self.DistributedMickey_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.VampireMickey, 'vmk')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.nametag.setName(TTLocalizer.Mickey)
+
+ def walkSpeed(self):
+ return ToontownGlobals.VampireMickeySpeed
diff --git a/toontown/classicchars/DistributedWesternPluto.py b/toontown/classicchars/DistributedWesternPluto.py
new file mode 100644
index 0000000..90418f2
--- /dev/null
+++ b/toontown/classicchars/DistributedWesternPluto.py
@@ -0,0 +1,26 @@
+from pandac.PandaModules import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from toontown.classicchars import DistributedPluto
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import DistributedCCharBase
+
+class DistributedWesternPluto(DistributedPluto.DistributedPluto):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedWesternPluto')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedPluto_initialized
+ except:
+ self.DistributedPluto_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.WesternPluto, 'wp')
+ self.fsm = ClassicFSM.ClassicFSM('DistributedWesternPluto', [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.nametag.setName(TTLocalizer.Pluto)
+
+ def walkSpeed(self):
+ return ToontownGlobals.WesternPlutoSpeed
diff --git a/toontown/classicchars/DistributedWitchMinnie.py b/toontown/classicchars/DistributedWitchMinnie.py
new file mode 100644
index 0000000..336bc06
--- /dev/null
+++ b/toontown/classicchars/DistributedWitchMinnie.py
@@ -0,0 +1,26 @@
+from pandac.PandaModules import *
+import DistributedCCharBase
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from toontown.classicchars import DistributedMinnie
+import CharStateDatas
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+import DistributedCCharBase
+
+class DistributedWitchMinnie(DistributedMinnie.DistributedMinnie):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedWitchMinnie')
+
+ def __init__(self, cr):
+ try:
+ self.DistributedMinnie_initialized
+ except:
+ self.DistributedMinnie_initialized = 1
+ DistributedCCharBase.DistributedCCharBase.__init__(self, cr, TTLocalizer.WitchMinnie, 'wmn')
+ self.fsm = ClassicFSM.ClassicFSM(self.getName(), [State.State('Off', self.enterOff, self.exitOff, ['Neutral']), State.State('Neutral', self.enterNeutral, self.exitNeutral, ['Walk']), State.State('Walk', self.enterWalk, self.exitWalk, ['Neutral'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.nametag.setName(TTLocalizer.Minnie)
+
+ def walkSpeed(self):
+ return ToontownGlobals.WitchMinnieSpeed
diff --git a/toontown/classicchars/__init__.py b/toontown/classicchars/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/toontown/coderedemption/TTCodeRedemptionConsts.py b/toontown/coderedemption/TTCodeRedemptionConsts.py
new file mode 100644
index 0000000..912b5a6
--- /dev/null
+++ b/toontown/coderedemption/TTCodeRedemptionConsts.py
@@ -0,0 +1,10 @@
+DefaultDbName = 'tt_code_redemption'
+RedeemErrors = Enum('Success, CodeDoesntExist, CodeIsInactive, CodeAlreadyRedeemed, AwardCouldntBeGiven, TooManyAttempts, SystemUnavailable, ')
+RedeemErrorStrings = {RedeemErrors.Success: 'Success',
+ RedeemErrors.CodeDoesntExist: 'Invalid code',
+ RedeemErrors.CodeIsInactive: 'Code is inactive',
+ RedeemErrors.CodeAlreadyRedeemed: 'Code has already been redeemed',
+ RedeemErrors.AwardCouldntBeGiven: 'Award could not be given',
+ RedeemErrors.TooManyAttempts: 'Too many attempts, code ignored',
+ RedeemErrors.SystemUnavailable: 'Code redemption is currently unavailable'}
+MaxCustomCodeLen = config.GetInt('tt-max-custom-code-len', 16)
diff --git a/toontown/coderedemption/TTCodeRedemptionMgr.py b/toontown/coderedemption/TTCodeRedemptionMgr.py
new file mode 100644
index 0000000..8f4d8a9
--- /dev/null
+++ b/toontown/coderedemption/TTCodeRedemptionMgr.py
@@ -0,0 +1,35 @@
+from direct.distributed.DistributedObject import DistributedObject
+from direct.directnotify.DirectNotifyGlobal import directNotify
+
+class TTCodeRedemptionMgr(DistributedObject):
+ neverDisable = 1
+ notify = directNotify.newCategory('TTCodeRedemptionMgr')
+
+ def __init__(self, cr):
+ DistributedObject.__init__(self, cr)
+
+ def announceGenerate(self):
+ DistributedObject.announceGenerate(self)
+ base.codeRedemptionMgr = self
+ self._contextGen = SerialMaskedGen(4294967295L)
+ self._context2callback = {}
+
+ def delete(self):
+ if hasattr(base, 'codeRedemptionMgr'):
+ if base.codeRedemptionMgr is self:
+ del base.codeRedemptionMgr
+ self._context2callback = None
+ self._contextGen = None
+ DistributedObject.delete(self)
+ return
+
+ def redeemCode(self, code, callback):
+ context = self._contextGen.next()
+ self._context2callback[context] = callback
+ self.notify.debug('redeemCode(%s, %s)' % (context, code))
+ self.sendUpdate('redeemCode', [context, code])
+
+ def redeemCodeResult(self, context, result, awardMgrResult):
+ self.notify.debug('redeemCodeResult(%s, %s, %s)' % (context, result, awardMgrResult))
+ callback = self._context2callback.pop(context)
+ callback(result, awardMgrResult)
diff --git a/toontown/coderedemption/__init__.py b/toontown/coderedemption/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/toontown/cogdominium/CogdoBarrelRoom.py b/toontown/cogdominium/CogdoBarrelRoom.py
new file mode 100644
index 0000000..8c52569
--- /dev/null
+++ b/toontown/cogdominium/CogdoBarrelRoom.py
@@ -0,0 +1,174 @@
+import random
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownGlobals, ToontownTimer
+from toontown.cogdominium import CogdoBarrelRoomConsts, CogdoBarrelRoomRewardPanel
+from toontown.distributed import DelayDelete
+
+class CogdoBarrelRoom:
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogdoBarrelRoom')
+
+ def __init__(self):
+ self.timer = None
+ self.model = None
+ self._isLoaded = False
+ self.dummyElevInNode = None
+ self.cogdoBarrelsNode = None
+ self.entranceNode = None
+ self.nearBattleNode = None
+ self.rewardUi = None
+ self.rewardUiTaskName = 'CogdoBarrelRoom-RewardUI'
+ self.rewardCameraTaskName = 'CogdoBarrelRoom-RewardCamera'
+ self.fog = None
+ self.defaultFar = None
+ self.stomperSfx = None
+ return
+
+ def destroy(self):
+ self.unload()
+
+ def load(self):
+ if self._isLoaded:
+ return
+ self.timer = ToontownTimer.ToontownTimer()
+ self.timer.stash()
+ self.model = loader.loadModel(CogdoBarrelRoomConsts.BarrelRoomModel)
+ self.model.setPos(*CogdoBarrelRoomConsts.BarrelRoomModelPos)
+ self.model.reparentTo(render)
+ self.model.stash()
+ self.dummyElevInNode = self.model.attachNewNode('elevator-in')
+ self.dummyElevInNode.hide()
+ self.entranceNode = self.model.attachNewNode('door-entrance')
+ self.entranceNode.setPos(0, -65, 0)
+ self.nearBattleNode = self.model.attachNewNode('near-battle')
+ self.nearBattleNode.setPos(0, -25, 0)
+ self.rewardUi = CogdoBarrelRoomRewardPanel.CogdoBarrelRoomRewardPanel()
+ self.hideRewardUi()
+ self.stomperSfx = base.loadSfx(CogdoBarrelRoomConsts.StomperSound)
+ self.fog = Fog('barrel-room-fog')
+ self.fog.setColor(CogdoBarrelRoomConsts.BarrelRoomFogColor)
+ self.fog.setLinearRange(*CogdoBarrelRoomConsts.BarrelRoomFogLinearRange)
+ self._isLoaded = True
+
+ def unload(self):
+ if self.model:
+ self.model.removeNode()
+ self.model = None
+ if self.timer:
+ self.timer.destroy()
+ self.timer = None
+ if self.rewardUi:
+ self.rewardUi.destroy()
+ self.rewardUi = None
+ if self.fog:
+ render.setFogOff()
+ del self.fog
+ taskMgr.remove(self.rewardUiTaskName)
+ taskMgr.remove(self.rewardCameraTaskName)
+ self._isLoaded = False
+ return
+
+ def isLoaded(self):
+ return self._isLoaded
+
+ def show(self):
+ if not self.cogdoBarrelsNode:
+ self.cogdoBarrelsNode = render.find('**/@@CogdoBarrels')
+ self.cogdoBarrelsNode.reparentTo(self.model)
+ self.cogdoBarrelsNode.unstash()
+ self.defaultFar = base.camLens.getFar()
+ base.camLens.setFar(CogdoBarrelRoomConsts.BarrelRoomCameraFar)
+ self.showBattleAreaLight(True)
+ render.setFog(self.fog)
+ self.model.unstash()
+
+ def hide(self):
+ self.model.stash()
+ render.setFogOff()
+ if self.defaultFar is not None:
+ base.camLens.setFar(self.defaultFar)
+ return
+
+ def activate(self):
+ self.notify.info('Activating barrel room: %d sec timer.' % CogdoBarrelRoomConsts.CollectionTime)
+ self.timer.unstash()
+ self.timer.posAboveShtikerBook()
+ self.timer.countdown(CogdoBarrelRoomConsts.CollectionTime)
+ base.cr.playGame.getPlace().fsm.request('walk')
+
+ def deactivate(self):
+ self.notify.info('Deactivating barrel room.')
+ self.timer.stop()
+ self.timer.stash()
+
+ def placeToonsAtEntrance(self, toons):
+ for i in range(len(toons)):
+ toons[i].setPosHpr(self.entranceNode, *CogdoBarrelRoomConsts.BarrelRoomPlayerSpawnPoints[i])
+
+ def placeToonsNearBattle(self, toons):
+ for i in range(len(toons)):
+ toons[i].setPosHpr(self.nearBattleNode, *CogdoBarrelRoomConsts.BarrelRoomPlayerSpawnPoints[i])
+
+ def showBattleAreaLight(self, visible = True):
+ lightConeNode = self.model.find('**/battleCone')
+ if lightConeNode != None and not lightConeNode.isEmpty():
+ if visible:
+ lightConeNode.show()
+ else:
+ lightConeNode.hide()
+ return
+
+ def getIntroInterval(self):
+ avatar = base.localAvatar
+ trackName = '__introBarrelRoom-%d' % avatar.doId
+ track = Parallel(name=trackName)
+ track.append(self.__stomperIntervals())
+ track.append(Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, self.model, -20.0, -87.9, 12.0, -30, 0, 0), Func(base.transitions.irisIn, 0.5), Wait(1.0), LerpHprInterval(camera, duration=2.0, startHpr=Vec3(-30, 0, 0), hpr=Vec3(0, 0, 0), blendType='easeInOut'), Wait(2.5), LerpHprInterval(camera, duration=3.0, startHpr=Vec3(0, 0, 0), hpr=Vec3(-45, 0, 0), blendType='easeInOut'), Wait(2.5)))
+ track.delayDelete = DelayDelete.DelayDelete(avatar, 'introBarrelRoomTrack')
+ track.setDoneEvent(trackName)
+ return (track, trackName)
+
+ def __stomperIntervals(self):
+ ivals = [SoundInterval(self.stomperSfx)]
+ i = 0
+ for stomperDef in CogdoBarrelRoomConsts.StomperProps:
+ stomperNode = render.find(stomperDef['path'])
+ if stomperNode:
+ maxZ = random.uniform(10, 20)
+ minZ = maxZ - 10
+ if stomperDef['motion'] == 'up':
+ startZ, destZ = minZ, maxZ
+ else:
+ startZ, destZ = maxZ, minZ
+ stomperNode.setPos(Point3(0, 0, startZ))
+ ivals.append(LerpPosInterval(stomperNode, CogdoBarrelRoomConsts.StomperHaltTime, Point3(0, 0, destZ), blendType='easeOut'))
+ i += 1
+
+ return Parallel(*tuple(ivals))
+
+ def __rewardUiTimeout(self, callback):
+ self.hideRewardUi()
+ if callback is not None:
+ callback()
+ return
+
+ def __rewardCamera(self):
+ trackName = 'cogdoBarrelRoom-RewardCamera'
+ track = Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, self.model, 0, 0, 11.0, 0, -14, 0), Func(self.showBattleAreaLight, False), name=trackName)
+ return (track, trackName)
+
+ def showRewardUi(self, results, callback = None):
+ track, trackName = self.__rewardCamera()
+ if CogdoBarrelRoomConsts.ShowRewardUI:
+ self.rewardUi.setRewards(results)
+ self.rewardUi.unstash()
+ taskMgr.doMethodLater(CogdoBarrelRoomConsts.RewardUiTime, self.__rewardUiTimeout, self.rewardUiTaskName, extraArgs=[callback])
+ return (track, trackName)
+
+ def setRewardResults(self, results):
+ self.rewardUi.setRewards(results)
+
+ def hideRewardUi(self):
+ self.rewardUi.stash()
+ taskMgr.remove(self.rewardUiTaskName)
diff --git a/toontown/cogdominium/CogdoBarrelRoomAI.py b/toontown/cogdominium/CogdoBarrelRoomAI.py
new file mode 100644
index 0000000..a44a10b
--- /dev/null
+++ b/toontown/cogdominium/CogdoBarrelRoomAI.py
@@ -0,0 +1,122 @@
+import random
+from sets import Set
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObjectAI
+from direct.showbase import PythonUtil
+from direct.task import Timer
+from direct.task.Task import Task
+from toontown.toonbase import ToontownGlobals
+from toontown.cogdominium import CogdoBarrelRoomConsts
+from toontown.cogdominium import DistributedCogdoBarrelAI
+
+class CogdoBarrelRoomAI:
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogdoBarrelRoomAI')
+
+ def __init__(self, cogdoInteriorAI):
+ self.cogdoInteriorAI = cogdoInteriorAI
+ self.allBarrelsCollectedTask = self.cogdoInteriorAI.taskName('allBarrelsCollectedTask')
+ self.collectionDoneEvent = self.cogdoInteriorAI.taskName('barrelCollectionDone')
+ self.collectTimer = None
+ self.results = [
+ [],
+ []]
+ for i in range(CogdoBarrelRoomConsts.MaxToons):
+ if i < len(self.cogdoInteriorAI.toons):
+ self.results[0].append(self.cogdoInteriorAI.toons[i])
+ else:
+ self.results[0].append(0)
+ self.results[1].append(0)
+
+ self.spawnedBarrels = []
+ self.__spawnBarrels()
+
+ def destroy(self):
+ taskMgr.remove(self.allBarrelsCollectedTask)
+ if self.collectTimer:
+ self.collectTimer.stop()
+ self.collectTimer = None
+
+ for barrel in self.spawnedBarrels:
+ barrel.requestDelete()
+
+ self.spawnedBarrels = []
+
+ def __spawnBarrels(self):
+ self.spawnedBarrels = []
+
+ def spawnBarrel(index):
+ barrel = DistributedCogdoBarrelAI.DistributedCogdoBarrelAI(self.cogdoInteriorAI.air, index, self.barrelCollected)
+ barrel.generateWithRequired(self.cogdoInteriorAI.zoneId)
+ self.spawnedBarrels.append(barrel)
+
+ for i in range(CogdoBarrelRoomConsts.numBarrels()):
+ spawnBarrel(i)
+
+ def reset(self):
+ for barrel in self.spawnedBarrels:
+ barrel.d_setState(CogdoBarrelRoomConsts.StateHidden)
+
+ for i in range(CogdoBarrelRoomConsts.MaxToons):
+ self.results[1][i] = 0
+
+ def activate(self):
+ self.collectTimer = Timer.Timer()
+ self.collectTimer.startCallback(CogdoBarrelRoomConsts.CollectionTime, self.__endCollectionPhase)
+ for barrel in self.spawnedBarrels:
+ barrel.interactive = True
+
+ taskMgr.remove(self.allBarrelsCollectedTask)
+ taskMgr.doMethodLater(CogdoBarrelRoomConsts.AllBarrelsCollectedTime, self.__checkAllBarrelsCollected, self.allBarrelsCollectedTask)
+
+ def __endCollectionPhase(self):
+ messenger.send(self.collectionDoneEvent)
+
+ def deactivate(self):
+ if self.collectTimer:
+ self.collectTimer.stop()
+ self.collectTimer = None
+
+ for barrel in self.spawnedBarrels:
+ barrel.interactive = False
+
+ def setScore(self, score):
+ numGood = int(score * (CogdoBarrelRoomConsts.numBarrels() + 0.5))
+ random.shuffle(self.spawnedBarrels)
+ for i in range(len(self.spawnedBarrels)):
+ if i < numGood:
+ state = CogdoBarrelRoomConsts.StateAvailable
+ else:
+ state = CogdoBarrelRoomConsts.StateCrushed
+ self.spawnedBarrels[i].d_setState(state)
+
+ def barrelCollected(self, barrel, avId):
+ try:
+ playerIndex = self.results[0].index(avId)
+ self.results[1][playerIndex] += barrel.laff
+ except ValueError:
+ self.notify.warning('barrelCollected: Unrecognized avId %s' % avId)
+
+ def __checkAllBarrelsCollected(self, task):
+ if not CogdoBarrelRoomConsts.EndWithAllBarrelsCollected:
+ return
+
+ if self.__allBarrelsCollected():
+ self.__endCollectionPhase()
+ else:
+ return Task.again
+
+ def __toonIdNeedsLaff(self, toonId):
+ toon = self.cogdoInteriorAI.air.doId2do.get(toonId)
+ if toon != None:
+ return not toon.isToonedUp()
+
+ def __allBarrelsCollected(self):
+ toonsNeedingLaff = Set(filter(lambda toon: self.__toonIdNeedsLaff(toon), self.cogdoInteriorAI.toons))
+ for barrel in self.spawnedBarrels:
+ if not toonsNeedingLaff.issubset(Set(barrel.grabbedBy)):
+ return False
+
+ return True
+
+ def __str__(self):
+ return str(self.cogdoInteriorAI) + '.CogdoBarrelRoomAI'
diff --git a/toontown/cogdominium/CogdoBarrelRoomConsts.py b/toontown/cogdominium/CogdoBarrelRoomConsts.py
new file mode 100644
index 0000000..48948a5
--- /dev/null
+++ b/toontown/cogdominium/CogdoBarrelRoomConsts.py
@@ -0,0 +1,97 @@
+from pandac.PandaModules import *
+CollectionTime = 30
+BarrelRoomIntroTimeout = 15.0
+RewardUiTime = 5.0
+EndWithAllBarrelsCollected = False
+ShowRewardUI = False
+AllBarrelsCollectedTime = 5.0
+ToonUp = (2, 4)
+BarrelProps = [{'pos': (-10, -66, 0),
+ 'heading': 9},
+ {'pos': (-7.8, -54.5, 0),
+ 'heading': 12},
+ {'pos': (-10.5, -44, 0),
+ 'heading': 166},
+ {'pos': (-8.9, -33.5, 0),
+ 'heading': 142},
+ {'pos': (-9.6, -19.8, 0),
+ 'heading': 94},
+ {'pos': (7.8, -63.9, 0),
+ 'heading': 169},
+ {'pos': (10, -44.5, 0),
+ 'heading': 120},
+ {'pos': (7.4, -36.6, 0),
+ 'heading': 127},
+ {'pos': (10.6, -27.5, 0),
+ 'heading': 141},
+ {'pos': (10, -14.4, 0),
+ 'heading': 2}]
+StomperProps = [{'path': '**/stomper_GRP_01/stomper_cylinder_01',
+ 'motion': 'up'},
+ {'path': '**/stomper_GRP_02/stomper_cylinder_034',
+ 'motion': 'down'},
+ {'path': '**/stomper_GRP_03/stomper_cylinder_034',
+ 'motion': 'up'},
+ {'path': '**/stomper_GRP_04/stomper_cylinder_034',
+ 'motion': 'up'},
+ {'path': '**/stomper_GRP_05/stomper_cylinder_034',
+ 'motion': 'down'},
+ {'path': '**/stomper_GRP_06/stomper_cylinder_034',
+ 'motion': 'up'},
+ {'path': '**/stomper_GRP_07/stomper_cylinder_034',
+ 'motion': 'down'},
+ {'path': '**/stomper_GRP_08/stomper_cylinder_034',
+ 'motion': 'up'},
+ {'path': '**/stomper_GRP_09/stomper_cylinder_034',
+ 'motion': 'up'},
+ {'path': '**/stomper_GRP_10/stomper_cylinder_034',
+ 'motion': 'down'},
+ {'path': '**/stomper_GRP_11/stomper_cylinder_034',
+ 'motion': 'up'},
+ {'path': '**/stomper_GRP_12/stomper_cylinder_034',
+ 'motion': 'up'}]
+StomperHaltTime = 7.3
+StomperSound = 'phase_9/audio/sfx/CHQ_FACT_stomper_raise.mp3'
+MaxToons = 4
+BarrelRoomModel = 'phase_5/models/cogdominium/tt_m_ara_cbr_barrelRoom'
+BarrelRoomModelPos = (0, 0, 0)
+BarrelRoomElevatorOutPath = '**/elevatorOut_locator'
+BarrelRoomPlayerSpawnPoints = [(-4,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0),
+ (-2,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0),
+ (0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0),
+ (2,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0)]
+BarrelRoomCameraFar = 525.0
+BarrelRoomFogColor = Vec4(0.65, 0.21, 0, 1.0)
+BarrelRoomFogLinearRange = (0.0, 800.0)
+BarrelModel = 'phase_5/models/cogdominium/tt_m_ara_cbr_laughBarrel'
+BarrelModelScale = 1.0
+BarrelCollParams = (0,
+ 0,
+ 2,
+ 2.0)
+BarrelBumpSound = 'phase_4/audio/sfx/Golf_Hit_Barrier_2.mp3'
+BarrelGrabSound = 'phase_4/audio/sfx/SZ_DD_treasure.mp3'
+StateHidden, StateAvailable, StateUsed, StateCrushed = range(4)
+
+def numBarrels():
+ return len(BarrelProps)
diff --git a/toontown/cogdominium/CogdoBarrelRoomRewardPanel.py b/toontown/cogdominium/CogdoBarrelRoomRewardPanel.py
new file mode 100644
index 0000000..cf22ee7
--- /dev/null
+++ b/toontown/cogdominium/CogdoBarrelRoomRewardPanel.py
@@ -0,0 +1,36 @@
+from pandac.PandaModules import *
+from direct.gui.DirectGui import *
+from toontown.toonbase import ToontownGlobals, TTLocalizer
+from toontown.cogdominium import CogdoBarrelRoomConsts
+
+class CogdoBarrelRoomRewardPanel(DirectFrame):
+
+ def __init__(self):
+ DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=TTLocalizer.RPdirectFrame, pos=(0, 0, 0.587))
+ self.initialiseoptions(CogdoBarrelRoomRewardPanel)
+ self.avNameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.3), text='Toon Ups', text_scale=0.08)
+ self.rewardLines = []
+ for i in range(CogdoBarrelRoomConsts.MaxToons):
+ rewardLine = {}
+ rewardLine['frame'] = DirectFrame(parent=self, relief=None, frameSize=(-0.5,
+ 0.5,
+ -0.045,
+ 0.042), pos=(0, 0, 0.1 + -0.09 * i))
+ rewardLine['name'] = DirectLabel(parent=rewardLine['frame'], relief=None, text='', text_scale=TTLocalizer.RPtrackLabels, text_align=TextNode.ALeft, pos=(-0.4, 0, 0), text_pos=(0, -0.02))
+ rewardLine['laff'] = DirectLabel(parent=rewardLine['frame'], relief=None, text='', text_scale=0.05, text_align=TextNode.ARight, pos=(0.4, 0, 0), text_pos=(0, -0.02))
+ self.rewardLines.append(rewardLine)
+
+ return
+
+ def setRewards(self, results):
+ for p in range(len(results[0])):
+ doId = results[0][p]
+ laff = results[1][p]
+ if doId > 0 and base.cr.doId2do.has_key(doId):
+ toon = base.cr.doId2do[doId]
+ self.rewardLines[p]['name'].setProp('text', toon.getName())
+ self.rewardLines[p]['laff'].setProp('text', str(laff))
+ if doId == base.localAvatar.getDoId():
+ self.rewardLines[p]['frame'].setProp('relief', DGG.RIDGE)
+ self.rewardLines[p]['frame'].setProp('borderWidth', (0.01, 0.01))
+ self.rewardLines[p]['frame'].setProp('frameColor', (1, 1, 1, 0.5))
diff --git a/toontown/cogdominium/CogdoBoardroomGameBase.py b/toontown/cogdominium/CogdoBoardroomGameBase.py
new file mode 100644
index 0000000..1c62c64
--- /dev/null
+++ b/toontown/cogdominium/CogdoBoardroomGameBase.py
@@ -0,0 +1,10 @@
+from toontown.cogdominium import CogdoBoardroomGameSpec
+from toontown.cogdominium import CogdoBoardroomGameConsts as Consts
+
+class CogdoBoardroomGameBase:
+
+ def getConsts(self):
+ return Consts
+
+ def getSpec(self):
+ return CogdoBoardroomGameSpec
diff --git a/toontown/cogdominium/CogdoBoardroomGameConsts.py b/toontown/cogdominium/CogdoBoardroomGameConsts.py
new file mode 100644
index 0000000..7e09e9b
--- /dev/null
+++ b/toontown/cogdominium/CogdoBoardroomGameConsts.py
@@ -0,0 +1,6 @@
+from direct.fsm.StatePush import StateVar
+from otp.level.EntityStateVarSet import EntityStateVarSet
+from toontown.cogdominium.CogdoEntityTypes import CogdoBoardroomGameSettings
+Settings = EntityStateVarSet(CogdoBoardroomGameSettings)
+GameDuration = StateVar(60.0)
+FinishDuration = StateVar(10.0)
diff --git a/toontown/cogdominium/CogdoBoardroomGameSpec.py b/toontown/cogdominium/CogdoBoardroomGameSpec.py
new file mode 100644
index 0000000..d8e808f
--- /dev/null
+++ b/toontown/cogdominium/CogdoBoardroomGameSpec.py
@@ -0,0 +1,28 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'modelFilename': 'phase_10/models/cogHQ/EndVault.bam'},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10000: {'type': 'cogdoBoardroomGameSettings',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'TimerScale': 0.5028702719683693}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/cogdominium/CogdoCraneGameBase.py b/toontown/cogdominium/CogdoCraneGameBase.py
new file mode 100644
index 0000000..095643a
--- /dev/null
+++ b/toontown/cogdominium/CogdoCraneGameBase.py
@@ -0,0 +1,10 @@
+from toontown.cogdominium import CogdoCraneGameSpec
+from toontown.cogdominium import CogdoCraneGameConsts as Consts
+
+class CogdoCraneGameBase:
+
+ def getConsts(self):
+ return Consts
+
+ def getSpec(self):
+ return CogdoCraneGameSpec
diff --git a/toontown/cogdominium/CogdoCraneGameConsts.py b/toontown/cogdominium/CogdoCraneGameConsts.py
new file mode 100644
index 0000000..2ea2f3e
--- /dev/null
+++ b/toontown/cogdominium/CogdoCraneGameConsts.py
@@ -0,0 +1,59 @@
+from direct.fsm.StatePush import StateVar
+from otp.level.EntityStateVarSet import EntityStateVarSet
+from toontown.cogdominium.CogdoEntityTypes import CogdoCraneGameSettings, CogdoCraneCogSettings
+Settings = EntityStateVarSet(CogdoCraneGameSettings)
+CogSettings = EntityStateVarSet(CogdoCraneCogSettings)
+CranePosHprs = [(13.4, -136.6, 6, -45, 0, 0),
+ (13.4, -91.4, 6, -135, 0, 0),
+ (58.6, -91.4, 6, 135, 0, 0),
+ (58.6, -136.6, 6, 45, 0, 0)]
+MoneyBagPosHprs = [[77.2 - 84,
+ -329.3 + 201,
+ 0,
+ -90,
+ 0,
+ 0],
+ [77.1 - 84,
+ -302.7 + 201,
+ 0,
+ -90,
+ 0,
+ 0],
+ [165.7 - 84,
+ -326.4 + 201,
+ 0,
+ 90,
+ 0,
+ 0],
+ [165.5 - 84,
+ -302.4 + 201,
+ 0,
+ 90,
+ 0,
+ 0],
+ [107.8 - 84,
+ -359.1 + 201,
+ 0,
+ 0,
+ 0,
+ 0],
+ [133.9 - 84,
+ -359.1 + 201,
+ 0,
+ 0,
+ 0,
+ 0],
+ [107.0 - 84,
+ -274.7 + 201,
+ 0,
+ 180,
+ 0,
+ 0],
+ [134.2 - 84,
+ -274.7 + 201,
+ 0,
+ 180,
+ 0,
+ 0]]
+for i in xrange(len(MoneyBagPosHprs)):
+ MoneyBagPosHprs[i][2] += 6
diff --git a/toontown/cogdominium/CogdoCraneGameSpec.py b/toontown/cogdominium/CogdoCraneGameSpec.py
new file mode 100644
index 0000000..129fbea
--- /dev/null
+++ b/toontown/cogdominium/CogdoCraneGameSpec.py
@@ -0,0 +1,42 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'modelFilename': 'phase_10/models/cogHQ/EndVault.bam'},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10001: {'type': 'cogdoCraneCogSettings',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'CogFlyAwayDuration': 4.0,
+ 'CogFlyAwayHeight': 50.0,
+ 'CogMachineInteractDuration': 2.0,
+ 'CogSpawnPeriod': 10.0,
+ 'CogWalkSpeed': 12.07161265369133},
+ 10000: {'type': 'cogdoCraneGameSettings',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'EmptyFrictionCoef': 0.1,
+ 'GameDuration': 180.0,
+ 'Gravity': -32,
+ 'MagnetMass': 1.0,
+ 'MoneyBagGrabHeight': -4.1,
+ 'RopeLinkMass': 1.0}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/cogdominium/CogdoElevatorMovie.py b/toontown/cogdominium/CogdoElevatorMovie.py
new file mode 100644
index 0000000..8e114de
--- /dev/null
+++ b/toontown/cogdominium/CogdoElevatorMovie.py
@@ -0,0 +1,118 @@
+from pandac.PandaModules import NodePath, Point3, PlaneNode, TextNode
+from direct.interval.IntervalGlobal import *
+from direct.showbase.ShowBase import Plane
+from direct.directnotify import DirectNotifyGlobal
+from direct.showbase.RandomNumGen import RandomNumGen
+from direct.interval.MetaInterval import Sequence, Parallel
+from direct.interval.FunctionInterval import Func, Wait
+from direct.gui.DirectGui import *
+from toontown.toonbase.ToontownGlobals import *
+from toontown.toonbase import TTLocalizer
+from toontown.suit import Suit, SuitDNA
+from toontown.toon import Toon, ToonHead, ToonDNA
+from CogdoUtil import CogdoGameMovie
+import CogdoUtil
+
+class CogdoElevatorMovie(CogdoGameMovie):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CogdoElevatorMovie')
+ elevatorDuration = 5
+
+ def __init__(self):
+ CogdoGameMovie.__init__(self)
+ self._toonDialogueSfx = None
+ self.toonHead = None
+ self.frame = None
+ return
+
+ def displayLine(self, text):
+ self.notify.debug('displayLine')
+ self._dialogueLabel.node().setText(text)
+ self.toonHead.reparentTo(aspect2d)
+ self._toonDialogueSfx.play()
+ self.toonHead.setClipPlane(self.clipPlane)
+
+ def makeSuit(self, suitType):
+ self.notify.debug('makeSuit()')
+ suit = Suit.Suit()
+ dna = SuitDNA.SuitDNA()
+ dna.newSuit(suitType)
+ suit.setStyle(dna)
+ suit.isDisguised = 1
+ suit.generateSuit()
+ suit.setScale(1, 1, 2)
+ suit.setPos(0, 0, -4.4)
+ suit.reparentTo(self.toonHead)
+ for part in suit.getHeadParts():
+ part.hide()
+
+ suit.loop('neutral')
+
+ def load(self):
+ self.notify.debug('load()')
+ CogdoGameMovie.load(self)
+ backgroundGui = loader.loadModel('phase_5/models/cogdominium/tt_m_gui_csa_flyThru')
+ self.bg = backgroundGui.find('**/background')
+ self.chatBubble = backgroundGui.find('**/chatBubble')
+ self.chatBubble.setScale(6.5, 6.5, 7.3)
+ self.chatBubble.setPos(0.32, 0, -0.78)
+ self.bg.setScale(5.2)
+ self.bg.setPos(0.14, 0, -0.6667)
+ self.bg.reparentTo(aspect2d)
+ self.chatBubble.reparentTo(aspect2d)
+ self.frame = DirectFrame(geom=self.bg, relief=None, pos=(0.2, 0, -0.6667))
+ self.bg.wrtReparentTo(self.frame)
+ self.gameTitleText = DirectLabel(parent=self.frame, text=TTLocalizer.CogdoExecutiveSuiteTitle, scale=TTLocalizer.MRPgameTitleText * 0.8, text_align=TextNode.ACenter, text_font=getSignFont(), text_fg=(1.0, 0.33, 0.33, 1.0), pos=TTLocalizer.MRgameTitleTextPos, relief=None)
+ self.chatBubble.wrtReparentTo(self.frame)
+ self.frame.hide()
+ backgroundGui.removeNode()
+ self.toonDNA = ToonDNA.ToonDNA()
+ self.toonDNA.newToonFromProperties('dss', 'ss', 'm', 'm', 2, 0, 2, 2, 1, 8, 1, 8, 1, 14)
+ self.toonHead = Toon.Toon()
+ self.toonHead.setDNA(self.toonDNA)
+ self.makeSuit('sc')
+ self.toonHead.getGeomNode().setDepthWrite(1)
+ self.toonHead.getGeomNode().setDepthTest(1)
+ self.toonHead.loop('neutral')
+ self.toonHead.setPosHprScale(-0.73, 0, -1.27, 180, 0, 0, 0.18, 0.18, 0.18)
+ self.toonHead.reparentTo(hidden)
+ self.toonHead.startBlink()
+ self.clipPlane = self.toonHead.attachNewNode(PlaneNode('clip'))
+ self.clipPlane.node().setPlane(Plane(0, 0, 1, 0))
+ self.clipPlane.setPos(0, 0, 2.45)
+ self._toonDialogueSfx = loader.loadSfx('phase_3.5/audio/dial/AV_dog_long.mp3')
+ self._camHelperNode = NodePath('CamHelperNode')
+ self._camHelperNode.reparentTo(render)
+ dialogue = TTLocalizer.CogdoElevatorRewardLaff
+
+ def start():
+ self.frame.show()
+ base.setCellsAvailable(base.bottomCells + base.leftCells + base.rightCells, 0)
+
+ def end():
+ self._dialogueLabel.reparentTo(hidden)
+ self.toonHead.reparentTo(hidden)
+ self.frame.hide()
+ base.setCellsAvailable(base.bottomCells + base.leftCells + base.rightCells, 1)
+ self._stopUpdateTask()
+
+ self._ival = Sequence(Func(start), Func(self.displayLine, dialogue), Wait(self.elevatorDuration), Func(end))
+ self._startUpdateTask()
+ return
+
+ def _updateTask(self, task):
+ dt = globalClock.getDt()
+ return task.cont
+
+ def unload(self):
+ self.frame.destroy()
+ del self.frame
+ self.bg.removeNode()
+ del self.bg
+ self.chatBubble.removeNode()
+ del self.chatBubble
+ self.toonHead.stopBlink()
+ self.toonHead.stop()
+ self.toonHead.removeNode()
+ self.toonHead.delete()
+ del self.toonHead
+ CogdoGameMovie.unload(self)
diff --git a/toontown/cogdominium/CogdoEntityCreator.py b/toontown/cogdominium/CogdoEntityCreator.py
new file mode 100644
index 0000000..c9d3715
--- /dev/null
+++ b/toontown/cogdominium/CogdoEntityCreator.py
@@ -0,0 +1,20 @@
+from otp.level import EntityCreator
+from toontown.cogdominium import CogdoCraneGameConsts
+from toontown.cogdominium.CogdoLevelMgr import CogdoLevelMgr
+from toontown.cogdominium import CogdoBoardroomGameConsts
+from toontown.cogdominium import CogdoCraneGameConsts
+
+class CogdoEntityCreator(EntityCreator.EntityCreator):
+
+ def __init__(self, level):
+ EntityCreator.EntityCreator.__init__(self, level)
+ nothing = EntityCreator.nothing
+ nonlocal = EntityCreator.nonlocal
+ self.privRegisterTypes({'levelMgr': CogdoLevelMgr,
+ 'cogdoBoardroomGameSettings': Functor(self._createCogdoSettings, CogdoBoardroomGameConsts.Settings),
+ 'cogdoCraneGameSettings': Functor(self._createCogdoSettings, CogdoCraneGameConsts.Settings),
+ 'cogdoCraneCogSettings': Functor(self._createCogdoSettings, CogdoCraneGameConsts.CogSettings)})
+
+ def _createCogdoSettings(self, ent, level, entId):
+ ent.initializeEntity(level, entId)
+ return ent
diff --git a/toontown/cogdominium/CogdoEntityCreatorAI.py b/toontown/cogdominium/CogdoEntityCreatorAI.py
new file mode 100644
index 0000000..7a4c1f2
--- /dev/null
+++ b/toontown/cogdominium/CogdoEntityCreatorAI.py
@@ -0,0 +1,22 @@
+from direct.showbase.PythonUtil import Functor
+from otp.level import EntityCreatorAI
+from toontown.cogdominium.CogdoLevelMgrAI import CogdoLevelMgrAI
+from toontown.cogdominium import CogdoBoardroomGameConsts
+from toontown.cogdominium import CogdoCraneGameConsts
+
+class CogdoEntityCreatorAI(EntityCreatorAI.EntityCreatorAI):
+
+ def __init__(self, level):
+ EntityCreatorAI.EntityCreatorAI.__init__(self, level)
+ cDE = EntityCreatorAI.createDistributedEntity
+ cLE = EntityCreatorAI.createLocalEntity
+ nothing = EntityCreatorAI.nothing
+ self.privRegisterTypes({
+ 'levelMgr': Functor(cLE, CogdoLevelMgrAI),
+ 'cogdoBoardroomGameSettings': Functor(cLE, Functor(self._createCogdoSettings, CogdoBoardroomGameConsts.Settings)),
+ 'cogdoCraneGameSettings': Functor(cLE, Functor(self._createCogdoSettings, CogdoCraneGameConsts.Settings)),
+ 'cogdoCraneCogSettings': Functor(cLE, Functor(self._createCogdoSettings, CogdoCraneGameConsts.CogSettings))})
+
+ def _createCogdoSettings(self, ent, level, entId):
+ ent.initializeEntity(level, entId)
+ return ent
diff --git a/toontown/cogdominium/CogdoEntityTypes.py b/toontown/cogdominium/CogdoEntityTypes.py
new file mode 100644
index 0000000..53b395f
--- /dev/null
+++ b/toontown/cogdominium/CogdoEntityTypes.py
@@ -0,0 +1,28 @@
+from otp.level.EntityTypes import *
+
+class CogdoLevelMgr(LevelMgr):
+ type = 'levelMgr'
+
+
+class CogdoBoardroomGameSettings(Entity):
+ type = 'cogdoBoardroomGameSettings'
+ attribs = (('TimerScale', 1.0, 'float'),)
+
+
+class CogdoCraneGameSettings(Entity):
+ type = 'cogdoCraneGameSettings'
+ attribs = (('GameDuration', 180.0, 'float'),
+ ('EmptyFrictionCoef', 0.1, 'float'),
+ ('Gravity', -32, 'int'),
+ ('RopeLinkMass', 1.0, 'float'),
+ ('MagnetMass', 1.0, 'float'),
+ ('MoneyBagGrabHeight', -8.2, 'float'))
+
+
+class CogdoCraneCogSettings(Entity):
+ type = 'cogdoCraneCogSettings'
+ attribs = (('CogSpawnPeriod', 10.0, 'float'),
+ ('CogWalkSpeed', 2.0, 'float'),
+ ('CogMachineInteractDuration', 2.0, 'float'),
+ ('CogFlyAwayHeight', 50.0, 'float'),
+ ('CogFlyAwayDuration', 4.0, 'float'))
diff --git a/toontown/cogdominium/CogdoExecutiveSuiteMovies.py b/toontown/cogdominium/CogdoExecutiveSuiteMovies.py
new file mode 100644
index 0000000..8c95103
--- /dev/null
+++ b/toontown/cogdominium/CogdoExecutiveSuiteMovies.py
@@ -0,0 +1,142 @@
+from pandac.PandaModules import NodePath, Point3, PlaneNode, TextNode
+from direct.interval.IntervalGlobal import *
+from direct.showbase.ShowBase import Plane
+from direct.directnotify import DirectNotifyGlobal
+from direct.showbase.RandomNumGen import RandomNumGen
+from direct.interval.MetaInterval import Sequence, Parallel
+from direct.interval.FunctionInterval import Func, Wait
+from direct.gui.DirectGui import *
+from toontown.toonbase.ToontownGlobals import *
+from toontown.toonbase import TTLocalizer
+from toontown.suit import Suit, SuitDNA
+from toontown.toon import Toon, ToonHead, ToonDNA
+from CogdoUtil import CogdoGameMovie
+import CogdoUtil
+
+class CogdoExecutiveSuiteIntro(CogdoGameMovie):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CogdoExecutiveSuiteIntro')
+ introDuration = 7
+ cameraMoveDuration = 3
+
+ def __init__(self, shopOwner):
+ CogdoGameMovie.__init__(self)
+ self._shopOwner = shopOwner
+ self._lookAtCamTarget = False
+ self._camTarget = None
+ self._camHelperNode = None
+ self._toonDialogueSfx = None
+ self.toonHead = None
+ self.frame = None
+ return
+
+ def displayLine(self, text):
+ self.notify.debug('displayLine')
+ self._dialogueLabel.node().setText(text)
+ self.toonHead.reparentTo(aspect2d)
+ self._toonDialogueSfx.play()
+ self.toonHead.setClipPlane(self.clipPlane)
+
+ def makeSuit(self, suitType):
+ self.notify.debug('makeSuit()')
+ suit = Suit.Suit()
+ dna = SuitDNA.SuitDNA()
+ dna.newSuit(suitType)
+ suit.setStyle(dna)
+ suit.isDisguised = 1
+ suit.generateSuit()
+ suit.setScale(1, 1, 2)
+ suit.setPos(0, 0, -4.4)
+ suit.reparentTo(self.toonHead)
+ for part in suit.getHeadParts():
+ part.hide()
+
+ suit.loop('neutral')
+
+ def load(self):
+ self.notify.debug('load()')
+ CogdoGameMovie.load(self)
+ backgroundGui = loader.loadModel('phase_5/models/cogdominium/tt_m_gui_csa_flyThru')
+ self.bg = backgroundGui.find('**/background')
+ self.chatBubble = backgroundGui.find('**/chatBubble')
+ self.chatBubble.setScale(6.5, 6.5, 7.3)
+ self.chatBubble.setPos(0.32, 0, -0.78)
+ self.bg.setScale(5.2)
+ self.bg.setPos(0.14, 0, -0.6667)
+ self.bg.reparentTo(aspect2d)
+ self.chatBubble.reparentTo(aspect2d)
+ self.frame = DirectFrame(geom=self.bg, relief=None, pos=(0.2, 0, -0.6667))
+ self.bg.wrtReparentTo(self.frame)
+ self.gameTitleText = DirectLabel(parent=self.frame, text=TTLocalizer.CogdoExecutiveSuiteTitle, scale=TTLocalizer.MRPgameTitleText * 0.8, text_align=TextNode.ACenter, text_font=getSignFont(), text_fg=(1.0, 0.33, 0.33, 1.0), pos=TTLocalizer.MRgameTitleTextPos, relief=None)
+ self.chatBubble.wrtReparentTo(self.frame)
+ self.frame.hide()
+ backgroundGui.removeNode()
+ self.toonDNA = ToonDNA.ToonDNA()
+ self.toonDNA.newToonFromProperties('dss', 'ss', 'm', 'm', 2, 0, 2, 2, 1, 8, 1, 8, 1, 14)
+ self.toonHead = Toon.Toon()
+ self.toonHead.setDNA(self.toonDNA)
+ self.makeSuit('sc')
+ self.toonHead.getGeomNode().setDepthWrite(1)
+ self.toonHead.getGeomNode().setDepthTest(1)
+ self.toonHead.loop('neutral')
+ self.toonHead.setPosHprScale(-0.73, 0, -1.27, 180, 0, 0, 0.18, 0.18, 0.18)
+ self.toonHead.reparentTo(hidden)
+ self.toonHead.startBlink()
+ self.clipPlane = self.toonHead.attachNewNode(PlaneNode('clip'))
+ self.clipPlane.node().setPlane(Plane(0, 0, 1, 0))
+ self.clipPlane.setPos(0, 0, 2.45)
+ self._toonDialogueSfx = loader.loadSfx('phase_3.5/audio/dial/AV_dog_long.mp3')
+ self._camHelperNode = NodePath('CamHelperNode')
+ self._camHelperNode.reparentTo(render)
+ dialogue = TTLocalizer.CogdoExecutiveSuiteIntroMessage
+
+ def start():
+ self.frame.show()
+ base.setCellsAvailable(base.bottomCells + base.leftCells + base.rightCells, 0)
+
+ def showShopOwner():
+ self._setCamTarget(self._shopOwner, -10, offset=Point3(0, 0, 5))
+
+ def end():
+ self._dialogueLabel.reparentTo(hidden)
+ self.toonHead.reparentTo(hidden)
+ self.frame.hide()
+ base.setCellsAvailable(base.bottomCells + base.leftCells + base.rightCells, 1)
+ self._stopUpdateTask()
+
+ self._ival = Sequence(Func(start), Func(self.displayLine, dialogue), Func(showShopOwner), ParallelEndTogether(camera.posInterval(self.cameraMoveDuration, Point3(8, 0, 13), blendType='easeInOut'), camera.hprInterval(0.5, self._camHelperNode.getHpr(), blendType='easeInOut')), Wait(self.introDuration), Func(end))
+ self._startUpdateTask()
+ return
+
+ def _setCamTarget(self, targetNP, distance, offset = Point3(0, 0, 0), angle = Point3(0, 0, 0)):
+ camera.wrtReparentTo(render)
+ self._camTarget = targetNP
+ self._camOffset = offset
+ self._camAngle = angle
+ self._camDistance = distance
+ self._camHelperNode.setPos(self._camTarget, self._camOffset)
+ self._camHelperNode.setHpr(self._camTarget, 180 + self._camAngle[0], self._camAngle[1], self._camAngle[2])
+ self._camHelperNode.setPos(self._camHelperNode, 0, self._camDistance, 0)
+
+ def _updateTask(self, task):
+ dt = globalClock.getDt()
+ return task.cont
+
+ def unload(self):
+ self._shopOwner = None
+ self._camTarget = None
+ if hasattr(self, '_camHelperNode') and self._camHelperNode:
+ self._camHelperNode.removeNode()
+ del self._camHelperNode
+ self.frame.destroy()
+ del self.frame
+ self.bg.removeNode()
+ del self.bg
+ self.chatBubble.removeNode()
+ del self.chatBubble
+ self.toonHead.stopBlink()
+ self.toonHead.stop()
+ self.toonHead.removeNode()
+ self.toonHead.delete()
+ del self.toonHead
+ CogdoGameMovie.unload(self)
+ return
diff --git a/toontown/cogdominium/CogdoFlyingCameraManager.py b/toontown/cogdominium/CogdoFlyingCameraManager.py
new file mode 100644
index 0000000..8df291b
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingCameraManager.py
@@ -0,0 +1,186 @@
+import math
+from pandac.PandaModules import NodePath, Vec3
+from pandac.PandaModules import CollisionTraverser, CollisionHandlerQueue
+from pandac.PandaModules import CollisionRay, CollisionNode
+from math import pi, sin, cos
+from direct.showbase.PythonUtil import bound as clamp
+from otp.otpbase import OTPGlobals
+from toontown.toonbase import ToontownGlobals
+import CogdoFlyingGameGlobals as Globals
+INVERSE_E = 1.0 / math.e
+
+def smooth(old, new):
+ return old * 0.7 + new * 0.3
+
+
+class CogdoFlyingCameraManager:
+
+ def __init__(self, cam, parent, player, level):
+ self._toon = player.toon
+ self._camera = cam
+ self._parent = parent
+ self._player = player
+ self._level = level
+ self._enabled = False
+
+ def enable(self):
+ if self._enabled:
+ return
+ self._toon.detachCamera()
+ self._prevToonY = 0.0
+ levelBounds = self._level.getBounds()
+ l = Globals.Camera.LevelBoundsFactor
+ self._bounds = ((levelBounds[0][0] * l[0], levelBounds[0][1] * l[0]), (levelBounds[1][0] * l[1], levelBounds[1][1] * l[1]), (levelBounds[2][0] * l[2], levelBounds[2][1] * l[2]))
+ self._lookAtZ = self._toon.getHeight() + Globals.Camera.LookAtToonHeightOffset
+ self._camParent = NodePath('CamParent')
+ self._camParent.reparentTo(self._parent)
+ self._camParent.setPos(self._toon, 0, 0, 0)
+ self._camParent.setHpr(180, Globals.Camera.Angle, 0)
+ self._camera.reparentTo(self._camParent)
+ self._camera.setPos(0, Globals.Camera.Distance, 0)
+ self._camera.lookAt(self._toon, 0, 0, self._lookAtZ)
+ self._cameraLookAtNP = NodePath('CameraLookAt')
+ self._cameraLookAtNP.reparentTo(self._camera.getParent())
+ self._cameraLookAtNP.setPosHpr(self._camera.getPos(), self._camera.getHpr())
+ self._levelBounds = self._level.getBounds()
+ self._enabled = True
+ self._frozen = False
+ self._initCollisions()
+
+ def _initCollisions(self):
+ self._camCollRay = CollisionRay()
+ camCollNode = CollisionNode('CameraToonRay')
+ camCollNode.addSolid(self._camCollRay)
+ camCollNode.setFromCollideMask(OTPGlobals.WallBitmask | OTPGlobals.CameraBitmask | ToontownGlobals.FloorEventBitmask | ToontownGlobals.CeilingBitmask)
+ camCollNode.setIntoCollideMask(0)
+ self._camCollNP = self._camera.attachNewNode(camCollNode)
+ self._camCollNP.show()
+ self._collOffset = Vec3(0, 0, 0.5)
+ self._collHandler = CollisionHandlerQueue()
+ self._collTrav = CollisionTraverser()
+ self._collTrav.addCollider(self._camCollNP, self._collHandler)
+ self._betweenCamAndToon = {}
+ self._transNP = NodePath('trans')
+ self._transNP.reparentTo(render)
+ self._transNP.setTransparency(True)
+ self._transNP.setAlphaScale(Globals.Camera.AlphaBetweenToon)
+ self._transNP.setBin('fixed', 10000)
+
+ def _destroyCollisions(self):
+ self._collTrav.removeCollider(self._camCollNP)
+ self._camCollNP.removeNode()
+ del self._camCollNP
+ del self._camCollRay
+ del self._collHandler
+ del self._collOffset
+ del self._betweenCamAndToon
+ self._transNP.removeNode()
+ del self._transNP
+
+ def freeze(self):
+ self._frozen = True
+
+ def unfreeze(self):
+ self._frozen = False
+
+ def disable(self):
+ if not self._enabled:
+ return
+ self._destroyCollisions()
+ self._camera.wrtReparentTo(render)
+ self._cameraLookAtNP.removeNode()
+ del self._cameraLookAtNP
+ self._camParent.removeNode()
+ del self._camParent
+ del self._prevToonY
+ del self._lookAtZ
+ del self._bounds
+ del self._frozen
+ self._enabled = False
+
+ def update(self, dt = 0.0):
+ self._updateCam(dt)
+ self._updateCollisions()
+
+ def _updateCam(self, dt):
+ toonPos = self._toon.getPos()
+ camPos = self._camParent.getPos()
+ x = camPos[0]
+ z = camPos[2]
+ toonWorldX = self._toon.getX(render)
+ maxX = Globals.Camera.MaxSpinX
+ toonWorldX = clamp(toonWorldX, -1.0 * maxX, maxX)
+ spinAngle = Globals.Camera.MaxSpinAngle * toonWorldX * toonWorldX / (maxX * maxX)
+ newH = 180.0 + spinAngle
+ self._camParent.setH(newH)
+ spinAngle = spinAngle * (pi / 180.0)
+ distBehindToon = Globals.Camera.SpinRadius * cos(spinAngle)
+ distToRightOfToon = Globals.Camera.SpinRadius * sin(spinAngle)
+ d = self._camParent.getX() - clamp(toonPos[0], *self._bounds[0])
+ if abs(d) > Globals.Camera.LeewayX:
+ if d > Globals.Camera.LeewayX:
+ x = toonPos[0] + Globals.Camera.LeewayX
+ else:
+ x = toonPos[0] - Globals.Camera.LeewayX
+ x = self._toon.getX(render) + distToRightOfToon
+ boundToonZ = min(toonPos[2], self._bounds[2][1])
+ d = z - boundToonZ
+ if d > Globals.Camera.MinLeewayZ:
+ if self._player.velocity[2] >= 0 and toonPos[1] != self._prevToonY or self._player.velocity[2] > 0:
+ z = boundToonZ + d * INVERSE_E ** (dt * Globals.Camera.CatchUpRateZ)
+ elif d > Globals.Camera.MaxLeewayZ:
+ z = boundToonZ + Globals.Camera.MaxLeewayZ
+ elif d < -Globals.Camera.MinLeewayZ:
+ z = boundToonZ - Globals.Camera.MinLeewayZ
+ if self._frozen:
+ y = camPos[1]
+ else:
+ y = self._toon.getY(render) - distBehindToon
+ self._camParent.setPos(x, smooth(camPos[1], y), smooth(camPos[2], z))
+ if toonPos[2] < self._bounds[2][1]:
+ h = self._cameraLookAtNP.getH()
+ if d >= Globals.Camera.MinLeewayZ:
+ self._cameraLookAtNP.lookAt(self._toon, 0, 0, self._lookAtZ)
+ elif d <= -Globals.Camera.MinLeewayZ:
+ self._cameraLookAtNP.lookAt(self._camParent, 0, 0, self._lookAtZ)
+ self._cameraLookAtNP.setHpr(h, self._cameraLookAtNP.getP(), 0)
+ self._camera.setHpr(smooth(self._camera.getHpr(), self._cameraLookAtNP.getHpr()))
+ self._prevToonY = toonPos[1]
+
+ def _updateCollisions(self):
+ pos = self._toon.getPos(self._camera) + self._collOffset
+ self._camCollRay.setOrigin(pos)
+ direction = -Vec3(pos)
+ direction.normalize()
+ self._camCollRay.setDirection(direction)
+ self._collTrav.traverse(render)
+ nodesInBetween = {}
+ if self._collHandler.getNumEntries() > 0:
+ self._collHandler.sortEntries()
+ for entry in self._collHandler.getEntries():
+ name = entry.getIntoNode().getName()
+ if name.find('col_') >= 0:
+ np = entry.getIntoNodePath().getParent()
+ if not nodesInBetween.has_key(np):
+ nodesInBetween[np] = np.getParent()
+
+ for np in nodesInBetween.keys():
+ if self._betweenCamAndToon.has_key(np):
+ del self._betweenCamAndToon[np]
+ else:
+ np.setTransparency(True)
+ np.wrtReparentTo(self._transNP)
+ if np.getName().find('lightFixture') >= 0:
+ np.find('**/*floor_mesh').hide()
+ elif np.getName().find('platform') >= 0:
+ np.find('**/*Floor').hide()
+
+ for np, parent in self._betweenCamAndToon.items():
+ np.wrtReparentTo(parent)
+ np.setTransparency(False)
+ if np.getName().find('lightFixture') >= 0:
+ np.find('**/*floor_mesh').show()
+ elif np.getName().find('platform') >= 0:
+ np.find('**/*Floor').show()
+
+ self._betweenCamAndToon = nodesInBetween
diff --git a/toontown/cogdominium/CogdoFlyingCollisions.py b/toontown/cogdominium/CogdoFlyingCollisions.py
new file mode 100644
index 0000000..23fb76b
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingCollisions.py
@@ -0,0 +1,132 @@
+from direct.controls.GravityWalker import GravityWalker
+from pandac.PandaModules import CollisionSphere, CollisionNode, BitMask32, CollisionHandlerEvent, CollisionRay, CollisionHandlerGravity, CollisionHandlerFluidPusher, CollisionHandlerPusher
+from toontown.toonbase import ToontownGlobals
+from otp.otpbase import OTPGlobals
+
+class CogdoFlyingCollisions(GravityWalker):
+ wantFloorSphere = 0
+
+ def __init__(self):
+ GravityWalker.__init__(self, gravity=0.0)
+
+ def initializeCollisions(self, collisionTraverser, avatarNodePath, avatarRadius = 1.4, floorOffset = 1.0, reach = 1.0):
+ self.cHeadSphereNodePath = None
+ self.cFloorEventSphereNodePath = None
+ self.setupHeadSphere(avatarNodePath)
+ self.setupFloorEventSphere(avatarNodePath, ToontownGlobals.FloorEventBitmask, avatarRadius)
+ GravityWalker.initializeCollisions(self, collisionTraverser, avatarNodePath, avatarRadius, floorOffset, reach)
+ return
+
+ def setupWallSphere(self, bitmask, avatarRadius):
+ self.avatarRadius = avatarRadius
+ cSphere = CollisionSphere(0.0, 0.0, self.avatarRadius + 0.75, self.avatarRadius)
+ cSphereNode = CollisionNode('Flyer.cWallSphereNode')
+ cSphereNode.addSolid(cSphere)
+ cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode)
+ cSphereNode.setFromCollideMask(bitmask)
+ cSphereNode.setIntoCollideMask(BitMask32.allOff())
+ if config.GetBool('want-fluid-pusher', 0):
+ self.pusher = CollisionHandlerFluidPusher()
+ else:
+ self.pusher = CollisionHandlerPusher()
+ self.pusher.addCollider(cSphereNodePath, self.avatarNodePath)
+ self.cWallSphereNodePath = cSphereNodePath
+
+ def setupEventSphere(self, bitmask, avatarRadius):
+ self.avatarRadius = avatarRadius
+ cSphere = CollisionSphere(0.0, 0.0, self.avatarRadius + 0.75, self.avatarRadius * 1.04)
+ cSphere.setTangible(0)
+ cSphereNode = CollisionNode('Flyer.cEventSphereNode')
+ cSphereNode.addSolid(cSphere)
+ cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode)
+ cSphereNode.setFromCollideMask(bitmask)
+ cSphereNode.setIntoCollideMask(BitMask32.allOff())
+ self.event = CollisionHandlerEvent()
+ self.event.addInPattern('enter%in')
+ self.event.addOutPattern('exit%in')
+ self.cEventSphereNodePath = cSphereNodePath
+
+ def setupRay(self, bitmask, floorOffset, reach):
+ cRay = CollisionRay(0.0, 0.0, 3.0, 0.0, 0.0, -1.0)
+ cRayNode = CollisionNode('Flyer.cRayNode')
+ cRayNode.addSolid(cRay)
+ self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode)
+ cRayNode.setFromCollideMask(bitmask)
+ cRayNode.setIntoCollideMask(BitMask32.allOff())
+ self.lifter = CollisionHandlerGravity()
+ self.lifter.setLegacyMode(self._legacyLifter)
+ self.lifter.setGravity(self.getGravity(0))
+ self.lifter.addInPattern('%fn-enter-%in')
+ self.lifter.addAgainPattern('%fn-again-%in')
+ self.lifter.addOutPattern('%fn-exit-%in')
+ self.lifter.setOffset(floorOffset)
+ self.lifter.setReach(reach)
+ self.lifter.addCollider(self.cRayNodePath, self.avatarNodePath)
+
+ def setupHeadSphere(self, avatarNodePath):
+ collSphere = CollisionSphere(0, 0, 0, 1)
+ collSphere.setTangible(1)
+ collNode = CollisionNode('Flyer.cHeadCollSphere')
+ collNode.setFromCollideMask(ToontownGlobals.CeilingBitmask)
+ collNode.setIntoCollideMask(BitMask32.allOff())
+ collNode.addSolid(collSphere)
+ self.cHeadSphereNodePath = avatarNodePath.attachNewNode(collNode)
+ self.cHeadSphereNodePath.setZ(base.localAvatar.getHeight() + 1.0)
+ self.headCollisionEvent = CollisionHandlerEvent()
+ self.headCollisionEvent.addInPattern('%fn-enter-%in')
+ self.headCollisionEvent.addOutPattern('%fn-exit-%in')
+ base.cTrav.addCollider(self.cHeadSphereNodePath, self.headCollisionEvent)
+
+ def setupFloorEventSphere(self, avatarNodePath, bitmask, avatarRadius):
+ cSphere = CollisionSphere(0.0, 0.0, 0.0, 0.75)
+ cSphereNode = CollisionNode('Flyer.cFloorEventSphere')
+ cSphereNode.addSolid(cSphere)
+ cSphereNodePath = avatarNodePath.attachNewNode(cSphereNode)
+ cSphereNode.setFromCollideMask(bitmask)
+ cSphereNode.setIntoCollideMask(BitMask32.allOff())
+ self.floorCollisionEvent = CollisionHandlerEvent()
+ self.floorCollisionEvent.addInPattern('%fn-enter-%in')
+ self.floorCollisionEvent.addAgainPattern('%fn-again-%in')
+ self.floorCollisionEvent.addOutPattern('%fn-exit-%in')
+ base.cTrav.addCollider(cSphereNodePath, self.floorCollisionEvent)
+ self.cFloorEventSphereNodePath = cSphereNodePath
+
+ def deleteCollisions(self):
+ GravityWalker.deleteCollisions(self)
+ if self.cHeadSphereNodePath != None:
+ base.cTrav.removeCollider(self.cHeadSphereNodePath)
+ self.cHeadSphereNodePath.detachNode()
+ self.cHeadSphereNodePath = None
+ self.headCollisionsEvent = None
+ if self.cFloorEventSphereNodePath != None:
+ base.cTrav.removeCollider(self.cFloorEventSphereNodePath)
+ self.cFloorEventSphereNodePath.detachNode()
+ self.cFloorEventSphereNodePath = None
+ self.floorCollisionEvent = None
+ self.cRayNodePath.detachNode()
+ del self.cRayNodePath
+ self.cEventSphereNodePath.detachNode()
+ del self.cEventSphereNodePath
+ return
+
+ def setCollisionsActive(self, active = 1):
+ if self.collisionsActive != active:
+ if self.cHeadSphereNodePath != None:
+ base.cTrav.removeCollider(self.cHeadSphereNodePath)
+ if active:
+ base.cTrav.addCollider(self.cHeadSphereNodePath, self.headCollisionEvent)
+ if self.cFloorEventSphereNodePath != None:
+ base.cTrav.removeCollider(self.cFloorEventSphereNodePath)
+ if active:
+ base.cTrav.addCollider(self.cFloorEventSphereNodePath, self.floorCollisionEvent)
+ GravityWalker.setCollisionsActive(self, active)
+ return
+
+ def enableAvatarControls(self):
+ pass
+
+ def disableAvatarControls(self):
+ pass
+
+ def handleAvatarControls(self, task):
+ pass
diff --git a/toontown/cogdominium/CogdoFlyingGame.py b/toontown/cogdominium/CogdoFlyingGame.py
new file mode 100644
index 0000000..c46fe74
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingGame.py
@@ -0,0 +1,469 @@
+from direct.showbase.DirectObject import DirectObject
+from direct.task.Task import Task
+from direct.showbase.RandomNumGen import RandomNumGen
+from direct.interval.FunctionInterval import Wait
+from direct.interval.IntervalGlobal import Func
+from direct.interval.MetaInterval import Sequence, Parallel
+from toontown.toonbase import TTLocalizer
+import CogdoFlyingGameGlobals as Globals
+from CogdoFlyingLocalPlayer import CogdoFlyingLocalPlayer
+from CogdoGameAudioManager import CogdoGameAudioManager
+from CogdoFlyingPlayer import CogdoFlyingPlayer
+from CogdoFlyingObjects import CogdoFlyingGatherable
+from CogdoFlyingObstacles import CogdoFlyingObstacle
+from CogdoFlyingLegalEagle import CogdoFlyingLegalEagle
+from CogdoFlyingGuiManager import CogdoFlyingGuiManager
+from CogdoFlyingLevel import CogdoFlyingLevelFactory
+from CogdoFlyingGameMovies import CogdoFlyingGameIntro, CogdoFlyingGameFinish
+
+class CogdoFlyingGame(DirectObject):
+ notify = directNotify.newCategory('CogdoFlyingGame')
+ UpdateTaskName = 'CogdoFlyingGameUpdate'
+ FirstPressOfCtrlTaskName = 'FirstPressOfCtrlTask'
+
+ def __init__(self, distGame):
+ self.distGame = distGame
+ self.toonId2Player = {}
+ self.players = []
+ self.index2LegalEagle = {}
+ self.legalEagles = []
+ self.isGameComplete = False
+ self._hints = {'targettedByEagle': False,
+ 'invulnerable': False}
+
+ def _initLegalEagles(self):
+ nestIndex = 1
+ nests = self.level.root.findAllMatches('**/%s;+s' % Globals.Level.LegalEagleNestName)
+ for nest in nests:
+ legalEagle = CogdoFlyingLegalEagle(nest, nestIndex)
+ self.legalEagles.append(legalEagle)
+ self.index2LegalEagle[nestIndex] = legalEagle
+ nestIndex += 1
+
+ def initPlayers(self):
+ for toonId in self.distGame.getToonIds():
+ toon = self.distGame.getToon(toonId)
+ i = self.distGame.getToonIds().index(toon.doId)
+ if toon is not None:
+ if toon.isLocal():
+ player = CogdoFlyingLocalPlayer(toon, self, self.level, self.guiMgr)
+ self.localPlayer = player
+ self.localPlayer.setPlayerNumber(i)
+ else:
+ player = CogdoFlyingPlayer(toon)
+ player.enable()
+ self._addPlayer(player)
+ self.guiMgr.addToonToProgressMeter(toon)
+
+ return
+
+ def placeEntranceElevator(self, elevator):
+ loc = self.level.startPlatform._model.find('**/door_loc')
+ offset = loc.getPos(render)
+ elevator.setPos(render, offset)
+
+ def load(self):
+ self.accept(self.distGame.getRemoteActionEventName(), self.handleRemoteAction)
+ self.audioMgr = CogdoGameAudioManager(Globals.Audio.MusicFiles, Globals.Audio.SfxFiles, base.localAvatar, cutoff=Globals.Audio.Cutoff)
+ factory = CogdoFlyingLevelFactory(render, Globals.Level.QuadLengthUnits, Globals.Level.QuadVisibilityAhead, Globals.Level.QuadVisibilityBehind, rng=RandomNumGen(self.distGame.doId))
+ self.level = factory.createLevel(self.distGame.getSafezoneId())
+ self.level.setCamera(camera)
+ self.guiMgr = CogdoFlyingGuiManager(self.level)
+ self.levelFog = factory.createLevelFog()
+ self._initLegalEagles()
+
+ def unload(self):
+ self.guiMgr.destroy()
+ del self.guiMgr
+ self.__stopUpdateTask()
+ for player in self.players:
+ player.unload()
+
+ del self.players[:]
+ self.toonId2Player.clear()
+ del self.localPlayer
+ for eagle in self.legalEagles:
+ eagle.destroy()
+
+ del self.legalEagles[:]
+ self.index2LegalEagle.clear()
+ self.levelFog.destroy()
+ del self.levelFog
+ self.level.destroy()
+ del self.level
+ self.audioMgr.destroy()
+ del self.audioMgr
+ self.ignoreAll()
+ del self.distGame
+
+ def onstage(self):
+ self.level.onstage()
+ self.levelFog.setVisible(True)
+ for eagle in self.legalEagles:
+ eagle.onstage()
+
+ self.localPlayer.request('Inactive')
+
+ def offstage(self):
+ self.__stopUpdateTask()
+ for eagle in self.legalEagles:
+ eagle.offstage()
+
+ self.level.offstage()
+ self.levelFog.setVisible(False)
+
+ def startIntro(self):
+ self._movie = CogdoFlyingGameIntro(self.level, RandomNumGen(self.distGame.doId))
+ self._movie.load()
+ self._movie.play()
+ self.audioMgr.playMusic('normal')
+
+ def endIntro(self):
+ self._movie.end()
+ self._movie.unload()
+ del self._movie
+ self.localPlayer.ready()
+ self.level.update(0.0)
+
+ def startFinish(self):
+ self._movie = CogdoFlyingGameFinish(self.level, self.players)
+ self._movie.load()
+ self._movie.play()
+ self.audioMgr.playMusic('end')
+
+ def endFinish(self):
+ self._movie.end()
+ self._movie.unload()
+ del self._movie
+ self.audioMgr.stopMusic()
+
+ def start(self):
+ self.level.start(self.distGame.getStartTime())
+ self.accept(CogdoFlyingObstacle.EnterEventName, self.handleLocalToonEnterObstacle)
+ self.accept(CogdoFlyingObstacle.ExitEventName, self.handleLocalToonExitObstacle)
+ self.accept(CogdoFlyingGatherable.EnterEventName, self.handleLocalToonEnterGatherable)
+ self.accept(CogdoFlyingLegalEagle.RequestAddTargetEventName, self.handleLocalToonEnterLegalEagleInterest)
+ self.accept(CogdoFlyingLegalEagle.RequestAddTargetAgainEventName, self.handleLocalToonAgainLegalEagleInterest)
+ self.accept(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, self.handleLocalToonExitLegalEagleInterest)
+ self.accept(CogdoFlyingLegalEagle.EnterLegalEagle, self.handleLocalToonEnterLegalEagle)
+ self.accept(CogdoFlyingLegalEagle.ChargingToAttackEventName, self.handlePlayerBackpackAttacked)
+ self.accept(CogdoFlyingLegalEagle.LockOnToonEventName, self.handlePlayerBackpackTargeted)
+ self.accept(CogdoFlyingLegalEagle.CooldownEventName, self.handlePlayerBackpackNormal)
+ self.accept(CogdoFlyingGuiManager.EagleTargetingLocalPlayerEventName, self.handleLocalPlayerTargetedByEagle)
+ self.accept(CogdoFlyingGuiManager.EagleAttackingLocalPlayerEventName, self.handleLocalPlayerAttackedByEagle)
+ self.accept(CogdoFlyingGuiManager.ClearMessageDisplayEventName, self.handleClearGuiMessage)
+ self.accept(CogdoFlyingGuiManager.InvulnerableEventName, self.handleLocalPlayerInvulnerable)
+ self.acceptOnce(CogdoFlyingGuiManager.FirstPressOfCtrlEventName, self.handleLocalPlayerFirstPressOfCtrl)
+ self.acceptOnce(CogdoFlyingGuiManager.PickedUpFirstPropellerEventName, self.handleLocalPlayerPickedUpFirstPropeller)
+ self.acceptOnce(CogdoFlyingGuiManager.StartRunningOutOfTimeMusicEventName, self.handleTimeRunningOut)
+ self.acceptOnce(CogdoFlyingLocalPlayer.PlayWaitingMusicEventName, self.handlePlayWaitingMusic)
+ self.acceptOnce(CogdoFlyingLocalPlayer.RanOutOfTimeEventName, self.handleLocalPlayerRanOutOfTime)
+ self.__startUpdateTask()
+ self.isGameComplete = False
+ if __debug__ and base.config.GetBool('schellgames-dev', True):
+ self.acceptOnce('end', self.guiMgr.forceTimerDone)
+
+ def toggleFog():
+ self.levelFog.setVisible(not self.levelFog.isVisible())
+
+ self.accept('home', toggleFog)
+ for eagle in self.legalEagles:
+ eagle.gameStart(self.distGame.getStartTime())
+
+ for player in self.players:
+ player.start()
+
+ self.guiMgr.onstage()
+ if not Globals.Dev.InfiniteTimeLimit:
+ self.guiMgr.startTimer(Globals.Gameplay.SecondsUntilGameOver, self._handleTimerExpired, keepHidden=True)
+
+ def exit(self):
+ self.ignore(CogdoFlyingObstacle.EnterEventName)
+ self.ignore(CogdoFlyingObstacle.ExitEventName)
+ self.ignore(CogdoFlyingGatherable.EnterEventName)
+ self.ignore(CogdoFlyingLegalEagle.ChargingToAttackEventName)
+ self.ignore(CogdoFlyingLegalEagle.LockOnToonEventName)
+ self.ignore(CogdoFlyingLegalEagle.CooldownEventName)
+ self.ignore(CogdoFlyingLocalPlayer.RanOutOfTimeEventName)
+ taskMgr.remove(CogdoFlyingGame.FirstPressOfCtrlTaskName)
+ self.__stopUpdateTask()
+ self.ignore(CogdoFlyingLegalEagle.EnterLegalEagle)
+ self.ignore(CogdoFlyingLegalEagle.RequestAddTargetEventName)
+ self.ignore(CogdoFlyingLegalEagle.RequestAddTargetAgainEventName)
+ self.ignore(CogdoFlyingLegalEagle.RequestRemoveTargetEventName)
+ self.ignore(CogdoFlyingLocalPlayer.PlayWaitingMusicEventName)
+ if __debug__ and base.config.GetBool('schellgames-dev', True):
+ self.ignore('end')
+ self.ignore('home')
+ self.level.update(0.0)
+ for eagle in self.legalEagles:
+ eagle.gameEnd()
+
+ for player in self.players:
+ player.exit()
+
+ self.guiMgr.offstage()
+
+ def _handleTimerExpired(self):
+ self.localPlayer.handleTimerExpired()
+
+ def _addPlayer(self, player):
+ self.players.append(player)
+ self.toonId2Player[player.toon.doId] = player
+ toon = player.toon
+ self.accept(toon.uniqueName('disable'), self._removePlayer, extraArgs=[toon.doId])
+
+ def _removePlayer(self, toonId):
+ if toonId in self.toonId2Player:
+ player = self.toonId2Player[toonId]
+ self.players.remove(player)
+ del self.toonId2Player[toonId]
+ self.guiMgr.removeToonFromProgressMeter(player.toon)
+ player.exit()
+ player.unload()
+
+ def setToonSad(self, toonId):
+ player = self.toonId2Player[toonId]
+ self.forceClearLegalEagleInterestInToon(toonId)
+ if player == self.localPlayer:
+ player.goSad()
+ self.exit()
+ else:
+ player.exit()
+
+ def setToonDisconnect(self, toonId):
+ player = self.toonId2Player[toonId]
+ self.forceClearLegalEagleInterestInToon(toonId)
+ if player == self.localPlayer:
+ self.exit()
+ else:
+ player.exit()
+
+ def handleRemoteAction(self, action, data):
+ if data != self.localPlayer.toon.doId:
+ if action == Globals.AI.GameActions.GotoWinState:
+ if self.localPlayer.state in ['WaitingForWin']:
+ self.localPlayer.request('Win')
+
+ def toonDied(self, toonId, elapsedTime):
+ player = self.toonId2Player[toonId]
+ if player is not None:
+ player.died(elapsedTime)
+ return
+
+ def toonSpawn(self, toonId, elapsedTime):
+ player = self.toonId2Player[toonId]
+ if player is not None:
+ player.spawn(elapsedTime)
+ return
+
+ def toonResetBlades(self, toonId):
+ player = self.toonId2Player[toonId]
+ if player is not None:
+ player.resetBlades()
+ return
+
+ def toonSetBlades(self, toonId, fuelState):
+ player = self.toonId2Player[toonId]
+ if player is not None:
+ player.setBlades(fuelState)
+ return
+
+ def toonBladeLost(self, toonId):
+ player = self.toonId2Player[toonId]
+ if player is not None:
+ player.bladeLost()
+ return
+
+ def handleLocalToonEnterGatherable(self, gatherable):
+ if gatherable.wasPickedUp():
+ return
+ if gatherable.isPowerUp() and gatherable.wasPickedUpByToon(self.localPlayer.toon):
+ return
+ if gatherable.type in [Globals.Level.GatherableTypes.LaffPowerup, Globals.Level.GatherableTypes.InvulPowerup]:
+ self.distGame.d_sendRequestPickup(gatherable.serialNum, gatherable.type)
+ elif gatherable.type == Globals.Level.GatherableTypes.Memo:
+ self.distGame.d_sendRequestPickup(gatherable.serialNum, gatherable.type)
+ gatherable.disable()
+ elif gatherable.type == Globals.Level.GatherableTypes.Propeller and self.localPlayer.fuel < 1.0:
+ self.distGame.d_sendRequestPickup(gatherable.serialNum, gatherable.type)
+
+ def pickUp(self, toonId, pickupNum, elapsedTime = 0.0):
+ self.notify.debugCall()
+ player = self.toonId2Player[toonId]
+ gatherable = self.level.getGatherable(pickupNum)
+ if gatherable is not None:
+ if not gatherable.isPowerUp() and not gatherable.wasPickedUp() or gatherable.isPowerUp() and not gatherable.wasPickedUpByToon(player.toon):
+ gatherable.pickUp(player.toon, elapsedTime)
+ player.handleEnterGatherable(gatherable, elapsedTime)
+ if gatherable.type in [Globals.Level.GatherableTypes.InvulPowerup]:
+ if player.toon.isLocal():
+ self.audioMgr.playMusic('invul')
+ else:
+ self.notify.warning('Trying to pickup gatherable nonetype:%s' % pickupNum)
+ return
+
+ def debuffPowerup(self, toonId, pickupType, elapsedTime):
+ self.notify.debugCall()
+ player = self.toonId2Player[toonId]
+ if player.isBuffActive(pickupType):
+ if pickupType in [Globals.Level.GatherableTypes.InvulPowerup]:
+ if self.guiMgr.isTimeRunningOut():
+ self.audioMgr.playMusic('timeRunningOut')
+ else:
+ self.audioMgr.playMusic('normal')
+ player.handleDebuffPowerup(pickupType, elapsedTime)
+
+ def handleLocalToonEnterLegalEagle(self, eagle, collEntry):
+ if not self.localPlayer.isEnemyHitting() and not self.localPlayer.isInvulnerable():
+ collPos = collEntry.getSurfacePoint(render)
+ self.localPlayer.handleEnterEnemyHit(eagle, collPos)
+ self.distGame.d_sendRequestAction(Globals.AI.GameActions.HitLegalEagle, 0)
+
+ def handleLocalToonEnterObstacle(self, obstacle, collEntry):
+ if self.localPlayer.isInvulnerable():
+ return
+ if obstacle.type == Globals.Level.ObstacleTypes.Whirlwind:
+ self.localPlayer.handleEnterWhirlwind(obstacle)
+ self.distGame.d_sendRequestAction(Globals.AI.GameActions.HitWhirlwind, 0)
+ if obstacle.type == Globals.Level.ObstacleTypes.Fan:
+ self.localPlayer.handleEnterFan(obstacle)
+ if obstacle.type == Globals.Level.ObstacleTypes.Minion:
+ if not self.localPlayer.isEnemyHitting():
+ collPos = collEntry.getSurfacePoint(render)
+ self.localPlayer.handleEnterEnemyHit(obstacle, collPos)
+ self.distGame.d_sendRequestAction(Globals.AI.GameActions.HitMinion, 0)
+
+ def handleLocalToonExitObstacle(self, obstacle, collEntry):
+ if obstacle.type == Globals.Level.ObstacleTypes.Fan:
+ self.localPlayer.handleExitFan(obstacle)
+
+ def __startUpdateTask(self):
+ self.__stopUpdateTask()
+ taskMgr.add(self.__updateTask, CogdoFlyingGame.UpdateTaskName, 45)
+
+ def __stopUpdateTask(self):
+ taskMgr.remove(CogdoFlyingGame.UpdateTaskName)
+
+ def handleTimeRunningOut(self):
+ if self.localPlayer.state not in ['WaitingForWin']:
+ self.audioMgr.playMusic('timeRunningOut')
+ self.guiMgr.presentTimerGui()
+ self.guiMgr.setTemporaryMessage(TTLocalizer.CogdoFlyingGameTimeIsRunningOut)
+
+ def handlePlayWaitingMusic(self):
+ self.audioMgr.playMusic('waiting')
+
+ def handleLocalPlayerFirstPressOfCtrl(self):
+ self.doMethodLater(3.0, self.guiMgr.setMessage, CogdoFlyingGame.FirstPressOfCtrlTaskName, extraArgs=[''])
+
+ def handleLocalPlayerRanOutOfTime(self):
+ self.guiMgr.setMemoCount(0)
+ self.distGame.d_sendRequestAction(Globals.AI.GameActions.RanOutOfTimePenalty, 0)
+ self.guiMgr.setMessage(TTLocalizer.CogdoFlyingGameTakingMemos)
+
+ def handleClearGuiMessage(self):
+ if not self.localPlayer.isInvulnerable():
+ self.guiMgr.setMessage('')
+
+ def handleLocalPlayerInvulnerable(self):
+ if not self._hints['invulnerable']:
+ self.guiMgr.setMessage(TTLocalizer.CogdoFlyingGameYouAreInvincible)
+ self._hints['invulnerable'] = True
+
+ def handleLocalPlayerPickedUpFirstPropeller(self):
+ self.guiMgr.setMessage(TTLocalizer.CogdoFlyingGamePressCtrlToFly)
+ self.guiMgr.presentRefuelGui()
+
+ def handleLocalPlayerTargetedByEagle(self):
+ if not self.localPlayer.isInvulnerable() and not self._hints['targettedByEagle']:
+ self.guiMgr.setMessage(TTLocalizer.CogdoFlyingGameLegalEagleTargeting)
+ self._hints['targettedByEagle'] = True
+
+ def handleLocalPlayerAttackedByEagle(self):
+ if not self.localPlayer.isInvulnerable():
+ self.guiMgr.setMessage(TTLocalizer.CogdoFlyingGameLegalEagleAttacking)
+
+ def handlePlayerBackpackAttacked(self, toonId):
+ if toonId in self.toonId2Player:
+ player = self.toonId2Player[toonId]
+ player.setBackpackState(Globals.Gameplay.BackpackStates.Attacked)
+
+ def handlePlayerBackpackTargeted(self, toonId):
+ if toonId in self.toonId2Player:
+ player = self.toonId2Player[toonId]
+ player.setBackpackState(Globals.Gameplay.BackpackStates.Targeted)
+
+ def handlePlayerBackpackNormal(self, toonId):
+ if toonId in self.toonId2Player:
+ player = self.toonId2Player[toonId]
+ player.setBackpackState(Globals.Gameplay.BackpackStates.Normal)
+
+ def handleLocalToonEnterLegalEagleInterest(self, index):
+ if index in self.index2LegalEagle:
+ legalEagle = self.index2LegalEagle[index]
+ if not self.localPlayer.isLegalEagleInterestRequestSent(index) and not self.localPlayer.isLegalEagleTarget():
+ if self.localPlayer.state in ['WaitingForWin', 'Win']:
+ return
+ self.localPlayer.setLegalEagleInterestRequest(index)
+ self.distGame.d_sendRequestAction(Globals.AI.GameActions.RequestEnterEagleInterest, index)
+ else:
+ self.notify.warning("Legal eagle %i isn't in index2LegalEagle dict" % index)
+
+ def handleLocalToonAgainLegalEagleInterest(self, index):
+ self.handleLocalToonEnterLegalEagleInterest(index)
+
+ def handleLocalToonExitLegalEagleInterest(self, index):
+ if index in self.index2LegalEagle:
+ legalEagle = self.index2LegalEagle[index]
+ if self.localPlayer.isLegalEagleInterestRequestSent(index):
+ self.localPlayer.clearLegalEagleInterestRequest(index)
+ self.distGame.d_sendRequestAction(Globals.AI.GameActions.RequestExitEagleInterest, index)
+ else:
+ self.notify.warning("Legal eagle %i isn't in index2LegalEagle dict" % index)
+
+ def forceClearLegalEagleInterestInToon(self, toonId):
+ player = self.toonId2Player[toonId]
+ if player:
+ for legalEagle in self.legalEagles:
+ index = legalEagle.index
+ if player.toon.isLocal():
+ if self.localPlayer.isLegalEagleInterestRequestSent(index):
+ self.localPlayer.clearLegalEagleInterestRequest(index)
+ self.distGame.d_sendRequestAction(Globals.AI.GameActions.RequestExitEagleInterest, index)
+ self.toonClearAsEagleTarget(toonId, index, 0)
+
+ def toonSetAsEagleTarget(self, toonId, eagleId, elapsedTime):
+ if eagleId in self.index2LegalEagle:
+ legalEagle = self.index2LegalEagle[eagleId]
+ if toonId in self.toonId2Player:
+ player = self.toonId2Player[toonId]
+ player.setAsLegalEagleTarget(legalEagle)
+ legalEagle.setTarget(player.toon, elapsedTime)
+
+ def toonClearAsEagleTarget(self, toonId, eagleId, elapsedTime):
+ if eagleId in self.index2LegalEagle:
+ legalEagle = self.index2LegalEagle[eagleId]
+ if toonId in self.toonId2Player:
+ player = self.toonId2Player[toonId]
+ player.removeAsLegalEagleTarget(legalEagle)
+ if legalEagle.getTarget() == player.toon:
+ legalEagle.clearTarget(elapsedTime)
+
+ def eagleExitCooldown(self, eagleId, elapsedTime):
+ if eagleId in self.index2LegalEagle:
+ legalEagle = self.index2LegalEagle[eagleId]
+ legalEagle.leaveCooldown(elapsedTime)
+
+ def gameComplete(self):
+ self.localPlayer.request('Win')
+
+ def __updateTask(self, task):
+ dt = globalClock.getDt()
+ self.localPlayer.update(dt)
+ self.level.update(dt)
+ for eagle in self.legalEagles:
+ eagle.update(dt, self.localPlayer)
+
+ self.guiMgr.update()
+ return Task.cont
diff --git a/toontown/cogdominium/CogdoFlyingGameGlobals.py b/toontown/cogdominium/CogdoFlyingGameGlobals.py
new file mode 100644
index 0000000..9a808ae
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingGameGlobals.py
@@ -0,0 +1,256 @@
+from direct.showbase import PythonUtil
+from pandac.PandaModules import VBase4, Vec3, Point3
+from CogdoUtil import VariableContainer, DevVariableContainer
+AI = VariableContainer()
+AI.GameActions = PythonUtil.Enum(('LandOnWinPlatform', 'WinStateFinished', 'GotoWinState', 'HitWhirlwind', 'HitLegalEagle', 'HitMinion', 'DebuffInvul', 'RequestEnterEagleInterest', 'RequestExitEagleInterest', 'RanOutOfTimePenalty', 'Died', 'Spawn', 'SetBlades', 'BladeLost'))
+AI.BroadcastPeriod = 0.3
+AI.SafezoneId2DeathDamage = {2000: 1,
+ 1000: 2,
+ 5000: 4,
+ 4000: 8,
+ 3000: 12,
+ 9000: 16}
+AI.SafezoneId2WhirlwindDamage = {2000: 1,
+ 1000: 2,
+ 5000: 4,
+ 4000: 8,
+ 3000: 12,
+ 9000: 16}
+AI.SafezoneId2LegalEagleDamage = {2000: 2,
+ 1000: 4,
+ 5000: 8,
+ 4000: 16,
+ 3000: 24,
+ 9000: 32}
+AI.SafezoneId2MinionDamage = {2000: 1,
+ 1000: 2,
+ 5000: 4,
+ 4000: 8,
+ 3000: 12,
+ 9000: 16}
+Camera = VariableContainer()
+Camera.Angle = 12.5
+Camera.Distance = 20
+Camera.LookAtToonHeightOffset = 1.0
+Camera.LeewayX = 0.5
+Camera.MinLeewayZ = 0.5
+Camera.MaxLeewayZ = 15.0
+Camera.CatchUpRateZ = 3.0
+Camera.LevelBoundsFactor = (0.6, 1.0, 0.9)
+Camera.AlphaBetweenToon = 0.35
+Camera.SpinRadius = 9.0
+Camera.MaxSpinAngle = 20.0
+Camera.MaxSpinX = 16.0
+Gameplay = VariableContainer()
+Gameplay.SecondsUntilGameOver = 60.0 * 3.0
+Gameplay.TimeRunningOutSeconds = 45.0
+Gameplay.IntroDurationSeconds = 24.0
+Gameplay.FinishDurationSeconds = 10.0
+Gameplay.GatherableFlashTime = 1.0
+Gameplay.ToonAcceleration = {'forward': 40.0,
+ 'backward': 40.0,
+ 'turning': 40.0,
+ 'boostUp': 15.0,
+ 'fall': 10.0,
+ 'activeDropDown': 20.0,
+ 'activeDropBack': 40.0,
+ 'fan': 80.0}
+Gameplay.ToonDeceleration = {'forward': 5.0,
+ 'backward': 3.0,
+ 'turning': 10.0,
+ 'fan': 25.0}
+Gameplay.ToonVelMax = {'forward': 15.0,
+ 'backward': 6.0,
+ 'turning': 10.0,
+ 'boost': 5.5,
+ 'fall': 10.0,
+ 'fallNoFuel': 70.0,
+ 'fan': 55.0}
+Gameplay.ToonTurning = {'turningSpeed': 15.0,
+ 'maxTurningAngle': 45.0}
+Gameplay.RayPlatformCollisionThreshold = 0.2
+Gameplay.UseVariableFanPower = True
+Gameplay.FanMaxPower = 1.0
+Gameplay.FanMinPower = 0.4
+Gameplay.FanCollisionTubeRadius = 4.0
+Gameplay.FanCollisionTubeHeight = 35.0
+Gameplay.FanStreamerMinDuration = 0.2
+Gameplay.FanStreamerMaxDuration = 0.5
+Gameplay.WhirlwindCollisionTubeHeight = 100
+Gameplay.WhirlwindCollisionTubeRadius = 4.0
+Gameplay.WhirlwindMoveBackDist = 15.0
+Gameplay.WhirlwindSpinTime = 1.0
+Gameplay.WhirlwindMoveBackTime = 0.5
+Gameplay.FlyingMinionCollisionRadius = 2.5
+Gameplay.FlyingMinionCollisionHeightOffset = 5.0
+Gameplay.FlyingMinionFloatOffset = 1.0
+Gameplay.FlyingMinionFloatTime = 1.0
+Gameplay.WalkingMinionCollisionRadius = 2.5
+Gameplay.WalkingMinionCollisionHeightOffset = 2.0
+Gameplay.MemoCollisionRadius = 1.5
+Gameplay.MemoSpinRate = 60.0
+Gameplay.DoesToonDieWithFuel = True
+Gameplay.SafezoneId2LaffPickupHealAmount = {2000: 1,
+ 1000: 2,
+ 5000: 4,
+ 4000: 8,
+ 3000: 12,
+ 9000: 16}
+Gameplay.InvulBuffTime = 15.0
+Gameplay.InvulBlinkTime = 5.0
+Gameplay.InvulSingleBlinkTime = 0.5
+Gameplay.PropellerCollisionRadius = 3.0
+Gameplay.PropellerRespawnTime = 5.0
+Gameplay.FuelBurnRate = 0.1
+Gameplay.DepleteFuelStates = ['FlyingUp']
+Gameplay.FuelNormalAmt = 1.0
+Gameplay.FuelLowAmt = 0.66
+Gameplay.FuelVeryLowAmt = 0.33
+Gameplay.FuelStates = PythonUtil.Enum(('FuelNoPropeller', 'FuelEmpty', 'FuelVeryLow', 'FuelLow', 'FuelNormal'))
+Gameplay.RefuelPropSpeed = 5.0
+Gameplay.OverdrivePropSpeed = 2.5
+Gameplay.NormalPropSpeed = 1.5
+Gameplay.TargetedWarningLabelScale = 3.5
+Gameplay.TargetedWarningSingleBlinkTime = 0.25
+Gameplay.TargetedWarningBlinkTime = 3.0
+Gameplay.HitKnockbackDist = 15.0
+Gameplay.HitKnockbackTime = 0.5
+Gameplay.HitCooldownTime = 2.0
+Gameplay.BackpackStates = PythonUtil.Enum(('Normal', 'Targeted', 'Attacked', 'Refuel'))
+Gameplay.BackpackRefuelDuration = 4.0
+Gameplay.BackpackState2TextureName = {Gameplay.BackpackStates.Normal: 'tt_t_ara_cfg_propellerPack',
+ Gameplay.BackpackStates.Targeted: 'tt_t_ara_cfg_propellerPack_eagleTarget',
+ Gameplay.BackpackStates.Attacked: 'tt_t_ara_cfg_propellerPack_eagleAttack',
+ Gameplay.BackpackStates.Refuel: 'tt_t_ara_cfg_propellerPack_flash'}
+Gameplay.MinionDnaName = 'bf'
+Gameplay.MinionScale = 0.8
+Gui = VariableContainer()
+Gui.FuelNumBladesPos2D = (-0.005, -0.017)
+Gui.FuelNumBladesScale = 0.07
+Gui.FuelPos2D = (-1.19, -0.24)
+Gui.NumBlades2FuelColor = {0: (0.9, 0.15, 0.15, 1.0),
+ 1: (0.9, 0.15, 0.15, 1.0),
+ 2: (0.9, 0.9, 0.15, 1.0),
+ 3: (0.25, 0.65, 0.25, 1.0)}
+Gui.FuelNormalColor = (0.25, 0.65, 0.25, 1.0)
+Gui.FuelLowColor = (0.9, 0.9, 0.15, 1.0)
+Gui.FuelVeryLowColor = (0.9, 0.15, 0.15, 1.0)
+Gui.ProgressPos2D = (1.15, -0.15)
+Gui.ProgressHPos2D = (0, 0.82)
+Gui.MarkerScale = 0.03
+Gui.LocalMarkerScale = 0.0425
+LegalEagle = VariableContainer()
+LegalEagle.EagleAndTargetDistCameraTrackThreshold = 30.0
+LegalEagle.InterestConeLength = 80
+LegalEagle.InterestConeOffset = 5.0
+LegalEagle.InterestConeAngle = 60
+LegalEagle.DamageSphereRadius = 3.0
+LegalEagle.OnNestDamageSphereRadius = 6.0
+LegalEagle.VerticalOffset = -6.0
+LegalEagle.PlatformVerticalOffset = 0.0
+LegalEagle.LiftOffTime = 0.5
+LegalEagle.LiftOffHeight = 5.0
+LegalEagle.LockOnSpeed = 3.0
+LegalEagle.LockOnTime = 2.0
+LegalEagle.ExtraPostCooldownTime = 2.0
+LegalEagle.LockOnDistanceFromNest = -7.0
+LegalEagle.ChargeUpTime = 0.75
+LegalEagle.RetreatToNestTime = 2.0
+LegalEagle.PreAttackTime = 0.75
+LegalEagle.PostAttackTime = 0.5
+LegalEagle.RetreatToSkyTime = 1.25
+LegalEagle.EagleAttackShouldXCorrect = True
+LegalEagle.AttackRateOfChangeVec = Vec3(1.0, 1.0, 2.0)
+LegalEagle.PostAttackLength = 5.0
+LegalEagle.CooldownTime = 5.0
+LegalEagle.PostCooldownHeightOffNest = 40.0
+Dev = DevVariableContainer('cogdoflying')
+Dev.DisableDeath = False
+Dev.InfiniteFuel = False
+Dev.InfiniteTimeLimit = True
+Dev.Invincibility = False
+Dev.NoLegalEagleAttacks = False
+Audio = VariableContainer()
+Audio.Cutoff = 75.0
+Audio.MusicFiles = {'normal': 'phase_4/audio/bgm/MG_cannon_game.mid',
+ 'end': 'phase_4/audio/bgm/FF_safezone.mid',
+ 'waiting': 'phase_4/audio/bgm/m_match_bg2.mid',
+ 'invul': 'phase_4/audio/bgm/MG_CogThief.mid',
+ 'timeRunningOut': 'phase_7/audio/bgm/encntr_suit_winning_indoor.mid'}
+Audio.SfxFiles = {'propeller': 'phase_4/audio/sfx/TB_propeller.wav',
+ 'propeller_damaged': 'phase_5/audio/sfx/tt_s_ara_cfg_propellers_damaged.mp3',
+ 'fan': 'phase_4/audio/sfx/target_wind_float_loop.wav',
+ 'getMemo': 'phase_4/audio/sfx/MG_maze_pickup.mp3',
+ 'getLaff': 'phase_4/audio/sfx/avatar_emotion_laugh.mp3',
+ 'getRedTape': 'phase_5/audio/sfx/SA_red_tape.mp3',
+ 'invulBuff': 'phase_4/audio/sfx/ring_get.mp3',
+ 'invulDebuff': 'phase_4/audio/sfx/ring_miss.mp3',
+ 'refuel': 'phase_5/audio/sfx/LB_receive_evidence.mp3',
+ 'bladeBreak': 'phase_5/audio/sfx/tt_s_ara_cfg_propellerBreaks.mp3',
+ 'popupHelpText': 'phase_3/audio/sfx/GUI_balloon_popup.mp3',
+ 'collide': 'phase_3.5/audio/sfx/AV_collision.mp3',
+ 'whirlwind': 'phase_5/audio/sfx/tt_s_ara_cfg_whirlwind.mp3',
+ 'toonInWhirlwind': 'phase_5/audio/sfx/tt_s_ara_cfg_toonInWhirlwind.mp3',
+ 'death': 'phase_5/audio/sfx/tt_s_ara_cfg_toonFalls.mp3',
+ 'legalEagleScream': 'phase_5/audio/sfx/tt_s_ara_cfg_eagleCry.mp3',
+ 'toonHit': 'phase_5/audio/sfx/tt_s_ara_cfg_toonHit.mp3',
+ 'lose': 'phase_4/audio/sfx/MG_lose.mp3',
+ 'win': 'phase_4/audio/sfx/MG_win.mp3',
+ 'refuelSpin': 'phase_4/audio/sfx/clock12.mp3',
+ 'cogDialogue': 'phase_3.5/audio/dial/COG_VO_statement.mp3',
+ 'toonDialogue': 'phase_3.5/audio/dial/AV_dog_long.mp3'}
+Level = VariableContainer()
+Level.GatherableTypes = PythonUtil.Enum(('Memo', 'Propeller', 'LaffPowerup', 'InvulPowerup'))
+Level.ObstacleTypes = PythonUtil.Enum(('Whirlwind', 'Fan', 'Minion'))
+Level.PlatformTypes = PythonUtil.Enum(('Platform', 'StartPlatform', 'EndPlatform'))
+Level.PlatformType2SpawnOffset = {Level.PlatformTypes.Platform: 2.5,
+ Level.PlatformTypes.StartPlatform: 5.0,
+ Level.PlatformTypes.EndPlatform: 5.0}
+Level.QuadLengthUnits = 170
+Level.QuadVisibilityAhead = 1
+Level.QuadVisibilityBehind = 0
+Level.StartPlatformLength = 55
+Level.StartPlatformHeight = 20
+Level.EndPlatformLength = 55
+Level.EndPlatformHeight = 0
+Level.FogColor = VBase4(0.18, 0.19, 0.32, 1.0)
+Level.RenderFogStartFactor = 0.22
+Level.GatherablesDefaultSpread = 1.0
+Level.NumMemosInRing = 6
+Level.PropellerSpinDuration = 10.0
+Level.QuadsByDifficulty = {1: (2, 4, 5),
+ 2: (1, 3, 7),
+ 3: (6, 8)}
+Level.DifficultyOrder = {2000: (1, 1, 1, 2, 1),
+ 1000: (1, 1, 2, 2, 1),
+ 5000: (1, 2, 1, 2, 2),
+ 4000: (1, 2, 1, 2, 3, 2),
+ 3000: (1, 2, 2, 3, 2, 3),
+ 9000: (2, 3, 2, 3, 2, 3, 2)}
+Dev.WantTempLevel = True
+Dev.DevQuadsOrder = (1, 2, 3, 4, 5, 6, 7, 8)
+Level.AddSparkleToPowerups = True
+Level.AddParticlesToStreamers = True
+Level.IgnoreLaffPowerups = False
+Level.SpawnLaffPowerupsInNests = True
+Level.LaffPowerupNestOffset = Point3(0.0, 2.0, 3.0)
+Level.PlatformName = '*lightFixture'
+Level.GatherablesPathName = 'gatherables_path*'
+Level.GatherablesRingName = 'gatherables_ring_path*'
+Level.PropellerName = '*propeller_loc*'
+Level.PowerupType2Loc = {Level.GatherableTypes.LaffPowerup: 'laff_powerup_loc*',
+ Level.GatherableTypes.InvulPowerup: 'invul_powerup_loc*'}
+Level.PowerupType2Model = {Level.GatherableTypes.LaffPowerup: 'legalEagleFeather',
+ Level.GatherableTypes.InvulPowerup: 'redTapePickup'}
+Level.PowerupType2Node = {Level.GatherableTypes.LaffPowerup: 'feather',
+ Level.GatherableTypes.InvulPowerup: 'redTape'}
+Level.GatherableType2TextureName = {Level.GatherableTypes.LaffPowerup: 'tt_t_ara_cfg_legalEagleFeather_flash',
+ Level.GatherableTypes.InvulPowerup: 'tt_t_ara_cfg_redTapePickup_flash',
+ Level.GatherableTypes.Memo: 'tt_t_ara_csa_memo_flash',
+ Level.GatherableTypes.Propeller: 'tt_t_ara_cfg_propellers_flash'}
+Level.WhirlwindName = '*whirlwindPlaceholder'
+Level.WhirlwindPathName = '_path*'
+Level.StreamerName = '*streamerPlaceholder'
+Level.MinionWalkingPathName = '*minion_walking_path*'
+Level.MinionFlyingPathName = '*minion_flying_path*'
+Level.LegalEagleNestName = '*eagleNest_loc*'
diff --git a/toontown/cogdominium/CogdoFlyingGameGuis.py b/toontown/cogdominium/CogdoFlyingGameGuis.py
new file mode 100644
index 0000000..f508c66
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingGameGuis.py
@@ -0,0 +1,202 @@
+from direct.interval.IntervalGlobal import LerpFunctionInterval
+from direct.gui.DirectGui import DirectLabel, DirectFrame, DGG
+from direct.showbase.PythonUtil import bound as clamp
+from pandac.PandaModules import TextNode, NodePath
+from toontown.toonbase import ToontownGlobals
+import CogdoUtil
+import CogdoFlyingGameGlobals as Globals
+
+class CogdoFlyingProgressGui(DirectFrame):
+
+ def __init__(self, parent, level, pos2d = Globals.Gui.ProgressPos2D):
+ DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX)
+ self._parent = parent
+ self._level = level
+ self.reparentTo(self._parent)
+ self.setPos(pos2d[0], 0.0, pos2d[1])
+ self._levelStartY = self._level.startPlatform.getModel().getY()
+ self._levelEndY = self._level.endPlatform.getModel().getY()
+ self._levelDistance = abs(self._levelEndY - self._levelStartY)
+ self._toonMarkers = {}
+ self._initModel()
+ return
+
+ def destroy(self):
+ self._laffMeterModel.removeNode()
+ del self._laffMeterModel
+ DirectFrame.destroy(self)
+
+ def _initModel(self):
+ self._laffMeterModel = loader.loadModel('phase_3/models/gui/laff_o_meter')
+ self._model = CogdoUtil.loadFlyingModel('progressMeter', group='gui')
+ self._model.reparentTo(self)
+ self._model.setBin('fixed', 0)
+ self._lineStart = self._model.find('**/start_loc').getZ()
+ self._lineEnd = self._model.find('**/end_loc').getZ()
+ self._lineDistance = abs(self._lineEnd - self._lineStart)
+
+ def addToon(self, toon):
+ marker = NodePath('toon_marker-%i' % toon.doId)
+ marker.reparentTo(self)
+ self._getToonMarker(toon).copyTo(marker)
+ marker.setColor(toon.style.getHeadColor())
+ if toon.isLocal():
+ marker.setScale(Globals.Gui.LocalMarkerScale)
+ marker.setBin('fixed', 10)
+ else:
+ marker.setScale(Globals.Gui.MarkerScale)
+ marker.setBin('fixed', 5)
+ marker.flattenStrong()
+ self._toonMarkers[toon] = marker
+
+ def removeToon(self, toon):
+ marker = self._toonMarkers.get(toon, None)
+ if marker is not None:
+ marker.removeNode()
+ del self._toonMarkers[toon]
+ return
+
+ def _getToonMarker(self, toon):
+ return self._laffMeterModel.find('**/' + toon.style.getType() + 'head')
+
+ def update(self):
+ for toon, marker in self._toonMarkers.items():
+ progress = clamp((toon.getY() - self._levelStartY) / self._levelDistance, self._levelStartY, self._levelEndY)
+ marker.setZ(clamp(self._lineStart + self._lineDistance * progress, self._lineStart, self._lineEnd))
+
+
+class CogdoFlyingFuelGui(DirectFrame):
+
+ def __init__(self, parent):
+ DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX)
+ self.reparentTo(parent)
+ self.active = 0
+ self._initModel()
+ self._initIntervals()
+ return
+
+ def _initModel(self):
+ self.setPos(Globals.Gui.FuelPos2D[0], 0.0, Globals.Gui.FuelPos2D[1])
+ self.gui = CogdoUtil.loadFlyingModel('propellerMeter', group='gui')
+ self.gui.reparentTo(self)
+ self.gui.setBin('fixed', 0)
+ self.healthBar = self.gui.find('**/healthBar')
+ self.healthBar.setBin('fixed', 1)
+ self.healthBar.setColor(*Globals.Gui.FuelNormalColor)
+ bottomBarLocator = self.gui.find('**/bottomOfBar_loc')
+ bottomBarPos = bottomBarLocator.getPos(render)
+ topBarLocator = self.gui.find('**/topOfBar_loc')
+ topBarPos = topBarLocator.getPos(render)
+ zDist = topBarPos.getZ() - bottomBarPos.getZ()
+ self.fuelLowIndicator = self.gui.find('**/fuelLowIndicator')
+ self.fuelLowIndicator.setBin('fixed', 2)
+ pos = self.fuelLowIndicator.getPos(render)
+ newPos = pos
+ newPos.setZ(bottomBarPos.getZ() + zDist * Globals.Gameplay.FuelLowAmt)
+ self.fuelLowIndicator.setPos(render, newPos)
+ self.fuelVeryLowIndicator = self.gui.find('**/fuelVeryLowIndicator')
+ self.fuelVeryLowIndicator.setBin('fixed', 2)
+ pos = self.fuelVeryLowIndicator.getPos(render)
+ newPos = pos
+ newPos.setZ(bottomBarPos.getZ() + zDist * Globals.Gameplay.FuelVeryLowAmt)
+ self.fuelVeryLowIndicator.setPos(render, newPos)
+ self.propellerMain = self.gui.find('**/propellers')
+ self.propellerMain.setBin('fixed', 3)
+ self.propellerHead = self.gui.find('**/propellerHead')
+ self.propellerHead.setBin('fixed', 4)
+ self.blades = []
+ self.activeBlades = []
+ index = 1
+ blade = self.propellerMain.find('**/propeller%d' % index)
+ while not blade.isEmpty():
+ self.blades.append(blade)
+ index += 1
+ blade = self.propellerMain.find('**/propeller%d' % index)
+
+ for blade in self.blades:
+ self.activeBlades.append(blade)
+
+ self.bladeNumberLabel = DirectLabel(parent=self.propellerHead, relief=None, pos=(Globals.Gui.FuelNumBladesPos2D[0], 0, Globals.Gui.FuelNumBladesPos2D[1]), scale=Globals.Gui.FuelNumBladesScale, text=str(len(self.activeBlades)), text_align=TextNode.ACenter, text_fg=(0.0,
+ 0.0,
+ -0.002,
+ 1), text_shadow=(0.75, 0.75, 0.75, 1), text_font=ToontownGlobals.getInterfaceFont())
+ self.bladeNumberLabel.setBin('fixed', 5)
+ return
+
+ def _initIntervals(self):
+ self._healthIval = LerpFunctionInterval(self.healthBar.setSz, fromData=0.0, toData=1.0, duration=2.0)
+ self.baseSpinDuration = 2.0
+ self._spinIval = LerpFunctionInterval(self.propellerMain.setR, fromData=0.0, toData=-360.0, duration=self.baseSpinDuration)
+
+ def show(self):
+ DirectFrame.show(self)
+ self._spinIval.loop()
+
+ def hide(self):
+ DirectFrame.hide(self)
+ self._spinIval.pause()
+
+ def resetBlades(self):
+ self.setBlades(len(self.blades))
+
+ def setBlades(self, fuelState):
+ if fuelState not in Globals.Gameplay.FuelStates:
+ return
+ numBlades = fuelState - 1
+ if len(self.activeBlades) != numBlades:
+ for i in range(len(self.activeBlades)):
+ blade = self.activeBlades.pop()
+ blade.stash()
+
+ if numBlades > len(self.blades):
+ numBlades = len(self.blades)
+ for i in range(numBlades):
+ blade = self.blades[i]
+ self.activeBlades.append(blade)
+ blade.unstash()
+
+ self.bladeNumberLabel['text'] = str(len(self.activeBlades))
+ self.bladeNumberLabel.setText()
+ self.updateHealthBarColor()
+
+ def bladeLost(self):
+ if len(self.activeBlades) > 0:
+ blade = self.activeBlades.pop()
+ blade.stash()
+ self.bladeNumberLabel['text'] = str(len(self.activeBlades))
+ self.bladeNumberLabel.setText()
+ self.updateHealthBarColor()
+
+ def updateHealthBarColor(self):
+ color = Globals.Gui.NumBlades2FuelColor[len(self.activeBlades)]
+ self.healthBar.setColor(*color)
+
+ def setPropellerSpinRate(self, newRate):
+ self._spinIval.setPlayRate(newRate)
+
+ def setRefuelLerpFromData(self):
+ startScale = self.healthBar.getSz()
+ self._healthIval.fromData = startScale
+
+ def setFuel(self, fuel):
+ self.fuel = fuel
+
+ def update(self):
+ self.healthBar.setSz(self.fuel)
+
+ def destroy(self):
+ self.bladeNumberLabel.removeNode()
+ self.bladeNumberLabel = None
+ self._healthIval.clearToInitial()
+ del self._healthIval
+ self.healthBar = None
+ self.fuelLowIndicator = None
+ self.fuelVeryLowIndicator = None
+ self.propellerMain = None
+ self.propellerHead = None
+ del self.blades[:]
+ del self.activeBlades[:]
+ self.gui.detachNode()
+ self.gui = None
+ DirectFrame.destroy(self)
+ return
diff --git a/toontown/cogdominium/CogdoFlyingGameMovies.py b/toontown/cogdominium/CogdoFlyingGameMovies.py
new file mode 100644
index 0000000..1f1b577
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingGameMovies.py
@@ -0,0 +1,144 @@
+from pandac.PandaModules import Point3, PlaneNode
+from direct.showbase.ShowBase import Plane
+from direct.showbase.RandomNumGen import RandomNumGen
+from direct.interval.MetaInterval import Sequence, Parallel
+from direct.interval.FunctionInterval import Func, Wait
+from toontown.toonbase import TTLocalizer
+from toontown.toon import Toon, ToonHead, ToonDNA
+from toontown.suit import Suit, SuitDNA
+import CogdoFlyingGameGlobals as Globals
+from CogdoUtil import CogdoGameMovie
+import CogdoUtil
+
+class CogdoFlyingGameIntro(CogdoGameMovie):
+
+ def __init__(self, level, rng):
+ CogdoGameMovie.__init__(self)
+ self._level = level
+ self._rng = RandomNumGen(rng)
+ self._exit = self._level.getExit()
+
+ def _getRandomLine(self, lineList):
+ return CogdoUtil.getRandomDialogueLine(lineList, self._rng)
+
+ def displayLine(self, who, text):
+ self._dialogueLabel.node().setText(text)
+ if who == 'toon':
+ self.toonHead.reparentTo(aspect2d)
+ self.cogHead.reparentTo(hidden)
+ self._toonDialogueSfx.play()
+ self.toonHead.setClipPlane(self.clipPlane)
+ else:
+ self.toonHead.reparentTo(hidden)
+ self.cogHead.reparentTo(aspect2d)
+ self._cogDialogueSfx.play()
+ self.cogHead.setClipPlane(self.clipPlane)
+
+ def makeSuit(self, suitType):
+ suit = Suit.Suit()
+ dna = SuitDNA.SuitDNA()
+ dna.newSuit(suitType)
+ suit.setStyle(dna)
+ suit.isDisguised = 1
+ suit.generateSuit()
+ suit.setScale(1, 1, 2)
+ suit.setPos(0, 0, -4.4)
+ suit.reparentTo(self.toonHead)
+ for part in suit.getHeadParts():
+ part.hide()
+
+ suit.loop('neutral')
+
+ def load(self):
+ CogdoGameMovie.load(self)
+ self.toonDNA = ToonDNA.ToonDNA()
+ self.toonDNA.newToonFromProperties('dss', 'ss', 'm', 'm', 2, 0, 2, 2, 1, 8, 1, 8, 1, 14)
+ self.toonHead = Toon.Toon()
+ self.toonHead.setDNA(self.toonDNA)
+ self.makeSuit('sc')
+ self.toonHead.getGeomNode().setDepthWrite(1)
+ self.toonHead.getGeomNode().setDepthTest(1)
+ self.toonHead.loop('neutral')
+ self.toonHead.setPosHprScale(-0.73, 0, -1.27, 180, 0, 0, 0.18, 0.18, 0.18)
+ self.toonHead.reparentTo(hidden)
+ self.toonHead.startBlink()
+ self.cogHead = Suit.Suit()
+ self.cogDNA = SuitDNA.SuitDNA()
+ self.cogDNA.newSuit('le')
+ self.cogHead.setDNA(self.cogDNA)
+ self.cogHead.getGeomNode().setDepthWrite(1)
+ self.cogHead.getGeomNode().setDepthTest(1)
+ self.cogHead.loop('neutral')
+ self.cogHead.setPosHprScale(-0.74, 0, -1.79, 180, 0, 0, 0.12, 0.14, 0.14)
+ self.cogHead.reparentTo(hidden)
+ self.clipPlane = self.toonHead.attachNewNode(PlaneNode('clip'))
+ self.clipPlane.node().setPlane(Plane(0, 0, 1, 0))
+ self.clipPlane.setPos(0, 0, 2.45)
+ audioMgr = base.cogdoGameAudioMgr
+ self._cogDialogueSfx = audioMgr.createSfx('cogDialogue')
+ self._toonDialogueSfx = audioMgr.createSfx('toonDialogue')
+
+ def start():
+ camera.wrtReparentTo(render)
+ self._startUpdateTask()
+
+ def end():
+ self._stopUpdateTask()
+
+ introDuration = Globals.Gameplay.IntroDurationSeconds
+ dialogue = TTLocalizer.CogdoFlyingIntroMovieDialogue
+ waitDur = introDuration / len(dialogue)
+ flyDur = introDuration - waitDur * 0.5
+ flyThroughIval = Parallel(camera.posInterval(flyDur, self._exit.getPos(render) + Point3(0, -22, 1), blendType='easeInOut'), camera.hprInterval(flyDur, Point3(0, 5, 0), blendType='easeInOut'))
+ self._ival = Sequence(Func(start), Parallel(flyThroughIval, Sequence(Func(self.displayLine, 'cog', self._getRandomLine(dialogue[0])), Wait(waitDur), Func(self.displayLine, 'toon', self._getRandomLine(dialogue[1])), Wait(waitDur), Func(self.displayLine, 'cog', self._getRandomLine(dialogue[2])), Wait(waitDur))), Func(end))
+
+ def _updateTask(self, task):
+ dt = globalClock.getDt()
+ self._level.update(dt)
+ return task.cont
+
+ def unload(self):
+ CogdoGameMovie.unload(self)
+ del self._cogDialogueSfx
+ del self._toonDialogueSfx
+ self.toonHead.stopBlink()
+ self.toonHead.stop()
+ self.toonHead.removeNode()
+ self.toonHead.delete()
+ del self.toonHead
+ self.cogHead.stop()
+ self.cogHead.removeNode()
+ self.cogHead.delete()
+ del self.cogHead
+ self._exit = None
+ self._level = None
+ return
+
+
+class CogdoFlyingGameFinish(CogdoGameMovie):
+
+ def __init__(self, level, players):
+ CogdoGameMovie.__init__(self)
+ self._exit = level.getExit()
+ self._players = players
+
+ def load(self):
+ CogdoGameMovie.load(self)
+
+ def showDoor():
+ camera.wrtReparentTo(render)
+ camera.setPos(self._exit, 0, -55, 40)
+ camera.lookAt(self._exit, 0, 0, -20)
+ self._exit.open()
+
+ exitDur = 1.0
+ showExitIval = Sequence(Func(camera.wrtReparentTo, render), Parallel(camera.posInterval(exitDur, Point3(0, -55, 40), other=self._exit, blendType='easeInOut'), camera.hprInterval(exitDur, Point3(0, -45, 0), blendType='easeInOut')))
+
+ def showPlayersLeaving():
+ for player in self._players:
+ self._exit.toonEnters(player.toon)
+
+ self._ival = Sequence(showExitIval, Func(self._exit.open), Func(showPlayersLeaving), Wait(Globals.Gameplay.FinishDurationSeconds - exitDur - 1.0), Func(base.transitions.irisOut), Wait(1.0))
+
+ def unload(self):
+ CogdoGameMovie.unload(self)
diff --git a/toontown/cogdominium/CogdoFlyingGuiManager.py b/toontown/cogdominium/CogdoFlyingGuiManager.py
new file mode 100644
index 0000000..b3b598b
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingGuiManager.py
@@ -0,0 +1,167 @@
+from pandac.PandaModules import NodePath
+from toontown.toonbase import ToontownIntervals
+from toontown.toonbase.ToontownTimer import ToontownTimer
+from CogdoFlyingGameGuis import CogdoFlyingFuelGui, CogdoFlyingProgressGui
+from CogdoGameMessageDisplay import CogdoGameMessageDisplay
+from CogdoMemoGui import CogdoMemoGui
+import CogdoFlyingGameGlobals as Globals
+
+class CogdoFlyingGuiManager:
+ ClearMessageDisplayEventName = 'ClearMessageDisplayEvent'
+ EagleTargetingLocalPlayerEventName = 'EagleTargetingLocalPlayerEvent'
+ EagleAttackingLocalPlayerEventName = 'EagleAttackingLocalPlayerEvent'
+ FirstPressOfCtrlEventName = 'FirstPressOfCtrlEvent'
+ PickedUpFirstPropellerEventName = 'PickedUpFirstPropellerEvent'
+ InvulnerableEventName = 'InvulnerableEvent'
+ StartRunningOutOfTimeMusicEventName = 'StartRunningOutOfTimeEvent'
+
+ def __init__(self, level):
+ self._level = level
+ self.root = NodePath('CogdoFlyingGui')
+ self.root.reparentTo(aspect2d)
+ self.root.stash()
+ self._initTimer()
+ self._initHud()
+ self._initMessageDisplay()
+ self.sentTimeRunningOutMessage = False
+ self._refuelGui = CogdoFlyingFuelGui(self.root)
+ self._progressGui = CogdoFlyingProgressGui(self.root, self._level)
+
+ def _initHud(self):
+ self._memoGui = CogdoMemoGui(self.root)
+ self._memoGui.posNextToLaffMeter()
+
+ def _initTimer(self):
+ self._timer = ToontownTimer()
+ self._timer.reparentTo(self.root)
+ self._timer.posInTopRightCorner()
+
+ def _initMessageDisplay(self):
+ audioMgr = base.cogdoGameAudioMgr
+ sound = audioMgr.createSfx('popupHelpText')
+ self._messageDisplay = CogdoGameMessageDisplay('CogdoFlyingMessageDisplay', self.root, sfx=sound)
+
+ def destroyTimer(self):
+ if self._timer is not None:
+ self._timer.stop()
+ self._timer.destroy()
+ self._timer = None
+ return
+
+ def onstage(self):
+ self.root.unstash()
+ self._refuelGui.hide()
+ self._progressGui.hide()
+
+ def presentProgressGui(self):
+ ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._progressGui, 'present_progress_gui'))
+
+ def presentRefuelGui(self):
+ ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._refuelGui, 'present_fuel_gui'))
+
+ def presentTimerGui(self):
+ ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._timer, 'present_timer_gui'))
+
+ def presentMemoGui(self):
+ ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._memoGui, 'present_memo_gui'))
+
+ def offstage(self):
+ self.root.stash()
+ self._refuelGui.hide()
+ self._progressGui.hide()
+ self.hideTimer()
+
+ def getTimeLeft(self):
+ return Globals.Gameplay.SecondsUntilGameOver - self._timer.getElapsedTime()
+
+ def isTimeRunningOut(self):
+ return self.getTimeLeft() < Globals.Gameplay.TimeRunningOutSeconds
+
+ def startTimer(self, duration, timerExpiredCallback = None, keepHidden = False):
+ if self._timer is None:
+ self._initTimer()
+ self._timer.setTime(duration)
+ self._timer.countdown(duration, timerExpiredCallback)
+ if keepHidden:
+ self.hideTimer()
+ else:
+ self.showTimer()
+ return
+
+ def stopTimer(self):
+ if hasattr(self, '_timer') and self._timer is not None:
+ self.hideTimer()
+ self._timer.stop()
+ return
+
+ def showTimer(self):
+ self._timer.show()
+
+ def hideTimer(self):
+ self._timer.hide()
+
+ def forceTimerDone(self):
+ if self._timer.countdownTask != None:
+ self._timer.countdownTask.duration = 0
+ return
+
+ def showRefuelGui(self):
+ self._refuelGui.show()
+
+ def hideRefuelGui(self):
+ self._refuelGui.hide()
+
+ def setMessage(self, text, color = None, transition = 'fade'):
+ self._messageDisplay.updateMessage(text, color, transition)
+
+ def setTemporaryMessage(self, text, duration = 3.0, color = None):
+ self._messageDisplay.showMessageTemporarily(text, duration, color)
+
+ def setFuel(self, fuel):
+ self._refuelGui.setFuel(fuel)
+
+ def resetBlades(self):
+ self._refuelGui.resetBlades()
+
+ def setBlades(self, fuelState):
+ self._refuelGui.setBlades(fuelState)
+
+ def bladeLost(self):
+ self._refuelGui.bladeLost()
+
+ def setPropellerSpinRate(self, newRate):
+ self._refuelGui.setPropellerSpinRate(newRate)
+
+ def setMemoCount(self, score):
+ self._memoGui.setCount(score)
+
+ def addToonToProgressMeter(self, toon):
+ self._progressGui.addToon(toon)
+
+ def removeToonFromProgressMeter(self, toon):
+ self._progressGui.removeToon(toon)
+
+ def update(self):
+ if self.isTimeRunningOut() and not self.sentTimeRunningOutMessage:
+ messenger.send(CogdoFlyingGuiManager.StartRunningOutOfTimeMusicEventName)
+ self.sentTimeRunningOutMessage = True
+ self._refuelGui.update()
+ self._progressGui.update()
+
+ def destroy(self):
+ ToontownIntervals.cleanup('present_fuel_gui')
+ ToontownIntervals.cleanup('present_timer_gui')
+ ToontownIntervals.cleanup('present_memo_gui')
+ ToontownIntervals.cleanup('present_progress_gui')
+ self._refuelGui.destroy()
+ self._refuelGui = None
+ self._memoGui.destroy()
+ self._memoGui = None
+ self._progressGui.destroy()
+ self._progressGui = None
+ self.destroyTimer()
+ self._messageDisplay.destroy()
+ self._messageDisplay = None
+ self.root.removeNode()
+ self.root = None
+ return
diff --git a/toontown/cogdominium/CogdoFlyingInputManager.py b/toontown/cogdominium/CogdoFlyingInputManager.py
new file mode 100644
index 0000000..d771f4c
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingInputManager.py
@@ -0,0 +1,42 @@
+from pandac.PandaModules import CollisionSphere, CollisionNode, BitMask32, CollisionHandlerEvent, CollisionRay
+from toontown.minigame import ArrowKeys
+
+class CogdoFlyingInputManager:
+
+ def __init__(self):
+ self.arrowKeys = ArrowKeys.ArrowKeys()
+ self.arrowKeys.disable()
+
+ def enable(self):
+ self.arrowKeys.setPressHandlers([self.__upArrowPressed,
+ self.__downArrowPressed,
+ self.__leftArrowPressed,
+ self.__rightArrowPressed,
+ self.__controlPressed])
+ self.arrowKeys.enable()
+
+ def disable(self):
+ self.arrowKeys.clearPressHandlers()
+ self.arrowKeys.disable()
+
+ def destroy(self):
+ self.disable()
+ self.arrowKeys.destroy()
+ self.arrowKeys = None
+ self.refuelLerp = None
+ return
+
+ def __upArrowPressed(self):
+ pass
+
+ def __downArrowPressed(self):
+ pass
+
+ def __leftArrowPressed(self):
+ pass
+
+ def __rightArrowPressed(self):
+ pass
+
+ def __controlPressed(self):
+ pass
diff --git a/toontown/cogdominium/CogdoFlyingLegalEagle.py b/toontown/cogdominium/CogdoFlyingLegalEagle.py
new file mode 100644
index 0000000..f7eabef
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingLegalEagle.py
@@ -0,0 +1,606 @@
+import math
+from direct.showbase.DirectObject import DirectObject
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm.FSM import FSM
+from direct.task.Task import Task
+from direct.interval.IntervalGlobal import Sequence, Parallel, LerpScaleInterval, LerpFunctionInterval, Func, Wait, LerpFunc, SoundInterval, ParallelEndTogether, LerpPosInterval, ActorInterval, LerpPosHprInterval, LerpHprInterval
+from direct.directutil import Mopath
+from direct.showbase.PythonUtil import bound as clamp
+from pandac.PandaModules import CollisionSphere, CollisionNode, CollisionTube, CollisionPolygon, Vec3, Point3
+from toontown.suit import Suit
+from toontown.suit import SuitDNA
+from toontown.toonbase import ToontownGlobals
+from toontown.battle import BattleProps
+from CogdoFlyingUtil import swapAvatarShadowPlacer
+import CogdoUtil
+import CogdoFlyingGameGlobals as Globals
+
+class CogdoFlyingLegalEagle(DirectObject, FSM):
+ CollSphereName = 'CogdoFlyingLegalEagleSphere'
+ CollisionEventName = 'CogdoFlyingLegalEagleCollision'
+ InterestCollName = 'CogdoFlyingLegalEagleInterestCollision'
+ RequestAddTargetEventName = 'CogdoFlyingLegalEagleRequestTargetEvent'
+ RequestAddTargetAgainEventName = 'CogdoFlyingLegalEagleRequestTargetAgainEvent'
+ RequestRemoveTargetEventName = 'CogdoFlyingLegalEagleRemoveTargetEvent'
+ ForceRemoveTargetEventName = 'CogdoFlyingLegalEagleForceRemoveTargetEvent'
+ EnterLegalEagle = 'CogdoFlyingLegalEagleDamageToon'
+ ChargingToAttackEventName = 'LegalEagleChargingToAttack'
+ LockOnToonEventName = 'LegalEagleLockOnToon'
+ CooldownEventName = 'LegalEagleCooldown'
+ notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingLegalEagle')
+
+ def __init__(self, nest, index, suitDnaName = 'le'):
+ FSM.__init__(self, 'CogdoFlyingLegalEagle')
+ self.defaultTransitions = {'Off': ['Roost'],
+ 'Roost': ['TakeOff', 'Off'],
+ 'TakeOff': ['LockOnToon', 'LandOnNest', 'Off'],
+ 'LockOnToon': ['RetreatToNest', 'ChargeUpAttack', 'Off'],
+ 'ChargeUpAttack': ['RetreatToNest', 'Attack', 'Off'],
+ 'Attack': ['RetreatToSky', 'Off'],
+ 'RetreatToSky': ['Cooldown', 'Off'],
+ 'Cooldown': ['LockOnToon', 'LandOnNest', 'Off'],
+ 'RetreatToNest': ['LandOnNest', 'Off'],
+ 'LandOnNest': ['Roost', 'Off']}
+ self.index = index
+ self.nest = nest
+ self.target = None
+ self.isEagleInterested = False
+ self.collSphere = None
+ self.suit = Suit.Suit()
+ d = SuitDNA.SuitDNA()
+ d.newSuit(suitDnaName)
+ self.suit.setDNA(d)
+ self.suit.reparentTo(render)
+ swapAvatarShadowPlacer(self.suit, 'legalEagle-%sShadowPlacer' % index)
+ self.suit.setPos(self.nest.getPos(render))
+ self.suit.setHpr(-180, 0, 0)
+ self.suit.stash()
+ self.prop = None
+ self.attachPropeller()
+ head = self.suit.find('**/joint_head')
+ self.interestConeOrigin = self.nest.attachNewNode('fakeHeadNodePath')
+ self.interestConeOrigin.setPos(render, head.getPos(render) + Vec3(0, Globals.LegalEagle.InterestConeOffset, 0))
+ self.attackTargetPos = None
+ self.startOfRetreatToSkyPos = None
+ pathModel = CogdoUtil.loadFlyingModel('legalEaglePaths')
+ self.chargeUpMotionPath = Mopath.Mopath(name='chargeUpMotionPath-%i' % self.index)
+ self.chargeUpMotionPath.loadNodePath(pathModel.find('**/charge_path'))
+ self.retreatToSkyMotionPath = Mopath.Mopath(name='retreatToSkyMotionPath-%i' % self.index)
+ self.retreatToSkyMotionPath.loadNodePath(pathModel.find('**/retreat_path'))
+ audioMgr = base.cogdoGameAudioMgr
+ self._screamSfx = audioMgr.createSfx('legalEagleScream', self.suit)
+ self.initIntervals()
+ return
+
+ def attachPropeller(self):
+ if self.prop == None:
+ self.prop = BattleProps.globalPropPool.getProp('propeller')
+ head = self.suit.find('**/joint_head')
+ self.prop.reparentTo(head)
+ return
+
+ def detachPropeller(self):
+ if self.prop:
+ self.prop.cleanup()
+ self.prop.removeNode()
+ self.prop = None
+ return
+
+ def _getAnimationIval(self, animName, startFrame = 0, endFrame = None, duration = 1):
+ if endFrame == None:
+ self.suit.getNumFrames(animName) - 1
+ frames = endFrame - startFrame
+ frameRate = self.suit.getFrameRate(animName)
+ newRate = frames / duration
+ playRate = newRate / frameRate
+ ival = Sequence(ActorInterval(self.suit, animName, playRate=playRate))
+ return ival
+
+ def initIntervals(self):
+ dur = Globals.LegalEagle.LiftOffTime
+ nestPos = self.nest.getPos(render)
+ airPos = nestPos + Vec3(0.0, 0.0, Globals.LegalEagle.LiftOffHeight)
+ self.takeOffSeq = Sequence(Parallel(Sequence(Wait(dur * 0.6), LerpPosInterval(self.suit, dur * 0.4, startPos=nestPos, pos=airPos, blendType='easeInOut'))), Wait(1.5), Func(self.request, 'next'), name='%s.takeOffSeq-%i' % (self.__class__.__name__, self.index))
+ self.landOnNestPosLerp = LerpPosInterval(self.suit, 1.0, startPos=airPos, pos=nestPos, blendType='easeInOut')
+ self.landingSeq = Sequence(Func(self.updateLandOnNestPosLerp), Parallel(self.landOnNestPosLerp), Func(self.request, 'next'), name='%s.landingSeq-%i' % (self.__class__.__name__, self.index))
+ dur = Globals.LegalEagle.ChargeUpTime
+ self.chargeUpPosLerp = LerpFunc(self.moveAlongChargeUpMopathFunc, fromData=0.0, toData=self.chargeUpMotionPath.getMaxT(), duration=dur, blendType='easeInOut')
+ self.chargeUpAttackSeq = Sequence(Func(self.updateChargeUpPosLerp), self.chargeUpPosLerp, Func(self.request, 'next'), name='%s.chargeUpAttackSeq-%i' % (self.__class__.__name__, self.index))
+ dur = Globals.LegalEagle.RetreatToNestTime
+ self.retreatToNestPosLerp = LerpPosInterval(self.suit, dur, startPos=Vec3(0, 0, 0), pos=airPos, blendType='easeInOut')
+ self.retreatToNestSeq = Sequence(Func(self.updateRetreatToNestPosLerp), self.retreatToNestPosLerp, Func(self.request, 'next'), name='%s.retreatToNestSeq-%i' % (self.__class__.__name__, self.index))
+ dur = Globals.LegalEagle.RetreatToSkyTime
+ self.retreatToSkyPosLerp = LerpFunc(self.moveAlongRetreatMopathFunc, fromData=0.0, toData=self.retreatToSkyMotionPath.getMaxT(), duration=dur, blendType='easeOut')
+ self.retreatToSkySeq = Sequence(Func(self.updateRetreatToSkyPosLerp), self.retreatToSkyPosLerp, Func(self.request, 'next'), name='%s.retreatToSkySeq-%i' % (self.__class__.__name__, self.index))
+ dur = Globals.LegalEagle.PreAttackTime
+ self.preAttackLerpXY = LerpFunc(self.updateAttackXY, fromData=0.0, toData=1.0, duration=dur)
+ self.preAttackLerpZ = LerpFunc(self.updateAttackZ, fromData=0.0, toData=1.0, duration=dur, blendType='easeOut')
+ dur = Globals.LegalEagle.PostAttackTime
+ self.postAttackPosLerp = LerpPosInterval(self.suit, dur, startPos=Vec3(0, 0, 0), pos=Vec3(0, 0, 0))
+ self.attackSeq = Sequence(Parallel(self.preAttackLerpXY, self.preAttackLerpZ), Func(self.updatePostAttackPosLerp), self.postAttackPosLerp, Func(self.request, 'next'), name='%s.attackSeq-%i' % (self.__class__.__name__, self.index))
+ dur = Globals.LegalEagle.CooldownTime
+ self.cooldownSeq = Sequence(Wait(dur), Func(self.request, 'next'), name='%s.cooldownSeq-%i' % (self.__class__.__name__, self.index))
+ self.propTrack = Sequence(ActorInterval(self.prop, 'propeller', startFrame=0, endFrame=14))
+ self.hoverOverNestSeq = Sequence(ActorInterval(self.suit, 'landing', startFrame=10, endFrame=20, playRate=0.5), ActorInterval(self.suit, 'landing', startFrame=20, endFrame=10, playRate=0.5))
+
+ def initCollision(self):
+ self.collSphere = CollisionSphere(0, 0, 0, 0)
+ self.collSphere.setTangible(0)
+ self.collNode = CollisionNode('%s-%s' % (self.CollSphereName, self.index))
+ self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
+ self.collNode.addSolid(self.collSphere)
+ self.collNodePath = self.suit.attachNewNode(self.collNode)
+ self.collNodePath.hide()
+ self.accept('enter%s-%s' % (self.CollSphereName, self.index), self.handleEnterSphere)
+ self.setCollSphereToNest()
+
+ def getInterestConeLength(self):
+ return Globals.LegalEagle.InterestConeLength + Globals.LegalEagle.InterestConeOffset
+
+ def isToonInView(self, toon):
+ distanceThreshold = self.getInterestConeLength()
+ angleThreshold = Globals.LegalEagle.InterestConeAngle
+ toonPos = toon.getPos(render)
+ nestPos = self.nest.getPos(render)
+ distance = toon.getDistance(self.interestConeOrigin)
+ if distance > distanceThreshold:
+ return False
+ if toonPos[1] > nestPos[1]:
+ return False
+ a = toon.getPos(render) - self.interestConeOrigin.getPos(render)
+ a.normalize()
+ b = Vec3(0, -1, 0)
+ dotProduct = a.dot(b)
+ angle = math.degrees(math.acos(dotProduct))
+ if angle <= angleThreshold / 2.0:
+ return True
+ else:
+ return False
+
+ def update(self, dt, localPlayer):
+ if Globals.Dev.NoLegalEagleAttacks:
+ return
+ inView = self.isToonInView(localPlayer.toon)
+ if inView and not self.isEagleInterested:
+ self.handleEnterInterest()
+ elif inView and self.isEagleInterested:
+ self.handleAgainInterest()
+ elif not inView and self.isEagleInterested:
+ self.handleExitInterest()
+
+ def updateLockOnTask(self):
+ dt = globalClock.getDt()
+ targetPos = self.target.getPos(render)
+ suitPos = self.suit.getPos(render)
+ nestPos = self.nest.getPos(render)
+ attackPos = Vec3(targetPos)
+ attackPos[1] = nestPos[1] + Globals.LegalEagle.LockOnDistanceFromNest
+ attackPos[2] += Globals.LegalEagle.VerticalOffset
+ if attackPos[2] < nestPos[2]:
+ attackPos[2] = nestPos[2]
+ attackChangeVec = (attackPos - suitPos) * Globals.LegalEagle.LockOnSpeed
+ self.suit.setPos(suitPos + attackChangeVec * dt)
+ return Task.cont
+
+ def updateAttackXY(self, value):
+ if Globals.LegalEagle.EagleAttackShouldXCorrect:
+ x = self.readyToAttackPos.getX() + (self.attackTargetPos.getX() - self.readyToAttackPos.getX()) * value
+ self.suit.setX(x)
+ y = self.readyToAttackPos.getY() + (self.attackTargetPos.getY() - self.readyToAttackPos.getY()) * value
+ self.suit.setY(y)
+
+ def updateAttackZ(self, value):
+ z = self.readyToAttackPos.getZ() + (self.attackTargetPos.getZ() - self.readyToAttackPos.getZ()) * value
+ self.suit.setZ(z)
+
+ def moveAlongChargeUpMopathFunc(self, value):
+ self.chargeUpMotionPath.goTo(self.suit, value)
+ self.suit.setPos(self.suit.getPos() + self.startOfChargeUpPos)
+
+ def moveAlongRetreatMopathFunc(self, value):
+ self.retreatToSkyMotionPath.goTo(self.suit, value)
+ self.suit.setPos(self.suit.getPos() + self.startOfRetreatToSkyPos)
+
+ def updateChargeUpPosLerp(self):
+ self.startOfChargeUpPos = self.suit.getPos(render)
+
+ def updateLandOnNestPosLerp(self):
+ self.landOnNestPosLerp.setStartPos(self.suit.getPos())
+
+ def updateRetreatToNestPosLerp(self):
+ self.retreatToNestPosLerp.setStartPos(self.suit.getPos())
+
+ def updateRetreatToSkyPosLerp(self):
+ self.startOfRetreatToSkyPos = self.suit.getPos(render)
+
+ def updatePostAttackPosLerp(self):
+ suitPos = self.suit.getPos(render)
+ finalPos = suitPos + Vec3(0, -Globals.LegalEagle.PostAttackLength, 0)
+ self.postAttackPosLerp.setStartPos(suitPos)
+ self.postAttackPosLerp.setEndPos(finalPos)
+
+ def handleEnterSphere(self, collEntry):
+ self.notify.debug('handleEnterSphere:%i' % self.index)
+ messenger.send(CogdoFlyingLegalEagle.EnterLegalEagle, [self, collEntry])
+
+ def handleEnterInterest(self):
+ self.notify.debug('handleEnterInterestColl:%i' % self.index)
+ self.isEagleInterested = True
+ messenger.send(CogdoFlyingLegalEagle.RequestAddTargetEventName, [self.index])
+
+ def handleAgainInterest(self):
+ self.isEagleInterested = True
+ messenger.send(CogdoFlyingLegalEagle.RequestAddTargetAgainEventName, [self.index])
+
+ def handleExitInterest(self):
+ self.notify.debug('handleExitInterestSphere:%i' % self.index)
+ self.isEagleInterested = False
+ messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [self.index])
+
+ def hasTarget(self):
+ if self.target != None:
+ return True
+ else:
+ return False
+ return
+
+ def setTarget(self, toon, elapsedTime = 0.0):
+ self.notify.debug('Setting eagle %i to target: %s, elapsed time: %s' % (self.index, toon.getName(), elapsedTime))
+ self.target = toon
+ if self.state == 'Roost':
+ self.request('next', elapsedTime)
+ if self.state == 'ChargeUpAttack':
+ messenger.send(CogdoFlyingLegalEagle.ChargingToAttackEventName, [self.target.doId])
+
+ def clearTarget(self, elapsedTime = 0.0):
+ self.notify.debug('Clearing target from eagle %i, elapsed time: %s' % (self.index, elapsedTime))
+ messenger.send(CogdoFlyingLegalEagle.CooldownEventName, [self.target.doId])
+ self.target = None
+ if self.state in ['LockOnToon']:
+ self.request('next', elapsedTime)
+ return
+
+ def leaveCooldown(self, elapsedTime = 0.0):
+ if self.state in ['Cooldown']:
+ self.request('next', elapsedTime)
+
+ def shouldBeInFrame(self):
+ if self.state in ['TakeOff', 'LockOnToon', 'ChargeUpAttack']:
+ return True
+ elif self.state == 'Attack':
+ distance = self.suit.getDistance(self.target)
+ threshold = Globals.LegalEagle.EagleAndTargetDistCameraTrackThreshold
+ suitPos = self.suit.getPos(render)
+ targetPos = self.target.getPos(render)
+ if distance > threshold and suitPos[1] > targetPos[1]:
+ return True
+ return False
+
+ def getTarget(self):
+ return self.target
+
+ def onstage(self):
+ self.suit.unstash()
+ self.request('Roost')
+
+ def offstage(self):
+ self.suit.stash()
+ self.request('Off')
+
+ def gameStart(self, gameStartTime):
+ self.gameStartTime = gameStartTime
+ self.initCollision()
+
+ def gameEnd(self):
+ self.shutdownCollisions()
+
+ def shutdownCollisions(self):
+ self.ignoreAll()
+ if self.collSphere != None:
+ del self.collSphere
+ self.collSphere = None
+ if self.collNodePath != None:
+ self.collNodePath.removeNode()
+ del self.collNodePath
+ self.collNodePath = None
+ if self.collNode != None:
+ del self.collNode
+ self.collNode = None
+ return
+
+ def destroy(self):
+ self.request('Off')
+ self.detachPropeller()
+ del self._screamSfx
+ self.suit.cleanup()
+ self.suit.removeNode()
+ self.suit.delete()
+ self.interestConeOrigin.removeNode()
+ del self.interestConeOrigin
+ self.nest = None
+ self.target = None
+ taskMgr.remove('updateLockOnTask-%i' % self.index)
+ taskMgr.remove('exitLockOnToon-%i' % self.index)
+ self.propTrack.clearToInitial()
+ del self.propTrack
+ del self.chargeUpMotionPath
+ del self.retreatToSkyMotionPath
+ self.takeOffSeq.clearToInitial()
+ del self.takeOffSeq
+ del self.landOnNestPosLerp
+ self.landingSeq.clearToInitial()
+ del self.landingSeq
+ del self.chargeUpPosLerp
+ self.chargeUpAttackSeq.clearToInitial()
+ del self.chargeUpAttackSeq
+ del self.retreatToNestPosLerp
+ self.retreatToNestSeq.clearToInitial()
+ del self.retreatToNestSeq
+ del self.retreatToSkyPosLerp
+ self.retreatToSkySeq.clearToInitial()
+ del self.retreatToSkySeq
+ del self.postAttackPosLerp
+ self.attackSeq.clearToInitial()
+ del self.attackSeq
+ self.cooldownSeq.clearToInitial()
+ del self.cooldownSeq
+ self.hoverOverNestSeq.clearToInitial()
+ del self.hoverOverNestSeq
+ del self.preAttackLerpXY
+ del self.preAttackLerpZ
+ return
+
+ def requestNext(self):
+ self.request('next')
+
+ def setCollSphereToNest(self):
+ if hasattr(self, 'collSphere') and self.collSphere is not None:
+ radius = Globals.LegalEagle.OnNestDamageSphereRadius
+ self.collSphere.setCenter(Point3(0.0, -Globals.Level.LaffPowerupNestOffset[1], self.suit.getHeight() / 2.0))
+ self.collSphere.setRadius(radius)
+ return
+
+ def setCollSphereToTargeting(self):
+ if hasattr(self, 'collSphere') and self.collSphere is not None:
+ radius = Globals.LegalEagle.DamageSphereRadius
+ self.collSphere.setCenter(Point3(0, 0, radius * 2))
+ self.collSphere.setRadius(radius)
+ return
+
+ def enterRoost(self):
+ self.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self.hoverOverNestSeq.loop()
+ self.propTrack.loop()
+ self.setCollSphereToNest()
+
+ def filterRoost(self, request, args):
+ self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
+ if request == self.state:
+ return None
+ elif request == 'next':
+ return 'TakeOff'
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitRoost(self):
+ self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.hoverOverNestSeq.pause()
+ self.setCollSphereToTargeting()
+
+ def enterTakeOff(self, elapsedTime = 0.0):
+ self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
+ self.oldState,
+ self.newState,
+ elapsedTime))
+ self.takeOffSeq.start(elapsedTime)
+ self.hoverOverNestSeq.loop()
+
+ def filterTakeOff(self, request, args):
+ self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
+ if request == self.state:
+ return None
+ elif request == 'next':
+ if self.hasTarget():
+ return 'LockOnToon'
+ else:
+ return 'LandOnNest'
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitTakeOff(self):
+ self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.takeOffSeq.clearToInitial()
+ self.hoverOverNestSeq.pause()
+
+ def enterLockOnToon(self, elapsedTime = 0.0):
+ self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
+ self.oldState,
+ self.newState,
+ elapsedTime))
+ taskName = 'updateLockOnTask-%i' % self.index
+ taskMgr.add(self.updateLockOnTask, taskName, 45, extraArgs=[])
+ messenger.send(CogdoFlyingLegalEagle.LockOnToonEventName, [self.target.doId])
+ range = self.target.getDistance(self.interestConeOrigin) / self.getInterestConeLength()
+ range = clamp(range, 0.0, 1.0)
+ dur = Globals.LegalEagle.LockOnTime
+ if self.oldState == 'TakeOff':
+ dur *= range
+ else:
+ dur += Globals.LegalEagle.ExtraPostCooldownTime
+ taskName = 'exitLockOnToon-%i' % self.index
+ taskMgr.doMethodLater(dur, self.requestNext, taskName, extraArgs=[])
+
+ def filterLockOnToon(self, request, args):
+ self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
+ if request == self.state:
+ return None
+ elif request == 'next':
+ if self.hasTarget():
+ return 'ChargeUpAttack'
+ else:
+ return 'RetreatToNest'
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitLockOnToon(self):
+ self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ taskMgr.remove('updateLockOnTask-%i' % self.index)
+ taskMgr.remove('exitLockOnToon-%i' % self.index)
+
+ def enterChargeUpAttack(self, elapsedTime = 0.0):
+ self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
+ self.oldState,
+ self.newState,
+ elapsedTime))
+ self.chargeUpAttackSeq.start(elapsedTime)
+ messenger.send(CogdoFlyingLegalEagle.ChargingToAttackEventName, [self.target.doId])
+
+ def filterChargeUpAttack(self, request, args):
+ self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
+ if request == self.state:
+ return None
+ elif request == 'next':
+ if self.hasTarget():
+ return 'Attack'
+ else:
+ return 'RetreatToNest'
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitChargeUpAttack(self):
+ self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.chargeUpAttackSeq.clearToInitial()
+
+ def enterAttack(self, elapsedTime = 0.0):
+ self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
+ self.oldState,
+ self.newState,
+ elapsedTime))
+ self.attackTargetPos = self.target.getPos(render)
+ targetState = self.target.animFSM.getCurrentState().getName()
+ self._screamSfx.play()
+ if targetState == 'jumpAirborne':
+ self.attackTargetPos[2] += Globals.LegalEagle.VerticalOffset
+ else:
+ self.attackTargetPos[2] += Globals.LegalEagle.PlatformVerticalOffset
+ self.readyToAttackPos = self.suit.getPos(render)
+ self.attackSeq.start(elapsedTime)
+
+ def filterAttack(self, request, args):
+ self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
+ if request == self.state:
+ return None
+ elif request == 'next':
+ return 'RetreatToSky'
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitAttack(self):
+ self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.attackSeq.clearToInitial()
+ taskMgr.remove('updateAttackPosTask-%i' % self.index)
+
+ def enterRetreatToSky(self, elapsedTime = 0.0):
+ self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
+ self.oldState,
+ self.newState,
+ elapsedTime))
+ self.retreatToSkySeq.start(elapsedTime)
+
+ def filterRetreatToSky(self, request, args):
+ self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
+ if request == self.state:
+ return None
+ elif request == 'next':
+ return 'Cooldown'
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitRetreatToSky(self):
+ self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.retreatToSkySeq.clearToInitial()
+
+ def enterCooldown(self):
+ if self.target != None:
+ messenger.send(CogdoFlyingLegalEagle.CooldownEventName, [self.target.doId])
+ self.suit.stash()
+ self.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ return
+
+ def filterCooldown(self, request, args):
+ self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
+ if request == self.state:
+ return None
+ elif request == 'next':
+ if self.hasTarget():
+ return 'LockOnToon'
+ else:
+ return 'LandOnNest'
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitCooldown(self):
+ self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.suit.unstash()
+ self.cooldownSeq.clearToInitial()
+ if self.newState != 'Off':
+ heightOffNest = Globals.LegalEagle.PostCooldownHeightOffNest
+ nestPos = self.nest.getPos(render)
+ if self.newState in ['LandOnNest']:
+ self.suit.setPos(nestPos + Vec3(0, 0, heightOffNest))
+ else:
+ targetPos = self.target.getPos(render)
+ attackPos = Vec3(targetPos)
+ attackPos[1] = nestPos[1]
+ attackPos[2] = nestPos[2] + heightOffNest
+ self.suit.setPos(attackPos)
+
+ def enterRetreatToNest(self, elapsedTime = 0.0):
+ self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
+ self.oldState,
+ self.newState,
+ elapsedTime))
+ self.retreatToNestSeq.start(elapsedTime)
+
+ def filterRetreatToNest(self, request, args):
+ self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
+ if request == self.state:
+ return None
+ elif request == 'next':
+ return 'LandOnNest'
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitRetreatToNest(self):
+ self.retreatToNestSeq.clearToInitial()
+
+ def enterLandOnNest(self, elapsedTime = 0.0):
+ self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
+ self.oldState,
+ self.newState,
+ elapsedTime))
+ self.landingSeq.start(elapsedTime)
+
+ def filterLandOnNest(self, request, args):
+ self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
+ if request == self.state:
+ return None
+ elif request == 'next':
+ if self.hasTarget():
+ return 'TakeOff'
+ else:
+ return 'Roost'
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitLandOnNest(self):
+ self.landingSeq.clearToInitial()
diff --git a/toontown/cogdominium/CogdoFlyingLevel.py b/toontown/cogdominium/CogdoFlyingLevel.py
new file mode 100644
index 0000000..3061ab5
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingLevel.py
@@ -0,0 +1,243 @@
+from pandac.PandaModules import NodePath, Plane, Vec3, Point3
+from pandac.PandaModules import CollisionPlane, CollisionNode
+from direct.showbase.RandomNumGen import RandomNumGen
+from direct.showbase.DirectObject import DirectObject
+from direct.showbase.PythonUtil import bound as clamp
+import CogdoUtil
+import CogdoFlyingGameGlobals as Globals
+from CogdoFlyingLevelQuadrant import CogdoFlyingLevelQuadrant
+from CogdoFlyingObjects import CogdoFlyingGatherableFactory, CogdoFlyingPlatform, CogdoFlyingLevelFog
+from CogdoFlyingObstacles import CogdoFlyingObtacleFactory
+from CogdoGameExit import CogdoGameExit
+from otp.otpbase import OTPGlobals
+
+class CogdoFlyingLevel(DirectObject):
+ notify = directNotify.newCategory('CogdoFlyingLevel')
+
+ def __init__(self, parent, frameModel, startPlatformModel, endPlatformModel, quadLengthUnits, quadVisibilityAhead, quadVisibiltyBehind):
+ self.parent = parent
+ self.quadLengthUnits = quadLengthUnits
+ self._halfQuadLengthUnits = quadLengthUnits / 2.0
+ self.quadVisibiltyAhead = quadVisibilityAhead
+ self.quadVisibiltyBehind = quadVisibiltyBehind
+ self._frameModel = frameModel
+ self.root = NodePath('CogdoFlyingLevel')
+ self.quadrantRoot = NodePath('QuadrantsRoot')
+ self.quadrantRoot.reparentTo(self.root)
+ self._startPlatformModel = startPlatformModel
+ self._startPlatformModel.reparentTo(self.root)
+ self._startPlatformModel.setZ(Globals.Level.StartPlatformHeight)
+ self._endPlatformModel = endPlatformModel
+ self._endPlatformModel.reparentTo(self.root)
+ self._endPlatformModel.setZ(Globals.Level.EndPlatformHeight)
+ self.wallR = self._frameModel.find('**/wallR')
+ self.wallL = self._frameModel.find('**/wallL')
+ self._exit = CogdoGameExit()
+ self._exit.reparentTo(self._endPlatformModel)
+ loc = self._endPlatformModel.find('**/exit_loc')
+ offset = loc.getPos(render)
+ self._exit.setPos(render, offset)
+ self.quadrants = []
+ self.visibleQuadIndices = []
+ self._numQuads = 0
+ self._currentQuadNum = -1
+ self._camera = None
+ self._initCollisions()
+ self.upLimit = self._frameModel.find('**/limit_up').getZ(render)
+ self.downLimit = self._frameModel.find('**/limit_down').getZ(render)
+ self.leftLimit = self._frameModel.find('**/limit_left').getX(render) - 30.0
+ self.rightLimit = self._frameModel.find('**/limit_right').getX(render) + 30.0
+ self.backLimit = -self.quadLengthUnits
+ self.forwardLimit = self.quadLengthUnits * 20
+ self._frameModel.flattenStrong()
+ self.gatherableFactory = CogdoFlyingGatherableFactory()
+ self.obstacleFactory = CogdoFlyingObtacleFactory()
+ return
+
+ def getExit(self):
+ return self._exit
+
+ def getBounds(self):
+ return ((self.leftLimit, self.rightLimit), (self.backLimit, self.forwardLimit), (self.downLimit, self.upLimit))
+
+ def getGatherable(self, serialNum):
+ for quadrant in self.quadrants:
+ for gatherable in quadrant.gatherables:
+ if gatherable.serialNum == serialNum:
+ return gatherable
+
+ return None
+
+ def ready(self):
+ self.gatherableFactory.destroy()
+ del self.gatherableFactory
+ self.obstacleFactory.destroy()
+ del self.obstacleFactory
+ self._initStartEndPlatforms()
+ self._frameModel.reparentTo(self.root)
+ self.root.reparentTo(self.parent)
+ self.root.stash()
+
+ def _initStartEndPlatforms(self):
+ self.startPlatform = CogdoFlyingPlatform(self._startPlatformModel, Globals.Level.PlatformTypes.StartPlatform)
+ self.endPlatform = CogdoFlyingPlatform(self._endPlatformModel, Globals.Level.PlatformTypes.EndPlatform)
+ self._endPlatformModel.setY(self.convertQuadNumToY(self._numQuads))
+ self.backLimit = self._startPlatformModel.getY(render) - Globals.Level.StartPlatformLength * 0.7
+ self.forwardLimit = self._endPlatformModel.getY(render) + Globals.Level.EndPlatformLength * 0.7
+
+ def _initCollisions(self):
+ self.collPlane = CollisionPlane(Plane(Vec3(0, 0, 1.0), Point3(0, 0, 10)))
+ self.collPlane.setTangible(0)
+ self.collNode = CollisionNode('fogPlane')
+ self.collNode.setIntoCollideMask(OTPGlobals.FloorBitmask)
+ self.collNode.addSolid(self.collPlane)
+ self.collNodePath = self.root.attachNewNode(self.collNode)
+ self.collNodePath.hide()
+
+ def destroy(self):
+ del self.collPlane
+ self.collNodePath.removeNode()
+ del self.collNodePath
+ del self.collNode
+ for quadrant in self.quadrants:
+ quadrant.destroy()
+
+ self._exit.destroy()
+ del self._exit
+ self.root.removeNode()
+ del self.root
+
+ def onstage(self):
+ self.root.unstash()
+ self.update(0.0)
+
+ def offstage(self):
+ self.root.stash()
+
+ def start(self, startTime = 0.0):
+ self._startTime = startTime
+
+ def stop(self):
+ pass
+
+ def getLength(self):
+ return self.quadLengthUnits * self.getNumQuadrants()
+
+ def appendQuadrant(self, model):
+ quadrant = CogdoFlyingLevelQuadrant(self._numQuads, model, self, self.root)
+ if self._numQuads == 0:
+ quadrant.generateGatherables(self._startPlatformModel)
+ quadrant.offstage()
+ self.quadrants.append(quadrant)
+ self._numQuads = len(self.quadrants)
+
+ def getNumQuadrants(self):
+ return self._numQuads
+
+ def setCamera(self, camera):
+ self._camera = camera
+
+ def getCameraActualQuadrant(self):
+ camY = self._camera.getY(render)
+ y = self.root.getY(render)
+ return self.convertYToQuadNum(camY - y)
+
+ def update(self, dt = 0.0):
+ if self._camera is None:
+ return
+ quadNum = clamp(self.getCameraActualQuadrant(), 0, self._numQuads - 1)
+ if quadNum < self._numQuads:
+ self.quadrants[quadNum].update(dt)
+ if quadNum + 1 < self._numQuads:
+ self.quadrants[quadNum + 1].update(dt)
+ if quadNum != self._currentQuadNum:
+ self._switchToQuadrant(quadNum)
+ return
+
+ def _switchToQuadrant(self, quadNum):
+ self.visibleQuadIndices = []
+ if quadNum >= 0:
+ if quadNum > 0:
+ self.quadrants[max(quadNum - self.quadVisibiltyBehind, 0)].onstage()
+ for i in range(quadNum, min(quadNum + self.quadVisibiltyAhead + 1, self._numQuads)):
+ self.quadrants[i].onstage()
+ self.visibleQuadIndices.append(i)
+ if i == 0:
+ self.startPlatform.onstage()
+ elif i == self._numQuads - 1:
+ self.endPlatform.onstage()
+
+ self._currentQuadNum = quadNum
+ for i in range(0, max(self._currentQuadNum - self.quadVisibiltyBehind, 0)) + range(min(self._currentQuadNum + self.quadVisibiltyAhead + 1, self._numQuads), self._numQuads):
+ self.quadrants[i].offstage()
+ if i == 0:
+ self.startPlatform.offstage()
+ elif i == self._numQuads - 1:
+ self.endPlatform.offstage()
+
+ def convertQuadNumToY(self, quadNum):
+ return quadNum * self.quadLengthUnits
+
+ def convertYToQuadNum(self, y):
+ return int(y / self.quadLengthUnits)
+
+ def convertCenterYToQuadNum(self, y):
+ return self.convertYToQuadNum(y + self._halfQuadLengthUnits)
+
+
+class CogdoFlyingLevelFactory:
+
+ def __init__(self, parent, quadLengthUnits, quadVisibilityAhead, quadVisibiltyBehind, rng = None):
+ self.parent = parent
+ self.quadLengthUnits = quadLengthUnits
+ self.quadVisibiltyAhead = quadVisibilityAhead
+ self.quadVisibiltyBehind = quadVisibiltyBehind
+ self._rng = rng or RandomNumGen(1)
+ self._level = None
+ return
+
+ def loadAndBuildLevel(self, safezoneId):
+ levelNode = NodePath('level')
+ frameModel = CogdoUtil.loadFlyingModel('level')
+ startPlatformModel = CogdoUtil.loadFlyingModel('levelStart')
+ endPlatformModel = CogdoUtil.loadFlyingModel('levelEnd')
+ for fan in frameModel.findAllMatches('**/*wallFan'):
+ fan.flattenStrong()
+
+ frameModel.find('**/fogOpaque').setBin('background', 1)
+ frameModel.find('**/ceiling').setBin('background', 2)
+ frameModel.find('**/fogTranslucent_bm').setBin('fixed', 1)
+ frameModel.find('**/wallR').setBin('opaque', 2)
+ frameModel.find('**/wallL').setBin('opaque', 2)
+ frameModel.find('**/fogTranslucent_top').setBin('fixed', 2)
+ frameModel.getChildren().reparentTo(levelNode)
+ levelNode.hide()
+ self._level = CogdoFlyingLevel(self.parent, levelNode, startPlatformModel, endPlatformModel, self.quadLengthUnits, self.quadVisibiltyAhead, self.quadVisibiltyBehind)
+ if Globals.Dev.WantTempLevel:
+ quads = Globals.Dev.DevQuadsOrder
+ else:
+ levelInfo = Globals.Level.DifficultyOrder[safezoneId]
+ quads = []
+ for difficulty in levelInfo:
+ quadList = Globals.Level.QuadsByDifficulty[difficulty]
+ quads.append(quadList[self._rng.randint(0, len(quadList) - 1)])
+
+ for i in quads:
+ filePath = CogdoUtil.getModelPath('quadrant%i' % i, 'flying')
+ quadModel = loader.loadModel(filePath)
+ for np in quadModel.findAllMatches('**/*lightCone*'):
+ CogdoUtil.initializeLightCone(np, 'fixed', 3)
+
+ self._level.appendQuadrant(quadModel)
+
+ self._level.ready()
+
+ def createLevel(self, safezoneId = 2000):
+ if self._level is None:
+ self.loadAndBuildLevel(safezoneId)
+ return self._level
+
+ def createLevelFog(self):
+ if self._level is None:
+ self.loadAndBuildLevel()
+ return CogdoFlyingLevelFog(self._level)
diff --git a/toontown/cogdominium/CogdoFlyingLevelQuadrant.py b/toontown/cogdominium/CogdoFlyingLevelQuadrant.py
new file mode 100644
index 0000000..51fadef
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingLevelQuadrant.py
@@ -0,0 +1,266 @@
+import math
+from direct.directutil import Mopath
+from pandac.PandaModules import NodePath, Point3, Vec4
+from CogdoFlyingObjects import CogdoFlyingPlatform
+import CogdoFlyingGameGlobals as Globals
+
+class CogdoFlyingLevelQuadrant:
+ notify = directNotify.newCategory('CogdoFlyingLevelQuadrant')
+
+ def __init__(self, serialNum, model, level, parent):
+ self.serialNum = serialNum
+ self._model = model
+ self._level = level
+ self._root = NodePath('Quadrant' + `serialNum`)
+ self._model.reparentTo(self._root)
+ self._root.reparentTo(parent)
+ self._visible = True
+ self.platforms = {}
+ self.gatherables = []
+ self.obstacles = []
+ self._playing = False
+ self._obstaclesRoot = NodePath('obstacles')
+ self._obstaclesRoot.reparentTo(self._root)
+ self._initObstacles(self._obstaclesRoot)
+ self._gatherablesRoot = NodePath('gatherables')
+ self._gatherablesRoot.reparentTo(self._root)
+ self._initGatherables(self._gatherablesRoot)
+ self._platformsRoot = NodePath('platforms')
+ self._platformsRoot.reparentTo(self._model)
+ self._initPlatforms(self._platformsRoot)
+ self._optimize()
+ self.place()
+
+ def _optimize(self):
+ lightCones = NodePath('lightCones')
+ for np in self._platformsRoot.findAllMatches('**/*lightCone*'):
+ np.wrtReparentTo(lightCones)
+
+ lightCones.reparentTo(self._model)
+ node = self._model.find('**/ducts')
+ if not node.isEmpty():
+ node.flattenStrong()
+ for np in node.getChildren():
+ np.wrtReparentTo(self._model)
+
+ node = self._model.find('**/nests')
+ if not node.isEmpty():
+ for np in node.getChildren():
+ np.flattenStrong()
+ np.wrtReparentTo(self._model)
+
+ for np in self._model.findAllMatches('**/*LayerStack*'):
+ np.wrtReparentTo(self._model)
+
+ for np in self._model.find('**/static').getChildren():
+ np.wrtReparentTo(self._model)
+
+ self._model.flattenMedium()
+
+ def _initPlatforms(self, parent):
+ platformModels = self._model.findAllMatches('**/%s' % Globals.Level.PlatformName)
+ for platformModel in platformModels:
+ platform = CogdoFlyingPlatform(platformModel, parent=parent)
+ self.platforms[platform.getName()] = platform
+
+ def _destroyPlatforms(self):
+ for platform in self.platforms.values():
+ platform.destroy()
+
+ del self.platforms
+
+ def _initGatherables(self, parent):
+ self.generateGatherables(self._model, parent=parent)
+ if Globals.Level.SpawnLaffPowerupsInNests:
+ self.generateNestPowerups(self._model, parent=parent)
+
+ def generateNestPowerups(self, gatherableModel, parent):
+ nests = gatherableModel.findAllMatches('**/%s;+s' % Globals.Level.LegalEagleNestName)
+ for nest in nests:
+ offset = Globals.Level.LaffPowerupNestOffset
+ pickup = self._level.gatherableFactory.createPowerup(Globals.Level.GatherableTypes.LaffPowerup)
+ pickup.reparentTo(parent)
+ pickup.setPos(parent, nest.getPos(parent) + offset)
+ if Globals.Level.AddSparkleToPowerups:
+ sparkles = self._level.gatherableFactory.createSparkles(Vec4(1, 1, 1, 1), Vec4(1, 1, 0, 1), 10.0)
+ sparkles.reparentTo(pickup)
+ sparkles.setPos(0, 0, 1)
+ sparkles.start()
+ self.gatherables.append(pickup)
+
+ def generateGatherables(self, gatherableModel, parent = None, spread = Globals.Level.GatherablesDefaultSpread):
+ parent = parent or self._root
+ mopath = Mopath.Mopath(name='gatherables')
+
+ def generateMemos():
+ gatherPaths = gatherableModel.findAllMatches('**/%s' % Globals.Level.GatherablesPathName)
+ for gatherPath in gatherPaths:
+ mopath.loadNodePath(gatherPath)
+ t = 0.0
+ while t < mopath.getMaxT():
+ pickup = self._level.gatherableFactory.createMemo()
+ pickup.reparentTo(parent)
+ mopath.goTo(pickup, t)
+ self.gatherables.append(pickup)
+ t += spread
+
+ gatherPath.removeNode()
+
+ angleSpread = 360.0 / Globals.Level.NumMemosInRing
+ gatherPaths = gatherableModel.findAllMatches('**/%s' % Globals.Level.GatherablesRingName)
+ for gatherPath in gatherPaths:
+ mopath.loadNodePath(gatherPath)
+ t = 0.0
+ while t < mopath.getMaxT():
+ angle = 0
+ r = 3
+ while angle < 360.0:
+ pickup = self._level.gatherableFactory.createMemo()
+ pickup.reparentTo(parent)
+ mopath.goTo(pickup, t)
+ pickup.setX(parent, pickup.getX() + r * math.cos(math.radians(angle)))
+ pickup.setZ(parent, pickup.getZ() + r * math.sin(math.radians(angle)))
+ self.gatherables.append(pickup)
+ angle += angleSpread
+
+ t += spread + 0.5
+
+ gatherPath.removeNode()
+
+ def generatePropellers():
+ gatherables = gatherableModel.findAllMatches('**/%s' % Globals.Level.PropellerName)
+ for gatherable in gatherables:
+ pickup = self._level.gatherableFactory.createPropeller()
+ pickup.reparentTo(gatherable.getParent())
+ pickup.setPos(parent, gatherable.getPos(parent))
+ self.gatherables.append(pickup)
+ gatherable.removeNode()
+
+ def generatePowerUps():
+ for powerupType, locName in Globals.Level.PowerupType2Loc.iteritems():
+ if powerupType == Globals.Level.GatherableTypes.LaffPowerup and Globals.Level.IgnoreLaffPowerups:
+ continue
+ gatherables = gatherableModel.findAllMatches('**/%s' % locName)
+ for gatherable in gatherables:
+ pickup = self._level.gatherableFactory.createPowerup(powerupType)
+ pickup.reparentTo(parent)
+ pickup.setPos(parent, gatherable.getPos(parent))
+ if Globals.Level.AddSparkleToPowerups:
+ sparkles = self._level.gatherableFactory.createSparkles(Vec4(1, 1, 1, 1), Vec4(1, 1, 0, 1), 10.0)
+ sparkles.reparentTo(pickup)
+ sparkles.setPos(0, 0, 1)
+ sparkles.start()
+ self.gatherables.append(pickup)
+ gatherable.removeNode()
+
+ generateMemos()
+ generatePropellers()
+ generatePowerUps()
+
+ def _initObstacles(self, parent):
+
+ def initWhirlwinds():
+ obstacles = self._root.findAllMatches('**/%s' % Globals.Level.WhirlwindName)
+ for obstacleLoc in obstacles:
+ motionPath = self._model.find('**/%s%s' % (obstacleLoc.getName(), Globals.Level.WhirlwindPathName))
+ if motionPath.isEmpty():
+ motionPath = None
+ obstacle = self._level.obstacleFactory.createWhirlwind(motionPath=motionPath)
+ obstacle.model.reparentTo(parent)
+ obstacle.model.setPos(parent, obstacleLoc.getPos(parent))
+ self.obstacles.append(obstacle)
+ obstacleLoc.removeNode()
+
+ return
+
+ def initStreamers():
+ obstacles = self._model.findAllMatches('**/%s' % Globals.Level.StreamerName)
+ for obstacleLoc in obstacles:
+ obstacle = self._level.obstacleFactory.createFan()
+ obstacle.model.reparentTo(parent)
+ obstacle.model.setPos(parent, obstacleLoc.getPos(parent))
+ obstacle.model.setHpr(parent, obstacleLoc.getHpr(parent))
+ obstacle.model.setScale(parent, obstacleLoc.getScale(parent))
+ obstacle.setBlowDirection()
+ if Globals.Level.AddParticlesToStreamers:
+ particles = self._level.obstacleFactory.createStreamerParticles(Vec4(1, 1, 1, 1), Vec4(1, 1, 1, 1), 10.0)
+ particles.reparentTo(obstacle.model)
+ particles.start()
+ self.obstacles.append(obstacle)
+ obstacleLoc.removeNode()
+
+ def initWalkingMinions():
+ motionPaths = self._model.findAllMatches('**/%s' % Globals.Level.MinionWalkingPathName)
+ for motionPath in motionPaths:
+ obstacle = self._level.obstacleFactory.createWalkingMinion(motionPath=motionPath)
+ obstacle.model.reparentTo(parent)
+ obstacle.model.setPos(parent, motionPath.getPos(parent))
+ self.obstacles.append(obstacle)
+
+ def initFlyingMinions():
+ motionPaths = self._model.findAllMatches('**/%s' % Globals.Level.MinionFlyingPathName)
+ for motionPath in motionPaths:
+ obstacle = self._level.obstacleFactory.createFlyingMinion(motionPath=motionPath)
+ obstacle.model.reparentTo(parent)
+ obstacle.model.setPos(parent, motionPath.getPos(parent))
+ self.obstacles.append(obstacle)
+
+ initWhirlwinds()
+ initStreamers()
+ initWalkingMinions()
+ initFlyingMinions()
+
+ def place(self):
+ self._root.setPos(0, self._level.convertQuadNumToY(self.serialNum), 0)
+
+ def destroy(self):
+ if self._visible:
+ self.offstage()
+ self._destroyPlatforms()
+ for obstacle in self.obstacles:
+ obstacle.destroy()
+
+ for gatherable in self.gatherables:
+ gatherable.destroy()
+
+ self._root.removeNode()
+ del self._root
+ del self._gatherablesRoot
+ del self._obstaclesRoot
+ del self._platformsRoot
+ del self._level
+
+ def onstage(self, elapsedTime = 0.0):
+ if self._visible:
+ return
+ self._root.unstash()
+ for obstacle in self.obstacles:
+ obstacle.startMoving(elapsedTime)
+
+ for gatherable in self.gatherables:
+ gatherable.show()
+
+ self._visible = True
+
+ def offstage(self):
+ if not self._visible:
+ return
+ self._root.stash()
+ for obstacle in self.obstacles:
+ obstacle.stopMoving()
+
+ for gatherable in self.gatherables:
+ gatherable.hide()
+
+ self._visible = False
+
+ def update(self, dt):
+ if self._visible:
+ for gatherable in self.gatherables:
+ gatherable.update(dt)
+
+ for obstacle in self.obstacles:
+ obstacle.update(dt)
+
+ def getModel(self):
+ return self._root
diff --git a/toontown/cogdominium/CogdoFlyingLocalPlayer.py b/toontown/cogdominium/CogdoFlyingLocalPlayer.py
new file mode 100644
index 0000000..8f739cf
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingLocalPlayer.py
@@ -0,0 +1,1100 @@
+import math
+import random
+from pandac.PandaModules import Vec3
+from direct.showbase import PythonUtil
+from direct.directnotify import DirectNotifyGlobal
+from direct.task.Task import Task
+from direct.interval.FunctionInterval import Wait
+from direct.interval.IntervalGlobal import Func, LerpFunc, LerpPosInterval, LerpHprInterval, LerpFunctionInterval
+from direct.interval.MetaInterval import Sequence, Parallel
+from direct.showbase.PythonUtil import bound as clamp
+from direct.distributed.ClockDelta import globalClockDelta
+from otp.otpbase import OTPGlobals
+from toontown.minigame.OrthoDrive import OrthoDrive
+from toontown.minigame.OrthoWalk import OrthoWalk
+from toontown.toonbase import TTLocalizer
+from CogdoFlyingCollisions import CogdoFlyingCollisions
+from CogdoFlyingPlayer import CogdoFlyingPlayer
+from CogdoFlyingGuiManager import CogdoFlyingGuiManager
+from CogdoFlyingInputManager import CogdoFlyingInputManager
+from CogdoFlyingCameraManager import CogdoFlyingCameraManager
+from CogdoFlyingObjects import CogdoFlyingPlatform, CogdoFlyingGatherable
+from CogdoFlyingLegalEagle import CogdoFlyingLegalEagle
+import CogdoFlyingGameGlobals as Globals
+
+class CogdoFlyingLocalPlayer(CogdoFlyingPlayer):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingLocalPlayer')
+ BroadcastPosTask = 'CogdoFlyingLocalPlayerBroadcastPos'
+ PlayWaitingMusicEventName = 'PlayWaitingMusicEvent'
+ RanOutOfTimeEventName = 'RanOutOfTimeEvent'
+ PropStates = PythonUtil.Enum(('Normal', 'Overdrive', 'Off'))
+
+ def __init__(self, toon, game, level, guiMgr):
+ CogdoFlyingPlayer.__init__(self, toon)
+ self.defaultTransitions = {'Inactive': ['FreeFly', 'Running'],
+ 'FreeFly': ['Inactive',
+ 'OutOfTime',
+ 'Death',
+ 'FlyingUp',
+ 'Running',
+ 'HitWhileFlying',
+ 'InWhirlwind'],
+ 'FlyingUp': ['Inactive',
+ 'OutOfTime',
+ 'Death',
+ 'FreeFly',
+ 'Running',
+ 'HitWhileFlying',
+ 'InWhirlwind'],
+ 'InWhirlwind': ['Inactive',
+ 'OutOfTime',
+ 'Death',
+ 'FreeFly',
+ 'HitWhileFlying'],
+ 'HitWhileFlying': ['Inactive',
+ 'OutOfTime',
+ 'Death',
+ 'FreeFly',
+ 'InWhirlwind'],
+ 'Death': ['Inactive', 'OutOfTime', 'Spawn'],
+ 'Running': ['Inactive',
+ 'OutOfTime',
+ 'FreeFly',
+ 'FlyingUp',
+ 'Refuel',
+ 'WaitingForWin',
+ 'HitWhileRunning'],
+ 'HitWhileRunning': ['Inactive',
+ 'OutOfTime',
+ 'Death',
+ 'Running',
+ 'FreeFly'],
+ 'Spawn': ['Inactive',
+ 'OutOfTime',
+ 'Running',
+ 'WaitingForWin'],
+ 'OutOfTime': ['Inactive', 'Spawn'],
+ 'WaitingForWin': ['Inactive', 'Win'],
+ 'Win': ['Inactive']}
+ self.game = game
+ self._level = level
+ self._guiMgr = guiMgr
+ self._inputMgr = CogdoFlyingInputManager()
+ self._cameraMgr = CogdoFlyingCameraManager(camera, render, self, self._level)
+ self.velocity = Vec3(0.0, 0.0, 0.0)
+ self.instantaneousVelocity = Vec3(0.0, 0.0, 0.0)
+ self.controlVelocity = Vec3(0.0, 0.0, 0.0)
+ self.fanVelocity = Vec3(0.0, 0.0, 0.0)
+ self.activeFans = []
+ self.fansStillHavingEffect = []
+ self.fanIndex2ToonVelocity = {}
+ self.legalEagleInterestRequest = {}
+ self.activeWhirlwind = None
+ self.oldPos = Vec3(0.0, 0.0, 0.0)
+ self.checkpointPlatform = None
+ self.isHeadInCeiling = False
+ self.isToonOnFloor = False
+ self.fuel = 0.0
+ self.score = 0
+ self.postSpawnState = 'Running'
+ self.didTimeRunOut = False
+ self.hasPressedCtrlYet = False
+ self.hasPickedUpFirstPropeller = False
+ self.surfacePoint = None
+ self.legalEagleHitting = False
+ self.propState = None
+ self.broadcastPeriod = Globals.AI.BroadcastPeriod
+ self.initSfx()
+ self.initLocalPlayerIntervals()
+ self.initCollisions()
+ self.initOrthoWalker()
+ self.playerNumber = -1
+ self.fuel = 0.0
+ self._guiMgr.setFuel(self.fuel)
+ self.setCheckpointPlatform(self._level.startPlatform)
+ return None
+
+ def initSfx(self):
+ audioMgr = base.cogdoGameAudioMgr
+ self._deathSfx = audioMgr.createSfx('death')
+ self._hitByWhirlwindSfx = audioMgr.createSfx('toonInWhirlwind')
+ self._bladeBreakSfx = audioMgr.createSfx('bladeBreak')
+ self._collideSfx = audioMgr.createSfx('collide')
+ self._toonHitSfx = audioMgr.createSfx('toonHit')
+ self._getMemoSfx = audioMgr.createSfx('getMemo')
+ self._getLaffSfx = audioMgr.createSfx('getLaff')
+ self._getRedTapeSfx = audioMgr.createSfx('getRedTape')
+ self._refuelSfx = audioMgr.createSfx('refuel')
+ self._fanSfx = audioMgr.createSfx('fan')
+ self._invulDebuffSfx = audioMgr.createSfx('invulDebuff')
+ self._invulBuffSfx = audioMgr.createSfx('invulBuff')
+ self._winSfx = audioMgr.createSfx('win')
+ self._loseSfx = audioMgr.createSfx('lose')
+ self._refuelSpinSfx = audioMgr.createSfx('refuelSpin')
+ self._propellerSfx = audioMgr.createSfx('propeller', self.toon)
+
+ def destroySfx(self):
+ del self._deathSfx
+ del self._hitByWhirlwindSfx
+ del self._bladeBreakSfx
+ del self._collideSfx
+ del self._toonHitSfx
+ del self._propellerSfx
+ del self._getMemoSfx
+ del self._getLaffSfx
+ del self._refuelSfx
+ del self._fanSfx
+ del self._invulBuffSfx
+ del self._invulDebuffSfx
+ del self._getRedTapeSfx
+ del self._refuelSpinSfx
+
+ def setPlayerNumber(self, num):
+ self.playerNumber = num
+
+ def getPlayerNumber(self):
+ return self.playerNumber
+
+ def initOrthoWalker(self):
+ orthoDrive = OrthoDrive(9.778, maxFrameMove=0.5, wantSound=True)
+ self.orthoWalk = OrthoWalk(orthoDrive, broadcast=False, collisions=False, broadcastPeriod=Globals.AI.BroadcastPeriod)
+
+ def initLocalPlayerIntervals(self):
+ self.coolDownAfterHitInterval = Sequence(Wait(Globals.Gameplay.HitCooldownTime), Func(self.setEnemyHitting, False), name='coolDownAfterHitInterval-%i' % self.toon.doId)
+ self.deathInterval = Sequence(Func(self.resetVelocities), Parallel(Parallel(Func(self._deathSfx.play), LerpHprInterval(self.toon, 1.0, Vec3(720, 0, 0)), LerpFunctionInterval(self.toon.setScale, fromData=1.0, toData=0.1, duration=1.0), self.toon.posInterval(0.5, Vec3(0, 0, -25), other=self.toon)), Sequence(Wait(0.5), Func(base.transitions.irisOut))), Func(self.toon.stash), Wait(1.0), Func(self.toonSpawnFunc), name='%s.deathInterval' % self.__class__.__name__)
+ self.outOfTimeInterval = Sequence(Func(messenger.send, CogdoFlyingLocalPlayer.PlayWaitingMusicEventName), Func(self._loseSfx.play), Func(base.transitions.irisOut), Wait(1.0), Func(self.resetVelocities), Func(self._guiMgr.setMessage, '', transition=None), Func(self.toon.stash), Func(self.toonSpawnFunc), name='%s.outOfTimeInterval' % self.__class__.__name__)
+ self.spawnInterval = Sequence(Func(self.resetToonFunc), Func(self._cameraMgr.update, 0.0), Func(self._level.update), Func(self.toon.cnode.broadcastPosHprFull), Func(base.transitions.irisIn), Wait(0.5), Func(self.toon.setAnimState, 'TeleportIn'), Func(self.toon.unstash), Wait(1.5), Func(self.requestPostSpawnState), name='%s.spawnInterval' % self.__class__.__name__)
+ self.waitingForWinInterval = Sequence(Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '.'), Wait(1.5), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '..'), Wait(1.5), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '...'), Wait(1.5), name='%s.waitingForWinInterval' % self.__class__.__name__)
+ self.waitingForWinSeq = Sequence(Func(self.setWaitingForWinState), Wait(4.0), Func(self.removeAllMemos), Wait(2.0), Func(self.game.distGame.d_sendRequestAction, Globals.AI.GameActions.LandOnWinPlatform, 0), Func(self.playWaitingForWinInterval), name='%s.waitingForWinSeq' % self.__class__.__name__)
+ self.winInterval = Sequence(Func(self._guiMgr.setMessage, ''), Wait(4.0), Func(self.game.distGame.d_sendRequestAction, Globals.AI.GameActions.WinStateFinished, 0), name='%s.winInterval' % self.__class__.__name__)
+ self.goSadSequence = Sequence(Wait(2.5), Func(base.transitions.irisOut, 1.5), name='%s.goSadSequence' % self.__class__.__name__)
+ self.introGuiSeq = Sequence(Wait(0.5), Parallel(Func(self._guiMgr.setTemporaryMessage, TTLocalizer.CogdoFlyingGameMinimapIntro, duration=5.0), Sequence(Wait(1.0), Func(self._guiMgr.presentProgressGui))), Wait(5.0), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGamePickUpAPropeller), name='%s.introGuiSeq' % self.__class__.__name__)
+ return
+
+ def goSad(self):
+ self.goSadSequence.start()
+
+ def setWaitingForWinState(self):
+ if self.didTimeRunOut:
+ self.toon.b_setAnimState('Sad')
+ self._guiMgr.setMessage(TTLocalizer.CogdoFlyingGameOutOfTime, transition='blink')
+ else:
+ self._winSfx.play()
+ messenger.send(CogdoFlyingLocalPlayer.PlayWaitingMusicEventName)
+ self.toon.b_setAnimState('victory')
+ self._guiMgr.setMessage(TTLocalizer.CogdoFlyingGameYouMadeIt)
+
+ def removeAllMemos(self):
+ if self.didTimeRunOut:
+ messenger.send(CogdoFlyingLocalPlayer.RanOutOfTimeEventName)
+
+ def playWaitingForWinInterval(self):
+ if not self.game.distGame.isSinglePlayer():
+ self.waitingForWinInterval.loop()
+
+ def resetToonFunc(self):
+ self.resetToon(resetFuel=self.hasPickedUpFirstPropeller)
+
+ def _loopPropellerSfx(self, playRate = 1.0, volume = 1.0):
+ self._propellerSfx.loop(playRate=playRate, volume=1.0)
+
+ def initCollisions(self):
+ avatarRadius = 2.0
+ reach = 4.0
+ self.flyerCollisions = CogdoFlyingCollisions()
+ self.flyerCollisions.setWallBitMask(OTPGlobals.WallBitmask)
+ self.flyerCollisions.setFloorBitMask(OTPGlobals.FloorBitmask)
+ self.flyerCollisions.initializeCollisions(base.cTrav, self.toon, avatarRadius, OTPGlobals.FloorOffset, reach)
+ self.flyerCollisions.setCollisionsActive(0)
+ floorColl = CogdoFlyingPlatform.FloorCollName
+ ceilingColl = CogdoFlyingPlatform.CeilingCollName
+ self.accept('Flyer.cHeadCollSphere-enter-%s' % ceilingColl, self.__handleHeadCollisionIntoCeiling)
+ self.accept('Flyer.cHeadCollSphere-exit-%s' % ceilingColl, self.__handleHeadCollisionExitCeiling)
+ self.accept('Flyer.cFloorEventSphere-exit-%s' % floorColl, self.__handleEventCollisionExitFloor)
+ self.accept('Flyer.cRayNode-enter-%s' % floorColl, self.__handleRayCollisionEnterFloor)
+ self.accept('Flyer.cRayNode-again-%s' % floorColl, self.__handleRayCollisionAgainFloor)
+
+ def enable(self):
+ CogdoFlyingPlayer.enable(self)
+ self.toon.hideName()
+
+ def disable(self):
+ CogdoFlyingPlayer.disable(self)
+
+ def isLegalEagleInterestRequestSent(self, index):
+ if index in self.legalEagleInterestRequest:
+ return True
+ else:
+ return False
+
+ def setLegalEagleInterestRequest(self, index):
+ if index not in self.legalEagleInterestRequest:
+ self.legalEagleInterestRequest[index] = True
+ else:
+ CogdoFlyingLocalPlayer.notify.warning('Attempting to set an legal eagle interest request when one already exists:%s' % index)
+
+ def clearLegalEagleInterestRequest(self, index):
+ if index in self.legalEagleInterestRequest:
+ del self.legalEagleInterestRequest[index]
+
+ def setBackpackState(self, state):
+ if state == self.backpackState:
+ return
+ CogdoFlyingPlayer.setBackpackState(self, state)
+ if state in Globals.Gameplay.BackpackStates:
+ if state == Globals.Gameplay.BackpackStates.Normal:
+ messenger.send(CogdoFlyingGuiManager.ClearMessageDisplayEventName)
+ elif state == Globals.Gameplay.BackpackStates.Targeted:
+ messenger.send(CogdoFlyingGuiManager.EagleTargetingLocalPlayerEventName)
+ elif state == Globals.Gameplay.BackpackStates.Attacked:
+ messenger.send(CogdoFlyingGuiManager.EagleAttackingLocalPlayerEventName)
+
+ def requestPostSpawnState(self):
+ self.request(self.postSpawnState)
+
+ def toonSpawnFunc(self):
+ self.game.distGame.b_toonSpawn(self.toon.doId)
+
+ def __handleHeadCollisionIntoCeiling(self, collEntry):
+ self.isHeadInCeiling = True
+ self.surfacePoint = self.toon.getPos()
+ self._collideSfx.play()
+ if self.controlVelocity[2] > 0.0:
+ self.controlVelocity[2] = -self.controlVelocity[2] / 2.0
+
+ def __handleHeadCollisionExitCeiling(self, collEntry):
+ self.isHeadInCeiling = False
+ self.surfacePoint = None
+ return
+
+ def landOnPlatform(self, collEntry):
+ surfacePoint = collEntry.getSurfacePoint(render)
+ intoNodePath = collEntry.getIntoNodePath()
+ platform = CogdoFlyingPlatform.getFromNode(intoNodePath)
+ if platform is not None:
+ if not platform.isStartOrEndPlatform():
+ taskMgr.doMethodLater(0.5, self.delayedLandOnPlatform, 'delayedLandOnPlatform', extraArgs=[platform])
+ elif platform.isEndPlatform():
+ taskMgr.doMethodLater(1.0, self.delayedLandOnWinPlatform, 'delayedLandOnWinPlatform', extraArgs=[platform])
+ self.isToonOnFloor = True
+ self.controlVelocity = Vec3(0.0, 0.0, 0.0)
+ self.toon.setPos(render, surfacePoint)
+ self.toon.setHpr(0, 0, 0)
+ self.request('Running')
+ return
+
+ def __handleRayCollisionEnterFloor(self, collEntry):
+ fromNodePath = collEntry.getFromNodePath()
+ intoNodePath = collEntry.getIntoNodePath()
+ intoName = intoNodePath.getName()
+ fromName = fromNodePath.getName()
+ toonPos = self.toon.getPos(render)
+ collPos = collEntry.getSurfacePoint(render)
+ if toonPos.getZ() < collPos.getZ() + Globals.Gameplay.RayPlatformCollisionThreshold:
+ if not self.isToonOnFloor and self.state in ['FreeFly', 'FlyingUp']:
+ self.landOnPlatform(collEntry)
+
+ def __handleRayCollisionAgainFloor(self, collEntry):
+ fromNodePath = collEntry.getFromNodePath()
+ intoNodePath = collEntry.getIntoNodePath()
+ intoName = intoNodePath.getName()
+ fromName = fromNodePath.getName()
+ toonPos = self.toon.getPos(render)
+ collPos = collEntry.getSurfacePoint(render)
+ if toonPos.getZ() < collPos.getZ() + Globals.Gameplay.RayPlatformCollisionThreshold:
+ if not self.isToonOnFloor and self.state in ['FreeFly', 'FlyingUp']:
+ self.landOnPlatform(collEntry)
+
+ def __handleEventCollisionExitFloor(self, collEntry):
+ fromNodePath = collEntry.getFromNodePath()
+ intoNodePath = collEntry.getIntoNodePath()
+ intoName = intoNodePath.getName()
+ fromName = fromNodePath.getName()
+ if self.isToonOnFloor:
+ self.notify.debug('~~~Exit Floor:%s -> %s' % (intoName, fromName))
+ self.isToonOnFloor = False
+ taskMgr.remove('delayedLandOnPlatform')
+ taskMgr.remove('delayedLandOnWinPlatform')
+ if self.state not in ['FlyingUp', 'Spawn']:
+ self.notify.debug('Exited floor')
+ self.request('FreeFly')
+
+ def delayedLandOnPlatform(self, platform):
+ self.setCheckpointPlatform(platform)
+ return Task.done
+
+ def delayedLandOnWinPlatform(self, platform):
+ self.setCheckpointPlatform(self._level.endPlatform)
+ self.request('WaitingForWin')
+ return Task.done
+
+ def handleTimerExpired(self):
+ if self.state not in ['WaitingForWin', 'Win']:
+ self.setCheckpointPlatform(self._level.endPlatform)
+ self.postSpawnState = 'WaitingForWin'
+ self.didTimeRunOut = True
+ if self.state not in ['Death']:
+ self.request('OutOfTime')
+
+ def ready(self):
+ self.resetToon(resetFuel=False)
+ self._cameraMgr.enable()
+ self._cameraMgr.update()
+
+ def start(self):
+ CogdoFlyingPlayer.start(self)
+ self.toon.collisionsOff()
+ self.flyerCollisions.setAvatar(self.toon)
+ self.flyerCollisions.setCollisionsActive(1)
+ self._levelBounds = self._level.getBounds()
+ self.introGuiSeq.start()
+ self.request('Running')
+
+ def exit(self):
+ self.request('Inactive')
+ CogdoFlyingPlayer.exit(self)
+ self._cameraMgr.disable()
+ self.flyerCollisions.setCollisionsActive(0)
+ self.flyerCollisions.setAvatar(None)
+ taskMgr.remove('delayedLandOnFuelPlatform')
+ taskMgr.remove('delayedLandOnWinPlatform')
+ self.ignoreAll()
+ return
+
+ def unload(self):
+ self.toon.showName()
+ self.toon.collisionsOn()
+ self._destroyEventIval()
+ self._destroyEnemyHitIval()
+ CogdoFlyingPlayer.unload(self)
+ self._fanSfx.stop()
+ self.flyerCollisions.deleteCollisions()
+ del self.flyerCollisions
+ self.ignoreAll()
+ taskMgr.remove('delayedLandOnPlatform')
+ taskMgr.remove('delayedLandOnWinPlatform')
+ self.checkpointPlatform = None
+ self._cameraMgr.disable()
+ del self._cameraMgr
+ del self.game
+ self._inputMgr.destroy()
+ del self._inputMgr
+ self.introGuiSeq.clearToInitial()
+ del self.introGuiSeq
+ if self.goSadSequence:
+ self.goSadSequence.clearToInitial()
+ del self.goSadSequence
+ if self.coolDownAfterHitInterval:
+ self.coolDownAfterHitInterval.clearToInitial()
+ del self.coolDownAfterHitInterval
+ if self.deathInterval:
+ self.deathInterval.clearToInitial()
+ del self.deathInterval
+ if self.spawnInterval:
+ self.spawnInterval.clearToInitial()
+ del self.spawnInterval
+ if self.outOfTimeInterval:
+ self.outOfTimeInterval.clearToInitial()
+ del self.outOfTimeInterval
+ if self.winInterval:
+ self.winInterval.clearToInitial()
+ del self.winInterval
+ if self.waitingForWinInterval:
+ self.waitingForWinInterval.clearToInitial()
+ del self.waitingForWinInterval
+ if self.waitingForWinSeq:
+ self.waitingForWinSeq.clearToInitial()
+ del self.waitingForWinSeq
+ del self.activeFans[:]
+ del self.fansStillHavingEffect[:]
+ self.fanIndex2ToonVelocity.clear()
+ self.orthoWalk.stop()
+ self.orthoWalk.destroy()
+ del self.orthoWalk
+ self.destroySfx()
+ return
+
+ def setCheckpointPlatform(self, platform):
+ self.checkpointPlatform = platform
+
+ def resetVelocities(self):
+ self.fanVelocity = Vec3(0.0, 0.0, 0.0)
+ self.controlVelocity = Vec3(0.0, 0.0, 0.0)
+ self.velocity = Vec3(0.0, 0.0, 0.0)
+
+ def resetToon(self, resetFuel = True):
+ CogdoFlyingPlayer.resetToon(self)
+ self.resetVelocities()
+ del self.activeFans[:]
+ del self.fansStillHavingEffect[:]
+ self.fanIndex2ToonVelocity.clear()
+ self._fanSfx.stop()
+ spawnPos = self.checkpointPlatform.getSpawnPosForPlayer(self.getPlayerNumber(), render)
+ self.activeWhirlwind = None
+ self.toon.setPos(render, spawnPos)
+ self.toon.setHpr(render, 0, 0, 0)
+ if resetFuel:
+ self.resetFuel()
+ self.isHeadInCeiling = False
+ self.isToonOnFloor = True
+ return
+
+ def activateFlyingBroadcast(self):
+ self.timeSinceLastPosBroadcast = 0.0
+ self.lastPosBroadcast = self.toon.getPos()
+ self.lastHprBroadcast = self.toon.getHpr()
+ toon = self.toon
+ toon.d_clearSmoothing()
+ toon.sendCurrentPosition()
+ taskMgr.remove(self.BroadcastPosTask)
+ taskMgr.add(self.doBroadcast, self.BroadcastPosTask)
+
+ def shutdownFlyingBroadcast(self):
+ taskMgr.remove(self.BroadcastPosTask)
+
+ def doBroadcast(self, task):
+ dt = globalClock.getDt()
+ self.timeSinceLastPosBroadcast += dt
+ if self.timeSinceLastPosBroadcast >= self.broadcastPeriod:
+ self.timeSinceLastPosBroadcast = 0.0
+ self.toon.cnode.broadcastPosHprFull()
+ return Task.cont
+
+ def died(self, timestamp):
+ self.request('Death')
+
+ def spawn(self, timestamp):
+ self.request('Spawn')
+
+ def updateToonFlyingState(self, dt):
+ leftPressed = self._inputMgr.arrowKeys.leftPressed()
+ rightPressed = self._inputMgr.arrowKeys.rightPressed()
+ upPressed = self._inputMgr.arrowKeys.upPressed()
+ downPressed = self._inputMgr.arrowKeys.downPressed()
+ jumpPressed = self._inputMgr.arrowKeys.jumpPressed()
+ if not self.hasPressedCtrlYet and jumpPressed and self.isFuelLeft():
+ self.hasPressedCtrlYet = True
+ messenger.send(CogdoFlyingGuiManager.FirstPressOfCtrlEventName)
+ if jumpPressed and self.isFuelLeft():
+ if self.state == 'FreeFly' and self.isInTransition() == False:
+ self.notify.debug('FreeFly -> FlyingUp')
+ self.request('FlyingUp')
+ elif self.state == 'FlyingUp' and self.isInTransition() == False:
+ self.notify.debug('FlyingUp -> FreeFly')
+ self.request('FreeFly')
+ if leftPressed and not rightPressed:
+ self.toon.setH(self.toon, Globals.Gameplay.ToonTurning['turningSpeed'] * dt)
+ max = Globals.Gameplay.ToonTurning['maxTurningAngle']
+ if self.toon.getH() > max:
+ self.toon.setH(max)
+ elif rightPressed and not leftPressed:
+ self.toon.setH(self.toon, -1.0 * Globals.Gameplay.ToonTurning['turningSpeed'] * dt)
+ min = -1.0 * Globals.Gameplay.ToonTurning['maxTurningAngle']
+ if self.toon.getH() < min:
+ self.toon.setH(min)
+
+ def updateControlVelocity(self, dt):
+ leftPressed = self._inputMgr.arrowKeys.leftPressed()
+ rightPressed = self._inputMgr.arrowKeys.rightPressed()
+ upPressed = self._inputMgr.arrowKeys.upPressed()
+ downPressed = self._inputMgr.arrowKeys.downPressed()
+ jumpPressed = self._inputMgr.arrowKeys.jumpPressed()
+ if leftPressed:
+ self.controlVelocity[0] -= Globals.Gameplay.ToonAcceleration['turning'] * dt
+ if rightPressed:
+ self.controlVelocity[0] += Globals.Gameplay.ToonAcceleration['turning'] * dt
+ if upPressed:
+ self.controlVelocity[1] += Globals.Gameplay.ToonAcceleration['forward'] * dt
+ if downPressed:
+ self.controlVelocity[2] -= Globals.Gameplay.ToonAcceleration['activeDropDown'] * dt
+ self.controlVelocity[1] -= Globals.Gameplay.ToonAcceleration['activeDropBack'] * dt
+ if jumpPressed and self.isFuelLeft():
+ self.controlVelocity[2] += Globals.Gameplay.ToonAcceleration['boostUp'] * dt
+ minVal = -Globals.Gameplay.ToonVelMax['turning']
+ maxVal = Globals.Gameplay.ToonVelMax['turning']
+ if not leftPressed and not rightPressed or self.controlVelocity[0] > maxVal or self.controlVelocity[0] < minVal:
+ x = self.dampenVelocityVal(self.controlVelocity[0], 'turning', 'turning', minVal, maxVal, dt)
+ self.controlVelocity[0] = x
+ minVal = -Globals.Gameplay.ToonVelMax['backward']
+ maxVal = Globals.Gameplay.ToonVelMax['forward']
+ if not upPressed and not downPressed or self.controlVelocity[1] > maxVal or self.controlVelocity[1] < minVal:
+ y = self.dampenVelocityVal(self.controlVelocity[1], 'backward', 'forward', minVal, maxVal, dt)
+ self.controlVelocity[1] = y
+ if self.isFuelLeft():
+ minVal = -Globals.Gameplay.ToonVelMax['fall']
+ else:
+ minVal = -Globals.Gameplay.ToonVelMax['fallNoFuel']
+ maxVal = Globals.Gameplay.ToonVelMax['boost']
+ if self.controlVelocity[2] > minVal:
+ if (not self._inputMgr.arrowKeys.jumpPressed() or not self.isFuelLeft()) and not self.isToonOnFloor:
+ self.controlVelocity[2] -= Globals.Gameplay.ToonAcceleration['fall'] * dt
+ if self.controlVelocity[2] < 0.0 and self.isToonOnFloor:
+ self.controlVelocity[2] = 0.0
+ minVal = -Globals.Gameplay.ToonVelMax['turning']
+ maxVal = Globals.Gameplay.ToonVelMax['turning']
+ self.controlVelocity[0] = clamp(self.controlVelocity[0], minVal, maxVal)
+ minVal = -Globals.Gameplay.ToonVelMax['backward']
+ maxVal = Globals.Gameplay.ToonVelMax['forward']
+ self.controlVelocity[1] = clamp(self.controlVelocity[1], minVal, maxVal)
+ if self.isFuelLeft():
+ minVal = -Globals.Gameplay.ToonVelMax['fall']
+ else:
+ minVal = -Globals.Gameplay.ToonVelMax['fallNoFuel']
+ maxVal = Globals.Gameplay.ToonVelMax['boost']
+ self.controlVelocity[2] = clamp(self.controlVelocity[2], minVal, maxVal)
+
+ def updateFanVelocity(self, dt):
+ fanHeight = Globals.Gameplay.FanCollisionTubeHeight
+ min = Globals.Gameplay.FanMinPower
+ max = Globals.Gameplay.FanMaxPower
+ powerRange = max - min
+ for fan in self.activeFans:
+ blowVec = fan.getBlowDirection()
+ blowVec *= Globals.Gameplay.ToonAcceleration['fan'] * dt
+ if Globals.Gameplay.UseVariableFanPower:
+ distance = fan.model.getDistance(self.toon)
+ power = math.fabs(distance / fanHeight - 1.0) * powerRange + min
+ power = clamp(power, min, max)
+ blowVec *= power
+ fanVelocity = self.fanIndex2ToonVelocity[fan.index]
+ fanVelocity += blowVec
+
+ removeList = []
+ for fan in self.fansStillHavingEffect:
+ if fan not in self.activeFans:
+ blowVec = fan.getBlowDirection()
+ blowVec *= Globals.Gameplay.ToonDeceleration['fan'] * dt
+ fanVelocity = Vec3(self.fanIndex2ToonVelocity[fan.index])
+ lastLen = fanVelocity.length()
+ fanVelocity -= blowVec
+ if fanVelocity.length() > lastLen:
+ removeList.append(fan)
+ else:
+ self.fanIndex2ToonVelocity[fan.index] = fanVelocity
+
+ for fan in removeList:
+ self.fansStillHavingEffect.remove(fan)
+ del self.fanIndex2ToonVelocity[fan.index]
+
+ self.fanVelocity = Vec3(0.0, 0.0, 0.0)
+ for fan in self.fansStillHavingEffect:
+ self.fanVelocity += self.fanIndex2ToonVelocity[fan.index]
+
+ minVal = -Globals.Gameplay.ToonVelMax['fan']
+ maxVal = Globals.Gameplay.ToonVelMax['fan']
+ self.fanVelocity[0] = clamp(self.fanVelocity[0], minVal, maxVal)
+ self.fanVelocity[1] = clamp(self.fanVelocity[1], minVal, maxVal)
+ self.fanVelocity[2] = clamp(self.fanVelocity[2], minVal, maxVal)
+
+ def dampenVelocityVal(self, velocityVal, typeNeg, typePos, minVal, maxVal, dt):
+ if velocityVal > 0.0:
+ velocityVal -= Globals.Gameplay.ToonDeceleration[typePos] * dt
+ velocityVal = clamp(velocityVal, 0.0, maxVal)
+ elif velocityVal < 0.0:
+ velocityVal += Globals.Gameplay.ToonDeceleration[typeNeg] * dt
+ velocityVal = clamp(velocityVal, minVal, 0.0)
+ return velocityVal
+
+ def allowFuelDeath(self):
+ if Globals.Gameplay.DoesToonDieWithFuel:
+ return True
+ else:
+ return not self.isFuelLeft()
+
+ def updateToonPos(self, dt):
+ toonWorldY = self.toon.getY(render)
+ if self.hasPickedUpFirstPropeller == False:
+ if toonWorldY > -7.6:
+ self.toon.setY(-7.6)
+ elif toonWorldY < -35.0:
+ self.toon.setY(-35.0)
+ return
+ self.velocity = self.controlVelocity + self.fanVelocity
+ vel = self.velocity * dt
+ self.toon.setPos(self.toon, vel[0], vel[1], vel[2])
+ toonPos = self.toon.getPos()
+ if Globals.Dev.DisableDeath:
+ pass
+ elif toonPos[2] < 0.0 and self.state in ['FreeFly', 'FlyingUp'] and self.allowFuelDeath():
+ self.postSpawnState = 'Running'
+ self.game.distGame.b_toonDied(self.toon.doId)
+ if toonPos[2] > self._levelBounds[2][1]:
+ self.controlVelocity[2] = 0.0
+ self.fanVelocity[2] = 0.0
+ toonPos = Vec3(clamp(toonPos[0], self._levelBounds[0][0], self._levelBounds[0][1]), clamp(toonPos[1], self._levelBounds[1][0], self._levelBounds[1][1]), clamp(toonPos[2], self._levelBounds[2][0], self._levelBounds[2][1]))
+ if self.isHeadInCeiling and toonPos[2] > self.surfacePoint[2]:
+ toonPos[2] = self.surfacePoint[2]
+ self.toon.setPos(toonPos)
+ if self.toon.getY(render) < -10:
+ self.toon.setY(-10.0)
+
+ def printFanInfo(self, string):
+ if len(self.fanIndex2ToonVelocity) > 0:
+ self.notify.info('==AFTER %s==' % string)
+ self.notify.info('Fan velocity:%s' % self.fanVelocity)
+ if len(self.activeFans) > 0:
+ self.notify.info('%s' % self.activeFans)
+ if len(self.fanIndex2ToonVelocity) > 0:
+ self.notify.info('%s' % self.fanIndex2ToonVelocity)
+ if len(self.fansStillHavingEffect) > 0:
+ self.notify.info('%s' % self.fansStillHavingEffect)
+
+ def resetFuel(self):
+ self.setFuel(Globals.Gameplay.FuelNormalAmt)
+
+ def isFuelLeft(self):
+ return self.fuel > 0.0
+
+ def setFuel(self, fuel):
+ self.fuel = fuel
+ self._guiMgr.setFuel(fuel)
+ if self.fuel <= 0.0:
+ fuelState = Globals.Gameplay.FuelStates.FuelEmpty
+ elif self.fuel < Globals.Gameplay.FuelVeryLowAmt:
+ fuelState = Globals.Gameplay.FuelStates.FuelVeryLow
+ elif self.fuel < Globals.Gameplay.FuelLowAmt:
+ fuelState = Globals.Gameplay.FuelStates.FuelLow
+ else:
+ fuelState = Globals.Gameplay.FuelStates.FuelNormal
+ if fuelState > self.fuelState:
+ self.game.distGame.b_toonSetBlades(self.toon.doId, fuelState)
+ if fuelState < self.fuelState:
+ if self.state in ['FlyingUp', 'FreeFly', 'Running']:
+ self.game.distGame.b_toonBladeLost(self.toon.doId)
+
+ def resetBlades(self):
+ CogdoFlyingPlayer.resetBlades(self)
+ self._guiMgr.resetBlades()
+
+ def setBlades(self, fuelState):
+ CogdoFlyingPlayer.setBlades(self, fuelState)
+ self._guiMgr.setBlades(fuelState)
+
+ def bladeLost(self):
+ CogdoFlyingPlayer.bladeLost(self)
+ self._bladeBreakSfx.play(volume=0.35)
+ self._guiMgr.bladeLost()
+
+ def updateFuel(self, dt):
+ if Globals.Dev.InfiniteFuel:
+ self.setFuel(Globals.Gameplay.FuelNormalAmt)
+ elif self.state in Globals.Gameplay.DepleteFuelStates and self.fuel > 0.0:
+ self.setFuel(self.fuel - Globals.Gameplay.FuelBurnRate * dt)
+ elif self.fuel < 0.0:
+ self.setFuel(0.0)
+
+ def update(self, dt = 0.0):
+ self.instantaneousVelocity = (self.toon.getPos() - self.oldPos) / dt
+ self.oldPos = self.toon.getPos()
+ self.updateFuel(dt)
+ if self.isFlying():
+ self.updateToonFlyingState(dt)
+ if self.state in ['FreeFly', 'FlyingUp', 'Death']:
+ self.updateControlVelocity(dt)
+ self.updateFanVelocity(dt)
+ self.updateToonPos(dt)
+ self._cameraMgr.update(dt)
+
+ def isFlying(self):
+ if self.state in ['FreeFly', 'FlyingUp']:
+ return True
+ else:
+ return False
+
+ def pressedControlWhileRunning(self):
+ if self.isFuelLeft() and self.state == 'Running':
+ self.notify.debug('Pressed Control and have fuel')
+ self.request('FlyingUp')
+ else:
+ self.ignore('control')
+ self.ignore('lcontrol')
+ self.acceptOnce('control', self.pressedControlWhileRunning)
+ self.acceptOnce('lcontrol', self.pressedControlWhileRunning)
+
+ def setPropellerState(self, propState):
+ if not self.hasPickedUpFirstPropeller:
+ propState = CogdoFlyingLocalPlayer.PropStates.Off
+ if self.propState != propState:
+ oldState = self.propState
+ self.propState = propState
+ if self.propState == CogdoFlyingLocalPlayer.PropStates.Normal:
+ if not self.propellerSpinLerp.isPlaying():
+ self.propellerSpinLerp.loop()
+ self.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed)
+ self._guiMgr.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed)
+ self._loopPropellerSfx(playRate=0.7, volume=0.8)
+ elif self.propState == CogdoFlyingLocalPlayer.PropStates.Overdrive:
+ if not self.propellerSpinLerp.isPlaying():
+ self.propellerSpinLerp.loop()
+ self.setPropellerSpinRate(Globals.Gameplay.OverdrivePropSpeed)
+ self._guiMgr.setPropellerSpinRate(Globals.Gameplay.OverdrivePropSpeed)
+ self._loopPropellerSfx(playRate=1.1)
+ elif self.propState == CogdoFlyingLocalPlayer.PropStates.Off:
+ self.propellerSpinLerp.pause()
+ self._propellerSfx.stop()
+
+ def enterInactive(self):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self._inputMgr.disable()
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off)
+ self.shutdownFlyingBroadcast()
+
+ def filterInactive(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitInactive(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self._inputMgr.enable()
+ self.activateFlyingBroadcast()
+
+ def enterSpawn(self):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self.toon.b_setAnimState('Happy', 1.0)
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
+ self.spawnInterval.start()
+
+ def filterSpawn(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitSpawn(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+
+ def enterFreeFly(self):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
+ if self.oldState in ['Running', 'HitWhileRunning']:
+ self.toon.jumpStart()
+ self.toon.setHpr(render, 0, 0, 0)
+
+ def filterFreeFly(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitFreeFly(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+
+ def enterFlyingUp(self):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Overdrive)
+ if self.oldState in ['Running']:
+ self.toon.jumpStart()
+ self.toon.setHpr(render, 0, 0, 0)
+
+ def filterFlyingUp(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitFlyingUp(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+
+ def enterHitWhileFlying(self, elapsedTime = 0.0):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self.setEnemyHitting(True)
+ self._toonHitSfx.play()
+ self.startHitFlyingToonInterval()
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
+
+ def filterHitWhileFlying(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitHitWhileFlying(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.enemyHitIval.clearToInitial()
+ self.coolDownAfterHitInterval.clearToInitial()
+ self.coolDownAfterHitInterval.start()
+
+ def enterInWhirlwind(self, elapsedTime = 0.0):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self._hitByWhirlwindSfx.play()
+ self.startHitByWhirlwindInterval()
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
+
+ def filterInWhirlwind(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitInWhirlwind(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.eventIval.clearToInitial()
+
+ def enterHitWhileRunning(self, elapsedTime = 0.0):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self.setEnemyHitting(True)
+ self._toonHitSfx.play()
+ self.toon.b_setAnimState('FallDown')
+ self.startHitRunningToonInterval()
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
+
+ def filterHitWhileRunning(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitHitWhileRunning(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.enemyHitIval.clearToInitial()
+ self.coolDownAfterHitInterval.clearToInitial()
+ self.coolDownAfterHitInterval.start()
+
+ def enterRunning(self):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self.toon.b_setAnimState('Happy', 1.0)
+ if self.oldState not in ['Spawn', 'HitWhileRunning', 'Inactive']:
+ self.toon.jumpHardLand()
+ self._collideSfx.play()
+ self.orthoWalk.start()
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
+ self.ignore('control')
+ self.ignore('lcontrol')
+ self.acceptOnce('control', self.pressedControlWhileRunning)
+ self.acceptOnce('lcontrol', self.pressedControlWhileRunning)
+
+ def filterRunning(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitRunning(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.orthoWalk.stop()
+ self.ignore('control')
+ self.ignore('lcontrol')
+
+ def enterOutOfTime(self):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ if self.spawnInterval.isPlaying():
+ self.spawnInterval.clearToInitial()
+ self.ignoreAll()
+ self.introGuiSeq.clearToInitial()
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off)
+ if not Globals.Dev.NoLegalEagleAttacks:
+ for eagle in self.legalEaglesTargeting:
+ messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [eagle.index])
+
+ taskMgr.remove('delayedLandOnPlatform')
+ taskMgr.remove('delayedLandOnWinPlatform')
+ self.outOfTimeInterval.start()
+
+ def filterOutOfTime(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitOutOfTime(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+
+ def enterDeath(self):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self.propellerSmoke.stop()
+ self.deathInterval.start()
+ self.toon.b_setAnimState('jumpAirborne', 1.0)
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off)
+ if not Globals.Dev.NoLegalEagleAttacks:
+ for eagle in self.legalEaglesTargeting:
+ messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [eagle.index])
+
+ def filterDeath(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitDeath(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.deathInterval.clearToInitial()
+
+ def enterWaitingForWin(self):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self.resetFuel()
+ self._guiMgr.hideRefuelGui()
+ self.waitingForWinSeq.start()
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
+ if not Globals.Dev.NoLegalEagleAttacks:
+ self.game.forceClearLegalEagleInterestInToon(self.toon.doId)
+
+ def filterWaitingForWin(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitWaitingForWin(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+ self.waitingForWinSeq.finish()
+ self.waitingForWinInterval.clearToInitial()
+
+ def enterWin(self):
+ CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
+ self._guiMgr.stopTimer()
+ self.winInterval.start()
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
+
+ def filterWin(self, request, args):
+ if request == self.state:
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitWin(self):
+ CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
+
+ def _destroyEventIval(self):
+ if hasattr(self, 'eventIval'):
+ self.eventIval.clearToInitial()
+ del self.eventIval
+
+ def startEventIval(self, ival):
+ self._destroyEventIval()
+ self.eventIval = ival
+ self.eventIval.start()
+
+ def _destroyEnemyHitIval(self):
+ if hasattr(self, 'enemyHitIval'):
+ self.enemyHitIval.clearToInitial()
+ del self.enemyHitIval
+
+ def startEnemyHitIval(self, ival):
+ self._destroyEnemyHitIval()
+ self.enemyHitIval = ival
+ self.enemyHitIval.start()
+
+ def isEnemyHitting(self):
+ return self.legalEagleHitting
+
+ def setEnemyHitting(self, value):
+ self.legalEagleHitting = value
+
+ def shouldLegalEagleBeInFrame(self):
+ if not self.isLegalEagleTarget():
+ return False
+ else:
+ index = len(self.legalEaglesTargeting) - 1
+ eagle = self.legalEaglesTargeting[index]
+ return eagle.shouldBeInFrame()
+
+ def startHitRunningToonInterval(self):
+ dur = self.toon.getDuration('slip-backward')
+ self.startEnemyHitIval(Sequence(Wait(dur), Func(self.request, 'Running'), name='hitByLegalEagleIval-%i' % self.toon.doId))
+
+ def startHitFlyingToonInterval(self):
+ hitByEnemyPos = self.toon.getPos(render)
+ collVec = hitByEnemyPos - self.collPos
+ collVec[2] = 0.0
+ collVec.normalize()
+ collVec *= Globals.Gameplay.HitKnockbackDist
+
+ def spinPlayer(t, rand):
+ if rand == 0:
+ self.toon.setH(-(t * 720.0))
+ else:
+ self.toon.setH(t * 720.0)
+
+ direction = random.randint(0, 1)
+ self.startEnemyHitIval(Sequence(Parallel(LerpFunc(spinPlayer, fromData=0.0, toData=1.0, duration=Globals.Gameplay.HitKnockbackTime, blendType='easeInOut', extraArgs=[direction]), LerpPosInterval(self.toon, duration=Globals.Gameplay.HitKnockbackTime, pos=hitByEnemyPos + collVec, blendType='easeOut')), Func(self.request, 'FreeFly'), name='hitByLegalEagleIval-%i' % self.toon.doId))
+
+ def startHitByWhirlwindInterval(self):
+
+ def spinPlayer(t):
+ self.controlVelocity[2] = 1.0
+ angle = math.radians(t * (720.0 * 2 - 180))
+ self.toon.setPos(self.activeWhirlwind.model.getX(self.game.level.root) + math.cos(angle) * 2, self.activeWhirlwind.model.getY(self.game.level.root) + math.sin(angle) * 2, self.toon.getZ())
+
+ def movePlayerBack(t):
+ self.toon.setY(self.activeWhirlwind.model.getY(self.game.level.root) - t * Globals.Gameplay.WhirlwindMoveBackDist)
+
+ self.startEventIval(Sequence(Func(self._cameraMgr.freeze), Func(self.activeWhirlwind.disable), LerpFunc(spinPlayer, fromData=0.0, toData=1.0, duration=Globals.Gameplay.WhirlwindSpinTime), LerpFunc(movePlayerBack, fromData=0.0, toData=1.0, duration=Globals.Gameplay.WhirlwindMoveBackTime, blendType='easeOut'), Func(self.activeWhirlwind.enable), Func(self._cameraMgr.unfreeze), Func(self.request, 'FreeFly'), name='spinPlayerIval-%i' % self.toon.doId))
+
+ def handleEnterWhirlwind(self, whirlwind):
+ self.activeWhirlwind = whirlwind
+ self.request('InWhirlwind')
+
+ def handleEnterEnemyHit(self, enemy, collPos):
+ self.collPos = collPos
+ if self.state in ['FlyingUp', 'FreeFly']:
+ self.request('HitWhileFlying')
+ elif self.state in ['Running']:
+ self.request('HitWhileRunning')
+
+ def handleEnterFan(self, fan):
+ if fan in self.activeFans:
+ return
+ if len(self.activeFans) == 0:
+ self._fanSfx.loop()
+ self.activeFans.append(fan)
+ if fan.index not in self.fanIndex2ToonVelocity:
+ self.fanIndex2ToonVelocity[fan.index] = Vec3(0.0, 0.0, 0.0)
+ if fan not in self.fansStillHavingEffect:
+ self.fansStillHavingEffect.append(fan)
+
+ def handleExitFan(self, fan):
+ if fan in self.activeFans:
+ self.activeFans.remove(fan)
+ if len(self.activeFans) == 0:
+ self._fanSfx.stop()
+
+ def handleDebuffPowerup(self, pickupType, elapsedTime):
+ self._invulDebuffSfx.play()
+ CogdoFlyingPlayer.handleDebuffPowerup(self, pickupType, elapsedTime)
+ messenger.send(CogdoFlyingGuiManager.ClearMessageDisplayEventName)
+
+ def handleEnterGatherable(self, gatherable, elapsedTime):
+ CogdoFlyingPlayer.handleEnterGatherable(self, gatherable, elapsedTime)
+ if gatherable.type == Globals.Level.GatherableTypes.Memo:
+ self.handleEnterMemo(gatherable)
+ elif gatherable.type == Globals.Level.GatherableTypes.Propeller:
+ self.handleEnterPropeller(gatherable)
+ elif gatherable.type == Globals.Level.GatherableTypes.LaffPowerup:
+ self._getLaffSfx.play()
+ elif gatherable.type == Globals.Level.GatherableTypes.InvulPowerup:
+ self._getRedTapeSfx.play()
+ messenger.send(CogdoFlyingGuiManager.InvulnerableEventName)
+
+ def handleEnterMemo(self, gatherable):
+ self.score += 1
+ if self.score == 1:
+ self._guiMgr.presentMemoGui()
+ self._guiMgr.setTemporaryMessage(TTLocalizer.CogdoFlyingGameMemoIntro, 4.0)
+ self._guiMgr.setMemoCount(self.score)
+ self._getMemoSfx.play()
+
+ def handleEnterPropeller(self, gatherable):
+ if self.fuel < 1.0:
+ if not self.hasPickedUpFirstPropeller:
+ messenger.send(CogdoFlyingGuiManager.PickedUpFirstPropellerEventName)
+ self.introGuiSeq.clearToInitial()
+ self.hasPickedUpFirstPropeller = True
+ self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
+ self.setFuel(1.0)
+ self._guiMgr.update()
+ self._refuelSfx.play()
+ self._refuelSpinSfx.play(volume=0.15)
diff --git a/toontown/cogdominium/CogdoFlyingObjects.py b/toontown/cogdominium/CogdoFlyingObjects.py
new file mode 100644
index 0000000..7f8a4a9
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingObjects.py
@@ -0,0 +1,437 @@
+import random
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import Sequence, Func, Parallel, Wait, LerpHprInterval, LerpScaleInterval, LerpFunctionInterval
+from otp.otpbase import OTPGlobals
+from toontown.toonbase import ToontownGlobals
+from CogdoGameGatherable import CogdoGameGatherable, CogdoMemo
+import CogdoFlyingGameGlobals as Globals
+import CogdoUtil
+from direct.particles import ParticleEffect
+from direct.particles import Particles
+from direct.particles import ForceGroup
+
+class CogdoFlyingGatherableFactory:
+
+ def __init__(self):
+ self._serialNum = -1
+ self._memoModel = CogdoUtil.loadModel('memo', 'shared').find('**/memo')
+ self._propellerModel = CogdoUtil.loadFlyingModel('propellers').find('**/mesh')
+ self._powerUpModels = {}
+ for type, modelName in Globals.Level.PowerupType2Model.items():
+ model = CogdoUtil.loadFlyingModel(modelName).find('**/' + Globals.Level.PowerupType2Node[type])
+ self._powerUpModels[type] = model
+ model.setTransparency(True)
+ model.setScale(0.5)
+
+ def createMemo(self):
+ self._serialNum += 1
+ return CogdoFlyingMemo(self._serialNum, self._memoModel)
+
+ def createPropeller(self):
+ self._serialNum += 1
+ return CogdoFlyingPropeller(self._serialNum, self._propellerModel)
+
+ def createPowerup(self, type):
+ self._serialNum += 1
+ return CogdoFlyingPowerup(self._serialNum, type, self._powerUpModels[type])
+
+ def createSparkles(self, color1, color2, amp):
+ self.f = ParticleEffect.ParticleEffect('particleEffect_sparkles')
+ p0 = Particles.Particles('particles-1')
+ p0.setFactory('PointParticleFactory')
+ p0.setRenderer('SparkleParticleRenderer')
+ p0.setEmitter('RingEmitter')
+ p0.setPoolSize(15)
+ p0.setBirthRate(0.1)
+ p0.setLitterSize(100)
+ p0.setLitterSpread(0)
+ p0.factory.setLifespanBase(0.6)
+ p0.factory.setLifespanSpread(0.1)
+ p0.factory.setMassBase(1.0)
+ p0.factory.setMassSpread(0.0)
+ p0.factory.setTerminalVelocityBase(3.0)
+ p0.factory.setTerminalVelocitySpread(1.0)
+ p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT)
+ p0.renderer.setUserAlpha(1.0)
+ p0.renderer.setCenterColor(color1)
+ p0.renderer.setEdgeColor(color2)
+ p0.renderer.setBirthRadius(0.3)
+ p0.renderer.setDeathRadius(0.3)
+ p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE)
+ p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE)
+ p0.emitter.setAmplitude(0)
+ p0.emitter.setAmplitudeSpread(0)
+ f0 = ForceGroup.ForceGroup('Gravity')
+ force0 = LinearVectorForce(Vec3(0.0, 0.0, -10.0), 1.0, 0)
+ force0.setVectorMasks(1, 1, 1)
+ force0.setActive(1)
+ f0.addForce(force0)
+ self.f.addForceGroup(f0)
+ p0.emitter.setRadius(2.0)
+ self.f.addParticles(p0)
+ self.f.setPos(0, 0, 0)
+ self.f.setHpr(0, random.random() * 180, random.random() * 180)
+ return self.f
+
+ def destroy(self):
+ self._memoModel.removeNode()
+ del self._memoModel
+ self._propellerModel.removeNode()
+ del self._propellerModel
+ for model in self._powerUpModels.values():
+ model.removeNode()
+
+ del self._powerUpModels
+ if Globals.Level.AddSparkleToPowerups:
+ self.f.cleanup()
+ del self.f
+
+
+class CogdoFlyingGatherableBase:
+
+ def __init__(self, type):
+ self.type = type
+ self.initFlash()
+
+ def initFlash(self):
+ model = CogdoUtil.loadFlyingModel('gatherableFlash_card')
+ texName = Globals.Level.GatherableType2TextureName[self.type]
+ tex = model.findTexture(texName)
+ tex.setWrapU(Texture.WMRepeat)
+ tex.setWrapV(Texture.WMRepeat)
+ del model
+ self.ts = TextureStage('ts')
+ self.ts.setMode(TextureStage.MCombine)
+ self.ts.setSort(1)
+ self.ts.setCombineRgb(TextureStage.CMInterpolate, TextureStage.CSPrevious, TextureStage.COSrcColor, TextureStage.CSTexture, TextureStage.COSrcColor, TextureStage.CSConstant, TextureStage.COSrcColor)
+ self.ts.setCombineAlpha(TextureStage.CMInterpolate, TextureStage.CSPrevious, TextureStage.COSrcAlpha, TextureStage.CSTexture, TextureStage.COSrcAlpha, TextureStage.CSConstant, TextureStage.COSrcAlpha)
+ self._model.setTexture(self.ts, tex)
+ dur = Globals.Gameplay.GatherableFlashTime
+ self.flashLoop = Sequence(LerpFunctionInterval(self.setTextureAlphaFunc, fromData=1.0, toData=0.25, duration=dur / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.setTextureAlphaFunc, fromData=0.25, toData=1.0, duration=dur / 2.0, blendType='easeInOut'), Wait(1.0), name='%s.flashLoop-%s' % (self.__class__.__name__, self.serialNum))
+
+ def show(self):
+ self.enableFlash()
+
+ def hide(self):
+ self.disableFlash()
+
+ def enable(self):
+ pass
+
+ def disable(self):
+ pass
+
+ def enableFlash(self):
+ self.flashLoop.loop()
+
+ def disableFlash(self):
+ self.flashLoop.clearToInitial()
+
+ def destroy(self):
+ self.disableFlash()
+ del self.flashLoop
+ del self.ts
+
+ def setTextureAlphaFunc(self, value):
+ self.ts.setColor(Vec4(value, value, value, value))
+
+ def isPowerUp(self):
+ return False
+
+
+class CogdoFlyingGatherable(CogdoGameGatherable, CogdoFlyingGatherableBase):
+
+ def __init__(self, type, serialNum, modelToInstance, triggerRadius, animate = True):
+ CogdoGameGatherable.__init__(self, serialNum, modelToInstance, triggerRadius, animate=animate)
+ CogdoFlyingGatherableBase.__init__(self, type)
+
+ def enable(self):
+ CogdoGameGatherable.enable(self)
+ CogdoFlyingGatherableBase.enable(self)
+
+ def disable(self):
+ CogdoGameGatherable.disable(self)
+ CogdoFlyingGatherableBase.disable(self)
+
+ def show(self):
+ CogdoGameGatherable.show(self)
+ CogdoFlyingGatherableBase.show(self)
+
+ def hide(self):
+ CogdoGameGatherable.hide(self)
+ CogdoFlyingGatherableBase.hide(self)
+
+ def destroy(self):
+ CogdoGameGatherable.destroy(self)
+ CogdoFlyingGatherableBase.destroy(self)
+
+
+class CogdoFlyingMemo(CogdoFlyingGatherableBase, CogdoMemo):
+
+ def __init__(self, serialNum, model):
+ CogdoMemo.__init__(self, serialNum, triggerRadius=Globals.Gameplay.MemoCollisionRadius, spinRate=Globals.Gameplay.MemoSpinRate, model=model)
+ CogdoFlyingGatherableBase.__init__(self, Globals.Level.GatherableTypes.Memo)
+ self.floatTimer = 0.0
+ self.floatSpeed = 1.0
+ self.floatDuration = 2.0
+
+ def _handleEnterCollision(self, collEntry):
+ CogdoGameGatherable._handleEnterCollision(self, collEntry)
+
+ def enable(self):
+ CogdoFlyingGatherableBase.enable(self)
+ CogdoMemo.enable(self)
+
+ def disable(self):
+ CogdoFlyingGatherableBase.disable(self)
+ CogdoMemo.disable(self)
+
+ def show(self):
+ CogdoFlyingGatherableBase.show(self)
+ CogdoMemo.show(self)
+
+ def hide(self):
+ CogdoFlyingGatherableBase.hide(self)
+ CogdoMemo.hide(self)
+
+ def destroy(self):
+ CogdoFlyingGatherableBase.destroy(self)
+ CogdoMemo.destroy(self)
+
+ def update(self, dt):
+ self.floatTimer += dt
+ if self.floatTimer < self.floatDuration:
+ self.setPos(self.getPos() + Vec3(0, 0, dt * self.floatSpeed))
+ elif self.floatTimer < self.floatDuration * 2.0:
+ self.setPos(self.getPos() - Vec3(0, 0, dt * self.floatSpeed))
+ else:
+ self.floatTimer = 0.0
+ self.floatSpeed = random.uniform(0.5, 1.0)
+ self.floatDuration = random.uniform(1.9, 2.1)
+
+
+class CogdoFlyingPowerup(CogdoFlyingGatherable):
+
+ def __init__(self, serialNum, powerupType, model):
+ self._pickedUpList = []
+ self._isToonLocal = False
+ CogdoFlyingGatherable.__init__(self, powerupType, serialNum, model, Globals.Gameplay.MemoCollisionRadius)
+ self.initInterval()
+
+ def initInterval(self):
+ bouncePercent = 1.2
+ scale = self._model.getScale()
+ shrinkPowerupLerp = LerpScaleInterval(self._model, 0.5, 0.0, startScale=0.0, blendType='easeInOut')
+ growPowerupLerp = LerpScaleInterval(self._model, 0.5, scale * bouncePercent, startScale=0.0, blendType='easeInOut')
+ bouncePowerupLerp = LerpScaleInterval(self._model, 0.25, scale, startScale=scale * bouncePercent, blendType='easeInOut')
+ self.pickUpSeq = Sequence(Func(self.updateLerpStartScale, shrinkPowerupLerp, self._model), shrinkPowerupLerp, Func(self.ghostPowerup), growPowerupLerp, bouncePowerupLerp, name='%s.pickUpSeq-%s' % (self.__class__.__name__, self.serialNum))
+
+ def isPowerUp(self):
+ return True
+
+ def updateLerpStartScale(self, lerp, nodepath):
+ lerp.setStartScale(nodepath.getScale())
+
+ def wasPickedUpByToon(self, toon):
+ if toon.doId in self._pickedUpList:
+ return True
+ return False
+
+ def ghostPowerup(self):
+ if self._isToonLocal:
+ self._model.setAlphaScale(0.5)
+ if Globals.Level.AddSparkleToPowerups:
+ self.f = self.find('**/particleEffect_sparkles')
+ self.f.hide()
+
+ def pickUp(self, toon, elapsedSeconds = 0.0):
+ if self.wasPickedUpByToon(toon) == True:
+ return
+ self._pickedUpList.append(toon.doId)
+ self._isToonLocal = toon.isLocal()
+ if self._animate:
+ self.pickUpSeq.clearToInitial()
+ self.pickUpSeq.start()
+ else:
+ self.ghostPowerup()
+
+ def destroy(self):
+ del self._pickedUpList[:]
+ self.pickUpSeq.clearToInitial()
+ del self.pickUpSeq
+ CogdoFlyingGatherable.destroy(self)
+
+ def update(self, dt):
+ self._model.setH(self._model.getH() + Globals.Gameplay.MemoSpinRate * dt)
+
+
+class CogdoFlyingPropeller(CogdoFlyingGatherable):
+
+ def __init__(self, serialNum, model):
+ CogdoFlyingGatherable.__init__(self, Globals.Level.GatherableTypes.Propeller, serialNum, model, Globals.Gameplay.PropellerCollisionRadius, animate=False)
+ self.activePropellers = []
+ self.usedPropellers = []
+ propellers = self._model.findAllMatches('**/propeller*')
+ for prop in propellers:
+ self.activePropellers.append(prop)
+
+ self.initIntervals()
+
+ def initIntervals(self):
+ self.animatedPropellerIval = Parallel(name='%s.object-%i-animatePropellerIval' % (self.__class__.__name__, self.serialNum))
+ for propeller in self.activePropellers:
+ self.animatedPropellerIval.append(LerpHprInterval(propeller, duration=Globals.Level.PropellerSpinDuration, startHpr=Vec3(0.0, 0.0, 0.0), hpr=Vec3(360.0, 0.0, 0.0)))
+
+ def show(self):
+ self.animatedPropellerIval.loop()
+ CogdoFlyingGatherable.show(self)
+
+ def hide(self):
+ self.animatedPropellerIval.clearToInitial()
+ CogdoFlyingGatherable.hide(self)
+
+ def destroy(self):
+ taskMgr.removeTasksMatching('propeller-respawn-*')
+ self.animatedPropellerIval.clearToInitial()
+ del self.animatedPropellerIval
+ CogdoFlyingGatherable.destroy(self)
+
+ def pickUp(self, toon, elapsedSeconds = 0.0):
+ prop = self.removePropeller()
+ if prop != None:
+ respawnTime = Globals.Gameplay.PropellerRespawnTime
+ if elapsedSeconds < respawnTime:
+ taskMgr.doMethodLater(respawnTime - elapsedSeconds, self.addPropeller, 'propeller-respawn-%i' % self.serialNum, extraArgs=[prop])
+ else:
+ self.addPropeller(prop)
+ else:
+ self.disable()
+ return
+
+ def addPropeller(self, prop):
+ if len(self.usedPropellers) > 0:
+ if len(self.activePropellers) == 0:
+ self.enable()
+ self.usedPropellers.remove(prop)
+ prop.show()
+ self.activePropellers.append(prop)
+ self._wasPickedUp = False
+
+ def removePropeller(self):
+ if len(self.activePropellers) > 0:
+ prop = self.activePropellers.pop()
+ prop.hide()
+ self.usedPropellers.append(prop)
+ if len(self.activePropellers) == 0:
+ self._wasPickedUp = True
+ return prop
+ return None
+
+ def isPropeller(self):
+ if len(self.activePropellers) > 0:
+ return True
+ else:
+ return False
+
+
+class CogdoFlyingLevelFog:
+
+ def __init__(self, level, color = Globals.Level.FogColor):
+ self._level = level
+ self.color = color
+ fogDistance = self._level.quadLengthUnits * max(1, self._level.quadVisibiltyAhead * 0.2)
+ self.fog = Fog('RenderFog')
+ self.fog.setColor(self.color)
+ self.fog.setLinearRange(fogDistance * Globals.Level.RenderFogStartFactor, fogDistance)
+ self._visible = False
+ self._clearColor = Vec4(base.win.getClearColor())
+ self._clearColor.setW(1.0)
+
+ def destroy(self):
+ self.setVisible(False)
+ if hasattr(self, 'fog'):
+ del self.fog
+
+ def isVisible(self):
+ return self._visible
+
+ def setVisible(self, visible):
+ self._visible = visible
+ if self._visible:
+ base.win.setClearColor(self.color)
+ render.setFog(self.fog)
+ else:
+ base.win.setClearColor(self._clearColor)
+ render.clearFog()
+
+
+class CogdoFlyingPlatform:
+ CeilingCollName = 'col_ceiling'
+ FloorCollName = 'col_floor'
+
+ def __init__(self, model, type = Globals.Level.PlatformTypes.Platform, parent = None):
+ self._model = model
+ self._type = type
+ if parent is not None:
+ self._model.reparentTo(parent)
+ self._initCollisions()
+ return
+
+ def __str__(self):
+ return '<%s model=%s, type=%s>' % (self.__class__.__name__, self._model, self._type)
+
+ def destroy(self):
+ self._floorColl.clearPythonTag('platform')
+ self._model.removeNode()
+ del self._model
+ del self._type
+ del self._floorColl
+ del self._ceilingColl
+
+ def onstage(self):
+ self._model.unstash()
+
+ def offstage(self):
+ self._model.stash()
+
+ def _initCollisions(self):
+ self._floorColl = self._model.find('**/*%s' % CogdoFlyingPlatform.FloorCollName)
+ self._floorColl.setName(CogdoFlyingPlatform.FloorCollName)
+ self._floorColl.node().setIntoCollideMask(ToontownGlobals.FloorEventBitmask | OTPGlobals.FloorBitmask)
+ self._floorColl.setPythonTag('platform', self)
+ self._ceilingColl = self._model.find('**/*%s' % CogdoFlyingPlatform.CeilingCollName)
+ self._ceilingColl.setName(CogdoFlyingPlatform.CeilingCollName)
+ self._ceilingColl.node().setIntoCollideMask(ToontownGlobals.CeilingBitmask)
+
+ def getType(self):
+ return self._type
+
+ def getName(self):
+ return self._model.getName()
+
+ def getModel(self):
+ return self._model
+
+ def isStartPlatform(self):
+ return self._type == Globals.Level.PlatformTypes.StartPlatform
+
+ def isEndPlatform(self):
+ return self._type == Globals.Level.PlatformTypes.EndPlatform
+
+ def isStartOrEndPlatform(self):
+ return self.isStartPlatform() or self.isEndPlatform()
+
+ def getSpawnPosForPlayer(self, playerNum, parent):
+ offset = Globals.Level.PlatformType2SpawnOffset[self._type]
+ spawnLoc = self._model.find('**/spawn_loc')
+ x = (playerNum - 2.0) % 2 * offset
+ y = (playerNum - 1.0) % 2 * offset
+ if not spawnLoc.isEmpty():
+ spawnPos = spawnLoc.getPos(parent) + Vec3(x, y, 0.0)
+ else:
+ spawnPos = self._floorColl.getPos(parent) + Vec3(x, y, 0.0)
+ return spawnPos
+
+ @staticmethod
+ def getFromNode(node):
+ return node.getPythonTag('platform')
diff --git a/toontown/cogdominium/CogdoFlyingObstacles.py b/toontown/cogdominium/CogdoFlyingObstacles.py
new file mode 100644
index 0000000..7cfbd22
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingObstacles.py
@@ -0,0 +1,390 @@
+import random
+from direct.showbase.DirectObject import DirectObject
+from direct.interval.IntervalGlobal import LerpFunc, ActorInterval, LerpPosInterval
+from direct.interval.MetaInterval import Sequence
+from direct.directutil import Mopath
+from direct.showbase import PythonUtil
+from pandac.PandaModules import *
+from toontown.toonbase import ToontownGlobals
+from toontown.suit import Suit
+from toontown.suit import SuitDNA
+from toontown.battle import BattleProps
+import CogdoUtil
+import CogdoFlyingGameGlobals as Globals
+from CogdoFlyingUtil import swapAvatarShadowPlacer
+from direct.particles import ParticleEffect
+from direct.particles import Particles
+from direct.particles import ForceGroup
+
+class CogdoFlyingObtacleFactory:
+
+ def __init__(self):
+ self._index = -1
+ self._whirlwindModel = CogdoUtil.loadFlyingModel('whirlwind').find('**/whirlwind')
+ self._fanModel = CogdoUtil.loadFlyingModel('streamer').find('**/streamer')
+
+ def destroy(self):
+ self._whirlwindModel.removeNode()
+ del self._whirlwindModel
+ self._fanModel.removeNode()
+ del self._fanModel
+ if Globals.Level.AddParticlesToStreamers:
+ self.f.cleanup()
+ del self.f
+
+ def createFan(self):
+ self._index += 1
+ return CogdoFlyingFan(self._index, self._fanModel)
+
+ def createFlyingMinion(self, motionPath = None):
+ self._index += 1
+ return CogdoFlyingMinionFlying(self._index, motionPath=motionPath)
+
+ def createWalkingMinion(self, motionPath = None):
+ self._index += 1
+ return CogdoFlyingMinionWalking(self._index, motionPath=motionPath)
+
+ def createWhirlwind(self, motionPath = None):
+ self._index += 1
+ return CogdoFlyingWhirlwind(self._index, self._whirlwindModel, motionPath=motionPath)
+
+ def createStreamerParticles(self, color1, color2, amp):
+ self.f = ParticleEffect.ParticleEffect('streamer_particles')
+ p0 = Particles.Particles('particles-1')
+ p0.setFactory('PointParticleFactory')
+ p0.setRenderer('SparkleParticleRenderer')
+ p0.setEmitter('RingEmitter')
+ p0.setPoolSize(80)
+ p0.setBirthRate(0.05)
+ p0.setLitterSize(100)
+ p0.setLitterSpread(0)
+ p0.factory.setLifespanBase(3.0)
+ p0.factory.setLifespanSpread(0.5)
+ p0.factory.setMassBase(1.0)
+ p0.factory.setMassSpread(0.0)
+ p0.factory.setTerminalVelocityBase(5.0)
+ p0.factory.setTerminalVelocitySpread(1.0)
+ p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT)
+ p0.renderer.setUserAlpha(1.0)
+ p0.renderer.setCenterColor(color1)
+ p0.renderer.setEdgeColor(color2)
+ p0.renderer.setBirthRadius(0.3)
+ p0.renderer.setDeathRadius(0.3)
+ p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE)
+ p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE)
+ p0.emitter.setAmplitude(0)
+ p0.emitter.setAmplitudeSpread(0)
+ f0 = ForceGroup.ForceGroup('Gravity')
+ force0 = LinearVectorForce(Vec3(0.0, 0.0, 10.0), 1.0, 0)
+ force0.setVectorMasks(1, 1, 1)
+ force0.setActive(1)
+ f0.addForce(force0)
+ self.f.addForceGroup(f0)
+ p0.emitter.setRadius(5.0)
+ self.f.addParticles(p0)
+ self.f.setPos(0, 0, 0)
+ self.f.setHpr(0, 0, 0)
+ return self.f
+
+
+class CogdoFlyingObstacle(DirectObject):
+ EnterEventName = 'CogdoFlyingObstacle_Enter'
+ ExitEventName = 'CogdoFlyingObstacle_Exit'
+ MotionTypes = PythonUtil.Enum(('BackForth', 'Loop'))
+
+ def __init__(self, type, index, model, collSolid, motionPath = None, motionPattern = None, blendMotion = True, instanceModel = True):
+ self.type = type
+ self.index = index
+ name = 'CogdoFlyingObstacle-%s-%i' % (self.type, self.index)
+ if instanceModel:
+ self.model = NodePath(name)
+ model.instanceTo(self.model)
+ else:
+ self.model = model
+ self.model.setName(name)
+ self.currentT = 0.0
+ self.direction = 1.0
+ self.collNode = None
+ self._initCollisions(name, collSolid)
+ self.motionPath = motionPath
+ self.motionPattern = motionPattern
+ self.motionSequence = None
+ if blendMotion:
+ blendType = 'easeInOut'
+ else:
+ blendType = 'noBlend'
+ if motionPath is not None:
+
+ def moveObstacle(value):
+ self.motionPath.goTo(self.model, value)
+
+ self.motionPath = Mopath.Mopath(name='obstacle-%i' % self.index)
+ self.motionPath.loadNodePath(motionPath)
+ dur = self.motionPath.getMaxT()
+ self.motionSequence = Sequence(name='%s.obstacle-%i-motionSequence' % (self.__class__.__name__, self.index))
+ movePart1 = LerpFunc(moveObstacle, fromData=0.0, toData=self.motionPath.getMaxT(), duration=dur, blendType=blendType)
+ self.motionSequence.append(movePart1)
+ if self.motionPattern == CogdoFlyingObstacle.MotionTypes.BackForth:
+ movePart2 = LerpFunc(moveObstacle, fromData=self.motionPath.getMaxT(), toData=0.0, duration=dur, blendType=blendType)
+ self.motionSequence.append(movePart2)
+ return
+
+ def _initCollisions(self, name, collSolid):
+ self.collName = name
+ self.collSolid = collSolid
+ self.collSolid.setTangible(0)
+ self.collNode = CollisionNode(self.collName)
+ self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
+ self.collNode.addSolid(self.collSolid)
+ self.collNodePath = self.model.attachNewNode(self.collNode)
+ self.collNodePath.hide()
+ self.accept('enter' + self.collName, self._handleEnterCollision)
+ self.accept('exit' + self.collName, self._handleExitCollision)
+
+ def disable(self):
+ if self.collNode is not None:
+ self.collNode.setIntoCollideMask(BitMask32(0))
+ return
+
+ def enable(self):
+ if self.collNode is not None:
+ self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
+ return
+
+ def startMoving(self, elapsedTime = 0.0):
+ if self.motionSequence is not None:
+ self.motionSequence.loop()
+ self.motionSequence.setT(elapsedTime % self.motionSequence.getDuration())
+ return
+
+ def stopMoving(self):
+ if self.motionSequence is not None:
+ self.motionSequence.pause()
+ return
+
+ def destroy(self):
+ self.ignoreAll()
+ if self.motionSequence is not None:
+ self.motionSequence.clearToInitial()
+ del self.motionSequence
+ del self.collSolid
+ self.collNodePath.removeNode()
+ del self.collNodePath
+ del self.collNode
+ self.model.removeNode()
+ del self.model
+ del self.motionPath
+ return
+
+ def update(self, dt):
+ pass
+
+ def hide(self):
+ self.ignoreAll()
+ self.model.hide()
+ self.collNode.setIntoCollideMask(BitMask32(0))
+
+ def _handleEnterCollision(self, collEntry):
+ messenger.send(CogdoFlyingObstacle.EnterEventName, [self, collEntry])
+
+ def _handleExitCollision(self, collEntry):
+ messenger.send(CogdoFlyingObstacle.ExitEventName, [self, collEntry])
+
+
+from pandac.PandaModules import TransformState
+
+class CogdoFlyingWhirlwind(CogdoFlyingObstacle):
+
+ def __init__(self, index, model, motionPath = None):
+ collSolid = CollisionTube(0, 0, 0, 0, 0, Globals.Gameplay.WhirlwindCollisionTubeHeight, Globals.Gameplay.WhirlwindCollisionTubeRadius)
+ CogdoFlyingObstacle.__init__(self, Globals.Level.ObstacleTypes.Whirlwind, index, model, collSolid, motionPath=motionPath, motionPattern=CogdoFlyingObstacle.MotionTypes.BackForth)
+ self.t = 0.0
+ self._initModel()
+
+ def _initModel(self):
+ self.model.setDepthWrite(False)
+ self._texStage = self.model.findTextureStage('*')
+ self._soundIval = base.cogdoGameAudioMgr.createSfxIval('whirlwind', source=self.model)
+ self.model.setBin('transparent', self.index)
+
+ def startMoving(self, elapsedTime):
+ CogdoFlyingObstacle.startMoving(self, elapsedTime)
+ self.t = 0.0
+ self._soundIval.loop()
+
+ def update(self, dt):
+ self.t += dt
+ trans = TransformState.makePos((self.t, -self.t, 0))
+ self.model.setTexTransform(self._texStage, trans)
+ trans = TransformState.makePos((self.t * 2.0, -self.t * 2.0, 0))
+
+ def stopMoving(self):
+ CogdoFlyingObstacle.stopMoving(self)
+ self._soundIval.pause()
+
+ def destroy(self):
+ self._soundIval.clearToInitial()
+ del self._soundIval
+ CogdoFlyingObstacle.destroy(self)
+
+
+class CogdoFlyingMinion(CogdoFlyingObstacle):
+
+ def __init__(self, index, collSolid, motionPath = None):
+ self.prop = None
+ self.suit = Suit.Suit()
+ d = SuitDNA.SuitDNA()
+ d.newSuit(Globals.Gameplay.MinionDnaName)
+ self.suit.setDNA(d)
+ self.suit.setScale(Globals.Gameplay.MinionScale)
+ swapAvatarShadowPlacer(self.suit, 'minion-%sShadowPlacer' % index)
+ self.mopathNodePath = NodePath('mopathNodePath')
+ self.suit.reparentTo(self.mopathNodePath)
+ CogdoFlyingObstacle.__init__(self, Globals.Level.ObstacleTypes.Minion, index, self.mopathNodePath, collSolid, motionPath=motionPath, motionPattern=CogdoFlyingObstacle.MotionTypes.Loop, blendMotion=False, instanceModel=False)
+ self.lastPos = None
+ self.suit.loop('neutral')
+ return
+
+ def attachPropeller(self):
+ if self.prop is None:
+ self.prop = BattleProps.globalPropPool.getProp('propeller')
+ head = self.suit.find('**/joint_head')
+ self.prop.reparentTo(head)
+ return
+
+ def detachPropeller(self):
+ if self.prop:
+ self.prop.cleanup()
+ self.prop.removeNode()
+ self.prop = None
+ return
+
+ def startMoving(self, elapsedTime):
+ CogdoFlyingObstacle.startMoving(self, elapsedTime)
+
+ def stopMoving(self):
+ CogdoFlyingObstacle.stopMoving(self)
+
+ def update(self, dt):
+ CogdoFlyingObstacle.update(self, dt)
+ self.currPos = self.mopathNodePath.getPos()
+ if self.lastPos != None:
+ vec = self.currPos - self.lastPos
+ self.mopathNodePath.lookAt(self.currPos + vec)
+ self.mopathNodePath.setP(0)
+ self.lastPos = self.mopathNodePath.getPos()
+ return
+
+ def destroy(self):
+ self.mopathNodePath.removeNode()
+ del self.mopathNodePath
+ self.suit.cleanup()
+ self.suit.removeNode()
+ self.suit.delete()
+ CogdoFlyingObstacle.destroy(self)
+
+
+class CogdoFlyingMinionFlying(CogdoFlyingMinion):
+
+ def __init__(self, index, motionPath = None):
+ radius = Globals.Gameplay.FlyingMinionCollisionRadius
+ offset = Globals.Gameplay.FlyingMinionCollisionHeightOffset
+ collSolid = CollisionSphere(0, 0, offset, radius)
+ CogdoFlyingMinion.__init__(self, index, collSolid, motionPath)
+ self.attachPropeller()
+ self.propTrack = Sequence(ActorInterval(self.prop, 'propeller', startFrame=0, endFrame=14))
+ dur = Globals.Gameplay.FlyingMinionFloatTime
+ offset = Globals.Gameplay.FlyingMinionFloatOffset
+ suitPos = self.suit.getPos()
+ upperPos = suitPos + Point3(0.0, 0.0, offset / 2.0)
+ lowerPos = suitPos + Point3(0.0, 0.0, -offset / 2.0)
+ self.floatSequence = Sequence(LerpPosInterval(self.suit, dur / 4.0, startPos=suitPos, pos=upperPos, blendType='easeInOut'), LerpPosInterval(self.suit, dur / 2.0, startPos=upperPos, pos=lowerPos, blendType='easeInOut'), LerpPosInterval(self.suit, dur / 4.0, startPos=lowerPos, pos=suitPos, blendType='easeInOut'), name='%s.floatSequence%i' % (self.__class__.__name__, self.index))
+
+ def startMoving(self, elapsedTime):
+ CogdoFlyingMinion.startMoving(self, elapsedTime)
+ self.floatSequence.loop(elapsedTime)
+ self.propTrack.loop(elapsedTime)
+ self.suit.pose('landing', 0)
+
+ def stopMoving(self):
+ CogdoFlyingMinion.stopMoving(self)
+ self.floatSequence.clearToInitial()
+ self.propTrack.pause()
+
+ def destroy(self):
+ self.floatSequence.clearToInitial()
+ del self.floatSequence
+ self.propTrack.clearToInitial()
+ del self.propTrack
+ CogdoFlyingMinion.destroy(self)
+
+
+class CogdoFlyingMinionWalking(CogdoFlyingMinion):
+
+ def __init__(self, index, motionPath = None):
+ radius = Globals.Gameplay.WalkingMinionCollisionRadius
+ offset = Globals.Gameplay.WalkingMinionCollisionHeightOffset
+ collSolid = CollisionSphere(0, 0, offset, radius)
+ CogdoFlyingMinion.__init__(self, index, collSolid, motionPath)
+
+ def startMoving(self, elapsedTime):
+ CogdoFlyingMinion.startMoving(self, elapsedTime)
+ self.suit.loop('walk')
+
+ def stopMoving(self):
+ CogdoFlyingMinion.stopMoving(self)
+ self.suit.loop('neutral')
+
+
+class CogdoFlyingFan(CogdoFlyingObstacle):
+
+ def __init__(self, index, model, motionPath = None):
+ collSolid = CollisionTube(0, 0, 0, 0, 0, Globals.Gameplay.FanCollisionTubeHeight, Globals.Gameplay.FanCollisionTubeRadius)
+ CogdoFlyingObstacle.__init__(self, Globals.Level.ObstacleTypes.Fan, index, model, collSolid)
+ self.streamers = self.model.findAllMatches('**/streamer*')
+ self._initIntervals()
+
+ def _initIntervals(self):
+ self.streamerIvals = []
+ minDur = Globals.Gameplay.FanStreamerMinDuration
+ maxDur = Globals.Gameplay.FanStreamerMaxDuration
+ for streamer in self.streamers:
+ dur = random.uniform(minDur, maxDur)
+ streamerLerp = LerpFunc(streamer.setH, fromData=0.0, toData=360.0, duration=dur, name='%s.streamerLerp%i-%s' % (self.__class__.__name__, self.index, streamer.getName()))
+ self.streamerIvals.append(streamerLerp)
+
+ def startMoving(self, elapsedTime = 0.0):
+ CogdoFlyingObstacle.startMoving(self, elapsedTime)
+ timeDelay = 0.0
+ maxDur = Globals.Gameplay.FanStreamerMaxDuration
+ for ival in self.streamerIvals:
+ taskName = 'delayedStreamerSpinTask-fan-%i-%s' % (self.index, ival.getName())
+ taskMgr.doMethodLater(timeDelay, ival.loop, taskName, extraArgs=[])
+ timeDelay += maxDur / (len(self.streamers) - 1)
+
+ def stopMoving(self):
+ CogdoFlyingObstacle.stopMoving(self)
+ taskMgr.removeTasksMatching('delayedStreamerSpinTask-fan-%i*' % self.index)
+ for streamerLerp in self.streamerIvals:
+ streamerLerp.pause()
+
+ def setBlowDirection(self):
+ tempNodePath = NodePath('temp')
+ tempNodePath.reparentTo(self.model)
+ tempNodePath.setPos(0, 0, 1)
+ self.blowVec = tempNodePath.getPos(render) - self.model.getPos(render)
+ self.blowVec.normalize()
+ tempNodePath.removeNode()
+ del tempNodePath
+
+ def getBlowDirection(self):
+ return Vec3(self.blowVec)
+
+ def destroy(self):
+ taskMgr.removeTasksMatching('delayedStreamerSpinTask-fan-%i*' % self.index)
+ for streamerLerp in self.streamerIvals:
+ streamerLerp.clearToInitial()
+
+ del self.streamerIvals[:]
+ CogdoFlyingObstacle.destroy(self)
+ del self.blowVec
diff --git a/toontown/cogdominium/CogdoFlyingPlayer.py b/toontown/cogdominium/CogdoFlyingPlayer.py
new file mode 100644
index 0000000..7c61092
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingPlayer.py
@@ -0,0 +1,365 @@
+from pandac.PandaModules import DepthOffsetAttrib, NodePath, Vec3, Vec4, TextNode
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm.FSM import FSM
+from direct.interval.FunctionInterval import Wait
+from direct.interval.IntervalGlobal import Func, LerpHprInterval, LerpScaleInterval, LerpFunctionInterval
+from direct.interval.MetaInterval import Sequence, Parallel
+from direct.distributed.ClockDelta import globalClockDelta
+from toontown.toonbase import ToontownGlobals
+from toontown.effects import DustCloud
+import CogdoFlyingGameGlobals as Globals
+import CogdoUtil
+from CogdoFlyingObjects import CogdoFlyingGatherable
+from CogdoFlyingUtil import swapAvatarShadowPlacer
+
+class CogdoFlyingPlayer(FSM):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingPlayer')
+
+ def __init__(self, toon):
+ FSM.__init__(self, 'CogdoFlyingPlayer')
+ self.toon = toon
+ self.toon.reparentTo(render)
+ self.legalEaglesTargeting = []
+ self.activeBuffs = []
+ self.initModels()
+ self.initIntervals()
+ self.netTimeSentToStartDeath = 0
+ self.backpackState = -1
+ self.lastBackpackState = -1
+ self.lastPropellerSpinRate = Globals.Gameplay.NormalPropSpeed
+ self.propellerSpinRate = Globals.Gameplay.NormalPropSpeed
+ self.setFuelState(Globals.Gameplay.FuelStates.FuelNoPropeller)
+ self.setOldFuelState(self.fuelState)
+ CogdoFlyingPlayer.setBlades(self, Globals.Gameplay.FuelStates.FuelNoPropeller)
+ self.setBackpackState(Globals.Gameplay.BackpackStates.Normal)
+
+ def initModels(self):
+ self.createPropeller()
+ self.createRedTapeRing()
+
+ def createPropeller(self):
+ self.propellerSmoke = DustCloud.DustCloud(self.toon, wantSound=False)
+ self.propellerSmoke.setBillboardPointEye()
+ self.propellerSmoke.setBin('fixed', 5002)
+ self.backpack = CogdoUtil.loadFlyingModel('propellerPack')
+ self.backpack.setScale(1.3)
+ self.backpack.setHpr(180.0, 0.0, 0.0)
+ self.backpackInstances = []
+ self.backpackTextureCard = CogdoUtil.loadFlyingModel('propellerPack_card')
+ parts = self.toon.getTorsoParts()
+ for part in parts:
+ backpackInstance = part.attachNewNode('backpackInstance')
+ animal = self.toon.style.getAnimal()
+ bodyScale = ToontownGlobals.toonBodyScales[animal]
+ backpackHeight = ToontownGlobals.torsoHeightDict[self.toon.style.torso] * bodyScale - 0.5
+ backpackInstance.setPos(0.0, -0.325, backpackHeight)
+ self.backpackInstances.append(backpackInstance)
+ self.backpack.instanceTo(backpackInstance)
+
+ self.propInstances = []
+ self.propeller = CogdoUtil.loadFlyingModel('toonPropeller')
+ for part in self.backpackInstances:
+ propInstance = part.attachNewNode('propInstance')
+ propInstance.setPos(0.0, -0.275, 0.0)
+ propInstance.setHpr(0.0, 20.0, 0.0)
+ propInstance.setScale(1.0, 1.0, 1.25)
+ self.propInstances.append(propInstance)
+ self.propeller.instanceTo(propInstance)
+
+ self.blades = []
+ self.activeBlades = []
+ index = 1
+ blade = self.propeller.find('**/propeller%d' % index)
+ while not blade.isEmpty():
+ self.blades.append(blade)
+ index += 1
+ blade = self.propeller.find('**/propeller%d' % index)
+
+ for blade in self.blades:
+ self.activeBlades.append(blade)
+
+ def createRedTapeRing(self):
+ self.redTapeRing = CogdoUtil.loadFlyingModel('redTapeRing')
+ self.redTapeRing.setTwoSided(True)
+ self.redTapeRing.reparentTo(self.toon)
+ self.redTapeRing.hide()
+ self.redTapeRing.setScale(1.25)
+ self.redTapeRing.setZ(self.toon.getHeight() / 2.0)
+
+ def initIntervals(self):
+ self.baseSpinDuration = 1.0
+ self.propellerSpinLerp = LerpFunctionInterval(self.propeller.setH, fromData=0.0, toData=360.0, duration=self.baseSpinDuration, name='%s.propellerSpinLerp-%s' % (self.__class__.__name__, self.toon.doId))
+ singleBlinkTime = Globals.Gameplay.TargetedWarningSingleBlinkTime
+ blinkTime = Globals.Gameplay.TargetedWarningBlinkTime
+ self.blinkLoop = Sequence(Wait(singleBlinkTime / 2.0), Func(self.setBackpackTexture, Globals.Gameplay.BackpackStates.Attacked), Wait(singleBlinkTime / 2.0), Func(self.setBackpackTexture, Globals.Gameplay.BackpackStates.Targeted), name='%s.blinkLoop-%s' % (self.__class__.__name__, self.toon.doId))
+ self.blinkWarningSeq = Sequence(Func(self.blinkLoop.loop), Wait(blinkTime), Func(self.blinkLoop.clearToInitial), name='%s.blinkWarningSeq-%s' % (self.__class__.__name__, self.toon.doId))
+ dur = Globals.Gameplay.BackpackRefuelDuration
+ self.refuelSeq = Sequence(Func(self.setPropellerSpinRate, Globals.Gameplay.RefuelPropSpeed), Wait(dur), Func(self.returnBackpackToLastStateFunc), name='%s.refuelSeq-%s' % (self.__class__.__name__, self.toon.doId))
+ scale = self.redTapeRing.getScale()
+ pulseTime = 1.0
+ self.pulseBubbleSeq = Parallel(Sequence(LerpFunctionInterval(self.redTapeRing.setScale, fromData=scale, toData=scale * 1.1, duration=pulseTime / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.redTapeRing.setScale, fromData=scale * 1.1, toData=scale, duration=pulseTime / 2.0, blendType='easeInOut')), LerpHprInterval(self.redTapeRing, pulseTime, Vec3(360, 0, 0), startHpr=Vec3(0, 0, 0)), name='%s.pulseBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
+ bouncePercent = 1.2
+ scaleTime = 0.5
+ scaleBounceTime = 0.25
+ self.popUpBubbleLerp = LerpScaleInterval(self.redTapeRing, scaleTime, scale * bouncePercent, startScale=0.0, blendType='easeInOut')
+ self.popUpBubbleSeq = Sequence(Func(self.updateLerpStartScale, self.popUpBubbleLerp, self.redTapeRing), Func(self.redTapeRing.show), self.popUpBubbleLerp, LerpScaleInterval(self.redTapeRing, scaleBounceTime, scale, startScale=scale * bouncePercent, blendType='easeInOut'), Func(self.pulseBubbleSeq.loop), name='%s.popUpBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
+ self.removeBubbleLerp = LerpScaleInterval(self.redTapeRing, scaleBounceTime, scale * bouncePercent, startScale=scale, blendType='easeInOut')
+ self.removeBubbleSeq = Sequence(Func(self.pulseBubbleSeq.clearToInitial), Func(self.updateLerpStartScale, self.removeBubbleLerp, self.redTapeRing), self.removeBubbleLerp, LerpScaleInterval(self.redTapeRing, scaleTime, 0.0, startScale=scale * bouncePercent, blendType='easeInOut'), Func(self.redTapeRing.hide), name='%s.removeBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
+ self.redTapeRing.setScale(0.0)
+ self.deathInterval = Sequence(Parallel(LerpHprInterval(self.toon, 1.0, Vec3(720, 0, 0)), LerpFunctionInterval(self.toon.setScale, fromData=1.0, toData=0.1, duration=1.0)), Func(self.toon.stash), name='%s.deathInterval-%s' % (self.__class__.__name__, self.toon.doId))
+ self.spawnInterval = Sequence(Func(self.toon.stash), Func(self.resetToon), Wait(1.0), Func(self.toon.setAnimState, 'TeleportIn'), Func(self.toon.unstash), name='%s.spawnInterval-%s' % (self.__class__.__name__, self.toon.doId))
+ singleBlinkTime = Globals.Gameplay.InvulSingleBlinkTime
+ blinkTime = Globals.Gameplay.InvulBlinkTime
+ invulBuffTime = Globals.Gameplay.InvulBuffTime
+ self.blinkBubbleLoop = Sequence(LerpFunctionInterval(self.redTapeRing.setAlphaScale, fromData=1.0, toData=0.0, duration=singleBlinkTime / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.redTapeRing.setAlphaScale, fromData=0.0, toData=1.0, duration=singleBlinkTime / 2.0, blendType='easeInOut'), name='%s.blinkBubbleLoop-%s' % (self.__class__.__name__, self.toon.doId))
+ self.blinkBubbleSeq = Sequence(Wait(invulBuffTime - blinkTime), Func(self.blinkBubbleLoop.loop), Wait(blinkTime), Func(self.blinkBubbleLoop.finish), name='%s.blinkBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
+
+ def returnBackpackToLastStateFunc(self):
+ if self.backpackState == Globals.Gameplay.BackpackStates.Refuel:
+ self.returnBackpackToLastState()
+
+ def setPropellerSpinRateFunc(self):
+ if self.propellerSpinRate == Globals.Gameplay.RefuelPropSpeed:
+ self.setPropellerSpinRate(self.lastPropellerSpinRate)
+
+ def returnBackpackToLastState(self):
+ self.setBackpackState(self.lastBackpackState)
+
+ def setBackpackState(self, state):
+ if state == self.backpackState:
+ return
+ self.lastBackpackState = self.backpackState
+ self.backpackState = state
+ self.blinkWarningSeq.clearToInitial()
+ self.refuelSeq.clearToInitial()
+ self.blinkLoop.clearToInitial()
+ if self.lastBackpackState == Globals.Gameplay.BackpackStates.Refuel:
+ self.setPropellerSpinRateFunc()
+ if state in Globals.Gameplay.BackpackStates:
+ if state == Globals.Gameplay.BackpackStates.Normal:
+ pass
+ elif state == Globals.Gameplay.BackpackStates.Targeted:
+ pass
+ elif state == Globals.Gameplay.BackpackStates.Refuel:
+ self.refuelSeq.start()
+ elif state == Globals.Gameplay.BackpackStates.Attacked:
+ self.blinkWarningSeq.start()
+ self.setBackpackTexture(state)
+
+ def setBackpackTexture(self, state):
+ texName = Globals.Gameplay.BackpackState2TextureName[state]
+ tex = self.backpackTextureCard.findTexture(texName)
+ self.backpack.setTexture(tex, 1)
+
+ def updateLerpStartScale(self, lerp, nodepath):
+ lerp.setStartScale(nodepath.getScale())
+
+ def handleEnterGatherable(self, gatherable, elapsedTime):
+ if gatherable.type == Globals.Level.GatherableTypes.InvulPowerup:
+ self.blinkBubbleSeq.clearToInitial()
+ self.blinkBubbleSeq.start(elapsedTime)
+ self.removeBubbleSeq.clearToInitial()
+ self.popUpBubbleSeq.start()
+ if gatherable.type not in self.activeBuffs:
+ self.activeBuffs.append(gatherable.type)
+ elif gatherable.type == Globals.Level.GatherableTypes.Propeller:
+ self.setBackpackState(Globals.Gameplay.BackpackStates.Refuel)
+
+ def handleDebuffPowerup(self, pickupType, elapsedTime):
+ if pickupType == Globals.Level.GatherableTypes.InvulPowerup:
+ self.blinkBubbleSeq.finish()
+ self.popUpBubbleSeq.clearToInitial()
+ self.removeBubbleSeq.start()
+ if pickupType in self.activeBuffs:
+ self.activeBuffs.remove(pickupType)
+
+ def isBuffActive(self, pickupType):
+ if pickupType in self.activeBuffs:
+ return True
+ return False
+
+ def isInvulnerable(self):
+ if Globals.Level.GatherableTypes.InvulPowerup in self.activeBuffs:
+ return True
+ return False
+
+ def setFuelState(self, fuelState):
+ self.fuelState = fuelState
+
+ def setOldFuelState(self, fuelState):
+ self.oldFuelState = fuelState
+
+ def hasFuelStateChanged(self):
+ if self.fuelState != self.oldFuelState:
+ return True
+ else:
+ return False
+
+ def updatePropellerSmoke(self):
+ if not self.hasFuelStateChanged():
+ return
+ if self.fuelState in [Globals.Gameplay.FuelStates.FuelNoPropeller, Globals.Gameplay.FuelStates.FuelNormal]:
+ self.propellerSmoke.stop()
+ elif self.fuelState in [Globals.Gameplay.FuelStates.FuelVeryLow, Globals.Gameplay.FuelStates.FuelEmpty]:
+ self.propellerSmoke.stop()
+ self.propellerSmoke.setScale(0.25)
+ self.propellerSmoke.setZ(self.toon.getHeight() + 2.5)
+ self.propellerSmoke.loop(rate=48)
+ elif self.fuelState in [Globals.Gameplay.FuelStates.FuelLow]:
+ self.propellerSmoke.stop()
+ self.propellerSmoke.setScale(0.0825)
+ self.propellerSmoke.setZ(self.toon.getHeight() + 2.0)
+ self.propellerSmoke.loop(rate=24)
+
+ def resetBlades(self):
+ self.setBlades(len(self.blades))
+
+ def setAsLegalEagleTarget(self, legalEagle):
+ if legalEagle not in self.legalEaglesTargeting:
+ self.legalEaglesTargeting.append(legalEagle)
+
+ def removeAsLegalEagleTarget(self, legalEagle):
+ if legalEagle in self.legalEaglesTargeting:
+ self.legalEaglesTargeting.remove(legalEagle)
+
+ def isLegalEagleTarget(self):
+ if len(self.legalEaglesTargeting) > 0:
+ return True
+ else:
+ return False
+
+ def setBlades(self, fuelState):
+ if fuelState not in Globals.Gameplay.FuelStates:
+ return
+ numBlades = fuelState - 1
+ if len(self.activeBlades) != numBlades:
+ for i in range(len(self.activeBlades)):
+ blade = self.activeBlades.pop()
+ blade.stash()
+
+ if numBlades > len(self.blades):
+ numBlades = len(self.blades)
+ if numBlades > 0:
+ for i in range(numBlades):
+ blade = self.blades[i]
+ self.activeBlades.append(blade)
+ blade.unstash()
+
+ if fuelState == Globals.Gameplay.FuelStates.FuelNoPropeller:
+ for prop in self.propInstances:
+ prop.hide()
+
+ else:
+ for prop in self.propInstances:
+ prop.show()
+
+ self.setFuelState(fuelState)
+ self.updatePropellerSmoke()
+ self.setOldFuelState(self.fuelState)
+
+ def bladeLost(self):
+ if len(self.activeBlades) > 0:
+ blade = self.activeBlades.pop()
+ blade.stash()
+ self.setFuelState(len(self.activeBlades) + 1)
+ self.updatePropellerSmoke()
+ self.setOldFuelState(self.fuelState)
+
+ def setPropellerSpinRate(self, newRate):
+ self.lastPropellerSpinRate = self.propellerSpinRate
+ self.propellerSpinRate = newRate
+ self.notify.debug('(%s) New propeller speed:%s, old propeller speed:%s' % (self.toon.doId, self.propellerSpinRate, self.lastPropellerSpinRate))
+ self.propellerSpinLerp.setPlayRate(newRate)
+
+ def died(self, elapsedTime):
+ self.deathInterval.start(elapsedTime)
+ self.propellerSmoke.stop()
+
+ def spawn(self, elapsedTime):
+ self.spawnInterval.start(elapsedTime)
+
+ def resetToon(self):
+ self.toon.setScale(1.0)
+
+ def enable(self):
+ self.toon.setAnimState('Happy', 1.0)
+ self.toon.setForceJumpIdle(True)
+ self.toon.setSpeed(0, 0)
+ self.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed)
+ self.propellerSpinLerp.loop()
+
+ def disable(self):
+ pass
+
+ def unload(self):
+ self.ignoreAll()
+ if self.toon:
+ self.toon.showName()
+ self.backpackTextureCard.removeNode()
+ del self.backpackTextureCard
+ self.refuelSeq.clearToInitial()
+ del self.refuelSeq
+ self.pulseBubbleSeq.clearToInitial()
+ del self.pulseBubbleSeq
+ self.blinkBubbleLoop.clearToInitial()
+ del self.blinkBubbleLoop
+ self.blinkBubbleSeq.clearToInitial()
+ del self.blinkBubbleSeq
+ self.popUpBubbleLerp.clearToInitial()
+ del self.popUpBubbleLerp
+ self.popUpBubbleSeq.clearToInitial()
+ del self.popUpBubbleSeq
+ self.removeBubbleLerp.clearToInitial()
+ del self.removeBubbleLerp
+ self.removeBubbleSeq.clearToInitial()
+ del self.removeBubbleSeq
+ self.propellerSmoke.destroy()
+ del self.propellerSmoke
+ self.blinkWarningSeq.clearToInitial()
+ del self.blinkWarningSeq
+ self.blinkLoop.clearToInitial()
+ del self.blinkLoop
+ self.redTapeRing.removeNode()
+ del self.redTapeRing
+ self.propellerSpinLerp.clearToInitial()
+ del self.propellerSpinLerp
+ for prop in self.propInstances:
+ prop.removeNode()
+
+ del self.propInstances[:]
+ self.propeller.removeNode()
+ del self.propeller
+ for backpack in self.backpackInstances:
+ backpack.removeNode()
+
+ del self.backpackInstances[:]
+ self.backpack.removeNode()
+ del self.backpack
+ del self.activeBuffs[:]
+ del self.legalEaglesTargeting[:]
+ del self.toon
+ self.toon = None
+ if self.deathInterval:
+ self.deathInterval.clearToInitial()
+ self.deathInterval = None
+ if self.spawnInterval:
+ self.spawnInterval.clearToInitial()
+ self.spawnInterval = None
+ return
+
+ def start(self):
+ swapAvatarShadowPlacer(self.toon, self.toon.uniqueName('toonShadowPlacer'))
+ self.toon.startSmooth()
+
+ def exit(self):
+ self.toon.setForceJumpIdle(False)
+ self.propellerSmoke.reparentTo(hidden)
+ self.propellerSmoke.stop()
+ if self.toon:
+ CogdoFlyingPlayer.resetToon(self)
+ self.toon.setActiveShadow(0)
+ self.toon.deleteDropShadow()
+ self.toon.initializeDropShadow()
+ self.toon.setActiveShadow(1)
+ else:
+ self.notify.warning("There's no toon in offstage, this is bad!")
diff --git a/toontown/cogdominium/CogdoFlyingShadowPlacer.py b/toontown/cogdominium/CogdoFlyingShadowPlacer.py
new file mode 100644
index 0000000..c7554f2
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingShadowPlacer.py
@@ -0,0 +1,68 @@
+from direct.controls.ControlManager import CollisionHandlerRayStart
+from direct.directnotify import DirectNotifyGlobal
+from pandac.PandaModules import *
+from direct.task.Task import Task
+from direct.showbase.DirectObject import DirectObject
+from direct.showbase.ShadowPlacer import ShadowPlacer
+
+class CogdoFlyingShadowPlacer(ShadowPlacer):
+
+ def __init__(self, cTrav, shadowNodePath, wallCollideMask, floorCollideMask, name):
+ ShadowPlacer.__init__(self, cTrav, shadowNodePath, wallCollideMask, floorCollideMask)
+ self.name = name
+
+ def setup(self, cTrav, shadowNodePath, wallCollideMask, floorCollideMask):
+ if not cTrav:
+ base.initShadowTrav()
+ cTrav = base.shadowTrav
+ self.cTrav = cTrav
+ self.shadowNodePath = shadowNodePath
+ floorOffset = 0.025
+ self.cRay = CollisionRay(0.0, 0.0, 1.0, 0.0, 0.0, -1.0)
+ cRayNode = CollisionNode('shadowPlacer')
+ cRayNode.addSolid(self.cRay)
+ self.cRayNodePath = NodePath(cRayNode)
+ self.cRayBitMask = floorCollideMask
+ cRayNode.setFromCollideMask(self.cRayBitMask)
+ cRayNode.setIntoCollideMask(BitMask32.allOff())
+ self.queue = CollisionHandlerQueue()
+
+ def update(self):
+ self.cTrav.traverse(render)
+ self.queue.sortEntries()
+ if self.queue.getNumEntries() > 0:
+ entry = self.queue.getEntry(0)
+ pos = entry.getSurfacePoint(render)
+ self.shadowNodePath.setPos(render, pos)
+ return Task.cont
+
+ def delete(self):
+ self.off()
+ del self.cTrav
+ del self.shadowNodePath
+ del self.cRay
+ self.cRayNodePath.removeNode()
+ del self.cRayNodePath
+ del self.queue
+
+ def on(self):
+ if self.isActive:
+ return
+ self.cRayNodePath.reparentTo(self.shadowNodePath.getParent())
+ self.cTrav.addCollider(self.cRayNodePath, self.queue)
+ self.isActive = 1
+ taskMgr.add(self.update, 'ShadowPlacer.update.%s' % self.name, -45, extraArgs=[])
+
+ def off(self):
+ if not self.isActive:
+ return
+ didIt = self.cTrav.removeCollider(self.cRayNodePath)
+ self.oneTimeCollide()
+ self.cRayNodePath.detachNode()
+ self.isActive = 0
+ taskMgr.remove('ShadowPlacer.update.%s' % self.name)
+
+ def oneTimeCollide(self):
+ tempCTrav = CollisionTraverser('oneTimeCollide')
+ tempCTrav.addCollider(self.cRayNodePath, self.queue)
+ tempCTrav.traverse(render)
diff --git a/toontown/cogdominium/CogdoFlyingUtil.py b/toontown/cogdominium/CogdoFlyingUtil.py
new file mode 100644
index 0000000..e411e2b
--- /dev/null
+++ b/toontown/cogdominium/CogdoFlyingUtil.py
@@ -0,0 +1,24 @@
+from otp.otpbase import OTPGlobals
+from CogdoFlyingShadowPlacer import CogdoFlyingShadowPlacer
+
+def loadMockup(fileName, dmodelsAlt = 'coffin'):
+ try:
+ model = loader.loadModel(fileName)
+ except IOError:
+ model = loader.loadModel('phase_4/models/props/%s' % dmodelsAlt)
+
+ return model
+
+
+def swapAvatarShadowPlacer(avatar, name):
+ avatar.setActiveShadow(0)
+ avatar.deleteDropShadow()
+ avatar.initializeDropShadow()
+ if avatar.shadowPlacer:
+ avatar.shadowPlacer.delete()
+ avatar.shadowPlacer = None
+ shadowPlacer = CogdoFlyingShadowPlacer(base.shadowTrav, avatar.dropShadow, OTPGlobals.WallBitmask, OTPGlobals.FloorBitmask, name)
+ avatar.shadowPlacer = shadowPlacer
+ avatar.setActiveShadow(0)
+ avatar.setActiveShadow(1)
+ return
diff --git a/toontown/cogdominium/CogdoGameAudioManager.py b/toontown/cogdominium/CogdoGameAudioManager.py
new file mode 100644
index 0000000..9e0242c
--- /dev/null
+++ b/toontown/cogdominium/CogdoGameAudioManager.py
@@ -0,0 +1,130 @@
+from pandac.PandaModules import AudioSound
+from direct.interval.SoundInterval import SoundInterval
+
+class CogdoGameSfx:
+
+ def __init__(self, audioSound, audioMgr, source = None):
+ self._audioSound = audioSound
+ self._audioMgr = audioMgr
+ self._source = source
+
+ def destroy(self):
+ self._audioMgr.removeSound(self._audioSound)
+ del self._audioSound
+ del self._audioMgr
+ del self._source
+
+ def getAudioSound(self):
+ return self._audioSound
+
+ def play(self, loop = False, playRate = 1.0, volume = 1.0, source = None):
+ if source is None:
+ source = self._source
+ self._audioMgr.playSound(self._audioSound, loop=loop, source=source, playRate=playRate, volume=volume)
+ return
+
+ def loop(self, playRate = 1.0, volume = 1.0, source = None):
+ if source is None:
+ source = self._source
+ self.play(loop=True, source=source, playRate=playRate, volume=volume)
+ return
+
+ def stop(self):
+ self._audioMgr.stopSound(self._audioSound)
+
+
+class CogdoGameAudioManager:
+ notify = directNotify.newCategory('CogdoGameAudioManager')
+
+ def __init__(self, musicFiles, sfxFiles, listener, cutoff = 75):
+ self._sfxFiles = sfxFiles
+ self._listener = listener
+ self._cutoff = cutoff
+ self.currentMusic = None
+ self._music = {}
+ for name, filePath in musicFiles.items():
+ self._music[name] = base.loadMusic(filePath)
+
+ self._audioSounds = []
+ self._soundIvals = {}
+ base.cogdoGameAudioMgr = self
+ return
+
+ def destroy(self):
+ del base.cogdoGameAudioMgr
+ self.stopAll()
+ self.currentMusic = None
+ del self.currentMusic
+ self._music.clear()
+ del self._sfxFiles
+ del self._audioSounds
+ return
+
+ def stopMusic(self):
+ if self.currentMusic is not None:
+ self.currentMusic.stop()
+ return
+
+ def playMusic(self, name, loop = True, swap = False):
+ time = 0.0
+ if self.currentMusic is not None:
+ if swap:
+ time = self.currentMusic.getTime()
+ self.stopMusic()
+ self.currentMusic = self._music[name]
+ self.currentMusic.setTime(time)
+ self.currentMusic.setLoop(loop)
+ self.currentMusic.play()
+ return
+
+ def createSfx(self, name, source = None):
+ sound = loader.loadSfx(self._sfxFiles[name])
+ self._audioSounds.append(sound)
+ gameSfx = CogdoGameSfx(sound, self, source)
+ return gameSfx
+
+ def removeSound(self, audioSound):
+ self._audioSounds.remove(audioSound)
+
+ def _cleanupSoundIval(self, audioSound):
+ if audioSound in self._soundIvals:
+ ival = self._soundIvals[audioSound]
+ if ival.isPlaying():
+ ival.finish()
+ del self._soundIvals[audioSound]
+
+ def createSfxIval(self, sfxName, volume = 1.0, duration = 0.0, startTime = 0.0, source = None, cutoff = None):
+ sound = loader.loadSfx(self._sfxFiles[sfxName])
+ self._audioSounds.append(sound)
+ return self._createSoundIval(sound, volume=volume, startTime=startTime, duration=duration, source=source, cutoff=cutoff)
+
+ def _createSoundIval(self, audioSound, volume = 1.0, duration = 0.0, startTime = 0.0, source = None, register = False, cutoff = None):
+ if cutoff == None:
+ cutoff = self._cutoff
+ ival = SoundInterval(audioSound, node=source, duration=duration, startTime=startTime, cutOff=cutoff, seamlessLoop=True, listenerNode=self._listener)
+ return ival
+
+ def playSound(self, audioSound, loop = False, source = None, playRate = 1.0, volume = 1.0):
+ audioSound.setPlayRate(playRate)
+ if source is not None and loop:
+ self._cleanupSoundIval(audioSound)
+ ival = self._createSoundIval(audioSound, volume=volume, source=source)
+ self._soundIvals[audioSound] = ival
+ ival.loop()
+ else:
+ base.playSfx(audioSound, looping=loop, node=source, volume=volume, listener=self._listener, cutoff=self._cutoff)
+ return
+
+ def stopSound(self, audioSound):
+ if audioSound in self._soundIvals:
+ self._cleanupSoundIval(audioSound)
+ elif audioSound.status() == AudioSound.PLAYING:
+ audioSound.stop()
+
+ def stopAllSfx(self):
+ for audioSound in self._audioSounds:
+ self.stopSound(audioSound)
+
+ def stopAll(self):
+ self.stopMusic()
+ self.stopAllSfx()
diff --git a/toontown/cogdominium/CogdoGameConsts.py b/toontown/cogdominium/CogdoGameConsts.py
new file mode 100644
index 0000000..474d3ec
--- /dev/null
+++ b/toontown/cogdominium/CogdoGameConsts.py
@@ -0,0 +1,13 @@
+from toontown.minigame.MinigameGlobals import getSafezoneId, DifficultyOverrideMult, QuantizeDifficultyOverride, NoDifficultyOverride, getDifficulty
+from toontown.minigame.MinigameGlobals import NoTrolleyZoneOverride as NoExteriorZoneOverride
+MaxPlayers = 4
+MessageLabelBlinkTime = 0.25
+MessageLabelFadeTime = 0.25
+MemoGuiTextScale = 0.11
+MemoGuiTextColor = (1, 1, 0, 1)
+ExitDoorMoveDuration = 1.0
+LaffRewardMin = 25
+LaffRewardRange = 112
+LaffPenalty = 50
+PenthouseElevatorInPath = '**/elevatorIN_node'
+PenthouseElevatorOutPath = '**/elevatorOUT_node'
diff --git a/toontown/cogdominium/CogdoGameExit.py b/toontown/cogdominium/CogdoGameExit.py
new file mode 100644
index 0000000..5f618ba
--- /dev/null
+++ b/toontown/cogdominium/CogdoGameExit.py
@@ -0,0 +1,123 @@
+from pandac.PandaModules import NodePath, Point3
+from direct.interval.MetaInterval import Parallel, Sequence
+from direct.interval.SoundInterval import SoundInterval
+from direct.interval.FunctionInterval import Wait, Func
+from toontown.building import ElevatorConstants
+from toontown.building import ElevatorUtils
+import CogdoUtil
+import CogdoGameConsts
+
+class CogdoGameExit(NodePath):
+
+ def __init__(self, openSfx = None, closeSfx = None):
+ NodePath.__init__(self, 'CogdoGameExit')
+ self._model = CogdoUtil.loadModel('exitDoor')
+ self._model.reparentTo(self)
+ self._leftDoor = self._model.find('**/left_door')
+ self._rightDoor = self._model.find('**/right_door')
+ self._openSfx = openSfx or base.loadSfx('phase_9/audio/sfx/CHQ_VP_door_open.mp3')
+ self._closeSfx = closeSfx or base.loadSfx('phase_9/audio/sfx/CHQ_VP_door_close.mp3')
+ self._elevatorPoints = []
+ for point in ElevatorConstants.ElevatorPoints:
+ self._elevatorPoints.append(point[0])
+
+ self._currentSlot = 0
+ self._ival = None
+ self._open = True
+ self._toon2track = {}
+ self.close(animate=False)
+ return
+
+ def destroy(self):
+ self._cleanToonTracks()
+ if self._ival is not None:
+ self._ival.clearToInitial()
+ del self._ival
+ self._model.removeNode()
+ del self._leftDoor
+ del self._rightDoor
+ del self._model
+ del self._openSfx
+ del self._closeSfx
+ del self._elevatorPoints
+ return
+
+ def isOpen(self):
+ return self._open
+
+ def uniqueName(self, name):
+ return self.getName() + name
+
+ def open(self, animate = True):
+ if self._open:
+ return
+ if animate:
+ self._finishIval()
+ self._ival = Sequence(Parallel(SoundInterval(self._closeSfx), self._leftDoor.posInterval(self.getOpenCloseDuration(), ElevatorUtils.getLeftOpenPoint(ElevatorConstants.ELEVATOR_NORMAL), startPos=ElevatorUtils.getLeftClosePoint(ElevatorConstants.ELEVATOR_NORMAL), blendType='easeInOut'), self._rightDoor.posInterval(self.getOpenCloseDuration(), ElevatorUtils.getRightOpenPoint(ElevatorConstants.ELEVATOR_NORMAL), startPos=ElevatorUtils.getRightClosePoint(ElevatorConstants.ELEVATOR_NORMAL), blendType='easeInOut')))
+ self._ival.start()
+ else:
+ ElevatorUtils.openDoors(self._leftDoor, self._rightDoor, type=ElevatorConstants.ELEVATOR_NORMAL)
+ self._open = True
+
+ def getOpenCloseDuration(self):
+ return CogdoGameConsts.ExitDoorMoveDuration
+
+ def close(self, animate = True):
+ if not self._open:
+ return
+ if animate:
+ self._finishIval()
+ self._ival = Sequence(Parallel(SoundInterval(self._closeSfx), self._leftDoor.posInterval(self.getOpenCloseDuration(), ElevatorUtils.getLeftClosePoint(ElevatorConstants.ELEVATOR_NORMAL), startPos=ElevatorUtils.getLeftOpenPoint(ElevatorConstants.ELEVATOR_NORMAL), blendType='easeIn'), self._rightDoor.posInterval(self.getOpenCloseDuration(), ElevatorUtils.getRightClosePoint(ElevatorConstants.ELEVATOR_NORMAL), startPos=ElevatorUtils.getRightOpenPoint(ElevatorConstants.ELEVATOR_NORMAL), blendType='easeIn')))
+ self._ival.start()
+ else:
+ ElevatorUtils.closeDoors(self._leftDoor, self._rightDoor, type=ElevatorConstants.ELEVATOR_NORMAL)
+ self._open = False
+
+ def _finishIval(self):
+ if self._ival is not None and self._ival.isPlaying():
+ self._ival.finish()
+ return
+
+ def toonEnters(self, toon, goInside = True):
+ self._runToonThroughSlot(toon, self._currentSlot, goInside=goInside)
+ self._currentSlot += 1
+ if self._currentSlot > len(self._elevatorPoints):
+ self._currentSlot = 0
+
+ def _runToonThroughSlot(self, toon, slot, goInside = True):
+ helperNode = NodePath('helper')
+ helperNode.reparentTo(toon.getParent())
+ helperNode.lookAt(self)
+ lookAtH = helperNode.getH(self._model)
+ toonH = toon.getH(self._model)
+ hDiff = abs(lookAtH - toonH)
+ distanceFromElev = toon.getDistance(self._model)
+ moveSpeed = 9.778
+ anim = 'run'
+ if toon.animFSM.getCurrentState() == 'Sad':
+ moveSpeed *= 0.5
+ anim = 'sad-walk'
+ runInsideDistance = 20
+ track = Sequence(Func(toon.stopSmooth), Func(toon.loop, anim, 1.0), Parallel(toon.hprInterval(hDiff / 360.0, Point3(lookAtH, 0, 0), other=self._model, blendType='easeIn'), toon.posInterval(distanceFromElev / moveSpeed, Point3(self._elevatorPoints[slot], 0, 0), other=self._model, blendType='easeIn')), name=toon.uniqueName('runThroughExit'), autoPause=1)
+ if goInside:
+ track.append(Parallel(toon.hprInterval(lookAtH / 360.0, Point3(0, 0, 0), other=self._model, blendType='easeOut'), toon.posInterval(runInsideDistance / moveSpeed, Point3(self._elevatorPoints[slot], runInsideDistance, 0), other=self._model, blendType='easeOut')))
+ track.append(Func(self._clearToonTrack, toon))
+ track.append(Func(toon.setAnimState, 'Happy', 1.0))
+ self._storeToonTrack(toon, track)
+ track.start()
+
+ def _cleanToonTracks(self):
+ toons = [ toon for toon in self._toon2track ]
+ for toon in toons:
+ self._clearToonTrack(toon)
+
+ def _clearToonTrack(self, toon):
+ oldTrack = self._toon2track.get(toon)
+ if oldTrack is not None:
+ oldTrack.pause()
+ del self._toon2track[toon]
+ return
+
+ def _storeToonTrack(self, toon, track):
+ self._clearToonTrack(toon)
+ self._toon2track[toon] = track
diff --git a/toontown/cogdominium/CogdoGameGatherable.py b/toontown/cogdominium/CogdoGameGatherable.py
new file mode 100644
index 0000000..ef5ebd4
--- /dev/null
+++ b/toontown/cogdominium/CogdoGameGatherable.py
@@ -0,0 +1,123 @@
+from pandac.PandaModules import CollisionSphere, CollisionNode
+from pandac.PandaModules import NodePath, BitMask32
+from direct.showbase.DirectObject import DirectObject
+from direct.interval.MetaInterval import Sequence
+from direct.interval.FunctionInterval import Func, Wait
+from direct.interval.IntervalGlobal import LerpFunc
+from toontown.toonbase import ToontownGlobals
+import CogdoUtil
+
+class CogdoGameGatherable(NodePath, DirectObject):
+ EnterEventName = 'CogdoGameGatherable_Enter'
+
+ def __init__(self, serialNum, model, triggerRadius, triggerOffset = (0, 0, 0), animate = True, animDuration = 0.2, instanceModel = True, name = 'CogdoGameGatherable'):
+ NodePath.__init__(self, '%s-%d' % (name, serialNum))
+ self.serialNum = serialNum
+ self._animate = animate
+ if instanceModel:
+ model.instanceTo(self)
+ self._model = self
+ else:
+ self._model = model
+ self._model.reparentTo(self)
+ self._model.setPosHpr(0, 0, 0, 0, 0, 0)
+ self._animDuration = animDuration
+ self._animSeq = None
+ self._initCollisions(triggerRadius, triggerOffset)
+ self._update = None
+ self._wasPickedUp = False
+ return
+
+ def _initCollisions(self, triggerRadius, triggerOffset):
+ self.collSphere = CollisionSphere(triggerOffset[0], triggerOffset[1], triggerOffset[2], triggerRadius)
+ self.collSphere.setTangible(0)
+ self.collNode = CollisionNode(self.getName())
+ self.collNode.addSolid(self.collSphere)
+ self.collNodePath = self.attachNewNode(self.collNode)
+
+ def destroy(self):
+ self.disable()
+ del self._model
+ if self._animSeq is not None:
+ self._animSeq.finish()
+ self._animSeq = None
+ self.collNodePath.removeNode()
+ self.removeNode()
+ return
+
+ def enable(self):
+ self.accept('enter' + self.getName(), self._handleEnterCollision)
+ self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
+
+ def disable(self):
+ self.ignoreAll()
+ self.collNode.setIntoCollideMask(BitMask32(0))
+
+ def show(self):
+ if not self.wasPickedUp():
+ NodePath.show(self)
+ self.enable()
+
+ def hide(self):
+ self.disable()
+ NodePath.hide(self)
+
+ def _handleEnterCollision(self, collEntry):
+ messenger.send(CogdoGameGatherable.EnterEventName, [self])
+
+ def wasPickedUp(self):
+ return self._wasPickedUp
+
+ def wasPickedUpByToon(self):
+ pass
+
+ def update(self, dt):
+ pass
+
+ def getModel(self):
+ return self._model
+
+ def pickUp(self, toon, elapsedSeconds = 0.0):
+ self._wasPickedUp = True
+ if self._animSeq is not None:
+ self._animSeq.finish()
+ self._animSeq = None
+ if self._animate:
+
+ def lerpFlyToToon(t):
+ vec = toon.getPos(render) - self.getPos(render)
+ vec[2] += toon.getHeight()
+ self.setPos(self.getPos() + vec * t)
+ self.setScale(1.0 - t * 0.8)
+
+ self._animSeq = Sequence(LerpFunc(lerpFlyToToon, fromData=0.0, toData=1.0, duration=self._animDuration), Wait(0.1), Func(self.hide))
+ self._animSeq.start(elapsedSeconds)
+ else:
+ self.hide()
+ return
+
+
+class CogdoMemo(CogdoGameGatherable):
+ EnterEventName = 'CogdoMemo_Enter'
+
+ def __init__(self, serialNum, model = None, pitch = 0, triggerRadius = 1.0, spinRate = 60):
+ if model is None:
+ node = CogdoUtil.loadModel('memo', 'shared')
+ model = node.find('**/memo')
+ model.detachNode()
+ node.removeNode()
+ model.setP(pitch)
+ self._spinRate = spinRate
+ CogdoGameGatherable.__init__(self, serialNum, model, triggerRadius, name='CogdoMemo')
+ return
+
+ def destroy(self):
+ del self._spinRate
+ CogdoGameGatherable.destroy(self)
+
+ def update(self, dt):
+ CogdoGameGatherable.update(self, dt)
+ self.setH(self.getH() + self._spinRate * dt)
+
+ def _handleEnterCollision(self, collEntry):
+ messenger.send(CogdoMemo.EnterEventName, [self])
diff --git a/toontown/cogdominium/CogdoGameMessageDisplay.py b/toontown/cogdominium/CogdoGameMessageDisplay.py
new file mode 100644
index 0000000..af7c2fb
--- /dev/null
+++ b/toontown/cogdominium/CogdoGameMessageDisplay.py
@@ -0,0 +1,76 @@
+from direct.interval.FunctionInterval import Func
+from direct.interval.LerpInterval import LerpFunc
+from direct.interval.MetaInterval import Sequence
+from pandac.PandaModules import TextNode
+from toontown.toonbase import ToontownGlobals
+import CogdoGameConsts
+
+class CogdoGameMessageDisplay:
+ UpdateMessageTaskName = 'MessageDisplay.updateMessage'
+
+ def __init__(self, name, parent, pos = (0.0, 0.0, -0.5), scale = 0.09, color = (1.0, 1.0, 0, 1), sfx = None):
+ self.color = color
+ self._displaySfx = sfx
+ textNode = TextNode('messageLabel.' + name)
+ textNode.setTextColor(self.color)
+ textNode.setAlign(TextNode.ACenter)
+ textNode.setFont(ToontownGlobals.getSignFont())
+ textNode.setShadow(0.06, 0.06)
+ textNode.setShadowColor(0.5, 0.5, 0.5, 1.0)
+ self.pos = pos
+ self.scale = scale
+ self.messageLabel = parent.attachNewNode(textNode)
+ self.messageLabel.setPos(self.pos)
+ self.messageLabel.setScale(self.scale)
+ self.messageLabel.stash()
+ self.transitionInterval = Sequence(name='%s.transitionInterval' % self.__class__.__name__)
+
+ def destroy(self):
+ taskMgr.remove(CogdoGameMessageDisplay.UpdateMessageTaskName)
+ self.transitionInterval.finish()
+ self.transitionInterval.clearIntervals()
+ del self.transitionInterval.pythonIvals[:]
+ del self.transitionInterval[:]
+ self.messageLabel.removeNode()
+ del self.messageLabel
+ if self._displaySfx != None:
+ del self._displaySfx
+ return
+
+ def updateMessage(self, message = '', color = None, transition = 'fade'):
+ taskMgr.remove(CogdoGameMessageDisplay.UpdateMessageTaskName)
+ if color is None:
+ color = self.color
+ self.transitionInterval.finish()
+ self.transitionInterval.clearIntervals()
+ del self.transitionInterval.pythonIvals[:]
+ del self.transitionInterval[:]
+ if message == '':
+ if transition in ('fade', 'blink'):
+ self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=1.0, toData=0.0, duration=CogdoGameConsts.MessageLabelFadeTime, extraArgs=[]))
+ self.transitionInterval.append(Func(self.messageLabel.stash))
+ else:
+ if self.messageLabel.isStashed():
+ self.transitionInterval.append(Func(self.messageLabel.setAlphaScale, 0.0))
+ self.transitionInterval.append(Func(self.messageLabel.unstash))
+ elif transition in ('fade', 'blink'):
+ self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=1.0, toData=0.0, duration=CogdoGameConsts.MessageLabelFadeTime, extraArgs=[]))
+ self.transitionInterval.append(Func(self.messageLabel.setPos, self.pos))
+ self.transitionInterval.append(Func(self.messageLabel.node().setText, message))
+ self.transitionInterval.append(Func(self.messageLabel.node().setTextColor, color))
+ if self._displaySfx != None:
+ self.transitionInterval.append(Func(self._displaySfx.play))
+ if transition == 'fade':
+ self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=0.0, toData=1.0, duration=CogdoGameConsts.MessageLabelFadeTime, extraArgs=[]))
+ elif transition == 'blink':
+ self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=0.0, toData=1.0, duration=CogdoGameConsts.MessageLabelBlinkTime, extraArgs=[]))
+ self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=1.0, toData=0.0, duration=CogdoGameConsts.MessageLabelBlinkTime, extraArgs=[]))
+ self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=0.0, toData=1.0, duration=CogdoGameConsts.MessageLabelBlinkTime, extraArgs=[]))
+ else:
+ self.transitionInterval.append(Func(self.messageLabel.setAlphaScale, 1.0))
+ self.transitionInterval.start()
+ return
+
+ def showMessageTemporarily(self, message = '', duration = 3.0, color = None):
+ self.updateMessage(message, color)
+ taskMgr.doMethodLater(duration, self.updateMessage, CogdoGameMessageDisplay.UpdateMessageTaskName, extraArgs=[])
diff --git a/toontown/cogdominium/CogdoGameRulesPanel.py b/toontown/cogdominium/CogdoGameRulesPanel.py
new file mode 100644
index 0000000..3f4555e
--- /dev/null
+++ b/toontown/cogdominium/CogdoGameRulesPanel.py
@@ -0,0 +1,67 @@
+from direct.task import Task
+from direct.fsm import StateData
+from toontown.toonbase.ToontownGlobals import *
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from toontown.toonbase import ToontownTimer
+from toontown.toonbase import TTLocalizer
+from toontown.minigame import MinigameGlobals
+
+class CogdoGameRulesPanel(StateData.StateData):
+ hiddenNode = NodePath('hiddenNode')
+
+ def __init__(self, panelName, gameTitle, instructions, doneEvent, timeout = MinigameGlobals.rulesDuration):
+ StateData.StateData.__init__(self, doneEvent)
+ self.gameTitle = gameTitle
+ self.instructions = instructions
+ self.TIMEOUT = timeout
+
+ def load(self):
+ minigameGui = loader.loadModel('phase_5/models/cogdominium/tt_m_gui_csa_flyThru')
+ self.bg = minigameGui.find('**/background')
+ self.chatBubble = minigameGui.find('**/chatBubble')
+ self.chatBubble.setScale(6.5, 6.5, 7.3)
+ self.chatBubble.setPos(0.32, 0, -0.78)
+ self.bg.setScale(5.2)
+ self.bg.setPos(0.14, 0, -0.6667)
+ self.bg.reparentTo(aspect2d)
+ self.chatBubble.reparentTo(aspect2d)
+ self.frame = DirectFrame(geom=self.bg, relief=None, pos=(0.2, 0, -0.6667))
+ self.gameTitleText = DirectLabel(parent=self.frame, text=self.gameTitle, scale=TTLocalizer.CRPgameTitleText, text_align=TextNode.ACenter, text_font=getSignFont(), text_fg=(1.0, 0.33, 0.33, 1.0), pos=TTLocalizer.CRPgameTitleTextPos, relief=None)
+ self.instructionsText = DirectLabel(parent=self.frame, text=self.instructions, scale=TTLocalizer.MRPinstructionsText, text_align=TextNode.ACenter, text_wordwrap=TTLocalizer.MRPinstructionsTextWordwrap, pos=TTLocalizer.MRPinstructionsTextPos, relief=None)
+ self.playButton = DirectButton(parent=self.frame, relief=None, geom=(minigameGui.find('**/buttonUp'), minigameGui.find('**/buttonDown'), minigameGui.find('**/buttonHover')), pos=(0.74, 0, 0.1), scale=(4.2, 5, 5), command=self.playCallback)
+ minigameGui.removeNode()
+ self.timer = ToontownTimer.ToontownTimer()
+ self.timer.reparentTo(self.frame)
+ self.timer.setScale(0.4)
+ self.timer.setPos(0.997, 0, 1.5)
+ self.frame.hide()
+ return
+
+ def unload(self):
+ self.frame.destroy()
+ del self.frame
+ del self.gameTitleText
+ del self.instructionsText
+ self.playButton.destroy()
+ del self.playButton
+ del self.timer
+ self.bg.reparentTo(self.hiddenNode)
+ del self.bg
+ self.chatBubble.reparentTo(self.hiddenNode)
+ del self.chatBubble
+
+ def enter(self):
+ self.frame.show()
+ self.timer.countdown(self.TIMEOUT, self.playCallback)
+ self.accept('enter', self.playCallback)
+
+ def exit(self):
+ self.frame.hide()
+ self.timer.stop()
+ self.ignore('enter')
+ self.bg.hide()
+ self.chatBubble.hide()
+
+ def playCallback(self):
+ messenger.send(self.doneEvent)
diff --git a/toontown/cogdominium/CogdoInterior.py b/toontown/cogdominium/CogdoInterior.py
new file mode 100644
index 0000000..54c724d
--- /dev/null
+++ b/toontown/cogdominium/CogdoInterior.py
@@ -0,0 +1,234 @@
+from pandac.PandaModules import ModelPool, TexturePool
+from direct.task.Task import Task
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from toontown.hood import Place
+from toontown.toonbase.ToonBaseGlobal import *
+from toontown.town import TownBattle
+from toontown.suit import Suit
+from toontown.building import Elevator
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import ToontownBattleGlobals
+
+class CogdoInterior(Place.Place):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CogdoInterior')
+
+ def __init__(self, loader, parentFSM, doneEvent):
+ Place.Place.__init__(self, loader, doneEvent)
+ self.fsm = ClassicFSM.ClassicFSM('CogdoInterior', [State.State('entrance', self.enterEntrance, self.exitEntrance, ['Game', 'walk']),
+ State.State('Elevator', self.enterElevator, self.exitElevator, ['Game',
+ 'battle',
+ 'walk',
+ 'crane']),
+ State.State('Game', self.enterGame, self.exitGame, ['battle',
+ 'died',
+ 'crane',
+ 'walk']),
+ State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'died']),
+ State.State('crane', self.enterCrane, self.exitCrane, ['walk',
+ 'battle',
+ 'finalBattle',
+ 'died',
+ 'ouch',
+ 'squished']),
+ State.State('walk', self.enterWalk, self.exitWalk, ['stickerBook',
+ 'stopped',
+ 'battle',
+ 'sit',
+ 'died',
+ 'teleportOut',
+ 'Elevator',
+ 'crane',
+ 'DFA',
+ 'trialerFA']),
+ State.State('sit', self.enterSit, self.exitSit, ['walk']),
+ State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk',
+ 'stopped',
+ 'sit',
+ 'died',
+ 'DFA',
+ 'trialerFA',
+ 'teleportOut',
+ 'Elevator']),
+ State.State('trialerFA', self.enterTrialerFA, self.exitTrialerFA, ['trialerFAReject', 'DFA']),
+ State.State('trialerFAReject', self.enterTrialerFAReject, self.exitTrialerFAReject, ['walk']),
+ State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject', 'teleportOut']),
+ State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walk']),
+ State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']),
+ State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn']),
+ State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'elevatorOut', 'battle']),
+ State.State('died', self.enterDied, self.exitDied, []),
+ State.State('elevatorOut', self.enterElevatorOut, self.exitElevatorOut, [])], 'entrance', 'elevatorOut')
+ self.parentFSM = parentFSM
+ self.elevatorDoneEvent = 'elevatorDoneSI'
+ self.currentFloor = 0
+
+ def enter(self, requestStatus):
+ self.fsm.enterInitialState()
+ self.zoneId = requestStatus['zoneId']
+ self.accept('DSIDoneEvent', self.handleDSIDoneEvent)
+
+ def exit(self):
+ self.ignoreAll()
+
+ def load(self):
+ Place.Place.load(self)
+ self.parentFSM.getStateNamed('cogdoInterior').addChild(self.fsm)
+ self.townBattle = TownBattle.TownBattle('town-battle-done')
+ self.townBattle.load()
+ for i in range(1, 3):
+ Suit.loadSuits(i)
+
+ def unload(self):
+ Place.Place.unload(self)
+ self.parentFSM.getStateNamed('cogdoInterior').removeChild(self.fsm)
+ del self.parentFSM
+ del self.fsm
+ self.ignoreAll()
+ ModelPool.garbageCollect()
+ TexturePool.garbageCollect()
+ self.townBattle.unload()
+ self.townBattle.cleanup()
+ del self.townBattle
+ for i in range(1, 3):
+ Suit.unloadSuits(i)
+
+ def setState(self, state, battleEvent = None):
+ if battleEvent:
+ self.fsm.request(state, [battleEvent])
+ else:
+ self.fsm.request(state)
+
+ def getZoneId(self):
+ return self.zoneId
+
+ def enterZone(self, zoneId):
+ pass
+
+ def isPeriodTimerEffective(self):
+ return 0
+
+ def handleDSIDoneEvent(self, requestStatus):
+ self.doneStatus = requestStatus
+ messenger.send(self.doneEvent)
+
+ def doRequestLeave(self, requestStatus):
+ self.fsm.request('trialerFA', [requestStatus])
+
+ def enterEntrance(self):
+ pass
+
+ def exitEntrance(self):
+ pass
+
+ def enterElevator(self, distElevator):
+ self.accept(self.elevatorDoneEvent, self.handleElevatorDone)
+ self.elevator = Elevator.Elevator(self.fsm.getStateNamed('Elevator'), self.elevatorDoneEvent, distElevator)
+ self.elevator.load()
+ self.elevator.enter()
+ base.localAvatar.cantLeaveGame = 1
+
+ def exitElevator(self):
+ base.localAvatar.cantLeaveGame = 0
+ self.ignore(self.elevatorDoneEvent)
+ self.elevator.unload()
+ self.elevator.exit()
+ del self.elevator
+ return None
+
+ def detectedElevatorCollision(self, distElevator):
+ self.fsm.request('Elevator', [distElevator])
+ return None
+
+ def handleElevatorDone(self, doneStatus):
+ self.notify.debug('handling elevator done event')
+ where = doneStatus['where']
+ if where == 'reject':
+ if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen():
+ pass
+ else:
+ self.fsm.request('walk')
+ elif where == 'exit':
+ self.fsm.request('walk')
+ elif where == 'cogdoInterior':
+ pass
+ else:
+ self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone')
+
+ def enterGame(self):
+ base.localAvatar.setTeleportAvailable(0)
+ base.localAvatar.laffMeter.start()
+
+ def exitGame(self):
+ base.localAvatar.laffMeter.stop()
+
+ def enterBattle(self, event):
+ mult = ToontownBattleGlobals.getCreditMultiplier(self.currentFloor)
+ self.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult)
+ base.localAvatar.b_setAnimState('off', 1)
+ base.localAvatar.cantLeaveGame = 1
+
+ def exitBattle(self):
+ self.townBattle.exit()
+ base.localAvatar.cantLeaveGame = 0
+
+ def enterCrane(self):
+ base.localAvatar.setTeleportAvailable(0)
+ base.localAvatar.laffMeter.start()
+ base.localAvatar.collisionsOn()
+
+ def exitCrane(self):
+ base.localAvatar.collisionsOff()
+ base.localAvatar.laffMeter.stop()
+
+ def enterWalk(self, teleportIn = 0):
+ Place.Place.enterWalk(self, teleportIn)
+ self.ignore('teleportQuery')
+ base.localAvatar.setTeleportAvailable(0)
+
+ def enterStickerBook(self, page = None):
+ Place.Place.enterStickerBook(self, page)
+ self.ignore('teleportQuery')
+ base.localAvatar.setTeleportAvailable(0)
+
+ def enterSit(self):
+ Place.Place.enterSit(self)
+ self.ignore('teleportQuery')
+ base.localAvatar.setTeleportAvailable(0)
+
+ def enterTeleportIn(self, requestStatus):
+ base.localAvatar.setPosHpr(2.5, 11.5, ToontownGlobals.FloorOffset, 45.0, 0.0, 0.0)
+ Place.Place.enterTeleportIn(self, requestStatus)
+
+ def enterTeleportOut(self, requestStatus):
+ Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone)
+
+ def __teleportOutDone(self, requestStatus):
+ hoodId = requestStatus['hoodId']
+ if hoodId == ToontownGlobals.MyEstate:
+ self.getEstateZoneAndGoHome(requestStatus)
+ else:
+ messenger.send('localToonLeft')
+ self.doneStatus = requestStatus
+ messenger.send(self.doneEvent)
+
+ def exitTeleportOut(self):
+ Place.Place.exitTeleportOut(self)
+
+ def goHomeFailed(self, task):
+ self.notifyUserGoHomeFailed()
+ self.ignore('setLocalEstateZone')
+ self.doneStatus['avId'] = -1
+ self.doneStatus['zoneId'] = self.getZoneId()
+ self.fsm.request('teleportIn', [self.doneStatus])
+ return Task.done
+
+ def enterElevatorOut(self):
+ return None
+
+ def __elevatorOutDone(self, requestStatus):
+ self.doneStatus = requestStatus
+ messenger.send(self.doneEvent)
+
+ def exitElevatorOut(self):
+ return None
diff --git a/toontown/cogdominium/CogdoLayout.py b/toontown/cogdominium/CogdoLayout.py
new file mode 100644
index 0000000..39dccfc
--- /dev/null
+++ b/toontown/cogdominium/CogdoLayout.py
@@ -0,0 +1,24 @@
+from direct.directnotify import DirectNotifyGlobal
+
+class CogdoLayout:
+ notify = DirectNotifyGlobal.directNotify.newCategory('CogdoLayout')
+
+ def __init__(self, numFloors):
+ self._numFloors = numFloors
+
+ def getNumGameFloors(self):
+ return self._numFloors
+
+ def hasBossBattle(self):
+ return self._numFloors >= 1
+
+ def getNumFloors(self):
+ if self.hasBossBattle():
+ return self._numFloors + 1
+ else:
+ return self._numFloors
+
+ def getBossBattleFloor(self):
+ if not self.hasBossBattle():
+ self.notify.error('getBossBattleFloor(): cogdo has no boss battle')
+ return self.getNumFloors() - 1
diff --git a/toontown/cogdominium/CogdoLevelGameBase.py b/toontown/cogdominium/CogdoLevelGameBase.py
new file mode 100644
index 0000000..cc63b5b
--- /dev/null
+++ b/toontown/cogdominium/CogdoLevelGameBase.py
@@ -0,0 +1,38 @@
+from direct.fsm.StatePush import FunctionCall
+from otp.level.EntityStateVarSet import EntityStateVarSet
+from otp.level.LevelSpec import LevelSpec
+
+class CogdoLevelGameBase:
+
+ def getLevelSpec(self):
+ return LevelSpec(self.getSpec())
+
+ if __dev__:
+
+ def startHandleEdits(self):
+ fcs = []
+ Consts = self.getConsts()
+ for item in Consts.__dict__.itervalues():
+ if isinstance(item, EntityStateVarSet):
+ for attribName in item._getAttributeNames():
+ handler = getattr(self, '_handle%sChanged' % attribName, None)
+ if handler:
+ stateVar = getattr(item, attribName)
+ fcs.append(FunctionCall(handler, stateVar))
+
+ self._functionCalls = fcs
+ return
+
+ def stopHandleEdits(self):
+ if __dev__:
+ for fc in self._functionCalls:
+ fc.destroy()
+
+ self._functionCalls = None
+ return
+
+ def getEntityTypeReg(self):
+ import CogdoEntityTypes
+ from otp.level import EntityTypeRegistry
+ typeReg = EntityTypeRegistry.EntityTypeRegistry(CogdoEntityTypes)
+ return typeReg
diff --git a/toontown/cogdominium/CogdoLevelMgr.py b/toontown/cogdominium/CogdoLevelMgr.py
new file mode 100644
index 0000000..92a534d
--- /dev/null
+++ b/toontown/cogdominium/CogdoLevelMgr.py
@@ -0,0 +1,6 @@
+from otp.level import LevelMgr
+from direct.showbase.PythonUtil import Functor
+from toontown.toonbase import ToontownGlobals
+
+class CogdoLevelMgr(LevelMgr.LevelMgr):
+ pass
diff --git a/toontown/cogdominium/CogdoLevelMgrAI.py b/toontown/cogdominium/CogdoLevelMgrAI.py
new file mode 100644
index 0000000..9bb6f7a
--- /dev/null
+++ b/toontown/cogdominium/CogdoLevelMgrAI.py
@@ -0,0 +1,4 @@
+from otp.level import LevelMgrAI
+
+class CogdoLevelMgrAI(LevelMgrAI.LevelMgrAI):
+ pass
diff --git a/toontown/cogdominium/CogdoMaze.py b/toontown/cogdominium/CogdoMaze.py
new file mode 100644
index 0000000..281accf
--- /dev/null
+++ b/toontown/cogdominium/CogdoMaze.py
@@ -0,0 +1,312 @@
+from pandac.PandaModules import NodePath, VBase4
+from direct.showbase.DirectObject import DirectObject
+from direct.showbase.RandomNumGen import RandomNumGen
+from toontown.minigame.MazeBase import MazeBase
+import CogdoMazeGameGlobals as Globals
+from CogdoMazeGameObjects import CogdoMazeWaterCooler
+import CogdoMazeData
+import CogdoUtil
+
+class CogdoMaze(MazeBase, DirectObject):
+
+ def __init__(self, model, data, cellWidth):
+ MazeBase.__init__(self, model, data, cellWidth)
+ self._initWaterCoolers()
+ self.elevatorPos = self.maze.find('**/elevator_loc').getPos(render)
+ self.exitPos = self.maze.find('**/exit_loc').getPos(render)
+ self.maze.flattenStrong()
+ self._clearColor = VBase4(base.win.getClearColor())
+ self._clearColor.setW(1.0)
+ base.win.setClearColor(VBase4(0.0, 0.0, 0.0, 1.0))
+ if __debug__ and base.config.GetBool('cogdomaze-dev', False):
+ self._initCollisionVisuals()
+
+ def _initWaterCoolers(self):
+ self._waterCoolers = []
+ self._waterCoolerRoot = NodePath('WaterCoolerRoot')
+ self._waterCoolerRoot.reparentTo(render)
+ models = []
+ for model in self.maze.findAllMatches('**/*waterCooler'):
+ model.wrtReparentTo(render)
+ models.append((model.getPos(self.maze), model.getHpr(self.maze), model))
+
+ models.sort()
+ i = 0
+ for pos, hpr, model in models:
+ wc = CogdoMazeWaterCooler(i, model)
+ wc.wrtReparentTo(self._waterCoolerRoot)
+ wc.setPos(pos)
+ wc.setHpr(hpr)
+ self._waterCoolers.append(wc)
+ i += 1
+
+ self._waterCoolerRoot.stash()
+
+ def getWaterCoolers(self):
+ return self._waterCoolers
+
+ def isAccessible(self, tX, tY):
+ if tX < 0 or tY < 0 or tX >= self.width or tY >= self.height:
+ return 0
+ return self.collisionTable[tY][tX] != 1
+
+ def destroy(self):
+ for waterCooler in self._waterCoolers:
+ waterCooler.destroy()
+
+ del self._waterCoolers
+ self._waterCoolerRoot.removeNode()
+ del self._waterCoolerRoot
+ base.win.setClearColor(self._clearColor)
+ del self._clearColor
+ MazeBase.destroy(self)
+ if __debug__ and hasattr(self, '_cubes'):
+ self.ignoreAll()
+ self._cubes.removeNode()
+ del self._cubes
+
+ def onstage(self):
+ MazeBase.onstage(self)
+ self._waterCoolerRoot.unstash()
+
+ def offstage(self):
+ self._waterCoolerRoot.stash()
+ MazeBase.offstage(self)
+
+
+BARRIER_DATA_RIGHT = 1
+BARRIER_DATA_TOP = 1
+
+class CogdoMazeFactory:
+
+ def __init__(self, randomNumGen, width, height, frameWallThickness = Globals.FrameWallThickness, cogdoMazeData = CogdoMazeData):
+ self._rng = RandomNumGen(randomNumGen)
+ self.width = width
+ self.height = height
+ self.frameWallThickness = frameWallThickness
+ self._cogdoMazeData = cogdoMazeData
+ self.quadrantSize = self._cogdoMazeData.QuadrantSize
+ self.cellWidth = self._cogdoMazeData.QuadrantCellWidth
+
+ def getMazeData(self):
+ if not hasattr(self, '_data'):
+ self._generateMazeData()
+ return self._data
+
+ def createCogdoMaze(self, flattenModel = True):
+ if not hasattr(self, '_maze'):
+ self._loadAndBuildMazeModel(flatten=flattenModel)
+ return CogdoMaze(self._model, self._data, self.cellWidth)
+
+ def _gatherQuadrantData(self):
+ self.openBarriers = []
+ barrierItems = range(Globals.TotalBarriers)
+ self._rng.shuffle(barrierItems)
+ for i in barrierItems[0:len(barrierItems) - Globals.NumBarriers]:
+ self.openBarriers.append(i)
+
+ self.quadrantData = []
+ quadrantKeys = self._cogdoMazeData.QuadrantCollisions.keys()
+ self._rng.shuffle(quadrantKeys)
+ i = 0
+ for y in range(self.height):
+ for x in range(self.width):
+ key = quadrantKeys[i]
+ collTable = self._cogdoMazeData.QuadrantCollisions[key]
+ angle = self._cogdoMazeData.QuadrantAngles[self._rng.randint(0, len(self._cogdoMazeData.QuadrantAngles) - 1)]
+ self.quadrantData.append((key, collTable[angle], angle))
+ i += 1
+ if x * y >= self._cogdoMazeData.NumQuadrants:
+ i = 0
+
+ def _generateBarrierData(self):
+ data = []
+ for y in range(self.height):
+ data.append([])
+ for x in range(self.width):
+ if x == self.width - 1:
+ ax = -1
+ else:
+ ax = 1
+ if y == self.height - 1:
+ ay = -1
+ else:
+ ay = 1
+ data[y].append([ax, ay])
+
+ dirUp = 0
+ dirDown = 1
+ dirLeft = 2
+ dirRight = 3
+
+ def getAvailableDirections(ax, ay, ignore = None):
+ dirs = []
+ if ax - 1 >= 0 and data[ay][ax - 1][BARRIER_DATA_RIGHT] == 1 and (ax, ay) != ignore:
+ dirs.append(dirLeft)
+ if ax + 1 < self.width and data[ay][ax][BARRIER_DATA_RIGHT] == 1 and (ax, ay) != ignore:
+ dirs.append(dirRight)
+ if ay - 1 >= 0 and data[ay - 1][ax][BARRIER_DATA_TOP] == 1 and (ax, ay) != ignore:
+ dirs.append(dirDown)
+ if ay + 1 < self.height and data[ay][ax][BARRIER_DATA_TOP] == 1 and (ax, ay) != ignore:
+ dirs.append(dirUp)
+ return dirs
+
+ visited = []
+
+ def tryVisitNeighbor(ax, ay, ad):
+ if ad == dirUp:
+ if data[ay][ax] in visited:
+ return None
+ visited.append(data[ay][ax])
+ data[ay][ax][BARRIER_DATA_TOP] = 0
+ ay += 1
+ elif ad == dirDown:
+ if data[ay - 1][ax] in visited:
+ return None
+ visited.append(data[ay - 1][ax])
+ data[ay - 1][ax][BARRIER_DATA_TOP] = 0
+ ay -= 1
+ elif ad == dirLeft:
+ if data[ay][ax - 1] in visited:
+ return None
+ visited.append(data[ay][ax - 1])
+ data[ay][ax - 1][BARRIER_DATA_RIGHT] = 0
+ ax -= 1
+ elif ad == dirRight:
+ if data[ay][ax] in visited:
+ return None
+ visited.append(data[ay][ax])
+ data[ay][ax][BARRIER_DATA_RIGHT] = 0
+ ax += 1
+ return (ax, ay)
+
+ def openBarriers(x, y):
+ dirs = getAvailableDirections(x, y)
+ for dir in dirs:
+ next = tryVisitNeighbor(x, y, dir)
+ if next is not None:
+ openBarriers(*next)
+
+ return
+
+ x = self._rng.randint(0, self.width - 1)
+ y = self._rng.randint(0, self.height - 1)
+ openBarriers(x, y)
+ self._barrierData = data
+ return
+
+ def _generateMazeData(self):
+ if not hasattr(self, 'quadrantData'):
+ self._gatherQuadrantData()
+ self._data = {}
+ self._data['width'] = (self.width + 1) * self.frameWallThickness + self.width * self.quadrantSize
+ self._data['height'] = (self.height + 1) * self.frameWallThickness + self.height * self.quadrantSize
+ self._data['originX'] = int(self._data['width'] / 2)
+ self._data['originY'] = int(self._data['height'] / 2)
+ collisionTable = []
+ horizontalWall = [ 1 for x in range(self._data['width']) ]
+ collisionTable.append(horizontalWall)
+ for i in range(0, len(self.quadrantData), self.width):
+ for y in range(self.quadrantSize):
+ row = [1]
+ for x in range(i, i + self.width):
+ if x == 1 and y < self.quadrantSize / 2 - 2:
+ newData = []
+ for j in self.quadrantData[x][1][y]:
+ if j == 0:
+ newData.append(2)
+ else:
+ newData.append(j + 0)
+
+ row += newData + [1]
+ else:
+ row += self.quadrantData[x][1][y] + [1]
+
+ collisionTable.append(row)
+
+ collisionTable.append(horizontalWall[:])
+
+ barriers = Globals.MazeBarriers
+ for i in range(len(barriers)):
+ for coords in barriers[i]:
+ collisionTable[coords[1]][coords[0]] = 0
+
+ y = self._data['originY']
+ for x in range(len(collisionTable[y])):
+ if collisionTable[y][x] == 0:
+ collisionTable[y][x] = 2
+
+ x = self._data['originX']
+ for y in range(len(collisionTable)):
+ if collisionTable[y][x] == 0:
+ collisionTable[y][x] = 2
+
+ self._data['collisionTable'] = collisionTable
+
+ def _loadAndBuildMazeModel(self, flatten = False):
+ self.getMazeData()
+ self._model = NodePath('CogdoMazeModel')
+ levelModel = CogdoUtil.loadMazeModel('level')
+ self.quadrants = []
+ quadrantUnitSize = int(self.quadrantSize * self.cellWidth)
+ frameActualSize = self.frameWallThickness * self.cellWidth
+ size = quadrantUnitSize + frameActualSize
+ halfWidth = int(self.width / 2)
+ halfHeight = int(self.height / 2)
+ i = 0
+ for y in range(self.height):
+ for x in range(self.width):
+ ax = (x - halfWidth) * size
+ ay = (y - halfHeight) * size
+ extension = ''
+ if hasattr(getBase(), 'air'):
+ extension = '.bam'
+ filepath = self.quadrantData[i][0] + extension
+ angle = self.quadrantData[i][2]
+ m = self._createQuadrant(filepath, i, angle, quadrantUnitSize)
+ m.setPos(ax, ay, 0)
+ m.reparentTo(self._model)
+ self.quadrants.append(m)
+ i += 1
+
+ quadrantHalfUnitSize = quadrantUnitSize * 0.5
+ barrierModel = CogdoUtil.loadMazeModel('grouping_blockerDivider').find('**/divider')
+ y = 3
+ for x in range(self.width):
+ if x == (self.width - 1) / 2:
+ continue
+ ax = (x - halfWidth) * size
+ ay = (y - halfHeight) * size - quadrantHalfUnitSize - (self.cellWidth - 0.5)
+ b = NodePath('barrier')
+ barrierModel.instanceTo(b)
+ b.setPos(ax, ay, 0)
+ b.reparentTo(self._model)
+
+ offset = self.cellWidth - 0.5
+ for x in (0, 3):
+ for y in range(self.height):
+ ax = (x - halfWidth) * size - quadrantHalfUnitSize - frameActualSize + offset
+ ay = (y - halfHeight) * size
+ b = NodePath('barrier')
+ barrierModel.instanceTo(b)
+ b.setPos(ax, ay, 0)
+ b.setH(90)
+ b.reparentTo(self._model)
+
+ offset -= 2.0
+
+ barrierModel.removeNode()
+ levelModel.getChildren().reparentTo(self._model)
+ for np in self._model.findAllMatches('**/*lightCone*'):
+ CogdoUtil.initializeLightCone(np, 'fixed', 3)
+
+ if flatten:
+ self._model.flattenStrong()
+ return self._model
+
+ def _createQuadrant(self, filepath, serialNum, angle, size):
+ root = NodePath('QuadrantRoot-%i' % serialNum)
+ quadrant = loader.loadModel(filepath)
+ quadrant.getChildren().reparentTo(root)
+ root.setH(angle)
+ return root
diff --git a/toontown/cogdominium/CogdoMazeCameraManager.py b/toontown/cogdominium/CogdoMazeCameraManager.py
new file mode 100644
index 0000000..8d48d2d
--- /dev/null
+++ b/toontown/cogdominium/CogdoMazeCameraManager.py
@@ -0,0 +1,135 @@
+from direct.showbase.PythonUtil import bound as clamp
+import CogdoMazeGameGlobals as Globals
+import math
+import random
+
+class CogdoMazeCameraManager:
+ toonJumpSpeed = 30.0
+ toonJumpDir = 1.0
+ toonMaxHeight = 1.8
+ maxHeightOffset = 1.5
+ savedShakeStrength = 0.0
+ toonIsShaking = False
+ rumbleOffDuration = 2.0
+ rumbleOnDuration = 1.5
+ rumbleOffTimer = 0.0
+ rumbleOnTimer = -1
+ savedCamZ = 0.0
+ savedCamX = 0.0
+ shakeStrengthThreshold = 2.8
+ savedRumbleTimer = 0.0
+ rumbleFreq = 0.1
+ rumbleSmall = 0.05
+ rumbleBig = 0.16
+ rumbleMagnitude = rumbleSmall
+
+ def __init__(self, toon, maze, cam, root):
+ self.toon = toon
+ self.maze = maze
+ self.camera = cam
+ self.root = root
+ self.shakeStrength = 0.0
+ self.shakeOffset = 0.0
+ self.shakeTime = 0.0
+ self.defaultHeight = 0.0
+ self.minPos = self.maze.tile2world(3, 5)
+ self.maxPos = self.maze.tile2world(self.maze.width - 3, self.maze.height - 3)
+ self._camAngle = Globals.CameraAngle
+ self._camDistance = Globals.CameraMinDistance
+ self._camTargetDistance = self._camDistance
+
+ def enable(self):
+ self.parent = self.root.attachNewNode('GameCamParent')
+ self.parent.setPos(self.toon, 0, 0, 0)
+ self.parent.setHpr(self.root, 180, self._camAngle, 0)
+ self.camera.reparentTo(self.parent)
+ self.camera.setPos(0, self._camDistance, 0)
+ self.camera.lookAt(self.toon)
+ self.defaultHeight = self.parent.getZ()
+ self.update(0)
+
+ def setCameraTargetDistance(self, distance):
+ self._camTargetDistance = distance
+
+ def disable(self):
+ self.camera.wrtReparentTo(render)
+ self.parent.removeNode()
+ del self.parent
+
+ def update(self, dt):
+ toonPos = self.toon.getPos()
+ self.parent.setPos(self.toon.getParent(), clamp(toonPos.getX(), self.minPos[0], self.maxPos[0]), clamp(toonPos.getY(), self.minPos[1], self.maxPos[1]), 0)
+ if self._camDistance != self._camTargetDistance:
+ self._updateCameraDistance()
+ if self.shakeOffset > 0 or self.shakeStrength > 0:
+ self.updateShake(dt)
+ self.updateRumble(dt)
+
+ def _updateCameraDistance(self):
+ if self._camDistance < self._camTargetDistance:
+ self._camDistance += min(0.4 * (self._camDistance / self._camTargetDistance), self._camTargetDistance - self._camDistance)
+ elif self._camDistance > self._camTargetDistance:
+ self._camDistance += max(-0.4 * (self._camDistance / self._camTargetDistance), self._camTargetDistance - self._camDistance)
+ self.camera.setY(self._camDistance)
+
+ def updateShake(self, dt):
+ if self.shakeStrength > 0:
+ if self.shakeStrength > Globals.CameraShakeMax:
+ self.shakeStrength = Globals.CameraShakeMax
+ height = self.defaultHeight + self.shakeStrength
+ self.shakeStrength = self.shakeStrength - Globals.CameraShakeFalloff * dt
+ if self.shakeStrength < 0.0:
+ self.shakeStrength = 0.0
+ else:
+ height = self.shakeStrength
+
+ def shake(self, strength):
+ self.shakeStrength += strength * 1.5
+ self.toonIsShaking = True
+
+ def updateToonShake(self, dt):
+ if self.toonIsShaking:
+ newHeight = self.toon.getZ() + self.toonJumpDir * dt * self.savedShakeStrength * self.toonJumpSpeed
+ maxHeight = self.savedShakeStrength * self.maxHeightOffset
+ if maxHeight > self.toonMaxHeight:
+ maxHeight = self.toonMaxHeight
+ if newHeight >= maxHeight:
+ newHeight = maxHeight
+ self.toonJumpDir = -1.0
+ elif newHeight <= 0.0:
+ newHeight = 0.0
+ self.toonJumpDir = 1.0
+ self.toonIsShaking = False
+ self.toon.setZ(newHeight)
+
+ def updateRumble(self, dt):
+ if self.rumbleOnTimer == -1:
+ self.rumbleOffTimer += dt
+ if self.rumbleOffTimer > self.rumbleOffDuration:
+ self.rumbleOnTimer = 0.0
+ self.savedRumbleTimer = 0.0
+ self.rumbleOffTimer = -1
+ self.rumbleOnDuration = 1.5
+ self.savedCamZ = self.camera.getZ()
+ self.savedCamX = self.camera.getX()
+ else:
+ if self.rumbleOnTimer > self.savedRumbleTimer + self.rumbleFreq:
+ self.savedRumbleTimer += self.rumbleFreq
+ self.rumble(self.rumbleMagnitude)
+ self.rumbleOnTimer += dt
+ if self.rumbleOnTimer > self.rumbleOnDuration:
+ self.rumbleOffTimer = 0.0
+ self.rumbleOnTimer = -1
+ if self.shakeStrength > self.shakeStrengthThreshold:
+ self.rumbleOffDuration = 0.5
+ self.rumbleMagnitude = self.rumbleBig
+ else:
+ self.rumbleOffDuration = random.uniform(1.0, 3.0)
+ self.rumbleMagnitude = self.rumbleSmall
+
+ def rumble(self, magnitude):
+ self.rumbleMagnitude *= -1.0
+ dz = self.rumbleMagnitude
+ self.camera.setZ(self.savedCamZ + dz)
+ dx = dz
+ self.camera.setZ(self.savedCamX + dx)
diff --git a/toontown/cogdominium/CogdoMazeData.py b/toontown/cogdominium/CogdoMazeData.py
new file mode 100644
index 0000000..3b8f633
--- /dev/null
+++ b/toontown/cogdominium/CogdoMazeData.py
@@ -0,0 +1,9244 @@
+FirstQuadrant = 1
+NumQuadrants = 9
+QuadrantCellWidth = 3
+QuadrantSize = 16
+QuadrantOriginXY = 8
+QuadrantAngles = (0,
+ 90,
+ 180,
+ 270)
+QuadrantCollisions = {}
+QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant1'] = {}
+collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant1']
+collTable[0] = [[0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1]]
+collTable[90] = [[1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1]]
+collTable[180] = [[1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0]]
+collTable[270] = [[1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1]]
+QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant2'] = {}
+collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant2']
+collTable[0] = [[1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1]]
+collTable[90] = [[1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1],
+ [1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1]]
+collTable[180] = [[1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1]]
+collTable[270] = [[1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1],
+ [1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1]]
+QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant3'] = {}
+collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant3']
+collTable[0] = [[1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1]]
+collTable[90] = [[1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1]]
+collTable[180] = [[1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1]]
+collTable[270] = [[1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1]]
+QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant4'] = {}
+collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant4']
+collTable[0] = [[1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1]]
+collTable[90] = [[1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1]]
+collTable[180] = [[1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1]]
+collTable[270] = [[1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1]]
+QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant5'] = {}
+collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant5']
+collTable[0] = [[1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1]]
+collTable[90] = [[1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1]]
+collTable[180] = [[1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1]]
+collTable[270] = [[1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1]]
+QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant6'] = {}
+collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant6']
+collTable[0] = [[0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1]]
+collTable[90] = [[0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1]]
+collTable[180] = [[1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0]]
+collTable[270] = [[1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0]]
+QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant7'] = {}
+collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant7']
+collTable[0] = [[1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1]]
+collTable[90] = [[1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1],
+ [1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1]]
+collTable[180] = [[1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1]]
+collTable[270] = [[1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1],
+ [1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1]]
+QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant8'] = {}
+collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant8']
+collTable[0] = [[1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1]]
+collTable[90] = [[1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1]]
+collTable[180] = [[1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1]]
+collTable[270] = [[1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1]]
+QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant9'] = {}
+collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant9']
+collTable[0] = [[1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0]]
+collTable[90] = [[1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0]]
+collTable[180] = [[0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1]]
+collTable[270] = [[0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1],
+ [1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1]]
diff --git a/toontown/cogdominium/CogdoMazeGame.py b/toontown/cogdominium/CogdoMazeGame.py
new file mode 100644
index 0000000..d950c61
--- /dev/null
+++ b/toontown/cogdominium/CogdoMazeGame.py
@@ -0,0 +1,580 @@
+from pandac.PandaModules import Point3, CollisionSphere, CollisionNode
+from direct.showbase.DirectObject import DirectObject
+from direct.showbase.PythonUtil import Functor
+from direct.showbase.RandomNumGen import RandomNumGen
+from direct.task.Task import Task
+from toontown.minigame.MazeSuit import MazeSuit
+from CogdoGameGatherable import CogdoMemo
+from CogdoMazePlayer import CogdoMazePlayer
+from CogdoMazeLocalPlayer import CogdoMazeLocalPlayer
+from CogdoMazeGuiManager import CogdoMazeGuiManager
+from CogdoGameAudioManager import CogdoGameAudioManager
+from CogdoMazeGameObjects import CogdoMazeExit, CogdoMazeDrop
+from CogdoMazeSuits import CogdoMazeSuit, CogdoMazeSlowMinionSuit, CogdoMazeFastMinionSuit, CogdoMazeBossSuit
+from CogdoMazeGameMovies import CogdoMazeGameIntro, CogdoMazeGameFinish
+import CogdoMazeGameGlobals as Globals
+import CogdoUtil
+import math
+import random
+
+class CogdoMazeGame(DirectObject):
+ notify = directNotify.newCategory('CogdoMazeGame')
+ UpdateTaskName = 'CogdoMazeGameUpdateTask'
+ RemoveGagTaskName = 'CogdoMazeGameRemoveGag'
+ PlayerCoolerCollision = '%s-into-%s' % (Globals.LocalPlayerCollisionName, Globals.WaterCoolerCollisionName)
+ PlayerDropCollision = '%s-into-%s' % (Globals.LocalPlayerCollisionName, Globals.DropCollisionName)
+
+ def __init__(self, distGame):
+ self.distGame = distGame
+ self._allowSuitsHitToons = base.config.GetBool('cogdomaze-suits-hit-toons', True)
+
+ def load(self, cogdoMazeFactory, numSuits, bossCode):
+ self._initAudio()
+ self.maze = cogdoMazeFactory.createCogdoMaze()
+ suitSpawnSpot = self.maze.createRandomSpotsList(numSuits, self.distGame.randomNumGen)
+ self.guiMgr = CogdoMazeGuiManager(self.maze, bossCode)
+ self.suits = []
+ self.suitsById = {}
+ self.shakers = []
+ self.toonsThatRevealedDoor = []
+ self.quake = 0
+ self.dropCounter = 0
+ self.drops = {}
+ self.gagCounter = 0
+ self.gags = []
+ self.hackTemp = False
+ self.dropGen = RandomNumGen(self.distGame.doId)
+ self.gagTimeoutTasks = []
+ self.finished = False
+ self.lastBalloonTimestamp = None
+ difficulty = self.distGame.getDifficulty()
+ serialNum = 0
+ for i in range(numSuits[0]):
+ suitRng = RandomNumGen(self.distGame.doId + serialNum * 10)
+ suit = CogdoMazeBossSuit(serialNum, self.maze, suitRng, difficulty, startTile=suitSpawnSpot[0][i])
+ self.addSuit(suit)
+ self.guiMgr.mazeMapGui.addSuit(suit.suit)
+ serialNum += 1
+
+ for i in range(numSuits[1]):
+ suitRng = RandomNumGen(self.distGame.doId + serialNum * 10)
+ suit = CogdoMazeFastMinionSuit(serialNum, self.maze, suitRng, difficulty, startTile=suitSpawnSpot[1][i])
+ self.addSuit(suit)
+ serialNum += 1
+
+ for i in range(numSuits[2]):
+ suitRng = RandomNumGen(self.distGame.doId + serialNum * 10)
+ suit = CogdoMazeSlowMinionSuit(serialNum, self.maze, suitRng, difficulty, startTile=suitSpawnSpot[2][i])
+ self.addSuit(suit)
+ serialNum += 1
+
+ self.toonId2Door = {}
+ self.keyIdToKey = {}
+ self.players = []
+ self.toonId2Player = {}
+ cellPos = (int(self.maze.width / 2), self.maze.height - 1)
+ pos = self.maze.tile2world(*cellPos)
+ self._exit = CogdoMazeExit()
+ self._exit.reparentTo(render)
+ self._exit.setPos(self.maze.exitPos)
+ self._exit.stash()
+ self.guiMgr.mazeMapGui.placeExit(*cellPos)
+ self._collNode2waterCooler = {}
+ for waterCooler in self.maze.getWaterCoolers():
+ pos = waterCooler.getPos(render)
+ tpos = self.maze.world2tile(pos[0], pos[1])
+ self.guiMgr.mazeMapGui.addWaterCooler(*tpos)
+ self._collNode2waterCooler[waterCooler.collNode] = waterCooler
+
+ self.pickups = []
+ self.gagModel = CogdoUtil.loadMazeModel('waterBalloon')
+ self._movie = CogdoMazeGameIntro(self.maze, self._exit, self.distGame.randomNumGen)
+ self._movie.load()
+ return
+
+ def _initAudio(self):
+ self._audioMgr = CogdoGameAudioManager(Globals.MusicFiles, Globals.SfxFiles, camera, cutoff=Globals.AudioCutoff)
+ self._quakeSfx1 = self._audioMgr.createSfx('quake')
+ self._quakeSfx2 = self._audioMgr.createSfx('quake')
+
+ def _destroyAudio(self):
+ self._quakeSfx1.destroy()
+ self._quakeSfx2.destroy()
+ del self._quakeSfx1
+ del self._quakeSfx2
+ self._audioMgr.destroy()
+ del self._audioMgr
+
+ def addSuit(self, suit):
+ id = suit.serialNum
+ self.suits.append(suit)
+ if suit.type == Globals.SuitTypes.Boss:
+ self.shakers.append(suit)
+ self.suitsById[id] = suit
+
+ def removeSuit(self, suit):
+ id = suit.serialNum
+ del self.suitsById[id]
+ if suit.type == Globals.SuitTypes.Boss:
+ self.shakers.remove(suit)
+ self.guiMgr.showBossCode(id)
+ self.guiMgr.mazeMapGui.removeSuit(suit.suit)
+ self.suits.remove(suit)
+ suit.destroy()
+
+ def unload(self):
+ self.toonsThatRevealedDoor = []
+ for suit in self.suits:
+ suit.destroy()
+
+ del self.suits
+ for id in self.drops.keys():
+ self.cleanupDrop(id)
+
+ self.__stopUpdateTask()
+ self.ignoreAll()
+ self.maze.destroy()
+ del self.maze
+ self._exit.destroy()
+ del self._exit
+ self.guiMgr.destroy()
+ del self.guiMgr
+ for player in self.players:
+ player.destroy()
+
+ del self.players
+ del self.toonId2Player
+ del self.localPlayer
+ for pickup in self.pickups:
+ pickup.destroy()
+
+ self._destroyAudio()
+ del self.distGame
+
+ def onstage(self):
+ self._exit.onstage()
+ self.maze.onstage()
+
+ def offstage(self):
+ self.maze.offstage()
+ self._exit.offstage()
+ for suit in self.suits:
+ suit.offstage()
+
+ def startIntro(self):
+ self._movie.play()
+ self._audioMgr.playMusic('normal')
+
+ def endIntro(self):
+ self._movie.end()
+ self._movie.unload()
+ del self._movie
+ for player in self.players:
+ self.placePlayer(player)
+ if player.toon is localAvatar:
+ localAvatar.sendCurrentPosition()
+ player.request('Ready')
+
+ def startFinish(self):
+ self._movie = CogdoMazeGameFinish(self.localPlayer, self._exit)
+ self._movie.load()
+ self._movie.play()
+
+ def endFinish(self):
+ self._movie.end()
+ self._movie.unload()
+ del self._movie
+
+ def placeEntranceElevator(self, elevator):
+ pos = self.maze.elevatorPos
+ elevator.setPos(pos)
+ tpos = self.maze.world2tile(pos[0], pos[1])
+ self.guiMgr.mazeMapGui.placeEntrance(*tpos)
+
+ def initPlayers(self):
+ for toonId in self.distGame.getToonIds():
+ toon = self.distGame.getToon(toonId)
+ if toon is not None:
+ if toon.isLocal():
+ player = CogdoMazeLocalPlayer(len(self.players), base.localAvatar, self, self.guiMgr)
+ self.localPlayer = player
+ else:
+ player = CogdoMazePlayer(len(self.players), toon)
+ self._addPlayer(player)
+
+ return
+
+ def start(self):
+ self.accept(self.PlayerDropCollision, self.handleLocalToonMeetsDrop)
+ self.accept(self.PlayerCoolerCollision, self.handleLocalToonMeetsWaterCooler)
+ self.accept(CogdoMazeExit.EnterEventName, self.handleLocalToonEntersDoor)
+ self.accept(CogdoMemo.EnterEventName, self.handleLocalToonMeetsPickup)
+ if self._allowSuitsHitToons:
+ self.accept(CogdoMazeSuit.COLLISION_EVENT_NAME, self.handleLocalToonMeetsSuit)
+ self.accept(CogdoMazePlayer.GagHitEventName, self.handleToonMeetsGag)
+ self.accept(CogdoMazeSuit.GagHitEventName, self.handleLocalSuitMeetsGag)
+ self.accept(CogdoMazeSuit.DeathEventName, self.handleSuitDeath)
+ self.accept(CogdoMazeSuit.ThinkEventName, self.handleSuitThink)
+ self.accept(CogdoMazeBossSuit.ShakeEventName, self.handleBossShake)
+ self.accept(CogdoMazePlayer.RemovedEventName, self._removePlayer)
+ self.__startUpdateTask()
+ for player in self.players:
+ player.handleGameStart()
+ player.request('Normal')
+
+ for suit in self.suits:
+ suit.onstage()
+ suit.gameStart(self.distGame.getStartTime())
+
+ def exit(self):
+ self._quakeSfx1.stop()
+ self._quakeSfx2.stop()
+ for suit in self.suits:
+ suit.gameEnd()
+
+ self.ignore(self.PlayerDropCollision)
+ self.ignore(self.PlayerCoolerCollision)
+ self.ignore(CogdoMazeExit.EnterEventName)
+ self.ignore(CogdoMemo.EnterEventName)
+ self.ignore(CogdoMazeSuit.COLLISION_EVENT_NAME)
+ self.ignore(CogdoMazePlayer.GagHitEventName)
+ self.ignore(CogdoMazeSuit.GagHitEventName)
+ self.ignore(CogdoMazeSuit.DeathEventName)
+ self.ignore(CogdoMazeSuit.ThinkEventName)
+ self.ignore(CogdoMazeBossSuit.ShakeEventName)
+ self.ignore(CogdoMazePlayer.RemovedEventName)
+ self.__stopUpdateTask()
+ for timeoutTask in self.gagTimeoutTasks:
+ taskMgr.remove(timeoutTask)
+
+ self.finished = True
+ self.localPlayer.handleGameExit()
+ for player in self.players:
+ player.request('Done')
+
+ self.guiMgr.hideTimer()
+ self.guiMgr.hideMazeMap()
+ self.guiMgr.hideBossGui()
+
+ def _addPlayer(self, player):
+ self.players.append(player)
+ self.toonId2Player[player.toon.doId] = player
+
+ def _removePlayer(self, player):
+ if player in self.players:
+ self.players.remove(player)
+ else:
+ for cPlayer in self.players:
+ if cPlayer.toon == player.toon:
+ self.players.remove(cPlayer)
+ break
+
+ if self.toonId2Player.has_key(player.toon.doId):
+ del self.toonId2Player[player.toon.doId]
+ self.guiMgr.mazeMapGui.removeToon(player.toon)
+
+ def handleToonLeft(self, toonId):
+ self._removePlayer(self.toonId2Player[toonId])
+
+ def __startUpdateTask(self):
+ self.__stopUpdateTask()
+ taskMgr.add(self.__updateTask, CogdoMazeGame.UpdateTaskName, 45)
+
+ def __stopUpdateTask(self):
+ taskMgr.remove(CogdoMazeGame.UpdateTaskName)
+ taskMgr.remove('loopSecondQuakeSound')
+
+ def __updateTask(self, task):
+ dt = globalClock.getDt()
+ self.localPlayer.update(dt)
+ for player in self.players:
+ curTX, curTY = self.maze.world2tileClipped(player.toon.getX(), player.toon.getY())
+ self.guiMgr.mazeMapGui.updateToon(player.toon, curTX, curTY)
+
+ self.__updateGags()
+ if Globals.QuakeSfxEnabled:
+ self.__updateQuakeSound()
+ for pickup in self.pickups:
+ pickup.update(dt)
+
+ MazeSuit.thinkSuits(self.suits, self.distGame.getStartTime())
+ return Task.cont
+
+ def __updateGags(self):
+ remove = []
+ for i in range(len(self.gags)):
+ balloon = self.gags[i]
+ if balloon.isSingleton():
+ remove.append(i)
+ elif balloon.getParent() == render:
+ loc = self.maze.world2tile(balloon.getX(), balloon.getY())
+ if not self.maze.isAccessible(loc[0], loc[1]):
+ self.removeGag(balloon)
+
+ remove.reverse()
+ for i in remove:
+ self.gags.pop(i)
+
+ def __updateQuakeSound(self):
+ shake = self.localPlayer.getCameraShake()
+ if shake < 1.0:
+ shake = 1.0
+ volume = 3.0 * shake / Globals.CameraShakeMax
+ if volume > 3.0:
+ volume = 3.0
+ if self._quakeSfx1.getAudioSound().status() != self._quakeSfx1.getAudioSound().PLAYING:
+ self._quakeSfx1.loop(volume=volume)
+ else:
+ self._quakeSfx1.getAudioSound().setVolume(volume)
+ volume = shake * shake / Globals.CameraShakeMax
+ if not self.hackTemp and self._quakeSfx2.getAudioSound().status() != self._quakeSfx2.getAudioSound().PLAYING:
+ taskMgr.doMethodLater(1.5, self._quakeSfx2.loop, 'loopSecondQuakeSound', extraArgs=[])
+ self.hackTemp = True
+ else:
+ self._quakeSfx2.getAudioSound().setVolume(volume)
+
+ def handleLocalToonMeetsSuit(self, suitType, suitNum):
+ if self.localPlayer.state == 'Normal' and not self.localPlayer.invulnerable:
+ self.distGame.b_toonHitBySuit(suitType, suitNum)
+
+ def toonHitBySuit(self, toonId, suitType, suitNum, elapsedTime = 0.0):
+ player = self.toonId2Player[toonId]
+ if player.state == 'Normal':
+ player.request('Hit', elapsedTime)
+
+ def handleSuitDeath(self, suitType, suitNum):
+ suit = self.suitsById[suitNum]
+ self.dropMemos(suit)
+ self.removeSuit(suit)
+
+ def dropMemos(self, suit):
+ numDrops = suit.memos
+ if numDrops > 0:
+ start = math.radians(random.randint(0, 360))
+ step = math.radians(360.0 / numDrops)
+ radius = 2.0
+ for i in range(numDrops):
+ angle = start + i * step
+ x = radius * math.cos(angle) + suit.suit.getX()
+ y = radius * math.sin(angle) + suit.suit.getY()
+ self.generatePickup(x, y)
+
+ def handleSuitThink(self, suit, TX, TY):
+ if suit in self.shakers:
+ self.guiMgr.mazeMapGui.updateSuit(suit.suit, TX, TY)
+ suit.dropTimer += 1
+ if suit.dropTimer >= Globals.DropFrequency:
+ self.doDrop(suit)
+ suit.dropTimer = 0
+
+ def doDrop(self, boss):
+ dropLoc = boss.pickRandomValidSpot()
+ self.generateDrop(dropLoc[0], dropLoc[1])
+
+ def handleBossShake(self, suit, strength):
+ if Globals.BossShakeEnabled:
+ self.shakeCamera(suit.suit, strength, Globals.BossMaxDistance)
+
+ def randomDrop(self, centerTX, centerTY, radius):
+ dropArray = []
+ for i in xrange(1, distance):
+ dropArray.append(i)
+ dropArray.append(-1 * i)
+
+ offsetTX = self.distGame.randomNumGen.choice(dropArray)
+ offsetTY = self.distGame.randomNumGen.choice(dropArray)
+ dropTX = sourceTX + offsetTX
+ dropTY = sourceTY + offsetTY
+ if self.maze.isWalkable(dropTX, dropTY):
+ self.generateDrop(dropTX, dropTY)
+
+ def generateDrop(self, TX, TY):
+ drop = self.maze.tile2world(TX, TY)
+ ival = self.createDrop(drop[0], drop[1])
+ ival.start()
+
+ def createDrop(self, x, y):
+ self.dropCounter = self.dropCounter + 1
+ id = self.dropCounter
+ drop = CogdoMazeDrop(self, id, x, y)
+ self.drops[id] = drop
+ return drop.getDropIval()
+
+ def cleanupDrop(self, id):
+ if id in self.drops.keys():
+ drop = self.drops[id]
+ drop.destroy()
+ del self.drops[id]
+
+ def dropHit(self, node, id):
+ if self.finished:
+ return
+ if Globals.DropShakeEnabled:
+ self.shakeCamera(node, Globals.DropShakeStrength, Globals.DropMaxDistance)
+
+ def shakeCamera(self, node, strength, distanceCutoff = 60.0):
+ distance = self.localPlayer.toon.getDistance(node)
+ shake = strength * (1 - distance / distanceCutoff)
+ if shake > 0:
+ self.localPlayer.shakeCamera(shake)
+
+ def handleLocalToonMeetsDrop(self, collEntry):
+ fcabinet = collEntry.getIntoNodePath()
+ if fcabinet.getTag('isFalling') == str('False'):
+ return
+ if self.localPlayer.state == 'Normal' and not self.localPlayer.invulnerable:
+ self.distGame.b_toonHitByDrop()
+
+ def toonHitByDrop(self, toonId):
+ player = self.toonId2Player[toonId]
+ player.hitByDrop()
+
+ def handleLocalToonMeetsGagPickup(self, collEntry):
+ if self.localPlayer.equippedGag != None:
+ return
+ into = collEntry.getIntoNodePath()
+ if into.hasPythonTag('id'):
+ id = into.getPythonTag('id')
+ self.distGame.d_sendRequestGagPickUp(id)
+ return
+
+ def hasGag(self, toonId, elapsedTime = 0.0):
+ player = self.toonId2Player[toonId]
+ player.equipGag()
+
+ def handleLocalToonMeetsWaterCooler(self, collEntry):
+ if self.localPlayer.equippedGag != None:
+ return
+ if self.lastBalloonTimestamp and globalClock.getFrameTime() - self.lastBalloonTimestamp < Globals.BalloonDelay:
+ return
+ collNode = collEntry.getIntoNode()
+ waterCooler = self._collNode2waterCooler[collNode]
+ self.lastBalloonTimestamp = globalClock.getFrameTime()
+ self.distGame.d_sendRequestGag(waterCooler.serialNum)
+ return
+
+ def requestUseGag(self, x, y, h):
+ self.distGame.b_toonUsedGag(x, y, h)
+
+ def toonUsedGag(self, toonId, x, y, h, elapsedTime = 0.0):
+ player = self.toonId2Player[toonId]
+ heading = h
+ pos = Point3(x, y, 0)
+ gag = player.showToonThrowingGag(heading, pos)
+ if gag is not None:
+ self.gags.append(gag)
+ return
+
+ def handleToonMeetsGag(self, playerId, gag):
+ self.removeGag(gag)
+ self.distGame.b_toonHitByGag(playerId)
+
+ def toonHitByGag(self, toonId, hitToon, elapsedTime = 0.0):
+ if toonId not in self.toonId2Player.keys() or hitToon not in self.toonId2Player.keys():
+ return
+ player = self.toonId2Player[hitToon]
+ player.hitByGag()
+
+ def handleLocalSuitMeetsGag(self, suitType, suitNum, gag):
+ self.removeGag(gag)
+ self.localPlayer.hitSuit(suitType)
+ self.distGame.b_suitHitByGag(suitType, suitNum)
+
+ def suitHitByGag(self, toonId, suitType, suitNum, elapsedTime = 0.0):
+ if suitType == Globals.SuitTypes.Boss:
+ self.guiMgr.showBossHit(suitNum)
+ if suitNum in self.suitsById.keys():
+ suit = self.suitsById[suitNum]
+ suit.hitByGag()
+
+ def removeGag(self, gag):
+ gag.detachNode()
+
+ def toonRevealsDoor(self, toonId):
+ self._exit.revealed = True
+
+ def openDoor(self, timeLeft):
+ self._audioMgr.playMusic('timeRunningOut')
+ if not self._exit.isOpen():
+ self._exit.open()
+ self.localPlayer.handleOpenDoor(self._exit)
+ self.guiMgr.showTimer(timeLeft, self._handleTimerExpired)
+ self.guiMgr.mazeMapGui.showExit()
+ self.guiMgr.mazeMapGui.revealAll()
+
+ def countdown(self, timeLeft):
+ self.guiMgr.showTimer(timeLeft, self._handleTimerExpired)
+
+ def timeAlert(self):
+ self._audioMgr.playMusic('timeRunningOut')
+
+ def _handleTimerExpired(self):
+ self.localPlayer.request('Done')
+
+ def toonEntersDoor(self, toonId):
+ player = self.toonId2Player[toonId]
+ self.guiMgr.mazeMapGui.removeToon(player.toon)
+ self._exit.playerEntersDoor(player)
+ self.localPlayer.handleToonEntersDoor(toonId, self._exit)
+ player.request('Done')
+
+ def generatePickup(self, x, y):
+ pickup = CogdoMemo(len(self.pickups), pitch=-90)
+ self.pickups.append(pickup)
+ pickup.reparentTo(self.maze.maze)
+ pickup.setPos(x, y, 1)
+ pickup.enable()
+
+ def handleLocalToonMeetsPickup(self, pickup):
+ pickup.disable()
+ self.distGame.d_sendRequestPickUp(pickup.serialNum)
+
+ def pickUp(self, toonId, pickupNum, elapsedTime = 0.0):
+ self.notify.debugCall()
+ player = self.toonId2Player[toonId]
+ pickup = self.pickups[pickupNum]
+ if not pickup.wasPickedUp():
+ pickup.pickUp(player.toon, elapsedTime)
+ self.localPlayer.handlePickUp(toonId)
+
+ def placePlayer(self, player):
+ toonIds = self.distGame.getToonIds()
+ if player.toon.doId not in toonIds:
+ return
+ i = toonIds.index(player.toon.doId)
+ x = int(self.maze.width / 2.0) - int(len(toonIds) / 2.0) + i
+ y = 2
+ while not self.maze.isAccessible(x, y):
+ y += 1
+
+ pos = self.maze.tile2world(x, y)
+ player.toon.setPos(pos[0], pos[1], 0)
+ self.guiMgr.mazeMapGui.addToon(player.toon, x, y)
+
+ def handleLocalToonEntersDoor(self, door):
+ localToonId = self.localPlayer.toon.doId
+ if self._exit.isOpen():
+ self.distGame.d_sendRequestAction(Globals.GameActions.EnterDoor, 0)
+ else:
+ if localToonId not in self.toonsThatRevealedDoor:
+ self.toonsThatRevealedDoor.append(localToonId)
+ self.localPlayer.handleToonRevealsDoor(localToonId, self._exit)
+ if not self._exit.revealed:
+ self.toonRevealsDoor(localToonId)
+ self.distGame.d_sendRequestAction(Globals.GameActions.RevealDoor, 0)
+
+ def handleToonWentSad(self, toonId):
+ if toonId == self.localPlayer.toon.doId:
+ for player in self.players:
+ player.removeGag()
+
+ elif toonId in self.toonId2Player.keys():
+ player = self.toonId2Player[toonId]
+ player.removeGag()
+
+ def handleToonDisconnected(self, toonId):
+ if toonId == self.localPlayer.toon.doId:
+ pass
+ elif toonId in self.toonId2Player.keys():
+ player = self.toonId2Player[toonId]
+ self._removePlayer(player)
diff --git a/toontown/cogdominium/CogdoMazeGameGlobals.py b/toontown/cogdominium/CogdoMazeGameGlobals.py
new file mode 100644
index 0000000..030b9b8
--- /dev/null
+++ b/toontown/cogdominium/CogdoMazeGameGlobals.py
@@ -0,0 +1,235 @@
+from direct.showbase import PythonUtil
+from pandac.PandaModules import VBase4
+GameActions = PythonUtil.Enum(('EnterDoor',
+ 'RevealDoor',
+ 'OpenDoor',
+ 'Countdown',
+ 'TimeAlert'))
+SecondsUntilTimeout = 4.0 * 60.0
+SecondsUntilGameEnds = 60.0
+SecondsForTimeAlert = 60.0
+MaxPlayers = 4
+IntroDurationSeconds = 24.0
+FinishDurationSeconds = 5.0
+PlayerCollisionName = 'CogdoMazePlayer_Collision'
+LocalPlayerCollisionName = 'CogdoMazeLocalPlayer_Collision'
+PlayerCollisionRadius = 1.0
+HitCooldownTime = 2.0
+HintTimeout = 6.0
+NumQuadrants = (3, 3)
+FrameWallThickness = 1
+QuadrantUnitGap = 3
+TotalBarriers = 12
+NumBarriers = 3
+MazeBarriers = ([(7, 34),
+ (8, 34),
+ (9, 34),
+ (10, 34)],
+ [(24, 34),
+ (25, 34),
+ (26, 34),
+ (27, 34)],
+ [(41, 34),
+ (42, 34),
+ (43, 34),
+ (44, 34)],
+ [(7, 17),
+ (8, 17),
+ (9, 17),
+ (10, 17)],
+ [(24, 17),
+ (25, 17),
+ (26, 17),
+ (27, 17)],
+ [(41, 17),
+ (42, 17),
+ (43, 17),
+ (44, 17)],
+ [(17, 41),
+ (17, 42),
+ (17, 43),
+ (17, 44)],
+ [(17, 24),
+ (17, 25),
+ (17, 26),
+ (17, 27)],
+ [(17, 7),
+ (17, 8),
+ (17, 9),
+ (17, 10)],
+ [(34, 41),
+ (34, 42),
+ (34, 43),
+ (34, 44)],
+ [(34, 24),
+ (34, 25),
+ (34, 26),
+ (34, 27)],
+ [(34, 7),
+ (34, 8),
+ (34, 9),
+ (34, 10)])
+ToonRunSpeed = 11.2
+CameraAngle = 60
+CameraRemoteToonRadius = 6
+CameraMinDistance = 40
+CameraMaxDistance = 61
+CamCutoffFactor = 1.34
+ToonAnimationInfo = {'hit': ('slip-backward', 2.25, 12)}
+NumPickups = 256
+PickupsUntilDoorOpens = int(NumPickups * 0.6)
+SuitCollisionName = 'CogdoMazeSuit_Collision'
+SuitWalkSameDirectionProb = 1
+SuitWalkTurnAroundProb = 100
+SuitTypes = PythonUtil.Enum(('Boss', 'FastMinion', 'SlowMinion'))
+SuitData = {}
+SuitData[SuitTypes.Boss] = {'dnaName': 'ms',
+ 'cellWalkPeriod': 192,
+ 'toonDamage': 3.0,
+ 'scale': 2.5,
+ 'hp': 2,
+ 'memos': 0}
+SuitData[SuitTypes.FastMinion] = {'dnaName': 'nd',
+ 'cellWalkPeriod': 64,
+ 'toonDamage': 1.0,
+ 'scale': 1.3,
+ 'hp': 1,
+ 'memos': 3}
+SuitData[SuitTypes.SlowMinion] = {'dnaName': 'cc',
+ 'cellWalkPeriod': 160,
+ 'toonDamage': 2.0,
+ 'scale': 1.33,
+ 'hp': 1,
+ 'memos': 2}
+NumSuits = (4, 5, 5)
+BossSpinTime = 1.0
+BossSpinCount = 2
+BlinkFrequency = 1.0
+BlinkSpeed = 0.5
+BlinkColor = VBase4(1.0, 0.4, 0.4, 1.0)
+SuitsModifier = (0, 6, 9)
+DamageModifier = 9.0
+DropShakeEnabled = True
+BossShakeEnabled = True
+DropShakeStrength = 4.0
+DropMaxDistance = 20.0
+BossShakeStrength = 1.2
+BossMaxDistance = 25.0
+BossShakeTime = 0.53
+BossStompSfxCutoff = 70.0
+BossCogStompAnimationPlayrateFactor = 0.75
+CameraShakeFalloff = 2.2
+CameraShakeMax = 5.0
+QuakeSfxFalloff = 0.01
+QuakeSfxMax = 2.0
+QuakeSfxEnabled = True
+DropFrequency = 3
+DropDamage = 0
+DropTime = 1.0
+ShadowTime = 2.0
+DropHeight = 70
+DropFadeTime = 1.0
+DropCollisionRadius = 1.0
+DropCollisionName = 'DropCollision'
+DroppedCollisionRadius = 2.0
+DropChance = 0.25
+GagChance = 0.5
+GagSitTime = 15.0
+BalloonDelay = 1.2
+ThrowDistance = 18
+ThrowDuration = 0.5
+ThrowStartFrame = 61
+ThrowEndFrame = 64
+ThrowPlayRate = 1.5
+GagPickupScale = 2.0
+GagPickupCollisionRadius = 1.0
+GagPickupCollisionName = 'PickUpCollision'
+GagColors = ((1.0,
+ 0.27,
+ 0.27,
+ 1.0),
+ (1.0,
+ 0.66,
+ 0.15,
+ 1.0),
+ (0.31,
+ 1.0,
+ 0.29,
+ 1.0),
+ (0.31,
+ 0.62,
+ 1.0,
+ 1.0),
+ (0.91,
+ 0.32,
+ 1.0,
+ 1.0))
+GagCollisionName = 'Gag_Collision'
+WaterCoolerTriggerRadius = 2.5
+WaterCoolerTriggerOffset = (0, -1.5, 0)
+WaterCoolerCollisionName = 'WaterCooler_Collision'
+WaterCoolerShowEventName = 'CogdoMazeWaterCooler_Show'
+WaterCoolerHideEventName = 'CogdoMazeWaterCooler_Hide'
+AudioCutoff = 75.0
+MusicFiles = {'normal': 'phase_9/audio/bgm/CHQ_FACT_bg.mid',
+ 'timeRunningOut': 'phase_7/audio/bgm/encntr_suit_winning_indoor.mid'}
+SfxFiles = {'toonHitByDrop': 'phase_5/audio/sfx/tt_s_ara_cmg_toonHit.mp3',
+ 'toonHit': 'phase_4/audio/sfx/MG_cannon_hit_dirt.mp3',
+ 'getMemo': 'phase_4/audio/sfx/MG_maze_pickup.mp3',
+ 'drop': 'phase_5/audio/sfx/tt_s_ara_cmg_itemHitsFloor.mp3',
+ 'throw': 'phase_3.5/audio/sfx/AA_pie_throw_only.mp3',
+ 'splat': 'phase_5/audio/sfx/SA_watercooler_spray_only.mp3',
+ 'cogSpin': 'phase_3.5/audio/sfx/Cog_Death.mp3',
+ 'cogDeath': 'phase_3.5/audio/sfx/ENC_cogfall_apart.mp3',
+ 'bossCogAngry': 'phase_5/audio/sfx/tt_s_ara_cmg_bossCogAngry.mp3',
+ 'cogStomp': 'phase_5/audio/sfx/tt_s_ara_cmg_cogStomp.mp3',
+ 'quake': 'phase_5/audio/sfx/tt_s_ara_cmg_groundquake.mp3',
+ 'waterCoolerFill': 'phase_5/audio/sfx/tt_s_ara_cmg_waterCoolerFill.mp3',
+ 'lose': 'phase_4/audio/sfx/MG_lose.mp3',
+ 'win': 'phase_4/audio/sfx/MG_win.mp3',
+ 'cogDialogue': 'phase_3.5/audio/dial/COG_VO_statement.mp3',
+ 'toonDialogue': 'phase_3.5/audio/dial/AV_dog_long.mp3'}
+MessageLabelPos = (0.0, 0.0, -0.4)
+MemoGuiPos = (-0.85, 0, -0.9)
+MemoGuiTextScale = 0.1
+MemoGuiTextColor = (0.95,
+ 0.95,
+ 0,
+ 1)
+MapGuiBgColor = (0.9, 0.9, 0.9)
+MapGuiFgColor = (0.5,
+ 0.5,
+ 0.5,
+ 1)
+MapGuiPos = (1.05, 0.0, -0.71)
+MapGuiScale = 0.225
+MapGuiSuitMarkerFlashColor = (1.0, 0.0, 0.0)
+MapGuiSuitMarkerSize = 0.075
+MapGuiWaterCoolerMarkerSize = 0.08
+QuestArrowScale = 5
+QuestArrowColor = (1,
+ 1,
+ 0,
+ 1)
+CoolerArrowScale = 8
+CoolerArrowColor = (1,
+ 1,
+ 0,
+ 1)
+CoolerArrowZ = 10
+CoolerArrowBounce = 2
+CoolerArrowSpeed = 2
+BossGuiScale = 0.8
+BossGuiPos = (0, 0, -0.83)
+BossGuiTitleLabelScale = 0.055
+BossCodeFrameWidth = 0.13
+BossCodeFrameGap = 0.005
+BossCodeFrameLabelScale = 0.12
+BossCodeFrameLabelNormalColor = (0,
+ 0,
+ 0,
+ 1)
+BossCodeFrameLabelHighlightColor = (0,
+ 0.5,
+ 0,
+ 1)
diff --git a/toontown/cogdominium/CogdoMazeGameGuis.py b/toontown/cogdominium/CogdoMazeGameGuis.py
new file mode 100644
index 0000000..52b20e8
--- /dev/null
+++ b/toontown/cogdominium/CogdoMazeGameGuis.py
@@ -0,0 +1,266 @@
+from direct.gui.DirectLabel import DirectLabel
+from direct.gui.DirectGui import DirectFrame, DGG
+from direct.task.Task import Task
+from direct.interval.MetaInterval import Sequence, Parallel
+from direct.interval.FunctionInterval import Wait, Func
+from pandac.PandaModules import TextNode, NodePath, Point3, CardMaker
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from toontown.toonbase import ToontownIntervals
+from toontown.minigame.MazeMapGui import MazeMapGui
+import CogdoMazeGameGlobals as Globals
+import CogdoUtil
+
+class CogdoMazeMapGui(MazeMapGui):
+
+ def __init__(self, mazeCollTable):
+ MazeMapGui.__init__(self, mazeCollTable, bgColor=Globals.MapGuiBgColor, fgColor=Globals.MapGuiFgColor)
+ self._suit2marker = {}
+ self._initModel()
+ self.setPos(*Globals.MapGuiPos)
+ self.setScale(Globals.MapGuiScale)
+
+ def destroy(self):
+ for marker in self._suit2marker.values():
+ marker.removeNode()
+
+ del self._suit2marker
+ self._entrance.removeNode()
+ del self._entrance
+ self._exit.removeNode()
+ del self._exit
+ del self._exitOpen
+ del self._exitClosed
+ self._suitMarkerTemplate.removeNode()
+ del self._suitMarkerTemplate
+ self._waterCoolerTemplate.removeNode()
+ del self._waterCoolerTemplate
+ MazeMapGui.destroy(self)
+
+ def _initModel(self):
+ baseName = '**/tt_t_gui_cmg_miniMap_'
+ cardModel = CogdoUtil.loadMazeModel('miniMap_cards', group='gui')
+ cm = CardMaker('bg')
+ cm.setFrame(-1.1, 1.1, -1.1, 1.1)
+ bg = self.attachNewNode(cm.generate())
+ bg.setColor(*self._bgColor)
+ bg.setBin('fixed', 0)
+ frame = cardModel.find(baseName + 'frame')
+ frame.reparentTo(self)
+ frame.setScale(2.5)
+ frame.setPos(0.01, 0, -0.01)
+ self._entrance = cardModel.find(baseName + 'entrance')
+ self._entrance.reparentTo(self)
+ self._entrance.setScale(0.35)
+ self._entrance.hide()
+ self._exit = NodePath('exit')
+ self._exit.setScale(0.35)
+ self._exit.reparentTo(self)
+ self._exitOpen = cardModel.find(baseName + 'exitOpen')
+ self._exitOpen.reparentTo(self._exit)
+ self._exitClosed = cardModel.find(baseName + 'exitClosed')
+ self._exitClosed.reparentTo(self._exit)
+ self._suitMarkerTemplate = cardModel.find(baseName + 'cogIcon')
+ self._suitMarkerTemplate.detachNode()
+ self._suitMarkerTemplate.setScale(0.225)
+ self._waterCoolerTemplate = cardModel.find(baseName + 'waterDrop')
+ self._waterCoolerTemplate.detachNode()
+ self._waterCoolerTemplate.setScale(0.225)
+ self._exit.hide()
+ cardModel.removeNode()
+
+ def addWaterCooler(self, tX, tY):
+ marker = NodePath('WaterCoolerMarker-%i-%i' % (tX, tY))
+ self._waterCoolerTemplate.copyTo(marker)
+ marker.reparentTo(self.maskedLayer)
+ x, y = self.tile2gui(tX, tY)
+ marker.setPos(*self.gui2pos(x, y))
+
+ def addSuit(self, suit):
+ marker = NodePath('SuitMarker-%i' % len(self._suit2marker))
+ self._suitMarkerTemplate.copyTo(marker)
+ marker.reparentTo(self)
+ self._suit2marker[suit] = marker
+
+ def removeSuit(self, suit):
+ self._suit2marker[suit].removeNode()
+ del self._suit2marker[suit]
+
+ def updateSuit(self, suit, tX, tY):
+ x, y = self.tile2gui(tX, tY)
+ self._suit2marker[suit].setPos(*self.gui2pos(x, y))
+
+ def showExit(self):
+ self._exit.show()
+ self._exitClosed.hide()
+
+ def hideExit(self):
+ self._exit.hide()
+
+ def placeExit(self, tX, tY):
+ x, y = self.tile2gui(tX, tY)
+ self._exit.setPos(*self.gui2pos(x, y))
+ self._exit.setZ(self._exit, 0.3)
+
+ def placeEntrance(self, tX, tY):
+ x, y = self.tile2gui(tX, tY)
+ self._entrance.setPos(*self.gui2pos(x, y))
+ self._entrance.setZ(self._entrance, -0.35)
+ self._entrance.show()
+
+
+class CogdoMazeBossCodeFrame(DirectFrame):
+
+ def __init__(self, id, code, modelToCopy):
+ DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX)
+ self._id = id
+ self._model = modelToCopy.copyTo(self)
+ self._model.setPos(0, 0, 0)
+ self._bg = self._model.find('**/bossBackground')
+ self._bossIcon = self._model.find('**/bossIcon')
+ self._bossIconX = self._model.find('**/bossIconX')
+ self._bossIconX.reparentTo(self._bossIcon)
+ self._bossIconX.hide()
+ self._bg.hide()
+ self._bossIcon.setBin('fixed', 2)
+ self._bg.setBin('fixed', 3)
+ self._label = DirectLabel(parent=self._bg, relief=None, scale=Globals.BossCodeFrameLabelScale, text=code, pos=(0, 0, -0.03), text_align=TextNode.ACenter, text_fg=Globals.BossCodeFrameLabelNormalColor, text_shadow=(0, 0, 0, 0), text_font=ToontownGlobals.getSuitFont())
+ return
+
+ def destroy(self):
+ ToontownIntervals.cleanup('boss_code%i' % self._id)
+ DirectFrame.destroy(self)
+
+ def showNumber(self):
+ self.setHit(False)
+ self._bossIconX.show()
+ ToontownIntervals.cleanup('boss_code%i' % self._id)
+ ToontownIntervals.start(Sequence(Parallel(ToontownIntervals.getPresentGuiIval(self._bossIcon, '', startPos=(0, 0, -0.15))), Wait(1.0), ToontownIntervals.getPulseLargerIval(self._bg, ''), name='boss_code%i' % self._id))
+
+ def setHit(self, hit):
+ if hit:
+ self._model.setColorScale(Globals.BlinkColor)
+ else:
+ self._model.setColorScale(1.0, 1.0, 1.0, 1.0)
+
+ def highlightNumber(self):
+ self._label['text_fg'] = Globals.BossCodeFrameLabelHighlightColor
+
+
+class CogdoMazeBossGui(DirectFrame):
+
+ def __init__(self, code):
+ DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX)
+ self._code = str(code)
+ self._codeLength = len(self._code)
+ self._markersShown = 0
+ self._markers = []
+ self._initModel()
+ self.setPos(*Globals.BossGuiPos)
+ self.setScale(Globals.BossGuiScale)
+ self.hide()
+ return
+
+ def destroy(self):
+ ToontownIntervals.cleanup('bosscodedoor')
+ self._model.removeNode()
+ del self._model
+ self._titleLabel.removeNode()
+ del self._titleLabel
+ for marker in self._markers:
+ marker.destroy()
+
+ del self._markers
+ DirectFrame.destroy(self)
+
+ def _initModel(self):
+ codeFrameGap = Globals.BossCodeFrameGap
+ codeFrameWidth = Globals.BossCodeFrameWidth
+ self._model = CogdoUtil.loadMazeModel('bossCog', group='gui')
+ self._model.reparentTo(self)
+ self._model.find('**/frame').setBin('fixed', 1)
+ titleLabelPos = self._model.find('**/title_label_loc').getPos()
+ self._titleLabel = DirectLabel(parent=self, relief=None, scale=Globals.BossGuiTitleLabelScale, text=TTLocalizer.CogdoMazeGameBossGuiTitle.upper(), pos=titleLabelPos, text_align=TextNode.ACenter, text_fg=(0, 0, 0, 1), text_shadow=(0, 0, 0, 0), text_font=ToontownGlobals.getSuitFont())
+ self._titleLabel.setBin('fixed', 1)
+ bossCard = self._model.find('**/bossCard')
+ self._openDoor = self._model.find('**/doorOpen')
+ self._closedDoor = self._model.find('**/doorClosed')
+ self._openDoor.stash()
+ spacingX = codeFrameWidth + codeFrameGap
+ startX = -0.5 * ((self._codeLength - 1) * spacingX - codeFrameGap)
+ for i in range(self._codeLength):
+ marker = CogdoMazeBossCodeFrame(i, self._code[i], bossCard)
+ marker.reparentTo(self)
+ marker.setPos(bossCard, startX + spacingX * i, 0, 0)
+ self._markers.append(marker)
+
+ bossCard.removeNode()
+ return
+
+ def showHit(self, bossIndex):
+ self._markers[bossIndex].setHit(True)
+
+ def showNumber(self, bossIndex):
+ self._markers[bossIndex].setHit(False)
+ self._markers[bossIndex].showNumber()
+ self._markersShown += 1
+ if self._markersShown == self._codeLength:
+ self._openDoor.unstash()
+ self._closedDoor.stash()
+ ToontownIntervals.start(ToontownIntervals.getPulseLargerIval(self._openDoor, 'bosscodedoor'))
+
+
+class CogdoMazeHud:
+
+ def __init__(self):
+ self._update = None
+ self._initQuestArrow()
+ return
+
+ def _initQuestArrow(self):
+ matchingGameGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui')
+ arrow = matchingGameGui.find('**/minnieArrow')
+ arrow.setScale(Globals.QuestArrowScale)
+ arrow.setColor(*Globals.QuestArrowColor)
+ arrow.setHpr(90, -90, 0)
+ self._questArrow = NodePath('Arrow')
+ arrow.reparentTo(self._questArrow)
+ self._questArrow.reparentTo(render)
+ self.hideQuestArrow()
+ matchingGameGui.removeNode()
+
+ def destroy(self):
+ self.__stopUpdateTask()
+ self._questArrow.removeNode()
+ self._questArrow = None
+ return
+
+ def showQuestArrow(self, parent, nodeToPoint, offset = Point3(0, 0, 0)):
+ self._questArrowNodeToPoint = nodeToPoint
+ self._questArrowParent = parent
+ self._questArrowOffset = offset
+ self._questArrow.unstash()
+ self._questArrowVisible = True
+ self.__startUpdateTask()
+
+ def hideQuestArrow(self):
+ self._questArrow.stash()
+ self.__stopUpdateTask()
+ self._questArrowVisible = False
+ self._questArrowNodeToPoint = None
+ return
+
+ def __startUpdateTask(self):
+ self.__stopUpdateTask()
+ self._update = taskMgr.add(self._updateTask, 'CogdoMazeHud_Update', 45)
+
+ def __stopUpdateTask(self):
+ if self._update is not None:
+ taskMgr.remove(self._update)
+ return
+
+ def _updateTask(self, task):
+ if self._questArrowVisible:
+ self._questArrow.setPos(self._questArrowParent, self._questArrowOffset)
+ self._questArrow.lookAt(self._questArrowNodeToPoint)
+ return Task.cont
diff --git a/toontown/cogdominium/CogdoMazeGameMovies.py b/toontown/cogdominium/CogdoMazeGameMovies.py
new file mode 100644
index 0000000..4057fe3
--- /dev/null
+++ b/toontown/cogdominium/CogdoMazeGameMovies.py
@@ -0,0 +1,195 @@
+from pandac.PandaModules import NodePath, Point3, PlaneNode
+from direct.showbase.ShowBase import Plane
+from direct.showbase.RandomNumGen import RandomNumGen
+from direct.interval.MetaInterval import Sequence, Parallel
+from direct.interval.FunctionInterval import Func, Wait
+from toontown.toonbase import TTLocalizer
+from toontown.suit import Suit, SuitDNA
+from toontown.toon import Toon, ToonHead, ToonDNA
+from CogdoUtil import CogdoGameMovie
+import CogdoMazeGameGlobals as Globals
+import CogdoUtil
+
+class CogdoMazeGameIntro(CogdoGameMovie):
+
+ def __init__(self, maze, exit, rng):
+ CogdoGameMovie.__init__(self)
+ self._maze = maze
+ self._exit = exit
+ self._rng = RandomNumGen(rng)
+ self._camTarget = None
+ self._state = 0
+ self._suits = []
+ return
+
+ def _getRandomLine(self, lineList):
+ return CogdoUtil.getRandomDialogueLine(lineList, self._rng)
+
+ def displayLine(self, who, text):
+ self._dialogueLabel.node().setText(text)
+ if who == 'toon':
+ self.toonHead.reparentTo(aspect2d)
+ self.cogHead.reparentTo(hidden)
+ self._toonDialogueSfx.play()
+ self.toonHead.setClipPlane(self.clipPlane)
+ else:
+ self.toonHead.reparentTo(hidden)
+ self.cogHead.reparentTo(aspect2d)
+ self._cogDialogueSfx.play()
+ self.cogHead.setClipPlane(self.clipPlane)
+
+ def makeSuit(self, suitType):
+ suit = Suit.Suit()
+ dna = SuitDNA.SuitDNA()
+ dna.newSuit(suitType)
+ suit.setStyle(dna)
+ suit.isDisguised = 1
+ suit.generateSuit()
+ suit.setScale(1, 1, 2)
+ suit.setPos(0, 0, -4.4)
+ suit.reparentTo(self.toonHead)
+ for part in suit.getHeadParts():
+ part.hide()
+
+ suit.loop('neutral')
+
+ def load(self):
+ CogdoGameMovie.load(self)
+ self.toonDNA = ToonDNA.ToonDNA()
+ self.toonDNA.newToonFromProperties('dss', 'ss', 'm', 'm', 2, 0, 2, 2, 1, 8, 1, 8, 1, 14)
+ self.toonHead = Toon.Toon()
+ self.toonHead.setDNA(self.toonDNA)
+ self.makeSuit('sc')
+ self.toonHead.getGeomNode().setDepthWrite(1)
+ self.toonHead.getGeomNode().setDepthTest(1)
+ self.toonHead.loop('neutral')
+ self.toonHead.setPosHprScale(-0.73, 0, -1.27, 180, 0, 0, 0.18, 0.18, 0.18)
+ self.toonHead.reparentTo(hidden)
+ self.toonHead.startBlink()
+ self.cogHead = Suit.Suit()
+ self.cogDNA = SuitDNA.SuitDNA()
+ self.cogDNA.newSuit('ms')
+ self.cogHead.setDNA(self.cogDNA)
+ self.cogHead.getGeomNode().setDepthWrite(1)
+ self.cogHead.getGeomNode().setDepthTest(1)
+ self.cogHead.loop('neutral')
+ self.cogHead.setPosHprScale(-0.73, 0, -1.46, 180, 0, 0, 0.14, 0.14, 0.14)
+ self.cogHead.reparentTo(hidden)
+ self.clipPlane = self.toonHead.attachNewNode(PlaneNode('clip'))
+ self.clipPlane.node().setPlane(Plane(0, 0, 1, 0))
+ self.clipPlane.setPos(0, 0, 2.45)
+ audioMgr = base.cogdoGameAudioMgr
+ self._cogDialogueSfx = audioMgr.createSfx('cogDialogue')
+ self._toonDialogueSfx = audioMgr.createSfx('toonDialogue')
+ suitData = Globals.SuitData[Globals.SuitTypes.Boss]
+ bossSuit = Suit.Suit()
+ d = SuitDNA.SuitDNA()
+ d.newSuit(suitData['dnaName'])
+ bossSuit.setDNA(d)
+ bossSuit.setScale(suitData['scale'])
+ bossSuit.loop('neutral')
+ bossSuit.reparentTo(render)
+ bossSuit.setPos(self._exit, -5, -5, 0)
+ bossSuit.lookAt(self._exit)
+ self._suits.append(bossSuit)
+ self._camHelperNode = NodePath('CamHelperNode')
+ self._camHelperNode.reparentTo(render)
+ dialogue = TTLocalizer.CogdoMazeIntroMovieDialogue
+ introDuration = Globals.IntroDurationSeconds
+ waitDuration = introDuration / len(dialogue)
+
+ def start():
+ camera.wrtReparentTo(render)
+ self._exit.open(animate=False)
+
+ def showBoss():
+ self._setCamTarget(bossSuit, 20, offset=Point3(0, 0, 7), angle=Point3(0, 15, 0))
+ bossSuit.loop('victory')
+ self._state = 1
+
+ def showExit():
+ self._setCamTarget(self._exit, 10, offset=Point3(0, 0, 0), angle=Point3(0, 60, 0))
+ self._exit.close()
+ self._state = 2
+
+ showExitIval = Parallel(camera.posInterval(waitDuration * 0.5, (10, -25, 20), other=self._exit, blendType='easeInOut'), Sequence(Wait(waitDuration * 0.25), Func(bossSuit.play, 'effort'), camera.hprInterval(waitDuration * 0.25, (30, -30, 0), blendType='easeInOut'), Func(self._exit.close), Wait(waitDuration * 0.5)))
+
+ def showWaterCooler():
+ wc = self._maze.getWaterCoolers()[0]
+ self._setCamTarget(wc, 25, angle=Point3(-30, 60, 0))
+ camera.wrtReparentTo(self._camHelperNode)
+ self._state = 3
+
+ def end():
+ self._stopUpdateTask()
+
+ self._ival = Sequence(Func(start), Func(self.displayLine, 'toon', self._getRandomLine(dialogue[0])), showExitIval, Func(showWaterCooler), Func(self.displayLine, 'toon', self._getRandomLine(dialogue[1])), Wait(waitDuration), Func(showBoss), bossSuit.hprInterval(1.0, bossSuit.getHpr() + Point3(180, 0, 0), blendType='easeInOut'), Func(self.displayLine, 'toon', self._getRandomLine(dialogue[2])), Wait(waitDuration - 1.0), Func(end))
+ self._startUpdateTask()
+
+ def _setCamTarget(self, targetNP, distance, offset = Point3(0, 0, 0), angle = Point3(0, 0, 0)):
+ camera.wrtReparentTo(render)
+ self._camTarget = targetNP
+ self._camOffset = offset
+ self._camAngle = angle
+ self._camDistance = distance
+ self._camHelperNode.setPos(self._camTarget, self._camOffset)
+ self._camHelperNode.setHpr(self._camTarget, 180 + self._camAngle[0], self._camAngle[1], self._camAngle[2])
+ camera.setPos(self._camHelperNode, 0, self._camDistance, 0)
+
+ def _updateTask(self, task):
+ dt = globalClock.getDt()
+ if self._state == 1:
+ self._camHelperNode.setPos(self._camTarget.getPos() + self._camOffset)
+ camera.setPos(self._camHelperNode, 0, self._camDistance, 0)
+ camera.lookAt(self._camTarget, 0, 0, 4)
+ elif self._state == 2:
+ camera.lookAt(self._camTarget, 0, 0, 5)
+ elif self._state == 3:
+ self._camHelperNode.setHpr(self._camHelperNode, dt, dt, 0)
+ camera.setY(camera, 0.8 * dt)
+ camera.lookAt(self._camTarget, 0, 0, 3)
+ return task.cont
+
+ def unload(self):
+ self._exit = None
+ self._camTarget = None
+ self._camHelperNode.removeNode()
+ del self._camHelperNode
+ for suit in self._suits:
+ suit.cleanup()
+ suit.removeNode()
+ suit.delete()
+
+ self._suits = []
+ CogdoGameMovie.unload(self)
+ del self._cogDialogueSfx
+ del self._toonDialogueSfx
+ self.toonHead.stopBlink()
+ self.toonHead.stop()
+ self.toonHead.removeNode()
+ self.toonHead.delete()
+ del self.toonHead
+ self.cogHead.stop()
+ self.cogHead.removeNode()
+ self.cogHead.delete()
+ del self.cogHead
+ return
+
+
+class CogdoMazeGameFinish(CogdoGameMovie):
+
+ def __init__(self, localPlayer, exit):
+ CogdoGameMovie.__init__(self)
+ self._localPlayer = localPlayer
+ self._exit = exit
+
+ def load(self):
+ CogdoGameMovie.load(self)
+ self._ival = Sequence()
+ if not self._exit.hasPlayer(self._localPlayer):
+ loseSfx = base.cogdoGameAudioMgr.createSfx('lose')
+ self._ival.append(Sequence(Func(loseSfx.play), Func(self._localPlayer.toon.setAnimState, 'Sad')))
+ self._ival.append(Sequence(Wait(Globals.FinishDurationSeconds - 1.0), Func(base.transitions.irisOut), Wait(1.0)))
+
+ def unload(self):
+ CogdoGameMovie.unload(self)
diff --git a/toontown/cogdominium/CogdoMazeGameObjects.py b/toontown/cogdominium/CogdoMazeGameObjects.py
new file mode 100644
index 0000000..914c7d7
--- /dev/null
+++ b/toontown/cogdominium/CogdoMazeGameObjects.py
@@ -0,0 +1,254 @@
+from pandac.PandaModules import CollisionSphere, CollisionTube, CollisionNode
+from pandac.PandaModules import NodePath, BitMask32
+from pandac.PandaModules import Point3, Point4, WaitInterval, Vec3, Vec4
+from direct.interval.IntervalGlobal import LerpScaleInterval, LerpColorScaleInterval, LerpPosInterval, LerpFunc
+from direct.interval.IntervalGlobal import Func, Sequence, Parallel
+from direct.showbase.DirectObject import DirectObject
+from direct.task.Task import Task
+from toontown.toonbase import ToontownGlobals
+import CogdoMazeGameGlobals as Globals
+from CogdoGameExit import CogdoGameExit
+import CogdoUtil
+import math
+import random
+
+class CogdoMazeSplattable:
+
+ def __init__(self, object, name, collisionRadius):
+ self.object = object
+ self.splat = CogdoUtil.loadMazeModel('splash')
+ self.splat.setBillboardPointEye()
+ self.splat.setBin('fixed', 40)
+ self.splat.setDepthTest(False)
+ self.splat.setDepthWrite(False)
+ self.splatTrack = None
+ self._splatSfxIval = base.cogdoGameAudioMgr.createSfxIval('splat')
+ self.initGagCollision(name, collisionRadius)
+ return
+
+ def destroy(self):
+ self.disableGagCollision()
+ if self._splatSfxIval.isPlaying():
+ self._splatSfxIval.finish()
+ del self._splatSfxIval
+
+ def initGagCollision(self, name, radius):
+ self.gagCollisionName = name
+ collision = CollisionTube(0, 0, 0, 0, 0, 4, radius)
+ collision.setTangible(1)
+ self.gagCollNode = CollisionNode(self.gagCollisionName)
+ self.gagCollNode.setIntoCollideMask(ToontownGlobals.PieBitmask)
+ self.gagCollNode.addSolid(collision)
+ self.gagCollNodePath = self.object.attachNewNode(self.gagCollNode)
+
+ def disableGagCollision(self):
+ self.gagCollNodePath.removeNode()
+
+ def doSplat(self):
+ if self.splatTrack and self.splatTrack.isPlaying():
+ self.splatTrack.finish()
+ self.splat.reparentTo(render)
+ self.splat.setPos(self.object, 0, 0, 3.0)
+ self.splat.setY(self.splat.getY() - 1.0)
+ self._splatSfxIval.node = self.splat
+ self.splatTrack = Parallel(self._splatSfxIval, Sequence(Func(self.splat.showThrough), LerpScaleInterval(self.splat, duration=0.5, scale=6, startScale=1, blendType='easeOut'), Func(self.splat.hide)))
+ self.splatTrack.start()
+
+
+class CogdoMazeDrop(NodePath, DirectObject):
+
+ def __init__(self, game, id, x, y):
+ NodePath.__init__(self, 'dropNode%s' % id)
+ self.game = game
+ self.id = id
+ self.reparentTo(hidden)
+ self.setPos(x, y, 0)
+ shadow = loader.loadModel('phase_3/models/props/square_drop_shadow')
+ shadow.setZ(0.2)
+ shadow.setBin('ground', 10)
+ shadow.setColor(1, 1, 1, 1)
+ shadow.reparentTo(self)
+ self.shadow = shadow
+ drop = CogdoUtil.loadMazeModel('cabinetSmFalling')
+ roll = random.randint(-15, 15)
+ drop.setHpr(0, 0, roll)
+ drop.setZ(Globals.DropHeight)
+ self.collTube = CollisionTube(0, 0, 0, 0, 0, 4, Globals.DropCollisionRadius)
+ self.collTube.setTangible(0)
+ name = Globals.DropCollisionName
+ self.collNode = CollisionNode(name)
+ self.collNode.addSolid(self.collTube)
+ self.collNodePath = drop.attachNewNode(self.collNode)
+ self.collNodePath.hide()
+ self.collNodePath.setTag('isFalling', str('True'))
+ drop.reparentTo(self)
+ self.drop = drop
+ self._dropSfx = base.cogdoGameAudioMgr.createSfxIval('drop', volume=0.6)
+
+ def disableCollisionDamage(self):
+ self.collTube.setTangible(1)
+ self.collTube.setRadius(Globals.DroppedCollisionRadius)
+ self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
+ self.collNodePath.setTag('isFalling', str('False'))
+
+ def getDropIval(self):
+ shadow = self.shadow
+ drop = self.drop
+ id = self.id
+ hangTime = Globals.ShadowTime
+ dropTime = Globals.DropTime
+ dropHeight = Globals.DropHeight
+ targetShadowScale = 0.5
+ targetShadowAlpha = 0.4
+ shadowScaleIval = LerpScaleInterval(shadow, dropTime, targetShadowScale, startScale=0)
+ shadowAlphaIval = LerpColorScaleInterval(shadow, hangTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0))
+ shadowIval = Parallel(shadowScaleIval, shadowAlphaIval)
+ startPos = Point3(0, 0, dropHeight)
+ drop.setPos(startPos)
+ dropIval = LerpPosInterval(drop, dropTime, Point3(0, 0, 0), startPos=startPos, blendType='easeIn')
+ dropSoundIval = self._dropSfx
+ dropSoundIval.node = self
+ self.drop.setTransparency(1)
+
+ def _setRandScale(t):
+ self.drop.setScale(self, 1 - random.random() / 16, 1 - random.random() / 16, 1 - random.random() / 4)
+
+ scaleChange = 0.4 + random.random() / 4
+ dropShakeSeq = Sequence(LerpScaleInterval(self.drop, 0.25, Vec3(1.0 + scaleChange, 1.0 + scaleChange / 2, 1.0 - scaleChange), blendType='easeInOut'), LerpScaleInterval(self.drop, 0.25, Vec3(1.0, 1.0, 1.0), blendType='easeInOut'), Func(self.disableCollisionDamage), LerpScaleInterval(self.drop, 0.2, Vec3(1.0 + scaleChange / 8, 1.0 + scaleChange / 8, 1.0 - scaleChange / 8), blendType='easeInOut'), LerpScaleInterval(self.drop, 0.2, Vec3(1.0, 1.0, 1.0), blendType='easeInOut'), LerpScaleInterval(self.drop, 0.15, Vec3(1.0 + scaleChange / 16, 1.0 + scaleChange / 16, 1.0 - scaleChange / 16), blendType='easeInOut'), LerpScaleInterval(self.drop, 0.15, Vec3(1.0, 1.0, 1.0), blendType='easeInOut'), LerpScaleInterval(self.drop, 0.1, Vec3(1.0 + scaleChange / 16, 1.0 + scaleChange / 8, 1.0 - scaleChange / 16), blendType='easeInOut'), LerpColorScaleInterval(self.drop, Globals.DropFadeTime, Vec4(1.0, 1.0, 1.0, 0.0)))
+ ival = Sequence(Func(self.reparentTo, render), Parallel(Sequence(WaitInterval(hangTime), dropIval), shadowIval), Parallel(Func(self.game.dropHit, self, id), dropSoundIval, dropShakeSeq), Func(self.game.cleanupDrop, id), name='drop%s' % id)
+ self.ival = ival
+ return ival
+
+ def destroy(self):
+ self.ival.pause()
+ self.ival = None
+ self._dropSfx.pause()
+ self._dropSfx = None
+ self.collTube = None
+ self.collNode = None
+ self.collNodePath.removeNode()
+ self.collNodePath = None
+ self.removeNode()
+ return
+
+
+class CogdoMazeExit(CogdoGameExit, DirectObject):
+ EnterEventName = 'CogdoMazeDoor_Enter'
+
+ def __init__(self):
+ CogdoGameExit.__init__(self)
+ self.revealed = False
+ self._players = []
+ self._initCollisions()
+
+ def _initCollisions(self):
+ collSphere = CollisionSphere(0, 0, 0, 3.0)
+ collSphere.setTangible(0)
+ self.collNode = CollisionNode(self.getName())
+ self.collNode.addSolid(collSphere)
+ self.collNP = self.attachNewNode(self.collNode)
+
+ def destroy(self):
+ self.ignoreAll()
+ CogdoGameExit.destroy(self)
+
+ def enable(self):
+ self.collNode.setFromCollideMask(ToontownGlobals.WallBitmask)
+ self.accept('enter' + self.getName(), self._handleEnterCollision)
+
+ def disable(self):
+ self.ignore('enter' + self.getName())
+ self.collNode.setFromCollideMask(BitMask32(0))
+
+ def _handleEnterCollision(self, collEntry):
+ messenger.send(CogdoMazeExit.EnterEventName, [self])
+
+ def onstage(self):
+ self.unstash()
+ self.enable()
+
+ def offstage(self):
+ self.stash()
+ self.disable()
+
+ def playerEntersDoor(self, player):
+ if player not in self._players:
+ self._players.append(player)
+ self.toonEnters(player.toon)
+
+ def getPlayerCount(self):
+ return len(self._players)
+
+ def hasPlayer(self, player):
+ return player in self._players
+
+
+class CogdoMazeWaterCooler(NodePath, DirectObject):
+ UpdateTaskName = 'CogdoMazeWaterCooler_Update'
+
+ def __init__(self, serialNum, model):
+ NodePath.__init__(self, 'CogdoMazeWaterCooler-%i' % serialNum)
+ self.serialNum = serialNum
+ self._model = model
+ self._model.reparentTo(self)
+ self._model.setPosHpr(0, 0, 0, 0, 0, 0)
+ self._initCollisions()
+ self._initArrow()
+ self._update = None
+ self.__startUpdateTask()
+ return
+
+ def destroy(self):
+ self.ignoreAll()
+ self.__stopUpdateTask()
+ self.collNodePath.removeNode()
+ self.removeNode()
+
+ def _initCollisions(self):
+ offset = Globals.WaterCoolerTriggerOffset
+ self.collSphere = CollisionSphere(offset[0], offset[1], offset[2], Globals.WaterCoolerTriggerRadius)
+ self.collSphere.setTangible(0)
+ name = Globals.WaterCoolerCollisionName
+ self.collNode = CollisionNode(name)
+ self.collNode.addSolid(self.collSphere)
+ self.collNodePath = self.attachNewNode(self.collNode)
+
+ def _initArrow(self):
+ matchingGameGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui')
+ arrow = matchingGameGui.find('**/minnieArrow')
+ arrow.setScale(Globals.CoolerArrowScale)
+ arrow.setColor(*Globals.CoolerArrowColor)
+ arrow.setPos(0, 0, Globals.CoolerArrowZ)
+ arrow.setHpr(0, 0, 90)
+ arrow.setBillboardAxis()
+ self._arrow = NodePath('Arrow')
+ arrow.reparentTo(self._arrow)
+ self._arrow.reparentTo(self)
+ self._arrowTime = 0
+ self.accept(Globals.WaterCoolerShowEventName, self.showArrow)
+ self.accept(Globals.WaterCoolerHideEventName, self.hideArrow)
+ matchingGameGui.removeNode()
+
+ def showArrow(self):
+ self._arrow.unstash()
+
+ def hideArrow(self):
+ self._arrow.stash()
+
+ def update(self, dt):
+ newZ = math.sin(globalClock.getFrameTime() * Globals.CoolerArrowSpeed) * Globals.CoolerArrowBounce
+ self._arrow.setZ(newZ)
+
+ def __startUpdateTask(self):
+ self.__stopUpdateTask()
+ self._update = taskMgr.add(self._updateTask, self.UpdateTaskName, 45)
+
+ def __stopUpdateTask(self):
+ if self._update is not None:
+ taskMgr.remove(self._update)
+ return
+
+ def _updateTask(self, task):
+ dt = globalClock.getDt()
+ self.update(dt)
+ return Task.cont
diff --git a/toontown/cogdominium/CogdoMazeGuiManager.py b/toontown/cogdominium/CogdoMazeGuiManager.py
new file mode 100644
index 0000000..8373ad2
--- /dev/null
+++ b/toontown/cogdominium/CogdoMazeGuiManager.py
@@ -0,0 +1,136 @@
+from pandac.PandaModules import NodePath
+from direct.interval.MetaInterval import Sequence
+from direct.interval.FunctionInterval import Func
+from toontown.toonbase.ToontownTimer import ToontownTimer
+from toontown.toonbase import ToontownIntervals
+from CogdoMazeGameGuis import CogdoMazeHud, CogdoMazeMapGui, CogdoMazeBossGui
+from CogdoGameMessageDisplay import CogdoGameMessageDisplay
+import CogdoMazeGameGlobals as Globals
+from CogdoMemoGui import CogdoMemoGui
+
+class CogdoMazeGuiManager:
+
+ def __init__(self, maze, bossCode):
+ self.maze = maze
+ self.root = NodePath('CogdoMazeGui')
+ self.root.reparentTo(aspect2d)
+ self.mazeMapGui = CogdoMazeMapGui(self.maze.collisionTable)
+ if bossCode is not None:
+ self._bossGui = CogdoMazeBossGui(bossCode)
+ else:
+ self._bossGui = None
+ self._memoGui = CogdoMemoGui(self.root)
+ self._memoGui.posNextToLaffMeter()
+ self._presentGuiIval = None
+ self._presentTimerIval = None
+ self._hud = CogdoMazeHud()
+ self._timer = None
+ self._initMessageDisplay()
+ return
+
+ def _initTimer(self):
+ self._timer = ToontownTimer()
+ self._timer.hide()
+ self._timer.posInTopRightCorner()
+
+ def _initMessageDisplay(self):
+ self.messageDisplay = CogdoGameMessageDisplay('CogdoMazeMessageDisplay', self.root, pos=Globals.MessageLabelPos)
+
+ def destroy(self):
+ ToontownIntervals.cleanup('present_gui')
+ ToontownIntervals.cleanup('present_timer')
+ ToontownIntervals.cleanup('present_memo')
+ self._hud.destroy()
+ self._hud = None
+ self._memoGui.destroy()
+ self._memoGui = None
+ if self._bossGui is not None:
+ self._bossGui.destroy()
+ self._bossGui = None
+ self.messageDisplay.destroy()
+ self.messageDisplay = None
+ self.destroyMazeMap()
+ self.destroyTimer()
+ if self._presentGuiIval:
+ self._presentGuiIval.pause()
+ self._presentGuiIval = None
+ if self._presentTimerIval:
+ self._presentTimerIval.pause()
+ self._presentTimerIval = None
+ return
+
+ def destroyMazeMap(self):
+ if hasattr(self, 'mazeMapGui') and self.mazeMapGui is not None:
+ self.mazeMapGui.destroy()
+ del self.mazeMapGui
+ return
+
+ def destroyTimer(self):
+ if self._timer is not None:
+ self._timer.stop()
+ self._timer.destroy()
+ self._timer = None
+ return
+
+ def showPickupCounter(self):
+ ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._memoGui, 'present_memo'))
+
+ def startGame(self, firstMessage):
+ self._presentGuiIval = ToontownIntervals.start(Sequence(ToontownIntervals.getPresentGuiIval(self._bossGui, '', startPos=(0, 0, -0.15)), Func(self.mazeMapGui.show), ToontownIntervals.getPulseLargerIval(self.mazeMapGui, '', scale=self.mazeMapGui.getScale()), Func(self.setMessage, firstMessage), name='present_gui'))
+
+ def hideMazeMap(self):
+ self.mazeMapGui.hide()
+
+ def showBossGui(self):
+ if self._bossGui is not None:
+ self._bossGui.show()
+ return
+
+ def hideBossGui(self):
+ if self._bossGui is not None:
+ self._bossGui.hide()
+ return
+
+ def revealMazeMap(self):
+ self.mazeMapGui.revealAll()
+
+ def hideLock(self, lockIndex):
+ self.mazeMapGui.hideLock(lockIndex)
+
+ def showTimer(self, duration, timerExpiredCallback = None):
+ if self._timer is None:
+ self._initTimer()
+ self._timer.setTime(duration)
+ self._timer.countdown(duration, timerExpiredCallback)
+ self._presentTimerIval = ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._timer, 'present_timer', startPos=(0, 0, 0.35)))
+ return
+
+ def hideTimer(self):
+ if hasattr(self, 'timer') and self._timer is not None:
+ self._timer.hide()
+ self._timer.stop()
+ return
+
+ def setMessage(self, text, color = None, transition = 'fade'):
+ self.messageDisplay.updateMessage(text, color, transition)
+
+ def setMessageTemporary(self, text, time = 3.0):
+ self.messageDisplay.showMessageTemporarily(text, time)
+
+ def clearMessage(self):
+ self.messageDisplay.updateMessage('')
+
+ def setPickupCount(self, count):
+ self._memoGui.setCount(count)
+
+ def showBossCode(self, bossIndex):
+ self._bossGui.showNumber(bossIndex)
+
+ def showBossHit(self, bossIndex):
+ self._bossGui.showHit(bossIndex)
+
+ def showQuestArrow(self, toon, target, offset):
+ self._hud.showQuestArrow(toon, target, offset)
+
+ def hideQuestArrow(self):
+ self._hud.hideQuestArrow()
diff --git a/toontown/cogdominium/CogdoMazeLocalPlayer.py b/toontown/cogdominium/CogdoMazeLocalPlayer.py
new file mode 100644
index 0000000..c5d15b4
--- /dev/null
+++ b/toontown/cogdominium/CogdoMazeLocalPlayer.py
@@ -0,0 +1,269 @@
+from pandac.PandaModules import Point3, CollisionNode, CollisionSphere, CollisionHandlerEvent
+from direct.interval.IntervalGlobal import Func, Sequence, Wait
+from direct.showbase.PythonUtil import bound as clamp
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import TTLocalizer
+from toontown.minigame.OrthoDrive import OrthoDrive
+from toontown.minigame.OrthoWalk import OrthoWalk
+from toontown.toonbase import ToontownGlobals
+import CogdoGameConsts
+import CogdoMazeGameGlobals as Globals
+from CogdoMazePlayer import CogdoMazePlayer
+from CogdoMazeCameraManager import CogdoMazeCameraManager
+
+class CogdoMazeLocalPlayer(CogdoMazePlayer):
+ notify = directNotify.newCategory('CogdoMazeLocalPlayer')
+
+ def __init__(self, id, toon, game, guiMgr):
+ CogdoMazePlayer.__init__(self, id, toon)
+ self.disableGagCollision()
+ self.game = game
+ self.maze = self.game.maze
+ self._guiMgr = guiMgr
+ self.cameraMgr = CogdoMazeCameraManager(self.toon, self.maze, camera, render)
+ self._proximityRadius = self.maze.cellWidth * Globals.CameraRemoteToonRadius
+ orthoDrive = OrthoDrive(Globals.ToonRunSpeed, maxFrameMove=self.maze.cellWidth / 2, customCollisionCallback=self.maze.doOrthoCollisions, wantSound=True)
+ self.orthoWalk = OrthoWalk(orthoDrive)
+ self._audioMgr = base.cogdoGameAudioMgr
+ self._getMemoSfx = self._audioMgr.createSfx('getMemo', source=self.toon)
+ self._waterCoolerFillSfx = self._audioMgr.createSfx('waterCoolerFill', source=self.toon)
+ self._hitByDropSfx = self._audioMgr.createSfx('toonHitByDrop', source=self.toon)
+ self._winSfx = self._audioMgr.createSfx('win')
+ self._loseSfx = self._audioMgr.createSfx('lose')
+ self.enabled = False
+ self.pickupCount = 0
+ self.numEntered = 0
+ self.throwPending = False
+ self.coolDownAfterHitInterval = Sequence(Wait(Globals.HitCooldownTime), Func(self.setInvulnerable, False), name='coolDownAfterHitInterval-%i' % self.toon.doId)
+ self.invulnerable = False
+ self.gagHandler = CollisionHandlerEvent()
+ self.gagHandler.addInPattern('%fn-into-%in')
+ self.exited = False
+ self.hints = {'find': False,
+ 'throw': False,
+ 'squashed': False,
+ 'boss': False,
+ 'minion': False}
+ self.accept('control', self.controlKeyPressed)
+
+ def destroy(self):
+ self.toon.showName()
+ self.ignoreAll()
+ self.coolDownAfterHitInterval.clearToInitial()
+ del self.coolDownAfterHitInterval
+ del self._getMemoSfx
+ del self._waterCoolerFillSfx
+ del self._hitByDropSfx
+ del self._winSfx
+ self.orthoWalk.stop()
+ self.orthoWalk.destroy()
+ del self.orthoWalk
+ CogdoMazePlayer.destroy(self)
+
+ def __initCollisions(self):
+ collSphere = CollisionSphere(0, 0, 0, Globals.PlayerCollisionRadius)
+ collSphere.setTangible(0)
+ self.mazeCollisionName = Globals.LocalPlayerCollisionName
+ collNode = CollisionNode(self.mazeCollisionName)
+ collNode.addSolid(collSphere)
+ collNodePath = self.toon.attachNewNode(collNode)
+ collNodePath.hide()
+ handler = CollisionHandlerEvent()
+ handler.addInPattern('%fn-into-%in')
+ base.cTrav.addCollider(collNodePath, handler)
+ self.handler = handler
+ self._collNodePath = collNodePath
+
+ def clearCollisions(self):
+ self.handler.clear()
+
+ def __disableCollisions(self):
+ self._collNodePath.removeNode()
+ del self._collNodePath
+
+ def _isNearPlayer(self, player):
+ return self.toon.getDistance(player.toon) <= self._proximityRadius
+
+ def update(self, dt):
+ if self.getCurrentOrNextState() != 'Off':
+ self._updateCamera(dt)
+
+ def _updateCamera(self, dt):
+ numPlayers = 0.0
+ for player in self.game.players:
+ if player != self and player.toon and self._isNearPlayer(player):
+ numPlayers += 1
+
+ d = clamp(Globals.CameraMinDistance + numPlayers / (CogdoGameConsts.MaxPlayers - 1) * (Globals.CameraMaxDistance - Globals.CameraMinDistance), Globals.CameraMinDistance, Globals.CameraMaxDistance)
+ self.cameraMgr.setCameraTargetDistance(d)
+ self.cameraMgr.update(dt)
+
+ def enterOff(self):
+ CogdoMazePlayer.enterOff(self)
+
+ def exitOff(self):
+ CogdoMazePlayer.exitOff(self)
+ self.toon.hideName()
+
+ def enterReady(self):
+ CogdoMazePlayer.enterReady(self)
+ self.cameraMgr.enable()
+
+ def exitReady(self):
+ CogdoMazePlayer.enterReady(self)
+
+ def enterNormal(self):
+ CogdoMazePlayer.enterNormal(self)
+ self.orthoWalk.start()
+
+ def exitNormal(self):
+ CogdoMazePlayer.exitNormal(self)
+ self.orthoWalk.stop()
+
+ def enterHit(self, elapsedTime = 0.0):
+ CogdoMazePlayer.enterHit(self, elapsedTime)
+ self.setInvulnerable(True)
+
+ def exitHit(self):
+ CogdoMazePlayer.exitHit(self)
+ self.coolDownAfterHitInterval.clearToInitial()
+ self.coolDownAfterHitInterval.start()
+
+ def enterDone(self):
+ CogdoMazePlayer.enterDone(self)
+ self._guiMgr.hideQuestArrow()
+ self.ignore('control')
+ self._guiMgr.setMessage('')
+ if self.exited == False:
+ self.lostMemos()
+
+ def exitDone(self):
+ CogdoMazePlayer.exitDone(self)
+
+ def hitByDrop(self):
+ if self.equippedGag is not None and not self.hints['squashed']:
+ self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeSquashHint, Globals.HintTimeout)
+ self.hints['squashed'] = True
+ self._hitByDropSfx.play()
+ CogdoMazePlayer.hitByDrop(self)
+ return
+
+ def equipGag(self):
+ CogdoMazePlayer.equipGag(self)
+ self._waterCoolerFillSfx.play()
+ messenger.send(Globals.WaterCoolerHideEventName, [])
+ if not self.hints['throw']:
+ self._guiMgr.setMessage(TTLocalizer.CogdoMazeThrowHint)
+ self.hints['throw'] = True
+
+ def hitSuit(self, suitType):
+ if suitType == Globals.SuitTypes.Boss and not self.hints['boss']:
+ self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeBossHint, Globals.HintTimeout)
+ self.hints['boss'] = True
+ if suitType != Globals.SuitTypes.Boss and not self.hints['minion']:
+ self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeMinionHint, Globals.HintTimeout)
+ self.hints['minion'] = True
+
+ def createThrowGag(self, gag):
+ throwGag = CogdoMazePlayer.createThrowGag(self, gag)
+ collSphere = CollisionSphere(0, 0, 0, 0.5)
+ collSphere.setTangible(0)
+ name = Globals.GagCollisionName
+ collNode = CollisionNode(name)
+ collNode.setFromCollideMask(ToontownGlobals.PieBitmask)
+ collNode.addSolid(collSphere)
+ colNp = throwGag.attachNewNode(collNode)
+ base.cTrav.addCollider(colNp, self.gagHandler)
+ return throwGag
+
+ def showToonThrowingGag(self, heading, pos):
+ self._guiMgr.clearMessage()
+ return CogdoMazePlayer.showToonThrowingGag(self, heading, pos)
+
+ def removeGag(self):
+ if self.equippedGag is None:
+ return
+ CogdoMazePlayer.removeGag(self)
+ self.throwPending = False
+ messenger.send(Globals.WaterCoolerShowEventName, [])
+ return
+
+ def controlKeyPressed(self):
+ if self.game.finished or self.throwPending or self.getCurrentOrNextState() == 'Hit' or self.equippedGag == None:
+ return
+ self.throwPending = True
+ heading = self.toon.getH()
+ pos = self.toon.getPos()
+ self.game.requestUseGag(pos.getX(), pos.getY(), heading)
+ return
+
+ def completeThrow(self):
+ self.clearCollisions()
+ CogdoMazePlayer.completeThrow(self)
+
+ def shakeCamera(self, strength):
+ self.cameraMgr.shake(strength)
+
+ def getCameraShake(self):
+ return self.cameraMgr.shakeStrength
+
+ def setInvulnerable(self, bool):
+ self.invulnerable = bool
+
+ def handleGameStart(self):
+ self.numEntered = len(self.game.players)
+ self.__initCollisions()
+ self._guiMgr.startGame(TTLocalizer.CogdoMazeFindHint)
+ self.hints['find'] = True
+ self.notify.info('toonId:%d laff:%d/%d %d player(s) started maze game' % (self.toon.doId,
+ self.toon.hp,
+ self.toon.maxHp,
+ len(self.game.players)))
+
+ def handleGameExit(self):
+ self.cameraMgr.disable()
+ self.__disableCollisions()
+
+ def handlePickUp(self, toonId):
+ if toonId == self.toon.doId:
+ self.pickupCount += 1
+ self._guiMgr.setPickupCount(self.pickupCount)
+ if self.pickupCount == 1:
+ self._guiMgr.showPickupCounter()
+ self._getMemoSfx.play()
+
+ def handleOpenDoor(self, door):
+ self._guiMgr.setMessage(TTLocalizer.CogdoMazeGameDoorOpens)
+ self._guiMgr.showQuestArrow(self.toon, door, Point3(0, 0, self.toon.getHeight() + 2))
+
+ def handleTimeAlert(self):
+ self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameTimeAlert)
+
+ def handleToonRevealsDoor(self, toonId, door):
+ if toonId == self.toon.doId:
+ self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameLocalToonFoundExit)
+
+ def handleToonEntersDoor(self, toonId, door):
+ self.exited = True
+ message = ''
+ if door.getPlayerCount() < len(self.game.players):
+ message = TTLocalizer.CogdoMazeGameWaitingForToons
+ if toonId == self.toon.doId:
+ self._guiMgr.setMessage(message)
+ self._winSfx.play()
+ self._audioMgr.stopMusic()
+ self.notify.info('toonId:%d laff:%d/%d %d player(s) succeeded in maze game. Going to the executive suit building.' % (toonId,
+ self.toon.hp,
+ self.toon.maxHp,
+ len(self.game.players)))
+ if self.numEntered > len(self.game.players):
+ self.notify.info('%d player(s) failed in maze game' % (self.numEntered - len(self.game.players)))
+
+ def lostMemos(self):
+ self.pickupCount = 0
+ self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameTimeOut)
+ self._guiMgr.setPickupCount(self.pickupCount)
+ self.notify.info('toonId:%d laff:%d/%d %d player(s) failed in maze game' % (self.toon.doId,
+ self.toon.hp,
+ self.toon.maxHp,
+ len(self.game.players)))
diff --git a/toontown/cogdominium/CogdoMazePlayer.py b/toontown/cogdominium/CogdoMazePlayer.py
new file mode 100644
index 0000000..c94dc51
--- /dev/null
+++ b/toontown/cogdominium/CogdoMazePlayer.py
@@ -0,0 +1,190 @@
+from pandac.PandaModules import Point3, NodePath
+from direct.fsm.FSM import FSM
+from direct.interval.IntervalGlobal import ProjectileInterval, Track, ActorInterval
+from direct.interval.IntervalGlobal import Func, Sequence, Parallel
+from CogdoMazeGameObjects import CogdoMazeSplattable
+import CogdoMazeGameGlobals as Globals
+import CogdoUtil
+import random
+
+class CogdoMazePlayer(FSM, CogdoMazeSplattable):
+ notify = directNotify.newCategory('CogdoMazePlayer')
+ _key = None
+ GagHitEventName = 'CogdoMazePlayer_GagHit'
+ RemovedEventName = 'CogdoMazePlayer_Removed'
+
+ def __init__(self, id, toon):
+ FSM.__init__(self, 'CogdoMazePlayer')
+ CogdoMazeSplattable.__init__(self, toon, '%s-%i' % (Globals.PlayerCollisionName, id), 0.5)
+ self.id = id
+ self.toon = toon
+ self.defaultTransitions = {'Off': ['Ready'],
+ 'Ready': ['Normal', 'Off'],
+ 'Normal': ['Hit', 'Done', 'Off'],
+ 'Hit': ['Normal', 'Done', 'Off'],
+ 'Done': ['Off']}
+ self.toon.reparentTo(render)
+ self.gagModel = CogdoUtil.loadMazeModel('waterBalloon')
+ self.equippedGag = None
+ self._toonHitSfx = base.cogdoGameAudioMgr.createSfx('toonHit', self.toon)
+ self._throwSfx = base.cogdoGameAudioMgr.createSfxIval('throw')
+ self.accept(toon.getDisableEvent(), self.removed)
+ self.request('Off')
+ return
+
+ def destroy(self):
+ if self.equippedGag:
+ self.removeGag()
+ del self._toonHitSfx
+ del self._throwSfx
+ CogdoMazeSplattable.destroy(self)
+
+ def enterOff(self):
+ self.toon.setAnimState('Happy', 1.0)
+ self.toon.setSpeed(0, 0)
+
+ def exitOff(self):
+ pass
+
+ def enterReady(self):
+ pass
+
+ def exitReady(self):
+ pass
+
+ def enterNormal(self):
+ self.toon.startSmooth()
+ if self.equippedGag is not None:
+ self.equippedGag.unstash()
+ return
+
+ def exitNormal(self):
+ self.toon.stopSmooth()
+ if self.equippedGag is not None:
+ self.equippedGag.stash()
+ return
+
+ def enterHit(self, elapsedTime = 0.0):
+ animationInfo = Globals.ToonAnimationInfo['hit']
+ self._hitIval = self._getToonAnimationIval(animationInfo[0], duration=animationInfo[1], startFrame=animationInfo[2], nextState='Normal')
+ self._hitIval.start(elapsedTime)
+ self._toonHitSfx.play()
+
+ def exitHit(self):
+ self._hitIval.pause()
+ del self._hitIval
+ if self.equippedGag is not None:
+ self.toon.setAnimState('Catching', 1.0)
+ else:
+ self.toon.setAnimState('Happy', 1.0)
+ return
+
+ def enterDone(self):
+ self.toon.setAnimState('off', 1.0)
+
+ def filterDone(self, request, args):
+ if request == 'Done':
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitDone(self):
+ pass
+
+ def handleGameStart(self):
+ self.accept(Globals.GagCollisionName + '-into-' + self.gagCollisionName, self.handleGagHit)
+
+ def hitByDrop(self):
+ if self.state == 'Normal':
+ self.request('Hit')
+ if self.equippedGag != None:
+ self.removeGag()
+ return
+
+ def handleGagHit(self, collEntry):
+ gagNodePath = collEntry.getFromNodePath().getParent()
+ messenger.send(self.GagHitEventName, [self.toon.doId, gagNodePath])
+
+ def hitByGag(self):
+ self.doSplat()
+
+ def equipGag(self):
+ if self.equippedGag != None:
+ return
+ self.toon.setAnimState('Catching')
+ holdingGag = self.gagModel.copyTo(self.toon.leftHand)
+ holdingGag.setScale(Globals.GagPickupScale)
+ color = random.choice(Globals.GagColors)
+ holdingGag.setColorScale(color)
+ holdingGag.setZ(-0.2)
+ holdingGag.setX(self.toon, 0)
+ holdingGag.setHpr(0, 0, 0)
+ self.equippedGag = holdingGag
+ return
+
+ def removeGag(self):
+ if self.equippedGag is None:
+ return
+ self.toon.setAnimState('Happy')
+ self.equippedGag.detachNode()
+ self.equippedGag = None
+ return
+
+ def createThrowGag(self, gag):
+ throwGag = gag.copyTo(NodePath('gag'))
+ return throwGag
+
+ def removed(self):
+ messenger.send(self.RemovedEventName, [self])
+
+ def showToonThrowingGag(self, heading, pos):
+ gag = self.equippedGag
+ if gag is None:
+ return
+ self.removeGag()
+ tossTrack, flyTrack, object = self.getThrowInterval(gag, pos[0], pos[1], pos[2], heading, 0, 0)
+
+ def matchRunningAnim(toon = self.toon):
+ toon.playingAnim = None
+ toon.setSpeed(toon.forwardSpeed, toon.rotateSpeed)
+ return
+
+ newTossTrack = Sequence(tossTrack, Func(matchRunningAnim))
+ throwTrack = Parallel(newTossTrack, flyTrack)
+ throwTrack.start(0)
+ return object
+
+ def completeThrow(self):
+ self.toon.loop('neutral')
+
+ def getThrowInterval(self, gag, x, y, z, h, p, r):
+ toon = self.toon
+ flyGag = self.createThrowGag(gag)
+ throwSoundIval = self._throwSfx
+ if throwSoundIval.isPlaying():
+ throwSoundIval.finish()
+ throwSoundIval.node = toon
+ toonThrowIval1 = ActorInterval(toon, 'throw', startFrame=Globals.ThrowStartFrame, endFrame=Globals.ThrowEndFrame, playRate=Globals.ThrowPlayRate, partName='torso')
+ toss = Track((0, Sequence(Func(toon.setPosHpr, x, y, z, h, p, r), Func(gag.reparentTo, toon.rightHand), Func(gag.setPosHpr, 0, 0, 0, 0, 0, 0), toonThrowIval1)), (toonThrowIval1.getDuration(), Parallel(Func(gag.detachNode), Sequence(ActorInterval(toon, 'throw', startFrame=Globals.ThrowEndFrame + 1, playRate=Globals.ThrowPlayRate, partName='torso'), Func(self.completeThrow)))))
+
+ def getEndPos(toon = toon):
+ return render.getRelativePoint(toon, Point3(0, Globals.ThrowDistance, 0))
+
+ fly = Track((0, throwSoundIval), (toonThrowIval1.getDuration(), Sequence(Func(flyGag.reparentTo, render), Func(flyGag.setPosHpr, toon, 0.52, 0.97, 2.24, 0, -45, 0), ProjectileInterval(flyGag, endPos=getEndPos, duration=Globals.ThrowDuration), Func(flyGag.detachNode))))
+ return (toss, fly, flyGag)
+
+ def _getToonAnimationIval(self, animName, startFrame = 0, duration = 1, nextState = None):
+ totalFrames = self.toon.getNumFrames(animName)
+ frames = totalFrames - 1 - startFrame
+ frameRate = self.toon.getFrameRate(animName)
+ newRate = frames / duration
+ playRate = newRate / frameRate
+ ival = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate))
+ if nextState is not None:
+
+ def done():
+ self.request(nextState)
+
+ ival.append(Func(done))
+ return ival
diff --git a/toontown/cogdominium/CogdoMazeSuits.py b/toontown/cogdominium/CogdoMazeSuits.py
new file mode 100644
index 0000000..cf7f9e5
--- /dev/null
+++ b/toontown/cogdominium/CogdoMazeSuits.py
@@ -0,0 +1,264 @@
+from pandac.PandaModules import Point3, VBase4
+from direct.fsm.FSM import FSM
+from direct.interval.IntervalGlobal import Sequence, Parallel, ActorInterval, Func, Wait, ParticleInterval, Track, LerpColorScaleInterval, LerpScaleInterval, LerpHprInterval
+from direct.task.Task import Task
+from toontown.battle import BattleParticles
+from toontown.battle import MovieUtil
+from toontown.minigame.MazeSuit import MazeSuit
+from CogdoMazeGameObjects import CogdoMazeSplattable
+import CogdoMazeGameGlobals as Globals
+import random
+
+class CogdoMazeSuit(MazeSuit, FSM, CogdoMazeSplattable):
+ GagHitEventName = 'CogdoMazeSuit_GagHit'
+ DeathEventName = 'CogdoMazeSuit_Death'
+ ThinkEventName = 'CogdoMazeSuit_Think'
+
+ def __init__(self, serialNum, maze, randomNumGen, difficulty, startTile, cogdoSuitType, walkAnimName = None):
+ data = Globals.SuitData[cogdoSuitType]
+ MazeSuit.__init__(self, serialNum, maze, randomNumGen, data['cellWalkPeriod'], difficulty, data['dnaName'], startTile=startTile, walkSameDirectionProb=Globals.SuitWalkSameDirectionProb, walkTurnAroundProb=Globals.SuitWalkTurnAroundProb, uniqueRandomNumGen=False, walkAnimName=walkAnimName)
+ FSM.__init__(self, 'CogdoMazeSuit')
+ CogdoMazeSplattable.__init__(self, self.suit, '%s-%i' % (Globals.SuitCollisionName, self.serialNum), 1.5)
+ if data.has_key('scale'):
+ self.suit.setScale(data['scale'])
+ self.hp = data['hp']
+ self.type = cogdoSuitType
+ self.memos = data['memos']
+ self.deathSuit = self.suit.getLoseActor()
+ self.deathSuit.pose('lose', 0)
+ BattleParticles.loadParticles()
+ self._initSfx()
+
+ def _initSfx(self):
+ audioMgr = base.cogdoGameAudioMgr
+ self._deathSoundIval = Sequence(audioMgr.createSfxIval('cogSpin', duration=1.6, startTime=0.6, volume=0.8, source=self.deathSuit), audioMgr.createSfxIval('cogDeath', volume=0.32, source=self.deathSuit))
+
+ def _destroySfx(self):
+ if self._deathSoundIval.isPlaying():
+ self._deathSoundIval.finish()
+ del self._deathSoundIval
+
+ def destroy(self):
+ BattleParticles.unloadParticles()
+ self.ignoreAll()
+ self._destroySfx()
+ CogdoMazeSplattable.destroy(self)
+ MazeSuit.destroy(self)
+
+ def handleEnterSphere(self, collEntry):
+ messenger.send(self.COLLISION_EVENT_NAME, [self.type, self.serialNum])
+
+ def gameStart(self, gameStartTime):
+ MazeSuit.gameStart(self, gameStartTime)
+ self.accept(Globals.GagCollisionName + '-into-' + self.gagCollisionName, self.handleGagHit)
+ messenger.send(self.ThinkEventName, [self, self.TX, self.TY])
+
+ def initCollisions(self):
+ MazeSuit.initCollisions(self)
+ self.collNodePath.setScale(0.75)
+ self.accept(self.uniqueName('again' + self.COLL_SPHERE_NAME), self.handleEnterSphere)
+
+ def think(self, curTic, curT, unwalkables):
+ MazeSuit.think(self, curTic, curT, unwalkables)
+ messenger.send(self.ThinkEventName, [self, self.TX, self.TY])
+
+ def handleGagHit(self, collEntry):
+ gagNodePath = collEntry.getFromNodePath().getParent()
+ messenger.send(self.GagHitEventName, [self.type, self.serialNum, gagNodePath])
+
+ def _getSuitAnimationIval(self, animName, startFrame = 0, duration = 1, partName = None, nextState = None):
+ totalFrames = self.suit.getNumFrames(animName)
+ frames = totalFrames - 1 - startFrame
+ frameRate = self.suit.getFrameRate(animName)
+ newRate = frames / duration
+ playRate = newRate / frameRate
+ ival = Sequence(ActorInterval(self.suit, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate, partName=partName))
+ if nextState is not None:
+
+ def done():
+ self.request(nextState)
+
+ ival.append(Func(done))
+ return ival
+
+ def hitByGag(self):
+ self.hp = self.hp - 1
+ self.doSplat()
+ if self.hp <= 0:
+ self.explode()
+
+ def explode(self):
+ self.doDeathTrack()
+ messenger.send(self.DeathEventName, [self.type, self.serialNum])
+
+ def doDeathTrack(self):
+
+ def removeDeathSuit(suit, deathSuit):
+ if not deathSuit.isEmpty():
+ deathSuit.detachNode()
+ suit.cleanupLoseActor()
+
+ self.deathSuit.reparentTo(self.suit.getParent())
+ self.deathSuit.setScale(self.suit.getScale())
+ self.deathSuit.setPos(render, self.suit.getPos(render))
+ self.deathSuit.setHpr(render, self.suit.getHpr(render))
+ self.suit.hide()
+ self.collNodePath.reparentTo(self.deathSuit)
+ gearPoint = Point3(0, 0, self.suit.height / 2.0 + 2.0)
+ smallGears = BattleParticles.createParticleEffect(file='gearExplosionSmall')
+ singleGear = BattleParticles.createParticleEffect('GearExplosion', numParticles=1)
+ smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10)
+ bigGearExplosion = BattleParticles.createParticleEffect('BigGearExplosion', numParticles=30)
+ smallGears.setPos(gearPoint)
+ singleGear.setPos(gearPoint)
+ smallGearExplosion.setPos(gearPoint)
+ bigGearExplosion.setPos(gearPoint)
+ smallGears.setDepthWrite(False)
+ singleGear.setDepthWrite(False)
+ smallGearExplosion.setDepthWrite(False)
+ bigGearExplosion.setDepthWrite(False)
+ suitTrack = Sequence(Func(self.collNodePath.stash), ActorInterval(self.deathSuit, 'lose', startFrame=80, endFrame=140), Func(removeDeathSuit, self.suit, self.deathSuit, name='remove-death-suit'))
+ explosionTrack = Sequence(Wait(1.5), MovieUtil.createKapowExplosionTrack(self.deathSuit, explosionPoint=gearPoint))
+ gears1Track = Sequence(ParticleInterval(smallGears, self.deathSuit, worldRelative=0, duration=4.3, cleanup=True), name='gears1Track')
+ gears2MTrack = Track((0.0, explosionTrack), (0.7, ParticleInterval(singleGear, self.deathSuit, worldRelative=0, duration=5.7, cleanup=True)), (5.2, ParticleInterval(smallGearExplosion, self.deathSuit, worldRelative=0, duration=1.2, cleanup=True)), (5.4, ParticleInterval(bigGearExplosion, self.deathSuit, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack')
+
+ def removeParticle(particle):
+ if particle and hasattr(particle, 'renderParent'):
+ particle.cleanup()
+ del particle
+
+ removeParticles = Sequence(Func(removeParticle, smallGears), Func(removeParticle, singleGear), Func(removeParticle, smallGearExplosion), Func(removeParticle, bigGearExplosion))
+ self.deathTrack = Sequence(Parallel(suitTrack, gears2MTrack, gears1Track, self._deathSoundIval), removeParticles)
+ self.deathTrack.start()
+
+
+class CogdoMazeSlowMinionSuit(CogdoMazeSuit):
+
+ def __init__(self, serialNum, maze, randomNumGen, difficulty, startTile = None):
+ CogdoMazeSuit.__init__(self, serialNum, maze, randomNumGen, difficulty, startTile, Globals.SuitTypes.SlowMinion)
+ self.defaultTransitions = {'Off': ['Normal'],
+ 'Normal': ['Attack', 'Off'],
+ 'Attack': ['Normal']}
+
+ def gameStart(self, gameStartTime):
+ CogdoMazeSuit.gameStart(self, gameStartTime)
+ self.request('Normal')
+
+ def enterNormal(self):
+ self.startWalkAnim()
+
+ def exitNormal(self):
+ pass
+
+ def enterAttack(self, elapsedTime):
+ self._attackIval = self._getSuitAnimationIval('finger-wag', duration=2.0, nextState='Normal')
+ self._attackIval.start(elapsedTime)
+
+ def filterAttack(self, request, args):
+ if request == 'Attack':
+ return None
+ else:
+ return self.defaultFilter(request, args)
+ return None
+
+ def exitAttack(self):
+ self._attackIval.pause()
+ del self._attackIval
+
+
+class CogdoMazeFastMinionSuit(CogdoMazeSuit):
+
+ def __init__(self, serialNum, maze, randomNumGen, difficulty, startTile = None):
+ CogdoMazeSuit.__init__(self, serialNum, maze, randomNumGen, difficulty, startTile, Globals.SuitTypes.FastMinion)
+
+
+class CogdoMazeBossSuit(CogdoMazeSuit):
+ BlinkTaskName = 'CogdoMazeBossBlinkTask'
+ ShakeTaskName = 'CogdoMazeBossShakeTask'
+ StartWalkTaskName = 'CogdoMazeBossStartWalkTask'
+ ShakeEventName = 'CogdoMazeSuitShake'
+
+ def __init__(self, serialNum, maze, randomNumGen, difficulty, startTile = None):
+ CogdoMazeSuit.__init__(self, serialNum, maze, randomNumGen, difficulty, startTile, Globals.SuitTypes.Boss, walkAnimName='stomp')
+ self.dropTimer = 0
+ self._walkSpeed = float(self.maze.cellWidth) / self.cellWalkDuration * 0.5
+
+ def _initSfx(self):
+ CogdoMazeSuit._initSfx(self)
+ audioMgr = base.cogdoGameAudioMgr
+ self._stompSfxIval = audioMgr.createSfxIval('cogStomp', source=self.suit, cutoff=Globals.BossStompSfxCutoff, volume=0.3)
+ self._hitSfx = audioMgr.createSfx('bossCogAngry', self.suit)
+
+ def _destroySfx(self):
+ del self._hitSfx
+ if self._stompSfxIval.isPlaying():
+ self._stompSfxIval.finish()
+ del self._stompSfxIval
+ CogdoMazeSuit._destroySfx(self)
+
+ def spin(self):
+ part = self.suit
+ time = Globals.BossSpinTime
+ degrees = 360 * Globals.BossSpinCount
+ spinIval = LerpHprInterval(part, time, (self.suit.getH() + degrees, 0, 0), blendType='easeOut')
+ spinIval.start()
+
+ def hitByGag(self):
+ if self.hp >= 2:
+ self._hitSfx.play()
+ self.spin()
+ self.suit.setColorScale(Globals.BlinkColor)
+ self.__startBlinkTask()
+ elif self.hp == 1:
+ self.__stopBlinkTask()
+ CogdoMazeSuit.hitByGag(self)
+
+ def gameStart(self, gameStartTime):
+ CogdoMazeSuit.gameStart(self, gameStartTime)
+
+ def startWalkAnim(self):
+ self.suit.loop(self._walkAnimName, fromFrame=43, toFrame=81)
+ self.suit.setPlayRate(self._walkSpeed * Globals.BossCogStompAnimationPlayrateFactor, self._walkAnimName)
+ self.__startShakeTask()
+
+ def destroy(self):
+ CogdoMazeSuit.destroy(self)
+ self.__stopShakeTask()
+ self.__stopBlinkTask()
+
+ def pickRandomValidSpot(self, r = 5):
+ validSpots = []
+ for x in range(self.TX - r, self.TX + r):
+ for y in range(self.TY - r, self.TY + r):
+ if self.maze.isWalkable(x, y):
+ validSpots.append([x, y])
+
+ return self.rng.choice(validSpots)
+
+ def __startShakeTask(self):
+ self.__stopShakeTask()
+ taskMgr.doMethodLater(Globals.BossShakeTime, self.__shake, self.uniqueName(CogdoMazeBossSuit.ShakeTaskName))
+ self.bossShakeLastTime = 0
+
+ def __stopShakeTask(self):
+ taskMgr.remove(self.uniqueName(CogdoMazeBossSuit.ShakeTaskName))
+
+ def __shake(self, task):
+ if task.time - self.bossShakeLastTime > Globals.BossShakeTime:
+ self.suit.setPlayRate(self._walkSpeed * Globals.BossCogStompAnimationPlayrateFactor, self._walkAnimName)
+ self._stompSfxIval.start()
+ messenger.send(self.ShakeEventName, [self, Globals.BossShakeStrength])
+ self.bossShakeLastTime = task.time
+ return task.cont
+
+ def __startBlinkTask(self):
+ self.__stopBlinkTask()
+ taskMgr.doMethodLater(Globals.BlinkFrequency, self.__blink, CogdoMazeBossSuit.BlinkTaskName)
+
+ def __stopBlinkTask(self):
+ taskMgr.remove(CogdoMazeBossSuit.BlinkTaskName)
+
+ def __blink(self, task):
+ blink = Sequence(LerpColorScaleInterval(self.suit, Globals.BlinkSpeed, VBase4(1.0, 1.0, 1.0, 1.0)), LerpColorScaleInterval(self.suit, Globals.BlinkSpeed, Globals.BlinkColor))
+ blink.start()
+ return Task.again
diff --git a/toontown/cogdominium/CogdoMemoGui.py b/toontown/cogdominium/CogdoMemoGui.py
new file mode 100644
index 0000000..47cf60c
--- /dev/null
+++ b/toontown/cogdominium/CogdoMemoGui.py
@@ -0,0 +1,40 @@
+from direct.gui.DirectGui import DGG, DirectFrame, DirectLabel
+from pandac.PandaModules import TextNode
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import ToontownIntervals
+from toontown.toonbase import TTLocalizer
+import CogdoUtil
+import CogdoGameConsts
+MEMOICON_SCALE = 0.2
+
+class CogdoMemoGui(DirectFrame):
+
+ def __init__(self, parent):
+ DirectFrame.__init__(self, parent=parent, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX)
+ self._initModel()
+ self.hide()
+ return
+
+ def destroy(self):
+ ToontownIntervals.cleanup('memocount_pulse')
+ self._countLabel.removeNode()
+ del self._countLabel
+ self._memoIcon.removeNode()
+ del self._memoIcon
+ DirectFrame.destroy(self)
+
+ def posNextToLaffMeter(self):
+ self.setPos(-0.975, 0, -0.875)
+
+ def _initModel(self):
+ self._countLabel = DirectLabel(parent=self, relief=None, pos=(0.0625, 0, -0.025), scale=CogdoGameConsts.MemoGuiTextScale, text=str(0), text_fg=CogdoGameConsts.MemoGuiTextColor, text_shadow=(0.2, 0.2, 0.2, 1), text_align=TextNode.ALeft, text_font=ToontownGlobals.getToonFont())
+ self._memoIcon = CogdoUtil.loadModel('memo_card', game='shared', group='gui')
+ self._memoIcon.reparentTo(self)
+ self._memoIcon.setScale(MEMOICON_SCALE)
+ return
+
+ def setCount(self, count):
+ self._countLabel['text'] = str(count)
+ self._countLabel.setText()
+ ToontownIntervals.cleanup('memocount_pulse')
+ ToontownIntervals.start(ToontownIntervals.getPulseLargerIval(self._memoIcon, 'memocount_pulse', scale=MEMOICON_SCALE))
diff --git a/toontown/cogdominium/CogdoUtil.py b/toontown/cogdominium/CogdoUtil.py
new file mode 100644
index 0000000..1d20843
--- /dev/null
+++ b/toontown/cogdominium/CogdoUtil.py
@@ -0,0 +1,147 @@
+from pandac.PandaModules import ColorBlendAttrib
+ModelPhase = 5
+ModelTypes = {'animation': 'a',
+ 'model': 'm',
+ 'rig': 'r'}
+ModelGroups = {'area': 'ara',
+ 'gui': 'gui'}
+Games = {'flying': 'cfg',
+ 'maze': 'cmg',
+ 'shared': 'csa'}
+
+def loadFlyingModel(baseName, type = 'model', group = 'area'):
+ return loadModel(baseName, 'flying', type=type, group=group)
+
+
+def loadMazeModel(baseName, type = 'model', group = 'area'):
+ return loadModel(baseName, 'maze', type=type, group=group)
+
+
+def getModelPath(baseName, game = 'shared', type = 'model', group = 'area'):
+ extension = ''
+ if hasattr(getBase(), 'air'):
+ extension = '.bam'
+ return 'phase_%i/models/cogdominium/tt_%s_%s_%s_%s%s' % (ModelPhase,
+ ModelTypes[type],
+ ModelGroups[group],
+ Games[game],
+ baseName,
+ extension)
+
+
+def loadModel(baseName, game = 'shared', type = 'model', group = 'area'):
+ return loader.loadModel(getModelPath(baseName, game, type, group))
+
+
+class VariableContainer:
+ pass
+
+class DevVariableContainer:
+
+ def __init__(self, name):
+ self.__dict__['_enabled'] = config.GetBool('%s-dev' % name, False)
+
+ def __setattr__(self, name, value):
+ self.__dict__[name] = self._enabled and value
+
+
+def getRandomDialogueLine(lineList, rng):
+ return lineList[rng.randint(0, len(lineList) - 1)]
+
+
+class CogdoGameMovie:
+
+ def __init__(self):
+ self._ival = None
+ self._task = None
+ return
+
+ def load(self):
+ from toontown.toonbase import ToontownGlobals
+ from pandac.PandaModules import TextNode
+ textNode = TextNode('moviedialogue')
+ textNode.setTextColor(0, 0, 0, 1)
+ textNode.setCardColor(1, 1, 1, 1)
+ textNode.setCardAsMargin(0, 0, 0, 0)
+ textNode.setCardDecal(True)
+ textNode.setWordwrap(27.0)
+ textNode.setAlign(TextNode.ACenter)
+ textNode.setFont(ToontownGlobals.getToonFont())
+ self._dialogueLabel = aspect2d.attachNewNode(textNode)
+ self._dialogueLabel.setScale(0.06, 0.06, 0.06)
+ self._dialogueLabel.setPos(0.32, 0, -0.75)
+ self._dialogueLabel.reparentTo(hidden)
+
+ def unload(self):
+ if self._ival is not None and self._ival.isPlaying():
+ self.finish()
+ self._ival = None
+ self._dialogueLabel.removeNode()
+ del self._dialogueLabel
+ return
+
+ def getIval(self):
+ return self._ival
+
+ def play(self, elapsedTime = 0.0):
+ self._dialogueLabel.reparentTo(aspect2d)
+ self._ival.start(elapsedTime)
+
+ def _startUpdateTask(self):
+ self._task = taskMgr.add(self._updateTask, 'CogdoGameMovie_updateTask', 45)
+
+ def _stopUpdateTask(self):
+ if self._task is not None:
+ taskMgr.remove(self._task)
+ self._task = None
+ return
+
+ def _updateTask(self, task):
+ return task.cont
+
+ def end(self):
+ self._ival.finish()
+
+
+def initializeLightCone(np, bin = 'fixed', sorting = 3):
+ np.node().setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne))
+ if bin:
+ np.setBin(bin, sorting)
+ np.setDepthWrite(False)
+ np.setTwoSided(True, 10000)
+
+
+ROTATE_TABLE_ALLOWED_ANGLES = (0, 90, 180, 270)
+
+def rotateTable(table, angle):
+ if angle == 0:
+ t = table[:]
+ elif angle == 90:
+ t = []
+ width = len(table[0])
+ height = len(table)
+ for j in range(width):
+ row = []
+ for i in range(height):
+ row.append(table[height - 1 - i][j])
+
+ t.append(row)
+
+ elif angle == 180:
+ t = table[:]
+ for row in t:
+ row.reverse()
+
+ t.reverse()
+ elif angle == 270:
+ t = []
+ width = len(table[0])
+ height = len(table)
+ for j in range(width):
+ row = []
+ for i in range(height):
+ row.append(table[i][width - 1 - j])
+
+ t.append(row)
+
+ return t
diff --git a/toontown/cogdominium/DistCogdoBoardroomGame.py b/toontown/cogdominium/DistCogdoBoardroomGame.py
new file mode 100644
index 0000000..a7cb615
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoBoardroomGame.py
@@ -0,0 +1,53 @@
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from toontown.cogdominium.DistCogdoLevelGame import DistCogdoLevelGame
+from toontown.cogdominium.CogdoBoardroomGameBase import CogdoBoardroomGameBase
+from toontown.cogdominium import CogdoBoardroomGameConsts as Consts
+from toontown.toonbase import ToontownTimer
+from toontown.toonbase import TTLocalizer as TTL
+
+class DistCogdoBoardroomGame(CogdoBoardroomGameBase, DistCogdoLevelGame):
+ notify = directNotify.newCategory('DistCogdoBoardroomGame')
+
+ def __init__(self, cr):
+ DistCogdoLevelGame.__init__(self, cr)
+
+ def getTitle(self):
+ return TTL.BoardroomGameTitle
+
+ def getInstructions(self):
+ return TTL.BoardroomGameInstructions
+
+ def announceGenerate(self):
+ DistCogdoLevelGame.announceGenerate(self)
+ self.timer = ToontownTimer.ToontownTimer()
+ self.timer.setScale(Consts.Settings.TimerScale.get())
+ self.timer.stash()
+
+ def disable(self):
+ self.timer.destroy()
+ self.timer = None
+ DistCogdoLevelGame.disable(self)
+ return
+
+ def enterGame(self):
+ DistCogdoLevelGame.enterGame(self)
+ timeLeft = Consts.GameDuration.get() - (globalClock.getRealTime() - self.getStartTime())
+ self.timer.setTime(timeLeft)
+ self.timer.countdown(timeLeft, self.timerExpired)
+ self.timer.unstash()
+
+ def enterFinish(self):
+ DistCogdoLevelGame.enterFinish(self)
+ timeLeft = Consts.FinishDuration.get() - (globalClock.getRealTime() - self.getFinishTime())
+ self.timer.setTime(timeLeft)
+ self.timer.countdown(timeLeft, self.timerExpired)
+ self.timer.unstash()
+
+ def timerExpired(self):
+ pass
+
+ if __dev__:
+
+ def _handleTimerScaleChanged(self, timerScale):
+ if hasattr(self, 'timer'):
+ self.timer.setScale(timerScale)
diff --git a/toontown/cogdominium/DistCogdoBoardroomGameAI.py b/toontown/cogdominium/DistCogdoBoardroomGameAI.py
new file mode 100644
index 0000000..0334d76
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoBoardroomGameAI.py
@@ -0,0 +1,34 @@
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from toontown.cogdominium.CogdoBoardroomGameBase import CogdoBoardroomGameBase
+from toontown.cogdominium.DistCogdoLevelGameAI import DistCogdoLevelGameAI
+from toontown.cogdominium import CogdoBoardroomGameConsts as Consts
+
+class DistCogdoBoardroomGameAI(CogdoBoardroomGameBase, DistCogdoLevelGameAI):
+ notify = directNotify.newCategory('DistCogdoBoardroomGameAI')
+
+ def __init__(self, air, interior):
+ DistCogdoLevelGameAI.__init__(self, air, interior)
+
+ def enterGame(self):
+ DistCogdoLevelGameAI.enterGame(self)
+ self._gameDoneEvent = taskMgr.doMethodLater(Consts.GameDuration.get(), self._gameDoneDL, self.uniqueName('boardroomGameDone'))
+
+ def exitGame(self):
+ taskMgr.remove(self._gameDoneEvent)
+ self._gameDoneEvent = None
+
+ def _gameDoneDL(self, task):
+ self._handleGameFinished()
+ return task.done
+
+ def enterFinish(self):
+ DistCogdoLevelGameAI.enterFinish(self)
+ self._finishDoneEvent = taskMgr.doMethodLater(Consts.FinishDuration.get(), self._finishDoneDL, self.uniqueName('boardroomFinishDone'))
+
+ def exitFinish(self):
+ taskMgr.remove(self._finishDoneEvent)
+ self._finishDoneEvent = None
+
+ def _finishDoneDL(self, task):
+ self.announceGameDone()
+ return task.done
diff --git a/toontown/cogdominium/DistCogdoCrane.py b/toontown/cogdominium/DistCogdoCrane.py
new file mode 100644
index 0000000..02a5c20
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoCrane.py
@@ -0,0 +1,884 @@
+from direct.gui.DirectGui import *
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from direct.distributed.ClockDelta import *
+from direct.fsm import FSM
+from direct.distributed import DistributedObject
+from direct.showutil import Rope
+from direct.showbase import PythonUtil
+from direct.task import Task
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import TTLocalizer
+from otp.otpbase import OTPGlobals
+from toontown.cogdominium import CogdoCraneGameConsts as GameConsts
+import random
+
+class DistCogdoCrane(DistributedObject.DistributedObject, FSM.FSM):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistCogdoCrane')
+ firstMagnetBit = 21
+ craneMinY = 8
+ craneMaxY = 25
+ armMinH = -45
+ armMaxH = 45
+ shadowOffset = 7
+ emptySlideSpeed = 10
+ emptyRotateSpeed = 20
+ lookAtPoint = Point3(0.3, 0, 0.1)
+ lookAtUp = Vec3(0, -1, 0)
+ neutralStickHinge = VBase3(0, 90, 0)
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ FSM.FSM.__init__(self, 'DistCogdoCrane')
+ self.craneGame = None
+ self.index = None
+ self.avId = 0
+ self.cableLength = 20
+ self.numLinks = 3
+ self.initialArmPosition = (0, 20, 0)
+ self.slideSpeed = self.emptySlideSpeed
+ self.rotateSpeed = self.emptyRotateSpeed
+ self.changeSeq = 0
+ self.lastChangeSeq = 0
+ self.moveSound = None
+ self.links = []
+ self.activeLinks = []
+ self.collisions = NodePathCollection()
+ self.physicsActivated = 0
+ self.snifferActivated = 0
+ self.magnetOn = 0
+ self.root = NodePath('root')
+ self.hinge = self.root.attachNewNode('hinge')
+ self.hinge.setPos(0, -17.6, 38.5)
+ self.controls = self.root.attachNewNode('controls')
+ self.controls.setPos(0, -4.9, 0)
+ self.arm = self.hinge.attachNewNode('arm')
+ self.crane = self.arm.attachNewNode('crane')
+ self.cable = self.hinge.attachNewNode('cable')
+ self.topLink = self.crane.attachNewNode('topLink')
+ self.topLink.setPos(0, 0, -1)
+ self.shadow = None
+ self.p0 = Point3(0, 0, 0)
+ self.v1 = Vec3(1, 1, 1)
+ self.armSmoother = SmoothMover()
+ self.armSmoother.setSmoothMode(SmoothMover.SMOn)
+ self.linkSmoothers = []
+ self.smoothStarted = 0
+ self.__broadcastPeriod = 0.2
+ self.cable.node().setFinal(1)
+ self.crane.setPos(*self.initialArmPosition)
+ self.heldObject = None
+ self.craneAdviceLabel = None
+ self.magnetAdviceLabel = None
+ self.atLimitSfx = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.mp3')
+ self.magnetOnSfx = base.loadSfx('phase_10/audio/sfx/CBHQ_CFO_magnet_on.mp3')
+ self.magnetLoopSfx = base.loadSfx('phase_10/audio/sfx/CBHQ_CFO_magnet_loop.wav')
+ self.magnetSoundInterval = Parallel(SoundInterval(self.magnetOnSfx), Sequence(Wait(0.5), Func(base.playSfx, self.magnetLoopSfx, looping=1)))
+ self.craneMoveSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_elevator_up_down.mp3')
+ self.fadeTrack = None
+ return
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ self.name = 'crane-%s' % self.doId
+ self.root.setName(self.name)
+ self.root.setPosHpr(*GameConsts.CranePosHprs[self.index])
+ self.rotateLinkName = self.uniqueName('rotateLink')
+ self.snifferEvent = self.uniqueName('sniffer')
+ self.triggerName = self.uniqueName('trigger')
+ self.triggerEvent = 'enter%s' % self.triggerName
+ self.shadowName = self.uniqueName('shadow')
+ self.flickerName = self.uniqueName('flicker')
+ self.smoothName = self.uniqueName('craneSmooth')
+ self.posHprBroadcastName = self.uniqueName('craneBroadcast')
+ self.craneAdviceName = self.uniqueName('craneAdvice')
+ self.magnetAdviceName = self.uniqueName('magnetAdvice')
+ self.controlModel = self.craneGame.controls.copyTo(self.controls)
+ self.cc = NodePath('cc')
+ column = self.controlModel.find('**/column')
+ column.getChildren().reparentTo(self.cc)
+ self.cc.reparentTo(column)
+ self.stickHinge = self.cc.attachNewNode('stickHinge')
+ self.stick = self.craneGame.stick.copyTo(self.stickHinge)
+ self.stickHinge.setHpr(self.neutralStickHinge)
+ self.stick.setHpr(0, -90, 0)
+ self.stick.flattenLight()
+ self.bottom = self.controlModel.find('**/bottom')
+ self.bottom.wrtReparentTo(self.cc)
+ self.bottomPos = self.bottom.getPos()
+ cs = CollisionSphere(0, -5, -2, 3)
+ cs.setTangible(0)
+ cn = CollisionNode(self.triggerName)
+ cn.addSolid(cs)
+ cn.setIntoCollideMask(OTPGlobals.WallBitmask)
+ self.trigger = self.root.attachNewNode(cn)
+ self.trigger.stash()
+ cs = CollisionTube(0, 2.7, 0, 0, 2.7, 3, 1.2)
+ cn = CollisionNode('tube')
+ cn.addSolid(cs)
+ cn.setIntoCollideMask(OTPGlobals.WallBitmask)
+ self.tube = self.controlModel.attachNewNode(cn)
+ cs = CollisionSphere(0, 0, 2, 3)
+ cn = CollisionNode('safetyBubble')
+ cn.addSolid(cs)
+ cn.setIntoCollideMask(ToontownGlobals.PieBitmask)
+ self.controls.attachNewNode(cn)
+ arm = self.craneGame.craneArm.copyTo(self.crane)
+ self.craneGame.cranes[self.index] = self
+
+ def disable(self):
+ DistributedObject.DistributedObject.disable(self)
+ del self.craneGame.cranes[self.index]
+ self.cleanup()
+
+ def cleanup(self):
+ if self.state != 'Off':
+ self.demand('Off')
+ self.craneGame = None
+ return
+
+ def accomodateToon(self, toon):
+ origScale = self.controlModel.getSz()
+ origCcPos = self.cc.getPos()
+ origBottomPos = self.bottom.getPos()
+ origStickHingeHpr = self.stickHinge.getHpr()
+ scale = toon.getGeomNode().getChild(0).getSz(render)
+ self.controlModel.setScale(scale)
+ self.cc.setPos(0, 0, 0)
+ toon.setPosHpr(self.controls, 0, 0, 0, 0, 0, 0)
+ toon.pose('leverNeutral', 0)
+ toon.update()
+ pos = toon.rightHand.getPos(self.cc)
+ self.cc.setPos(pos[0], pos[1], pos[2] - 1)
+ self.bottom.setZ(toon, 0.0)
+ self.bottom.setPos(self.bottomPos[0], self.bottomPos[1], self.bottom.getZ())
+ self.stickHinge.lookAt(toon.rightHand, self.lookAtPoint, self.lookAtUp)
+ lerpTime = 0.5
+ return Parallel(self.controlModel.scaleInterval(lerpTime, scale, origScale, blendType='easeInOut'), self.cc.posInterval(lerpTime, self.cc.getPos(), origCcPos, blendType='easeInOut'), self.bottom.posInterval(lerpTime, self.bottom.getPos(), origBottomPos, blendType='easeInOut'), self.stickHinge.quatInterval(lerpTime, self.stickHinge.getHpr(), origStickHingeHpr, blendType='easeInOut'))
+
+ def getRestoreScaleInterval(self):
+ lerpTime = 1
+ return Parallel(self.controlModel.scaleInterval(lerpTime, 1, blendType='easeInOut'), self.cc.posInterval(lerpTime, Point3(0, 0, 0), blendType='easeInOut'), self.bottom.posInterval(lerpTime, self.bottomPos, blendType='easeInOut'), self.stickHinge.quatInterval(lerpTime, self.neutralStickHinge, blendType='easeInOut'))
+
+ def makeToonGrabInterval(self, toon):
+ origPos = toon.getPos()
+ origHpr = toon.getHpr()
+ a = self.accomodateToon(toon)
+ newPos = toon.getPos()
+ newHpr = toon.getHpr()
+ origHpr.setX(PythonUtil.fitSrcAngle2Dest(origHpr[0], newHpr[0]))
+ toon.setPosHpr(origPos, origHpr)
+ walkTime = 0.2
+ reach = ActorInterval(toon, 'leverReach')
+ if reach.getDuration() < walkTime:
+ reach = Sequence(ActorInterval(toon, 'walk', loop=1, duration=walkTime - reach.getDuration()), reach)
+ i = Sequence(Parallel(toon.posInterval(walkTime, newPos, origPos), toon.hprInterval(walkTime, newHpr, origHpr), reach), Func(self.startWatchJoystick, toon))
+ i = Parallel(i, a)
+ return i
+
+ def __toonPlayWithCallback(self, animName, numFrames):
+ duration = numFrames / 24.0
+ self.toon.play(animName)
+ taskMgr.doMethodLater(duration, self.__toonPlayCallback, self.uniqueName('toonPlay'))
+
+ def __toonPlayCallback(self, task):
+ if self.changeSeq == self.lastChangeSeq:
+ self.__toonPlayWithCallback('leverNeutral', 40)
+ else:
+ self.__toonPlayWithCallback('leverPull', 40)
+ self.lastChangeSeq = self.changeSeq
+
+ def startWatchJoystick(self, toon):
+ self.toon = toon
+ taskMgr.add(self.__watchJoystick, self.uniqueName('watchJoystick'))
+ self.__toonPlayWithCallback('leverNeutral', 40)
+ self.accept(toon.uniqueName('disable'), self.__handleUnexpectedExit, extraArgs=[toon.doId])
+
+ def stopWatchJoystick(self):
+ taskMgr.remove(self.uniqueName('toonPlay'))
+ taskMgr.remove(self.uniqueName('watchJoystick'))
+ if self.toon:
+ self.ignore(self.toon.uniqueName('disable'))
+ self.toon = None
+ return
+
+ def __watchJoystick(self, task):
+ self.toon.setPosHpr(self.controls, 0, 0, 0, 0, 0, 0)
+ self.toon.update()
+ self.stickHinge.lookAt(self.toon.rightHand, self.lookAtPoint, self.lookAtUp)
+ return Task.cont
+
+ def __handleUnexpectedExit(self, toonId):
+ self.notify.warning('%s: unexpected exit for %s' % (self.doId, toonId))
+ if self.toon and self.toon.doId == toonId:
+ self.stopWatchJoystick()
+
+ def __activatePhysics(self):
+ if not self.physicsActivated:
+ for an, anp, cnp in self.activeLinks:
+ self.craneGame.physicsMgr.attachPhysicalNode(an)
+ base.cTrav.addCollider(cnp, self.handler)
+
+ self.collisions.unstash()
+ self.physicsActivated = 1
+
+ def __deactivatePhysics(self):
+ if self.physicsActivated:
+ for an, anp, cnp in self.activeLinks:
+ self.craneGame.physicsMgr.removePhysicalNode(an)
+ base.cTrav.removeCollider(cnp)
+
+ self.collisions.stash()
+ self.physicsActivated = 0
+
+ def __straightenCable(self):
+ for linkNum in range(self.numLinks):
+ an, anp, cnp = self.activeLinks[linkNum]
+ an.getPhysicsObject().setVelocity(0, 0, 0)
+ z = float(linkNum + 1) / float(self.numLinks) * self.cableLength
+ anp.setPos(self.crane.getPos(self.cable))
+ anp.setZ(-z)
+
+ def setCableLength(self, length):
+ self.cableLength = length
+ linkWidth = float(length) / float(self.numLinks)
+ self.shell.setRadius(linkWidth + 1)
+
+ def setupCable(self):
+ activated = self.physicsActivated
+ self.clearCable()
+ self.handler = PhysicsCollisionHandler()
+ self.handler.setStaticFrictionCoef(0.1)
+ self.handler.setDynamicFrictionCoef(GameConsts.Settings.EmptyFrictionCoef.get())
+ linkWidth = float(self.cableLength) / float(self.numLinks)
+ self.shell = CollisionInvSphere(0, 0, 0, linkWidth + 1)
+ self.links = []
+ self.links.append((self.topLink, Point3(0, 0, 0)))
+ anchor = self.topLink
+ for linkNum in range(self.numLinks):
+ anchor = self.__makeLink(anchor, linkNum)
+
+ self.collisions.stash()
+ self.bottomLink = self.links[-1][0]
+ self.middleLink = self.links[-2][0]
+ self.magnet = self.bottomLink.attachNewNode('magnet')
+ self.wiggleMagnet = self.magnet.attachNewNode('wiggleMagnet')
+ taskMgr.add(self.__rotateMagnet, self.rotateLinkName)
+ magnetModel = self.craneGame.magnet.copyTo(self.wiggleMagnet)
+ magnetModel.setHpr(90, 45, 90)
+ self.gripper = magnetModel.attachNewNode('gripper')
+ self.gripper.setPos(0, 0, -4)
+ cn = CollisionNode('sniffer')
+ self.sniffer = magnetModel.attachNewNode(cn)
+ self.sniffer.stash()
+ cs = CollisionSphere(0, 0, -10, 6)
+ cs.setTangible(0)
+ cn.addSolid(cs)
+ cn.setIntoCollideMask(BitMask32(0))
+ cn.setFromCollideMask(ToontownGlobals.CashbotBossObjectBitmask)
+ self.snifferHandler = CollisionHandlerEvent()
+ self.snifferHandler.addInPattern(self.snifferEvent)
+ self.snifferHandler.addAgainPattern(self.snifferEvent)
+ rope = self.makeSpline()
+ rope.reparentTo(self.cable)
+ rope.setTexture(self.craneGame.cableTex)
+ ts = TextureStage.getDefault()
+ rope.setTexScale(ts, 0.15, 0.13)
+ rope.setTexOffset(ts, 0.83, 0.01)
+ if activated:
+ self.__activatePhysics()
+
+ def clearCable(self):
+ self.__deactivatePhysics()
+ taskMgr.remove(self.rotateLinkName)
+ self.links = []
+ self.activeLinks = []
+ self.linkSmoothers = []
+ self.collisions.clear()
+ self.cable.getChildren().detach()
+ self.topLink.getChildren().detach()
+ self.gripper = None
+ return
+
+ def makeSpline(self):
+ rope = Rope.Rope()
+ rope.setup(min(len(self.links), 4), self.links)
+ rope.curve.normalizeKnots()
+ rn = rope.ropeNode
+ rn.setRenderMode(RopeNode.RMTube)
+ rn.setNumSlices(3)
+ rn.setTubeUp(Vec3(0, -1, 0))
+ rn.setUvMode(RopeNode.UVParametric)
+ rn.setUvDirection(1)
+ rn.setThickness(0.5)
+ return rope
+
+ def startShadow(self):
+ self.shadow = self.craneGame.geomRoot.attachNewNode('%s-shadow' % self.name)
+ self.shadow.setColor(1, 1, 1, 0.3)
+ self.shadow.setDepthWrite(0)
+ self.shadow.setTransparency(1)
+ self.shadow.setBin('shadow', 0)
+ self.shadow.node().setFinal(1)
+ self.magnetShadow = loader.loadModel('phase_3/models/props/drop_shadow')
+ self.magnetShadow.reparentTo(self.shadow)
+ self.craneShadow = loader.loadModel('phase_3/models/props/square_drop_shadow')
+ self.craneShadow.setScale(0.5, 4, 1)
+ self.craneShadow.setPos(0, -12, 0)
+ self.craneShadow.flattenLight()
+ self.craneShadow.reparentTo(self.shadow)
+ taskMgr.add(self.__followShadow, self.shadowName)
+ rope = self.makeSpline()
+ rope.reparentTo(self.shadow)
+ rope.setColor(1, 1, 1, 0.2)
+ tex = self.craneShadow.findTexture('*')
+ rope.setTexture(tex)
+ rn = rope.ropeNode
+ rn.setRenderMode(RopeNode.RMTape)
+ rn.setNumSubdiv(6)
+ rn.setThickness(0.8)
+ rn.setTubeUp(Vec3(0, 0, 1))
+ rn.setMatrix(Mat4.translateMat(0, 0, self.shadowOffset) * Mat4.scaleMat(1, 1, 0.01))
+
+ def stopShadow(self):
+ if self.shadow:
+ self.shadow.removeNode()
+ self.shadow = None
+ self.magnetShadow = None
+ self.craneShadow = None
+ taskMgr.remove(self.shadowName)
+ return
+
+ def __followShadow(self, task):
+ p = self.magnet.getPos(self.craneGame.geomRoot)
+ self.magnetShadow.setPos(p[0], p[1], self.shadowOffset)
+ self.craneShadow.setPosHpr(self.crane, 0, 0, 0, 0, 0, 0)
+ self.craneShadow.setZ(self.shadowOffset)
+ return Task.cont
+
+ def __makeLink(self, anchor, linkNum):
+ an = ActorNode('link%s' % linkNum)
+ an.getPhysicsObject().setMass(GameConsts.Settings.RopeLinkMass.get())
+ anp = NodePath(an)
+ cn = CollisionNode('cn')
+ sphere = CollisionSphere(0, 0, 0, 1)
+ cn.addSolid(sphere)
+ cnp = anp.attachNewNode(cn)
+ self.handler.addCollider(cnp, anp)
+ self.activeLinks.append((an, anp, cnp))
+ self.linkSmoothers.append(SmoothMover())
+ anp.reparentTo(self.cable)
+ z = float(linkNum + 1) / float(self.numLinks) * self.cableLength
+ anp.setPos(self.crane.getPos())
+ anp.setZ(-z)
+ mask = BitMask32.bit(self.firstMagnetBit + linkNum)
+ cn.setFromCollideMask(mask)
+ cn.setIntoCollideMask(BitMask32(0))
+ shellNode = CollisionNode('shell%s' % linkNum)
+ shellNode.addSolid(self.shell)
+ shellNP = anchor.attachNewNode(shellNode)
+ shellNode.setIntoCollideMask(mask)
+ self.collisions.addPath(shellNP)
+ self.collisions.addPath(cnp)
+ self.links.append((anp, Point3(0, 0, 0)))
+ return anp
+
+ def __rotateMagnet(self, task):
+ self.magnet.lookAt(self.middleLink, self.p0, self.v1)
+ return Task.cont
+
+ def __enableControlInterface(self):
+ gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui')
+ self.accept('control', self.__controlPressed)
+ self.accept('control-up', self.__controlReleased)
+ self.accept('InputState-forward', self.__upArrow)
+ self.accept('InputState-reverse', self.__downArrow)
+ self.accept('InputState-turnLeft', self.__leftArrow)
+ self.accept('InputState-turnRight', self.__rightArrow)
+ taskMgr.add(self.__watchControls, 'watchCraneControls')
+ taskMgr.doMethodLater(5, self.__displayCraneAdvice, self.craneAdviceName)
+ taskMgr.doMethodLater(10, self.__displayMagnetAdvice, self.magnetAdviceName)
+ NametagGlobals.setOnscreenChatForced(1)
+ self.arrowVert = 0
+ self.arrowHorz = 0
+
+ def __disableControlInterface(self):
+ self.__turnOffMagnet()
+ self.__cleanupCraneAdvice()
+ self.__cleanupMagnetAdvice()
+ self.ignore('escape')
+ self.ignore('control')
+ self.ignore('control-up')
+ self.ignore('InputState-forward')
+ self.ignore('InputState-reverse')
+ self.ignore('InputState-turnLeft')
+ self.ignore('InputState-turnRight')
+ self.arrowVert = 0
+ self.arrowHorz = 0
+ NametagGlobals.setOnscreenChatForced(0)
+ taskMgr.remove('watchCraneControls')
+ self.__setMoveSound(None)
+ return
+
+ def __displayCraneAdvice(self, task):
+ if self.craneAdviceLabel == None:
+ self.craneAdviceLabel = DirectLabel(text=TTLocalizer.CashbotCraneAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.69), scale=0.1)
+ return
+
+ def __cleanupCraneAdvice(self):
+ if self.craneAdviceLabel:
+ self.craneAdviceLabel.destroy()
+ self.craneAdviceLabel = None
+ taskMgr.remove(self.craneAdviceName)
+ return
+
+ def __displayMagnetAdvice(self, task):
+ if self.magnetAdviceLabel == None:
+ self.magnetAdviceLabel = DirectLabel(text=TTLocalizer.CashbotMagnetAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.55), scale=0.1)
+ return
+
+ def __cleanupMagnetAdvice(self):
+ if self.magnetAdviceLabel:
+ self.magnetAdviceLabel.destroy()
+ self.magnetAdviceLabel = None
+ taskMgr.remove(self.magnetAdviceName)
+ return
+
+ def __watchControls(self, task):
+ if self.arrowHorz or self.arrowVert:
+ self.__moveCraneArcHinge(self.arrowHorz, self.arrowVert)
+ else:
+ self.__setMoveSound(None)
+ return Task.cont
+
+ def __incrementChangeSeq(self):
+ self.changeSeq = self.changeSeq + 1 & 255
+
+ def __controlPressed(self):
+ self.__cleanupMagnetAdvice()
+ self.__turnOnMagnet()
+
+ def __controlReleased(self):
+ self.__turnOffMagnet()
+
+ def __turnOnMagnet(self):
+ if not self.magnetOn:
+ self.__incrementChangeSeq()
+ self.magnetOn = 1
+ if not self.heldObject:
+ self.__activateSniffer()
+
+ def __turnOffMagnet(self):
+ if self.magnetOn:
+ self.magnetOn = 0
+ self.__deactivateSniffer()
+ self.releaseObject()
+
+ def __upArrow(self, pressed):
+ self.__incrementChangeSeq()
+ self.__cleanupCraneAdvice()
+ if pressed:
+ self.arrowVert = 1
+ elif self.arrowVert > 0:
+ self.arrowVert = 0
+
+ def __downArrow(self, pressed):
+ self.__incrementChangeSeq()
+ self.__cleanupCraneAdvice()
+ if pressed:
+ self.arrowVert = -1
+ elif self.arrowVert < 0:
+ self.arrowVert = 0
+
+ def __rightArrow(self, pressed):
+ self.__incrementChangeSeq()
+ self.__cleanupCraneAdvice()
+ if pressed:
+ self.arrowHorz = 1
+ elif self.arrowHorz > 0:
+ self.arrowHorz = 0
+
+ def __leftArrow(self, pressed):
+ self.__incrementChangeSeq()
+ self.__cleanupCraneAdvice()
+ if pressed:
+ self.arrowHorz = -1
+ elif self.arrowHorz < 0:
+ self.arrowHorz = 0
+
+ def __moveCraneArcHinge(self, xd, yd):
+ dt = globalClock.getDt()
+ h = self.arm.getH() - xd * self.rotateSpeed * dt
+ limitH = max(min(h, self.armMaxH), self.armMinH)
+ self.arm.setH(limitH)
+ y = self.crane.getY() + yd * self.slideSpeed * dt
+ limitY = max(min(y, self.craneMaxY), self.craneMinY)
+ atLimit = (limitH != h or limitY != y)
+ if atLimit:
+ now = globalClock.getFrameTime()
+ x = math.sin(now * 79) * 0.05
+ z = math.sin(now * 70) * 0.02
+ self.crane.setPos(x, limitY, z)
+ self.__setMoveSound(self.atLimitSfx)
+ else:
+ self.crane.setPos(0, limitY, 0)
+ self.__setMoveSound(self.craneMoveSfx)
+
+ def __setMoveSound(self, sfx):
+ if sfx != self.moveSound:
+ if self.moveSound:
+ self.moveSound.stop()
+ self.moveSound = sfx
+ if self.moveSound:
+ base.playSfx(self.moveSound, looping=1, volume=0.5)
+
+ def __activateSniffer(self):
+ if not self.snifferActivated:
+ self.sniffer.unstash()
+ base.cTrav.addCollider(self.sniffer, self.snifferHandler)
+ self.accept(self.snifferEvent, self.__sniffedSomething)
+ self.startFlicker()
+ self.snifferActivated = 1
+
+ def __deactivateSniffer(self):
+ if self.snifferActivated:
+ base.cTrav.removeCollider(self.sniffer)
+ self.sniffer.stash()
+ self.ignore(self.snifferEvent)
+ self.stopFlicker()
+ self.snifferActivated = 0
+
+ def startFlicker(self):
+ self.magnetSoundInterval.start()
+ self.lightning = []
+ for i in range(4):
+ t = float(i) / 3.0 - 0.5
+ l = self.craneGame.lightning.copyTo(self.gripper)
+ l.setScale(random.choice([1, -1]), 1, 5)
+ l.setZ(random.uniform(-5, -5.5))
+ l.flattenLight()
+ l.setTwoSided(1)
+ l.setBillboardAxis()
+ l.setScale(random.uniform(0.5, 1.0))
+ if t < 0:
+ l.setX(t - 0.7)
+ else:
+ l.setX(t + 0.7)
+ l.setR(-20 * t)
+ l.setP(random.uniform(-20, 20))
+ self.lightning.append(l)
+
+ taskMgr.add(self.__flickerLightning, self.flickerName)
+
+ def stopFlicker(self):
+ self.magnetSoundInterval.finish()
+ self.magnetLoopSfx.stop()
+ taskMgr.remove(self.flickerName)
+ for l in self.lightning:
+ l.detachNode()
+
+ self.lightning = None
+ return
+
+ def __flickerLightning(self, task):
+ for l in self.lightning:
+ if random.random() < 0.5:
+ l.hide()
+ else:
+ l.show()
+
+ return Task.cont
+
+ def __sniffedSomething(self, entry):
+ np = entry.getIntoNodePath()
+ doId = int(np.getNetTag('object'))
+ obj = base.cr.doId2do.get(doId)
+ if obj and obj.state != 'LocalDropped' and (obj.state != 'Dropped' or obj.craneId != self.doId):
+ obj.d_requestGrab()
+ obj.demand('LocalGrabbed', localAvatar.doId, self.doId)
+
+ def grabObject(self, obj):
+ if self.state == 'Off':
+ return
+ if self.heldObject != None:
+ self.releaseObject()
+ self.__deactivateSniffer()
+ obj.wrtReparentTo(self.gripper)
+ if obj.lerpInterval:
+ obj.lerpInterval.finish()
+ obj.lerpInterval = Parallel(obj.posInterval(ToontownGlobals.CashbotBossToMagnetTime, Point3(*obj.grabPos)), obj.quatInterval(ToontownGlobals.CashbotBossToMagnetTime, VBase3(obj.getH(), 0, 0)), obj.toMagnetSoundInterval)
+ obj.lerpInterval.start()
+ self.heldObject = obj
+ self.handler.setDynamicFrictionCoef(obj.craneFrictionCoef)
+ self.slideSpeed = obj.craneSlideSpeed
+ self.rotateSpeed = obj.craneRotateSpeed
+ if self.avId == localAvatar.doId and not self.magnetOn:
+ self.releaseObject()
+ return
+
+ def dropObject(self, obj):
+ if obj.lerpInterval:
+ obj.lerpInterval.finish()
+ obj.wrtReparentTo(render)
+ obj.lerpInterval = Parallel(obj.quatInterval(ToontownGlobals.CashbotBossFromMagnetTime, VBase3(obj.getH(), 0, 0), blendType='easeOut'))
+ obj.lerpInterval.start()
+ p1 = self.bottomLink.node().getPhysicsObject()
+ v = render.getRelativeVector(self.bottomLink, p1.getVelocity())
+ obj.physicsObject.setVelocity(v * 1.5)
+ if self.heldObject == obj:
+ self.heldObject = None
+ self.handler.setDynamicFrictionCoef(GameConsts.Settings.EmptyFrictionCoef.get())
+ self.slideSpeed = self.emptySlideSpeed
+ self.rotateSpeed = self.emptyRotateSpeed
+ return
+
+ def releaseObject(self):
+ if self.heldObject:
+ obj = self.heldObject
+ obj.d_requestDrop()
+ if obj.state == 'Grabbed':
+ obj.demand('LocalDropped', localAvatar.doId, self.doId)
+
+ def __hitTrigger(self, event):
+ pass
+
+ def setCraneGameId(self, craneGameId):
+ self.craneGameId = craneGameId
+ self.craneGame = base.cr.doId2do[craneGameId]
+
+ def setIndex(self, index):
+ self.index = index
+
+ def setState(self, state, avId):
+ if state == 'C':
+ self.demand('Controlled', avId)
+ elif state == 'F':
+ self.demand('Free')
+ else:
+ self.notify.error('Invalid state from AI: %s' % state)
+
+ def d_requestControl(self):
+ self.sendUpdate('requestControl')
+
+ def d_requestFree(self):
+ self.sendUpdate('requestFree')
+
+ def b_clearSmoothing(self):
+ self.d_clearSmoothing()
+ self.clearSmoothing()
+
+ def d_clearSmoothing(self):
+ self.sendUpdate('clearSmoothing', [0])
+
+ def clearSmoothing(self, bogus = None):
+ self.armSmoother.clearPositions(1)
+ for smoother in self.linkSmoothers:
+ smoother.clearPositions(1)
+
+ def reloadPosition(self):
+ self.armSmoother.clearPositions(0)
+ self.armSmoother.setPos(self.crane.getPos())
+ self.armSmoother.setHpr(self.arm.getHpr())
+ self.armSmoother.setPhonyTimestamp()
+ for linkNum in range(self.numLinks):
+ smoother = self.linkSmoothers[linkNum]
+ an, anp, cnp = self.activeLinks[linkNum]
+ smoother.clearPositions(0)
+ smoother.setPos(anp.getPos())
+ smoother.setPhonyTimestamp()
+
+ def doSmoothTask(self, task):
+ self.armSmoother.computeAndApplySmoothPosHpr(self.crane, self.arm)
+ for linkNum in range(self.numLinks):
+ smoother = self.linkSmoothers[linkNum]
+ anp = self.activeLinks[linkNum][1]
+ smoother.computeAndApplySmoothPos(anp)
+
+ return Task.cont
+
+ def startSmooth(self):
+ if not self.smoothStarted:
+ taskName = self.smoothName
+ taskMgr.remove(taskName)
+ self.reloadPosition()
+ taskMgr.add(self.doSmoothTask, taskName)
+ self.smoothStarted = 1
+
+ def stopSmooth(self):
+ if self.smoothStarted:
+ taskName = self.smoothName
+ taskMgr.remove(taskName)
+ self.forceToTruePosition()
+ self.smoothStarted = 0
+
+ def forceToTruePosition(self):
+ if self.armSmoother.getLatestPosition():
+ self.armSmoother.applySmoothPos(self.crane)
+ self.armSmoother.applySmoothHpr(self.arm)
+ self.armSmoother.clearPositions(1)
+ for linkNum in range(self.numLinks):
+ smoother = self.linkSmoothers[linkNum]
+ an, anp, cnp = self.activeLinks[linkNum]
+ if smoother.getLatestPosition():
+ smoother.applySmoothPos(anp)
+ smoother.clearPositions(1)
+
+ def setCablePos(self, changeSeq, y, h, links, timestamp):
+ self.changeSeq = changeSeq
+ if self.smoothStarted:
+ now = globalClock.getFrameTime()
+ local = globalClockDelta.networkToLocalTime(timestamp, now)
+ self.armSmoother.setY(y)
+ self.armSmoother.setH(h)
+ self.armSmoother.setTimestamp(local)
+ self.armSmoother.markPosition()
+ for linkNum in range(self.numLinks):
+ smoother = self.linkSmoothers[linkNum]
+ lp = links[linkNum]
+ smoother.setPos(*lp)
+ smoother.setTimestamp(local)
+ smoother.markPosition()
+
+ else:
+ self.crane.setY(y)
+ self.arm.setH(h)
+
+ def d_sendCablePos(self):
+ timestamp = globalClockDelta.getFrameNetworkTime()
+ links = []
+ for linkNum in range(self.numLinks):
+ an, anp, cnp = self.activeLinks[linkNum]
+ p = anp.getPos()
+ links.append((p[0], p[1], p[2]))
+
+ self.sendUpdate('setCablePos', [self.changeSeq,
+ self.crane.getY(),
+ self.arm.getH(),
+ links,
+ timestamp])
+
+ def stopPosHprBroadcast(self):
+ taskName = self.posHprBroadcastName
+ taskMgr.remove(taskName)
+
+ def startPosHprBroadcast(self):
+ taskName = self.posHprBroadcastName
+ self.b_clearSmoothing()
+ self.d_sendCablePos()
+ taskMgr.remove(taskName)
+ taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName)
+
+ def __posHprBroadcast(self, task):
+ self.d_sendCablePos()
+ taskName = self.posHprBroadcastName
+ taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName)
+ return Task.done
+
+ def enterOff(self):
+ self.clearCable()
+ self.root.detachNode()
+
+ def exitOff(self):
+ if self.craneGame:
+ self.setupCable()
+ self.root.reparentTo(render)
+
+ def enterControlled(self, avId):
+ self.avId = avId
+ toon = base.cr.doId2do.get(avId)
+ if not toon:
+ return
+ self.grabTrack = self.makeToonGrabInterval(toon)
+ if avId == localAvatar.doId:
+ self.craneGame.toCraneMode()
+ camera.reparentTo(self.hinge)
+ camera.setPosHpr(0, -20, -5, 0, -20, 0)
+ self.tube.stash()
+ localAvatar.setPosHpr(self.controls, 0, 0, 0, 0, 0, 0)
+ localAvatar.sendCurrentPosition()
+ self.__activatePhysics()
+ self.__enableControlInterface()
+ self.startPosHprBroadcast()
+ self.startShadow()
+ else:
+ self.startSmooth()
+ toon.stopSmooth()
+ self.grabTrack = Sequence(self.grabTrack, Func(toon.startSmooth))
+ self.grabTrack.start()
+
+ def exitControlled(self):
+ self.grabTrack.finish()
+ del self.grabTrack
+ if self.toon and not self.toon.isDisabled():
+ self.toon.loop('neutral')
+ self.toon.startSmooth()
+ self.stopWatchJoystick()
+ self.stopPosHprBroadcast()
+ self.stopShadow()
+ self.stopSmooth()
+ if self.avId == localAvatar.doId:
+ self.__disableControlInterface()
+ self.__deactivatePhysics()
+ self.tube.unstash()
+ camera.reparentTo(base.localAvatar)
+ camera.setPos(base.localAvatar.cameraPositions[0][0])
+ camera.setHpr(0, 0, 0)
+ self.__straightenCable()
+
+ def enterFree(self):
+ if self.fadeTrack:
+ self.fadeTrack.finish()
+ self.fadeTrack = None
+ self.restoreScaleTrack = Sequence(Wait(6), self.getRestoreScaleInterval())
+ self.restoreScaleTrack.start()
+ if self.avId == localAvatar.doId:
+ self.controlModel.setAlphaScale(0.3)
+ self.controlModel.setTransparency(1)
+ taskMgr.doMethodLater(5, self.__allowDetect, self.triggerName)
+ self.fadeTrack = Sequence(Func(self.controlModel.setTransparency, 1), self.controlModel.colorScaleInterval(0.2, VBase4(1, 1, 1, 0.3)))
+ self.fadeTrack.start()
+ else:
+ self.trigger.unstash()
+ self.accept(self.triggerEvent, self.__hitTrigger)
+ self.avId = 0
+ return
+
+ def __allowDetect(self, task):
+ if self.fadeTrack:
+ self.fadeTrack.finish()
+ self.fadeTrack = Sequence(self.controlModel.colorScaleInterval(0.2, VBase4(1, 1, 1, 1)), Func(self.controlModel.clearColorScale), Func(self.controlModel.clearTransparency))
+ self.fadeTrack.start()
+ self.trigger.unstash()
+ self.accept(self.triggerEvent, self.__hitTrigger)
+
+ def exitFree(self):
+ if self.fadeTrack:
+ self.fadeTrack.finish()
+ self.fadeTrack = None
+ self.restoreScaleTrack.pause()
+ del self.restoreScaleTrack
+ taskMgr.remove(self.triggerName)
+ self.controlModel.clearColorScale()
+ self.controlModel.clearTransparency()
+ self.trigger.stash()
+ self.ignore(self.triggerEvent)
+ return
+
+ def enterMovie(self):
+ self.__activatePhysics()
+
+ def exitMovie(self):
+ self.__deactivatePhysics()
+ self.__straightenCable()
+
+ if __dev__:
+
+ def _handleEmptyFrictionCoefChanged(self, coef):
+ self.handler.setDynamicFrictionCoef(coef)
+
+ def _handleRopeLinkMassChanged(self, mass):
+ for an, anp, cnp in self.activeLinks:
+ an.getPhysicsObject().setMass(mass)
+
+ def _handleMagnetMassChanged(self, mass):
+ pass
diff --git a/toontown/cogdominium/DistCogdoCraneAI.py b/toontown/cogdominium/DistCogdoCraneAI.py
new file mode 100644
index 0000000..6b1becb
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoCraneAI.py
@@ -0,0 +1,52 @@
+from pandac.PandaModules import *
+from direct.distributed import DistributedObjectAI
+from toontown.toonbase import ToontownGlobals
+from otp.otpbase import OTPGlobals
+from direct.fsm import FSM
+
+class DistCogdoCraneAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM):
+
+ def __init__(self, air, craneGame, index):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ FSM.FSM.__init__(self, 'DistCogdoCraneAI')
+ self.craneGame = craneGame
+ self.index = index
+ self.avId = 0
+ self.objectId = 0
+
+ def getCraneGameId(self):
+ return self.craneGame.doId
+
+ def getIndex(self):
+ return self.index
+
+ def generate(self):
+ DistributedObjectAI.DistributedObjectAI.generate(self)
+ self.request('Free')
+
+ def d_setState(self, state, avId):
+ self.sendUpdate('setState', [
+ state,
+ avId])
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def enterControlled(self, avId):
+ self.avId = avId
+ self.d_setState('C', avId)
+
+ def exitControlled(self):
+ if self.objectId:
+ obj = self.air.doId2do[self.objectId]
+ obj.request('Dropped', self.avId, self.doId)
+
+ def enterFree(self):
+ self.avId = 0
+ self.d_setState('F', 0)
+
+ def exitFree(self):
+ pass
diff --git a/toontown/cogdominium/DistCogdoCraneCog.py b/toontown/cogdominium/DistCogdoCraneCog.py
new file mode 100644
index 0000000..b22c8e8
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoCraneCog.py
@@ -0,0 +1,61 @@
+from pandac import PandaModules as PM
+from direct.distributed.ClockDelta import globalClockDelta
+from direct.distributed.DistributedObject import DistributedObject
+from direct.interval import IntervalGlobal as IG
+from toontown.cogdominium import CogdoCraneGameConsts as GameConsts
+from toontown.suit.Suit import Suit
+
+class DistCogdoCraneCog(Suit, DistributedObject):
+
+ def __init__(self, cr):
+ DistributedObject.__init__(self, cr)
+ Suit.__init__(self)
+ self._moveIval = None
+ return
+
+ def setGameId(self, gameId):
+ self._gameId = gameId
+
+ def getGame(self):
+ return self.cr.doId2do.get(self._gameId)
+
+ def setSpawnInfo(self, entranceId, timestamp):
+ self._startMoveIval(entranceId, globalClockDelta.networkToLocalTime(timestamp))
+
+ def _startMoveIval(self, entranceId, startT):
+ self._stopMoveIval()
+ unitVecs = (PM.Vec3(1, 0, 0),
+ PM.Vec3(0, 1, 0),
+ PM.Vec3(-1, 0, 0),
+ PM.Vec3(0, -1, 0))
+ machineDistance = 4
+ entranceDistance = 60
+ startPos = unitVecs[entranceId] * entranceDistance
+ endPos = unitVecs[entranceId] * machineDistance
+ walkDur = (endPos - startPos).length() / GameConsts.CogSettings.CogWalkSpeed.get()
+ sceneRoot = self.getGame().getSceneRoot()
+ moveIval = IG.Sequence(IG.Func(self.reparentTo, sceneRoot), IG.Func(self.setPos, startPos), IG.Func(self.lookAt, sceneRoot), IG.Func(self.loop, 'walk'), IG.LerpPosInterval(self, walkDur, endPos, startPos=startPos))
+ interactIval = IG.Sequence(IG.Func(self.loop, 'neutral'), IG.Wait(GameConsts.CogSettings.CogMachineInteractDuration.get()))
+ flyIval = IG.Sequence(IG.Func(self.pose, 'landing', 0), IG.LerpPosInterval(self, GameConsts.CogSettings.CogFlyAwayDuration.get(), self._getFlyAwayDest, blendType='easeIn'))
+ self._moveIval = IG.Sequence(moveIval, interactIval, flyIval)
+ self._moveIval.start(globalClock.getFrameTime() - startT)
+
+ def _getFlyAwayDest(self):
+ return self.getPos() + PM.Vec3(0, 0, GameConsts.CogSettings.CogFlyAwayHeight.get())
+
+ def _stopMoveIval(self):
+ if self._moveIval:
+ self._moveIval.finish()
+ self._moveIval = None
+ return
+
+ def disable(self):
+ self._stopMoveIval()
+ DistributedObject.disable(self)
+
+ def delete(self):
+ Suit.delete(self)
+ DistributedObject.delete(self)
+
+ def setDNAString(self, dnaString):
+ Suit.setDNAString(self, dnaString)
diff --git a/toontown/cogdominium/DistCogdoCraneCogAI.py b/toontown/cogdominium/DistCogdoCraneCogAI.py
new file mode 100644
index 0000000..f6ed97e
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoCraneCogAI.py
@@ -0,0 +1,20 @@
+from direct.distributed.ClockDelta import globalClockDelta
+from direct.distributed.DistributedObjectAI import DistributedObjectAI
+
+class DistCogdoCraneCogAI(DistributedObjectAI):
+
+ def __init__(self, air, game, dna, entranceId, spawnTime):
+ DistributedObjectAI.__init__(self, air)
+ self._gameId = game.doId
+ self._dna = dna
+ self._entranceId = entranceId
+ self._spawnTime = spawnTime
+
+ def getGameId(self):
+ return self._gameId
+
+ def getDNAString(self):
+ return self._dna.makeNetString()
+
+ def getSpawnInfo(self):
+ return (self._entranceId, globalClockDelta.localToNetworkTime(self._spawnTime))
diff --git a/toontown/cogdominium/DistCogdoCraneGame.py b/toontown/cogdominium/DistCogdoCraneGame.py
new file mode 100644
index 0000000..98f688a
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoCraneGame.py
@@ -0,0 +1,211 @@
+from pandac import PandaModules as PM
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from direct.task.Task import Task
+from otp.level import LevelConstants
+from otp.otpbase import OTPGlobals
+from toontown.cogdominium.DistCogdoLevelGame import DistCogdoLevelGame
+from toontown.cogdominium import CogdoCraneGameConsts as GameConsts
+from toontown.cogdominium.CogdoCraneGameBase import CogdoCraneGameBase
+from toontown.toonbase import ToontownTimer
+from toontown.toonbase import TTLocalizer as TTL
+from toontown.toonbase import ToontownGlobals
+
+class DistCogdoCraneGame(CogdoCraneGameBase, DistCogdoLevelGame):
+ notify = directNotify.newCategory('DistCogdoCraneGame')
+
+ def __init__(self, cr):
+ DistCogdoLevelGame.__init__(self, cr)
+ self.cranes = {}
+ self.moneyBags = {}
+
+ def getTitle(self):
+ return TTL.CogdoCraneGameTitle
+
+ def getInstructions(self):
+ return TTL.CogdoCraneGameInstructions
+
+ def announceGenerate(self):
+ DistCogdoLevelGame.announceGenerate(self)
+ self.timer = ToontownTimer.ToontownTimer()
+ self.timer.stash()
+ if __dev__:
+ self._durationChangedEvent = self.uniqueName('durationChanged')
+
+ def disable(self):
+ self.timer.destroy()
+ self.timer = None
+ DistCogdoLevelGame.disable(self)
+ return
+
+ def enterLoaded(self):
+ DistCogdoLevelGame.enterLoaded(self)
+ self.lightning = loader.loadModel('phase_10/models/cogHQ/CBLightning.bam')
+ self.magnet = loader.loadModel('phase_10/models/cogHQ/CBMagnet.bam')
+ self.craneArm = loader.loadModel('phase_10/models/cogHQ/CBCraneArm.bam')
+ self.controls = loader.loadModel('phase_10/models/cogHQ/CBCraneControls.bam')
+ self.stick = loader.loadModel('phase_10/models/cogHQ/CBCraneStick.bam')
+ self.cableTex = self.craneArm.findTexture('MagnetControl')
+ self.moneyBag = loader.loadModel('phase_10/models/cashbotHQ/MoneyBag')
+ self.geomRoot = PM.NodePath('geom')
+ self.sceneRoot = self.geomRoot.attachNewNode('sceneRoot')
+ self.sceneRoot.setPos(35.84, -115.46, 6.46)
+ self.physicsMgr = PM.PhysicsManager()
+ integrator = PM.LinearEulerIntegrator()
+ self.physicsMgr.attachLinearIntegrator(integrator)
+ fn = PM.ForceNode('gravity')
+ self.fnp = self.geomRoot.attachNewNode(fn)
+ gravity = PM.LinearVectorForce(0, 0, GameConsts.Settings.Gravity.get())
+ fn.addForce(gravity)
+ self.physicsMgr.addLinearForce(gravity)
+ self._gravityForce = gravity
+ self._gravityForceNode = fn
+
+ def getSceneRoot(self):
+ return self.sceneRoot
+
+ def privGotSpec(self, levelSpec):
+ DistCogdoLevelGame.privGotSpec(self, levelSpec)
+ levelMgr = self.getEntity(LevelConstants.LevelMgrEntId)
+ self.endVault = levelMgr.geom
+ self.endVault.reparentTo(self.geomRoot)
+ self.endVault.findAllMatches('**/MagnetArms').detach()
+ self.endVault.findAllMatches('**/Safes').detach()
+ self.endVault.findAllMatches('**/MagnetControlsAll').detach()
+ cn = self.endVault.find('**/wallsCollision').node()
+ cn.setIntoCollideMask(OTPGlobals.WallBitmask | ToontownGlobals.PieBitmask | PM.BitMask32.lowerOn(3) << 21)
+ walls = self.endVault.find('**/RollUpFrameCillison')
+ walls.detachNode()
+ self.evWalls = self.replaceCollisionPolysWithPlanes(walls)
+ self.evWalls.reparentTo(self.endVault)
+ self.evWalls.stash()
+ floor = self.endVault.find('**/EndVaultFloorCollision')
+ floor.detachNode()
+ self.evFloor = self.replaceCollisionPolysWithPlanes(floor)
+ self.evFloor.reparentTo(self.endVault)
+ self.evFloor.setName('floor')
+ plane = PM.CollisionPlane(PM.Plane(PM.Vec3(0, 0, 1), PM.Point3(0, 0, -50)))
+ planeNode = PM.CollisionNode('dropPlane')
+ planeNode.addSolid(plane)
+ planeNode.setCollideMask(ToontownGlobals.PieBitmask)
+ self.geomRoot.attachNewNode(planeNode)
+
+ def replaceCollisionPolysWithPlanes(self, model):
+ newCollisionNode = PM.CollisionNode('collisions')
+ newCollideMask = PM.BitMask32(0)
+ planes = []
+ collList = model.findAllMatches('**/+CollisionNode')
+ if not collList:
+ collList = [model]
+ for cnp in collList:
+ cn = cnp.node()
+ if not isinstance(cn, PM.CollisionNode):
+ self.notify.warning('Not a collision node: %s' % repr(cnp))
+ break
+ newCollideMask = newCollideMask | cn.getIntoCollideMask()
+ for i in range(cn.getNumSolids()):
+ solid = cn.getSolid(i)
+ if isinstance(solid, PM.CollisionPolygon):
+ plane = PM.Plane(solid.getPlane())
+ planes.append(plane)
+ else:
+ self.notify.warning('Unexpected collision solid: %s' % repr(solid))
+ newCollisionNode.addSolid(plane)
+
+ newCollisionNode.setIntoCollideMask(newCollideMask)
+ threshold = 0.1
+ planes.sort(lambda p1, p2: p1.compareTo(p2, threshold))
+ lastPlane = None
+ for plane in planes:
+ if lastPlane == None or plane.compareTo(lastPlane, threshold) != 0:
+ cp = PM.CollisionPlane(plane)
+ newCollisionNode.addSolid(cp)
+ lastPlane = plane
+
+ return PM.NodePath(newCollisionNode)
+
+ def exitLoaded(self):
+ self.fnp.removeNode()
+ self.physicsMgr.clearLinearForces()
+ self.geomRoot.removeNode()
+ self._gravityForce = None
+ self._gravityForceNode = None
+ DistCogdoLevelGame.exitLoaded(self)
+ return
+
+ def toCraneMode(self):
+ if self.cr:
+ place = self.cr.playGame.getPlace()
+ if place and hasattr(place, 'fsm'):
+ place.setState('crane')
+
+ def enterVisible(self):
+ DistCogdoLevelGame.enterVisible(self)
+ self.geomRoot.reparentTo(render)
+
+ def placeEntranceElev(self, elev):
+ elev.setPos(-10.63, -113.64, 6.03)
+ elev.setHpr(90, 0, 0)
+
+ def enterGame(self):
+ DistCogdoLevelGame.enterGame(self)
+ self._physicsTask = taskMgr.add(self._doPhysics, self.uniqueName('physics'), priority=25)
+ self.evWalls.stash()
+ self._startTimer()
+ if __dev__:
+ self.accept(self._durationChangedEvent, self._startTimer)
+
+ def _startTimer(self):
+ timeLeft = GameConsts.Settings.GameDuration.get() - self.getCurrentGameTime()
+ self.timer.posInTopRightCorner()
+ self.timer.setTime(timeLeft)
+ self.timer.countdown(timeLeft, self.timerExpired)
+ self.timer.unstash()
+
+ def _doPhysics(self, task):
+ dt = globalClock.getDt()
+ self.physicsMgr.doPhysics(dt)
+ return Task.cont
+
+ def exitGame(self):
+ if __dev__:
+ self.ignore(self._durationChangedEvent)
+ DistCogdoLevelGame.exitGame(self)
+ self._physicsTask.remove()
+
+ def enterFinish(self):
+ DistCogdoLevelGame.enterFinish(self)
+ timeLeft = 10 - (globalClock.getRealTime() - self.getFinishTime())
+ self.timer.setTime(timeLeft)
+ self.timer.countdown(timeLeft, self.timerExpired)
+ self.timer.unstash()
+
+ def timerExpired(self):
+ pass
+
+ if __dev__:
+
+ def _handleGameDurationChanged(self, gameDuration):
+ messenger.send(self._durationChangedEvent)
+
+ def _handleGravityChanged(self, gravity):
+ self.physicsMgr.removeLinearForce(self._gravityForce)
+ self._gravityForceNode.removeForce(self._gravityForce)
+ self._gravityForce = PM.LinearVectorForce(0, 0, gravity)
+ self.physicsMgr.addLinearForce(self._gravityForce)
+ self._gravityForceNode.addForce(self._gravityForce)
+
+ def _handleEmptyFrictionCoefChanged(self, coef):
+ for crane in self.cranes.itervalues():
+ crane._handleEmptyFrictionCoefChanged(coef)
+
+ def _handleRopeLinkMassChanged(self, mass):
+ for crane in self.cranes.itervalues():
+ crane._handleRopeLinkMassChanged(mass)
+
+ def _handleMagnetMassChanged(self, mass):
+ for crane in self.cranes.itervalues():
+ crane._handleMagnetMassChanged(mass)
+
+ def _handleMoneyBagGrabHeightChanged(self, height):
+ for moneyBag in self.moneyBags.itervalues():
+ moneyBag._handleMoneyBagGrabHeightChanged(height)
diff --git a/toontown/cogdominium/DistCogdoCraneGameAI.py b/toontown/cogdominium/DistCogdoCraneGameAI.py
new file mode 100644
index 0000000..81b6168
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoCraneGameAI.py
@@ -0,0 +1,107 @@
+from pandac import PandaModules as PM
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from toontown.cogdominium.DistCogdoLevelGameAI import DistCogdoLevelGameAI
+from toontown.cogdominium.DistCogdoCraneAI import DistCogdoCraneAI
+from toontown.cogdominium import CogdoCraneGameConsts as GameConsts
+from toontown.cogdominium.CogdoCraneGameBase import CogdoCraneGameBase
+from toontown.cogdominium import CogdoGameConsts
+from toontown.cogdominium.DistCogdoCraneMoneyBagAI import DistCogdoCraneMoneyBagAI
+from toontown.cogdominium.DistCogdoCraneCogAI import DistCogdoCraneCogAI
+from toontown.suit.SuitDNA import SuitDNA
+import random
+
+class DistCogdoCraneGameAI(CogdoCraneGameBase, DistCogdoLevelGameAI, PM.NodePath):
+ notify = directNotify.newCategory('DistCogdoCraneGameAI')
+
+ def __init__(self, air, interior):
+ PM.NodePath.__init__(self, uniqueName('CraneGameAI'))
+ DistCogdoLevelGameAI.__init__(self, air, interior)
+ self._cranes = [
+ None] * CogdoGameConsts.MaxPlayers
+ self._moneyBags = [
+ None] * 8
+
+ def delete(self):
+ DistCogdoLevelGameAI.delete(self)
+ self.removeNode()
+
+ def enterLoaded(self):
+ DistCogdoLevelGameAI.enterLoaded(self)
+ self.scene = PM.NodePath('scene')
+ cn = PM.CollisionNode('walls')
+ cs = PM.CollisionSphere(0, 0, 0, 13)
+ cn.addSolid(cs)
+ cs = PM.CollisionInvSphere(0, 0, 0, 42)
+ cn.addSolid(cs)
+ self.attachNewNode(cn)
+ for i in xrange(CogdoGameConsts.MaxPlayers):
+ crane = DistCogdoCraneAI(self.air, self, i)
+ crane.generateWithRequired(self.zoneId)
+ self._cranes[i] = crane
+
+ for i in xrange(len(self._moneyBags)):
+ mBag = DistCogdoCraneMoneyBagAI(self.air, self, i)
+ mBag.generateWithRequired(self.zoneId)
+ self._moneyBags[i] = mBag
+
+ def exitLoaded(self):
+ for i in xrange(len(self._moneyBags)):
+ if self._moneyBags[i]:
+ self._moneyBags[i].requestDelete()
+ self._moneyBags[i] = None
+
+ for i in xrange(CogdoGameConsts.MaxPlayers):
+ if self._cranes[i]:
+ self._cranes[i].requestDelete()
+ self._cranes[i] = None
+
+ DistCogdoLevelGameAI.exitLoaded(self)
+
+ def enterGame(self):
+ DistCogdoLevelGameAI.enterGame(self)
+ for i in xrange(self.getNumPlayers()):
+ self._cranes[i].request('Controlled', self.getToonIds()[i])
+
+ for i in xrange(len(self._moneyBags)):
+ if self._moneyBags[i]:
+ self._moneyBags[i].request('Initial')
+
+ self._cog = DistCogdoCraneCogAI(self.air, self, self.getDroneCogDNA(), random.randrange(4), globalClock.getFrameTime())
+ self._cog.generateWithRequired(self.zoneId)
+ self._scheduleGameDone()
+
+ def _scheduleGameDone(self):
+ timeLeft = GameConsts.Settings.GameDuration.get() - (globalClock.getRealTime() - self.getStartTime())
+ if timeLeft > 0:
+ self._gameDoneEvent = taskMgr.doMethodLater(timeLeft, self._gameDoneDL, self.uniqueName('boardroomGameDone'))
+ else:
+ self._gameDoneDL()
+
+ def exitGame(self):
+ self._cog.requestDelete()
+ self._cog = None
+ taskMgr.remove(self._gameDoneEvent)
+ self._gameDoneEvent = None
+
+ def _gameDoneDL(self, task = None):
+ self._handleGameFinished()
+ return task.done
+
+ def enterFinish(self):
+ DistCogdoLevelGameAI.enterFinish(self)
+ self._finishDoneEvent = taskMgr.doMethodLater(10.0, self._finishDoneDL, self.uniqueName('boardroomFinishDone'))
+
+ def exitFinish(self):
+ taskMgr.remove(self._finishDoneEvent)
+ self._finishDoneEvent = None
+
+ def _finishDoneDL(self, task):
+ self.announceGameDone()
+ return task.done
+
+ if __dev__:
+
+ def _handleGameDurationChanged(self, gameDuration):
+ if hasattr(self, '_gameDoneEvent') and self._gameDoneEvent != None:
+ taskMgr.remove(self._gameDoneEvent)
+ self._scheduleGameDone()
diff --git a/toontown/cogdominium/DistCogdoCraneMoneyBag.py b/toontown/cogdominium/DistCogdoCraneMoneyBag.py
new file mode 100644
index 0000000..00bf41a
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoCraneMoneyBag.py
@@ -0,0 +1,89 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import ToontownGlobals
+from otp.otpbase import OTPGlobals
+from toontown.cogdominium.DistCogdoCraneObject import DistCogdoCraneObject
+from toontown.cogdominium import CogdoCraneGameConsts as GameConsts
+
+class DistCogdoCraneMoneyBag(DistCogdoCraneObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistCogdoCraneMoneyBag')
+ grabPos = (0, 0, GameConsts.Settings.MoneyBagGrabHeight.get())
+ craneFrictionCoef = 0.2
+ craneSlideSpeed = 11
+ craneRotateSpeed = 16
+ wantsWatchDrift = 0
+
+ def __init__(self, cr):
+ DistCogdoCraneObject.__init__(self, cr)
+ NodePath.__init__(self, 'object')
+ self.index = None
+ self.flyToMagnetSfx = loader.loadSfx('phase_5/audio/sfx/TL_rake_throw_only.mp3')
+ self.hitMagnetSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_safe.mp3')
+ self.toMagnetSoundInterval = Parallel(SoundInterval(self.flyToMagnetSfx, duration=ToontownGlobals.CashbotBossToMagnetTime, node=self), Sequence(Wait(ToontownGlobals.CashbotBossToMagnetTime - 0.02), SoundInterval(self.hitMagnetSfx, duration=1.0, node=self)))
+ self.hitFloorSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_bigweight_miss.mp3')
+ self.hitFloorSoundInterval = SoundInterval(self.hitFloorSfx, node=self)
+ return
+
+ def announceGenerate(self):
+ DistCogdoCraneObject.announceGenerate(self)
+ self.name = 'moneyBag-%s' % self.doId
+ self.setName(self.name)
+ self.craneGame.moneyBag.copyTo(self)
+ self.shadow = NodePath('notAShadow')
+ self.collisionNode.setName('moneyBag')
+ cs = CollisionSphere(0, 0, 4, 4)
+ self.collisionNode.addSolid(cs)
+ self.craneGame.moneyBags[self.index] = self
+ self.setupPhysics('moneyBag')
+ self.resetToInitialPosition()
+
+ def disable(self):
+ del self.craneGame.moneyBags[self.index]
+ DistCogdoCraneObject.disable(self)
+
+ def hideShadows(self):
+ self.shadow.hide()
+
+ def showShadows(self):
+ self.shadow.show()
+
+ def getMinImpact(self):
+ if self.craneGame.heldObject:
+ return ToontownGlobals.CashbotBossSafeKnockImpact
+ else:
+ return ToontownGlobals.CashbotBossSafeNewImpact
+
+ def resetToInitialPosition(self):
+ posHpr = GameConsts.MoneyBagPosHprs[self.index]
+ self.setPosHpr(*posHpr)
+ self.physicsObject.setVelocity(0, 0, 0)
+
+ def fellOut(self):
+ self.deactivatePhysics()
+ self.d_requestInitial()
+
+ def setIndex(self, index):
+ self.index = index
+
+ def setObjectState(self, state, avId, craneId):
+ if state == 'I':
+ self.demand('Initial')
+ else:
+ DistCogdoCraneObject.setObjectState(self, state, avId, craneId)
+
+ def d_requestInitial(self):
+ self.sendUpdate('requestInitial')
+
+ def enterInitial(self):
+ self.resetToInitialPosition()
+ self.showShadows()
+
+ def exitInitial(self):
+ pass
+
+ if __dev__:
+
+ def _handleMoneyBagGrabHeightChanged(self, height):
+ grabPos = DistCogdoCraneMoneyBag.grabPos
+ DistCogdoCraneMoneyBag.grabPos = (grabPos[0], grabPos[1], height)
diff --git a/toontown/cogdominium/DistCogdoCraneMoneyBagAI.py b/toontown/cogdominium/DistCogdoCraneMoneyBagAI.py
new file mode 100644
index 0000000..29ddfa9
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoCraneMoneyBagAI.py
@@ -0,0 +1,76 @@
+from pandac.PandaModules import *
+from toontown.toonbase import ToontownGlobals
+from otp.otpbase import OTPGlobals
+from toontown.cogdominium.DistCogdoCraneObjectAI import DistCogdoCraneObjectAI
+from toontown.cogdominium import CogdoCraneGameConsts as GameConsts
+
+class DistCogdoCraneMoneyBagAI(DistCogdoCraneObjectAI):
+ wantsWatchDrift = 0
+
+ def __init__(self, air, boss, index):
+ DistCogdoCraneObjectAI.__init__(self, air, boss)
+ self.index = index
+ self.avoidHelmet = 0
+ cn = CollisionNode('sphere')
+ cs = CollisionSphere(0, 0, 0, 6)
+ cn.addSolid(cs)
+ self.attachNewNode(cn)
+
+ def resetToInitialPosition(self):
+ posHpr = GameConsts.MoneyBagPosHprs[self.index]
+ self.setPosHpr(*posHpr)
+
+ def getIndex(self):
+ return self.index
+
+ def hitBoss(self, impact):
+ avId = self.air.getAvatarIdFromSender()
+ self.validate(avId, impact <= 1.0, 'invalid hitBoss impact %s' % impact)
+ if avId not in self.boss.involvedToons:
+ return
+
+ if self.state != 'Dropped' and self.state != 'Grabbed':
+ return
+
+ if self.avoidHelmet or self == self.boss.heldObject:
+ return
+
+ if self.boss.heldObject == None:
+ if self.boss.attackCode == ToontownGlobals.BossCogDizzy:
+ damage = int(impact * 50)
+ self.boss.recordHit(max(damage, 2))
+ elif self.boss.acceptHelmetFrom(avId):
+ self.demand('Grabbed', self.boss.doId, self.boss.doId)
+ self.boss.heldObject = self
+
+ elif impact >= ToontownGlobals.CashbotBossSafeKnockImpact:
+ self.boss.heldObject.demand('Dropped', avId, self.boss.doId)
+ self.boss.heldObject.avoidHelmet = 1
+ self.boss.heldObject = None
+ self.avoidHelmet = 1
+ self.boss.waitForNextHelmet()
+
+ def requestInitial(self):
+ avId = self.air.getAvatarIdFromSender()
+ if avId == self.avId:
+ self.demand('Initial')
+
+ def enterGrabbed(self, avId, craneId):
+ DistCogdoCraneObjectAI.enterGrabbed(self, avId, craneId)
+ self.avoidHelmet = 0
+
+ def enterInitial(self):
+ self.avoidHelmet = 0
+ self.resetToInitialPosition()
+ if self.index == 0:
+ self.stash()
+
+ self.d_setObjectState('I', 0, 0)
+
+ def exitInitial(self):
+ if self.index == 0:
+ self.unstash()
+
+ def enterFree(self):
+ DistCogdoCraneObjectAI.enterFree(self)
+ self.avoidHelmet = 0
diff --git a/toontown/cogdominium/DistCogdoCraneObject.py b/toontown/cogdominium/DistCogdoCraneObject.py
new file mode 100644
index 0000000..8ab8e16
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoCraneObject.py
@@ -0,0 +1,322 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedSmoothNode
+from toontown.toonbase import ToontownGlobals
+from otp.otpbase import OTPGlobals
+from direct.fsm import FSM
+from direct.task import Task
+smileyDoId = 1
+
+class DistCogdoCraneObject(DistributedSmoothNode.DistributedSmoothNode, FSM.FSM):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistCogdoCraneObject')
+ wantsWatchDrift = 1
+
+ def __init__(self, cr):
+ DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr)
+ FSM.FSM.__init__(self, 'DistCogdoCraneObject')
+ self.craneGame = None
+ self.avId = 0
+ self.craneId = 0
+ self.cleanedUp = 0
+ self.collisionNode = CollisionNode('object')
+ self.collisionNode.setIntoCollideMask(ToontownGlobals.PieBitmask | OTPGlobals.WallBitmask | ToontownGlobals.CashbotBossObjectBitmask | OTPGlobals.CameraBitmask)
+ self.collisionNode.setFromCollideMask(ToontownGlobals.PieBitmask | OTPGlobals.FloorBitmask)
+ self.collisionNodePath = NodePath(self.collisionNode)
+ self.physicsActivated = 0
+ self.toMagnetSoundInterval = Sequence()
+ self.hitFloorSoundInterval = Sequence()
+ self.hitBossSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_safe_miss.mp3')
+ self.hitBossSoundInterval = SoundInterval(self.hitBossSfx)
+ self.touchedBossSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_sandbag.mp3')
+ self.touchedBossSoundInterval = SoundInterval(self.touchedBossSfx, duration=0.8)
+ self.lerpInterval = None
+ return
+
+ def disable(self):
+ self.cleanup()
+ self.stopSmooth()
+ DistributedSmoothNode.DistributedSmoothNode.disable(self)
+
+ def cleanup(self):
+ if self.cleanedUp:
+ return
+ else:
+ self.cleanedUp = 1
+ self.demand('Off')
+ self.detachNode()
+ self.toMagnetSoundInterval.finish()
+ self.hitFloorSoundInterval.finish()
+ self.hitBossSoundInterval.finish()
+ self.touchedBossSoundInterval.finish()
+ del self.toMagnetSoundInterval
+ del self.hitFloorSoundInterval
+ del self.hitBossSoundInterval
+ del self.touchedBossSoundInterval
+ self.craneGame = None
+ return
+
+ def setupPhysics(self, name):
+ an = ActorNode('%s-%s' % (name, self.doId))
+ anp = NodePath(an)
+ if not self.isEmpty():
+ self.reparentTo(anp)
+ NodePath.assign(self, anp)
+ self.physicsObject = an.getPhysicsObject()
+ self.setTag('object', str(self.doId))
+ self.collisionNodePath.reparentTo(self)
+ self.handler = PhysicsCollisionHandler()
+ self.handler.addCollider(self.collisionNodePath, self)
+ self.collideName = self.uniqueName('collide')
+ self.handler.addInPattern(self.collideName + '-%in')
+ self.handler.addAgainPattern(self.collideName + '-%in')
+ self.watchDriftName = self.uniqueName('watchDrift')
+
+ def activatePhysics(self):
+ if not self.physicsActivated:
+ self.craneGame.physicsMgr.attachPhysicalNode(self.node())
+ base.cTrav.addCollider(self.collisionNodePath, self.handler)
+ self.physicsActivated = 1
+ self.accept(self.collideName + '-floor', self.__hitFloor)
+ self.accept(self.collideName + '-goon', self.__hitGoon)
+ self.acceptOnce(self.collideName + '-headTarget', self.__hitBoss)
+ self.accept(self.collideName + '-dropPlane', self.__hitDropPlane)
+
+ def deactivatePhysics(self):
+ if self.physicsActivated:
+ self.craneGame.physicsMgr.removePhysicalNode(self.node())
+ base.cTrav.removeCollider(self.collisionNodePath)
+ self.physicsActivated = 0
+ self.ignore(self.collideName + '-floor')
+ self.ignore(self.collideName + '-goon')
+ self.ignore(self.collideName + '-headTarget')
+ self.ignore(self.collideName + '-dropPlane')
+
+ def hideShadows(self):
+ pass
+
+ def showShadows(self):
+ pass
+
+ def stashCollisions(self):
+ self.collisionNodePath.stash()
+
+ def unstashCollisions(self):
+ self.collisionNodePath.unstash()
+
+ def __hitFloor(self, entry):
+ if self.state == 'Dropped' or self.state == 'LocalDropped':
+ self.d_hitFloor()
+ self.demand('SlidingFloor', localAvatar.doId)
+
+ def __hitGoon(self, entry):
+ if self.state == 'Dropped' or self.state == 'LocalDropped':
+ goonId = int(entry.getIntoNodePath().getNetTag('doId'))
+ goon = self.cr.doId2do.get(goonId)
+ if goon:
+ self.doHitGoon(goon)
+
+ def doHitGoon(self, goon):
+ pass
+
+ def __hitBoss(self, entry):
+ if (self.state == 'Dropped' or self.state == 'LocalDropped') and self.craneId != self.craneGame.doId:
+ vel = self.physicsObject.getVelocity()
+ vel = self.crane.root.getRelativeVector(render, vel)
+ vel.normalize()
+ impact = vel[1]
+ if impact >= self.getMinImpact():
+ print 'hit! %s' % impact
+ self.hitBossSoundInterval.start()
+ self.doHitBoss(impact)
+ else:
+ self.touchedBossSoundInterval.start()
+ print '--not hard enough: %s' % impact
+
+ def doHitBoss(self, impact):
+ self.d_hitBoss(impact)
+
+ def __hitDropPlane(self, entry):
+ self.notify.info('%s fell out of the world.' % self.doId)
+ self.fellOut()
+
+ def fellOut(self):
+ raise StandardError, 'fellOut unimplented'
+
+ def getMinImpact(self):
+ return 0
+
+ def __watchDrift(self, task):
+ v = self.physicsObject.getVelocity()
+ if abs(v[0]) < 0.0001 and abs(v[1]) < 0.0001:
+ self.d_requestFree()
+ self.demand('Free')
+ return Task.cont
+
+ def prepareGrab(self):
+ pass
+
+ def prepareRelease(self):
+ pass
+
+ def setCraneGameId(self, craneGameId):
+ self.craneGameId = craneGameId
+ self.craneGame = base.cr.doId2do[craneGameId]
+
+ def setObjectState(self, state, avId, craneId):
+ if state == 'G':
+ self.demand('Grabbed', avId, craneId)
+ elif state == 'D':
+ if self.state != 'Dropped':
+ self.demand('Dropped', avId, craneId)
+ elif state == 's':
+ if self.state != 'SlidingFloor':
+ self.demand('SlidingFloor', avId)
+ elif state == 'F':
+ self.demand('Free')
+ else:
+ self.notify.error('Invalid state from AI: %s' % state)
+
+ def d_requestGrab(self):
+ self.sendUpdate('requestGrab')
+
+ def rejectGrab(self):
+ if self.state == 'LocalGrabbed':
+ self.demand('LocalDropped', self.avId, self.craneId)
+
+ def d_requestDrop(self):
+ self.sendUpdate('requestDrop')
+
+ def d_hitFloor(self):
+ self.sendUpdate('hitFloor')
+
+ def d_requestFree(self):
+ self.sendUpdate('requestFree', [self.getX(),
+ self.getY(),
+ self.getZ(),
+ self.getH()])
+
+ def d_hitBoss(self, impact):
+ self.sendUpdate('hitBoss', [impact])
+
+ def defaultFilter(self, request, args):
+ if self.craneGame == None:
+ raise FSM.RequestDenied, request
+ return FSM.FSM.defaultFilter(self, request, args)
+
+ def enterOff(self):
+ self.detachNode()
+ if self.lerpInterval:
+ self.lerpInterval.finish()
+ self.lerpInterval = None
+ return
+
+ def exitOff(self):
+ self.reparentTo(render)
+
+ def enterLocalGrabbed(self, avId, craneId):
+ self.avId = avId
+ self.craneId = craneId
+ self.crane = self.cr.doId2do.get(craneId)
+ self.hideShadows()
+ self.prepareGrab()
+ self.crane.grabObject(self)
+
+ def exitLocalGrabbed(self):
+ if self.newState != 'Grabbed':
+ self.crane.dropObject(self)
+ self.prepareRelease()
+ del self.crane
+ self.showShadows()
+
+ def enterGrabbed(self, avId, craneId):
+ if self.oldState == 'LocalGrabbed':
+ if craneId == self.craneId:
+ return
+ else:
+ self.crane.dropObject(self)
+ self.prepareRelease()
+ self.avId = avId
+ self.craneId = craneId
+ self.crane = self.cr.doId2do.get(craneId)
+ self.hideShadows()
+ self.prepareGrab()
+ self.crane.grabObject(self)
+
+ def exitGrabbed(self):
+ self.crane.dropObject(self)
+ self.prepareRelease()
+ self.showShadows()
+ del self.crane
+
+ def enterLocalDropped(self, avId, craneId):
+ self.avId = avId
+ self.craneId = craneId
+ self.crane = self.cr.doId2do.get(craneId)
+ self.activatePhysics()
+ self.startPosHprBroadcast()
+ self.hideShadows()
+ self.handler.setStaticFrictionCoef(0)
+ self.handler.setDynamicFrictionCoef(0)
+
+ def exitLocalDropped(self):
+ if self.newState != 'SlidingFloor' and self.newState != 'Dropped':
+ self.deactivatePhysics()
+ self.stopPosHprBroadcast()
+ del self.crane
+ self.showShadows()
+
+ def enterDropped(self, avId, craneId):
+ self.avId = avId
+ self.craneId = craneId
+ self.crane = self.cr.doId2do.get(craneId)
+ if self.avId == base.localAvatar.doId:
+ self.activatePhysics()
+ self.startPosHprBroadcast()
+ self.handler.setStaticFrictionCoef(0)
+ self.handler.setDynamicFrictionCoef(0)
+ else:
+ self.startSmooth()
+ self.hideShadows()
+
+ def exitDropped(self):
+ if self.avId == base.localAvatar.doId:
+ if self.newState != 'SlidingFloor':
+ self.deactivatePhysics()
+ self.stopPosHprBroadcast()
+ else:
+ self.stopSmooth()
+ del self.crane
+ self.showShadows()
+
+ def enterSlidingFloor(self, avId):
+ self.avId = avId
+ if self.lerpInterval:
+ self.lerpInterval.finish()
+ self.lerpInterval = None
+ if self.avId == base.localAvatar.doId:
+ self.activatePhysics()
+ self.startPosHprBroadcast()
+ self.handler.setStaticFrictionCoef(0.9)
+ self.handler.setDynamicFrictionCoef(0.5)
+ if self.wantsWatchDrift:
+ taskMgr.add(self.__watchDrift, self.watchDriftName)
+ else:
+ self.startSmooth()
+ self.hitFloorSoundInterval.start()
+ return
+
+ def exitSlidingFloor(self):
+ if self.avId == base.localAvatar.doId:
+ taskMgr.remove(self.watchDriftName)
+ self.deactivatePhysics()
+ self.stopPosHprBroadcast()
+ else:
+ self.stopSmooth()
+
+ def enterFree(self):
+ self.avId = 0
+ self.craneId = 0
+
+ def exitFree(self):
+ pass
diff --git a/toontown/cogdominium/DistCogdoCraneObjectAI.py b/toontown/cogdominium/DistCogdoCraneObjectAI.py
new file mode 100644
index 0000000..c754e9f
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoCraneObjectAI.py
@@ -0,0 +1,144 @@
+from pandac.PandaModules import *
+from direct.distributed import DistributedSmoothNodeAI
+from toontown.toonbase import ToontownGlobals
+from otp.otpbase import OTPGlobals
+from direct.fsm import FSM
+from direct.task import Task
+
+class DistCogdoCraneObjectAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, FSM.FSM):
+ wantsWatchDrift = 1
+
+ def __init__(self, air, craneGame):
+ DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air)
+ FSM.FSM.__init__(self, 'DistCogdoCraneObjectAI')
+ self.craneGame = craneGame
+ self.reparentTo(self.craneGame.scene)
+ self.avId = 0
+ self.craneId = 0
+
+ def cleanup(self):
+ self.detachNode()
+ self.stopWaitFree()
+
+ def delete(self):
+ self.cleanup()
+ DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self)
+
+ def startWaitFree(self, delayTime):
+ waitFreeEvent = self.uniqueName('waitFree')
+ taskMgr.remove(waitFreeEvent)
+ taskMgr.doMethodLater(delayTime, self.doFree, waitFreeEvent)
+
+ def stopWaitFree(self):
+ waitFreeEvent = self.uniqueName('waitFree')
+ taskMgr.remove(waitFreeEvent)
+
+ def doFree(self, task):
+ if not self.isDeleted():
+ self.demand('Free')
+ p = self.getPos()
+ h = self.getH()
+ self.d_setPosHpr(p[0], p[1], 0, h, 0, 0)
+
+ return Task.done
+
+ def getCraneGameId(self):
+ return self.craneGame.doId
+
+ def d_setObjectState(self, state, avId, craneId):
+ self.sendUpdate('setObjectState', [
+ state,
+ avId,
+ craneId])
+
+ def requestGrab(self):
+ avId = self.air.getAvatarIdFromSender()
+ if self.state != 'Grabbed' and self.state != 'Off':
+ (craneId, objectId) = self.__getCraneAndObject(avId)
+ if craneId != 0 and objectId == 0:
+ self.demand('Grabbed', avId, craneId)
+ return
+
+ self.sendUpdateToAvatarId(avId, 'rejectGrab', [])
+
+ def requestDrop(self):
+ avId = self.air.getAvatarIdFromSender()
+ if avId == self.avId and self.state == 'Grabbed':
+ (craneId, objectId) = self.__getCraneAndObject(avId)
+ if craneId != 0 and objectId == self.doId:
+ self.demand('Dropped', avId, craneId)
+
+ def hitFloor(self):
+ avId = self.air.getAvatarIdFromSender()
+ if avId == self.avId and self.state == 'Dropped':
+ self.demand('SlidingFloor', avId)
+
+ def requestFree(self, x, y, z, h):
+ avId = self.air.getAvatarIdFromSender()
+ if avId == self.avId:
+ self.setPosHpr(x, y, 0, h, 0, 0)
+ self.demand('WaitFree')
+
+ def hitBoss(self, impact):
+ pass
+
+ def removeToon(self, avId):
+ if avId == self.avId:
+ self.doFree(None)
+
+ def __getCraneAndObject(self, avId):
+ if self.craneGame and self.craneGame._cranes != None:
+ for crane in self.craneGame._cranes:
+ if crane.avId == avId:
+ return (crane.doId, crane.objectId)
+
+ return (0, 0)
+
+ def __setCraneObject(self, craneId, objectId):
+ if self.air:
+ crane = self.air.doId2do.get(craneId)
+ if crane:
+ crane.objectId = objectId
+
+ def enterGrabbed(self, avId, craneId):
+ self.avId = avId
+ self.craneId = craneId
+ self.__setCraneObject(self.craneId, self.doId)
+ self.d_setObjectState('G', avId, craneId)
+
+ def exitGrabbed(self):
+ self.__setCraneObject(self.craneId, 0)
+
+ def enterDropped(self, avId, craneId):
+ self.avId = avId
+ self.craneId = craneId
+ self.d_setObjectState('D', avId, craneId)
+ self.startWaitFree(10)
+
+ def exitDropped(self):
+ self.stopWaitFree()
+
+ def enterSlidingFloor(self, avId):
+ self.avId = avId
+ self.d_setObjectState('s', avId, 0)
+ if self.wantsWatchDrift:
+ self.startWaitFree(5)
+
+ def exitSlidingFloor(self):
+ self.stopWaitFree()
+
+ def enterWaitFree(self):
+ self.avId = 0
+ self.craneId = 0
+ self.startWaitFree(1)
+
+ def exitWaitFree(self):
+ self.stopWaitFree()
+
+ def enterFree(self):
+ self.avId = 0
+ self.craneId = 0
+ self.d_setObjectState('F', 0, 0)
+
+ def exitFree(self):
+ pass
diff --git a/toontown/cogdominium/DistCogdoFlyingGame.py b/toontown/cogdominium/DistCogdoFlyingGame.py
new file mode 100644
index 0000000..3c49f03
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoFlyingGame.py
@@ -0,0 +1,155 @@
+from direct.distributed.ClockDelta import globalClockDelta
+from toontown.toonbase import TTLocalizer
+from CogdoFlyingGame import CogdoFlyingGame
+from DistCogdoGame import DistCogdoGame
+import CogdoFlyingGameGlobals
+import CogdoFlyingGameGlobals as Globals
+
+class DistCogdoFlyingGame(DistCogdoGame):
+ notify = directNotify.newCategory('DistCogdoFlyingGame')
+
+ def __init__(self, cr):
+ DistCogdoGame.__init__(self, cr)
+ if __debug__ and base.config.GetBool('schellgames-dev', True):
+ self.accept('onCodeReload', self.__sgOnCodeReload)
+ self.game = CogdoFlyingGame(self)
+
+ def delete(self):
+ del self.game
+ DistCogdoGame.delete(self)
+
+ def getTitle(self):
+ return TTLocalizer.CogdoFlyingGameTitle
+
+ def getInstructions(self):
+ return TTLocalizer.CogdoFlyingGameInstructions
+
+ def placeEntranceElev(self, elev):
+ self.game.placeEntranceElevator(elev)
+
+ def d_sendRequestAction(self, action, data):
+ self.sendUpdate('requestAction', [action, data])
+
+ def doAction(self, action, data):
+ messenger.send(self.getRemoteActionEventName(), [action, data])
+
+ def toonSetAsEagleTarget(self, toonId, eagleId, networkTime):
+ self.notify.debugCall()
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.toonSetAsEagleTarget(toonId, eagleId, elapsedTime)
+
+ def toonClearAsEagleTarget(self, toonId, eagleId, networkTime):
+ self.notify.debugCall()
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.toonClearAsEagleTarget(toonId, eagleId, elapsedTime)
+
+ def eagleExitCooldown(self, eagleId, networkTime):
+ self.notify.debugCall()
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.eagleExitCooldown(eagleId, elapsedTime)
+
+ def debuffPowerup(self, toonId, pickupType, networkTime):
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.debuffPowerup(toonId, pickupType, elapsedTime)
+
+ def toonDied(self, toonId, networkTime):
+ if toonId != base.localAvatar.doId:
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.toonDied(toonId, elapsedTime)
+
+ def b_toonDied(self, toonId):
+ self.game.toonDied(toonId, 0)
+ self.d_sendRequestAction(Globals.AI.GameActions.Died, 0)
+
+ def toonSpawn(self, toonId, networkTime):
+ if toonId != base.localAvatar.doId:
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.toonSpawn(toonId, elapsedTime)
+
+ def b_toonSpawn(self, toonId):
+ self.game.toonSpawn(toonId, 0)
+ self.d_sendRequestAction(Globals.AI.GameActions.Spawn, 0)
+
+ def pickUp(self, toonId, pickupNum, networkTime):
+ if not self.getToon(base.localAvatar.doId):
+ return
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.pickUp(toonId, pickupNum, elapsedTime)
+
+ def d_sendRequestPickup(self, pickupNum, pickupType):
+ self.sendUpdate('requestPickUp', [pickupNum, pickupType])
+
+ def toonSetBlades(self, toonId, fuelState):
+ if toonId != base.localAvatar.doId:
+ self.game.toonSetBlades(toonId, fuelState)
+
+ def b_toonSetBlades(self, toonId, fuelState):
+ self.game.toonSetBlades(toonId, fuelState)
+ self.d_sendRequestAction(Globals.AI.GameActions.SetBlades, fuelState)
+
+ def toonBladeLost(self, toonId):
+ if toonId != base.localAvatar.doId:
+ self.game.toonBladeLost(toonId)
+
+ def b_toonBladeLost(self, toonId):
+ self.game.toonBladeLost(toonId)
+ self.d_sendRequestAction(Globals.AI.GameActions.BladeLost, 0)
+
+ def getRemoteActionEventName(self):
+ return self._remoteActionEventName
+
+ def setToonSad(self, toonId):
+ self.game.setToonSad(toonId)
+ DistCogdoGame.setToonSad(self, toonId)
+
+ def setToonDisconnect(self, toonId):
+ self.game.setToonDisconnect(toonId)
+ DistCogdoGame.setToonDisconnect(self, toonId)
+
+ def __handleUnexpectedExit(self, toonId):
+ self.notify.warning('%s: unexpected exit for %s' % (self.doId, toonId))
+ self.game.removePlayer(toonId)
+
+ def enterLoaded(self):
+ DistCogdoGame.enterLoaded(self)
+ self._remoteActionEventName = self.uniqueName('doAction')
+ self.game.load()
+ self.game.initPlayers()
+
+ def exitLoaded(self):
+ self.ignoreAll()
+ self.game.unload()
+ DistCogdoGame.exitLoaded(self)
+
+ def enterVisible(self):
+ DistCogdoGame.enterVisible(self)
+ self.game.onstage()
+
+ def exitVisible(self):
+ DistCogdoGame.exitVisible(self)
+
+ def enterIntro(self):
+ DistCogdoGame.enterIntro(self, Globals.Gameplay.IntroDurationSeconds)
+ self.game.startIntro()
+
+ def exitIntro(self):
+ DistCogdoGame.exitIntro(self)
+ self.game.endIntro()
+ self.stashEntranceElevator()
+
+ def enterGame(self):
+ DistCogdoGame.enterGame(self)
+ self.game.start()
+
+ def exitGame(self):
+ self.game.exit()
+ DistCogdoGame.exitGame(self)
+
+ def enterFinish(self):
+ DistCogdoGame.enterFinish(self)
+ self.game.startFinish()
+
+ def exitFinish(self):
+ self.game.endFinish()
+ self.game.offstage()
+ DistCogdoGame.exitFinish(self)
diff --git a/toontown/cogdominium/DistCogdoFlyingGameAI.py b/toontown/cogdominium/DistCogdoFlyingGameAI.py
new file mode 100644
index 0000000..2045e2a
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoFlyingGameAI.py
@@ -0,0 +1,348 @@
+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
diff --git a/toontown/cogdominium/DistCogdoGame.py b/toontown/cogdominium/DistCogdoGame.py
new file mode 100644
index 0000000..e0f87ef
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoGame.py
@@ -0,0 +1,256 @@
+from pandac.PandaModules import VBase4
+from direct.gui.DirectGui import DirectLabel
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from direct.distributed.ClockDelta import globalClockDelta
+from direct.distributed.DistributedObject import DistributedObject
+from direct.fsm import ClassicFSM, State
+from direct.fsm.StatePush import StateVar, FunctionCall
+from toontown.cogdominium import CogdoGameConsts
+from toontown.cogdominium.DistCogdoGameBase import DistCogdoGameBase
+from toontown.minigame.MinigameRulesPanel import MinigameRulesPanel
+from toontown.cogdominium.CogdoGameRulesPanel import CogdoGameRulesPanel
+from toontown.minigame import MinigameGlobals
+from toontown.toonbase import TTLocalizer as TTL
+SCHELLGAMES_DEV = __debug__ and base.config.GetBool('schellgames-dev', False)
+
+class DistCogdoGame(DistCogdoGameBase, DistributedObject):
+ notify = directNotify.newCategory('DistCogdoGame')
+
+ def __init__(self, cr):
+ DistributedObject.__init__(self, cr)
+ base.cogdoGame = self
+ self._waitingStartLabel = DirectLabel(text=TTL.MinigameWaitingForOtherPlayers, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075)
+ self._waitingStartLabel.hide()
+ self.loadFSM = ClassicFSM.ClassicFSM('DistCogdoGame.loaded', [State.State('NotLoaded', self.enterNotLoaded, self.exitNotLoaded, ['Loaded']), State.State('Loaded', self.enterLoaded, self.exitLoaded, ['NotLoaded'])], 'NotLoaded', 'NotLoaded')
+ self.loadFSM.enterInitialState()
+ self.fsm = ClassicFSM.ClassicFSM('DistCogdoGame', [State.State('Visible', self.enterVisible, self.exitVisible, ['Intro']),
+ State.State('Intro', self.enterIntro, self.exitIntro, ['WaitServerStart']),
+ State.State('WaitServerStart', self.enterWaitServerStart, self.exitWaitServerStart, ['Game']),
+ State.State('Game', self.enterGame, self.exitGame, ['Finish']),
+ State.State('Finish', self.enterFinish, self.exitFinish, ['Off']),
+ State.State('Off', self.enterOff, self.exitOff, ['Visible'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.difficultyOverride = None
+ self.exteriorZoneOverride = None
+ self._gotInterior = StateVar(False)
+ self._toonsInEntranceElev = StateVar(False)
+ self._wantStashElevator = StateVar(False)
+ self._stashElevatorFC = FunctionCall(self._doStashElevator, self._toonsInEntranceElev, self._gotInterior, self._wantStashElevator)
+ return
+
+ def getTitle(self):
+ pass
+
+ def getInstructions(self):
+ pass
+
+ def setInteriorId(self, interiorId):
+ self._interiorId = interiorId
+
+ def setExteriorZone(self, exteriorZone):
+ self.exteriorZone = exteriorZone
+
+ def setDifficultyOverrides(self, difficultyOverride, exteriorZoneOverride):
+ if difficultyOverride != CogdoGameConsts.NoDifficultyOverride:
+ self.difficultyOverride = difficultyOverride / float(CogdoGameConsts.DifficultyOverrideMult)
+ if exteriorZoneOverride != CogdoGameConsts.NoExteriorZoneOverride:
+ self.exteriorZoneOverride = exteriorZoneOverride
+
+ def getInterior(self):
+ return self.cr.getDo(self._interiorId)
+
+ def getEntranceElevator(self, callback):
+ return self.getInterior().getEntranceElevator(callback)
+
+ def getToonIds(self):
+ interior = self.getInterior()
+ if interior is not None:
+ return interior.getToonIds()
+ else:
+ return []
+ return
+
+ def getToon(self, toonId):
+ if self.cr.doId2do.has_key(toonId):
+ return self.cr.doId2do[toonId]
+ else:
+ return None
+ return None
+
+ def getNumPlayers(self):
+ return len(self.getToonIds())
+
+ def isSinglePlayer(self):
+ if self.getNumPlayers() == 1:
+ return 1
+ else:
+ return 0
+
+ def announceGenerate(self):
+ DistributedObject.announceGenerate(self)
+ self._requestInterior()
+ self.loadFSM.request('Loaded')
+ self.notify.info('difficulty: %s, safezoneId: %s' % (self.getDifficulty(), self.getSafezoneId()))
+
+ def _requestInterior(self):
+ self.cr.relatedObjectMgr.requestObjects([self._interiorId], allCallback=self._handleGotInterior)
+
+ def _handleGotInterior(self, objs):
+ self._gotInterior.set(True)
+ self.getEntranceElevator(self.placeEntranceElev)
+
+ def stashEntranceElevator(self):
+ self._wantStashElevator.set(True)
+
+ def placeEntranceElev(self, elev):
+ pass
+
+ def _doStashElevator(self, toonsInEntranceElev, gotInterior, wantStashElevator):
+ if gotInterior:
+ interior = self.getInterior()
+ if interior:
+ if not toonsInEntranceElev and wantStashElevator:
+ interior.stashElevatorIn()
+ else:
+ interior.stashElevatorIn(False)
+
+ def disable(self):
+ base.cogdoGame = None
+ self.fsm.requestFinalState()
+ self.loadFSM.requestFinalState()
+ self.fsm = None
+ self.loadFSM = None
+ DistributedObject.disable(self)
+ return
+
+ def delete(self):
+ self._stashElevatorFC.destroy()
+ self._wantStashElevator.destroy()
+ self._toonsInEntranceElev.destroy()
+ self._gotInterior.destroy()
+ self._waitingStartLabel.destroy()
+ self._waitingStartLabel = None
+ DistributedObject.delete(self)
+ return
+
+ def getDifficulty(self):
+ if self.difficultyOverride is not None:
+ return self.difficultyOverride
+ if hasattr(base, 'cogdoGameDifficulty'):
+ return float(base.cogdoGameDifficulty)
+ return CogdoGameConsts.getDifficulty(self.getSafezoneId())
+
+ def getSafezoneId(self):
+ if self.exteriorZoneOverride is not None:
+ return self.exteriorZoneOverride
+ if hasattr(base, 'cogdoGameSafezoneId'):
+ return CogdoGameConsts.getSafezoneId(base.cogdoGameSafezoneId)
+ return CogdoGameConsts.getSafezoneId(self.exteriorZone)
+
+ def enterNotLoaded(self):
+ pass
+
+ def exitNotLoaded(self):
+ pass
+
+ def enterLoaded(self):
+ pass
+
+ def exitLoaded(self):
+ pass
+
+ def enterOff(self):
+ pass
+
+ def exitOff(self):
+ pass
+
+ def setVisible(self):
+ self.fsm.request('Visible')
+
+ def setIntroStart(self):
+ self.fsm.request('Intro')
+
+ def enterVisible(self):
+ self._toonsInEntranceElev.set(True)
+
+ def exitVisible(self):
+ pass
+
+ def enterIntro(self, duration = MinigameGlobals.rulesDuration):
+ base.cr.playGame.getPlace().fsm.request('Game')
+ self._rulesDoneEvent = self.uniqueName('cogdoGameRulesDone')
+ self.accept(self._rulesDoneEvent, self._handleRulesDone)
+ self._rulesPanel = CogdoGameRulesPanel('CogdoGameRulesPanel', self.getTitle(), '', self._rulesDoneEvent, timeout=duration)
+ self._rulesPanel.load()
+ self._rulesPanel.enter()
+
+ def exitIntro(self):
+ self._toonsInEntranceElev.set(False)
+ self.ignore(self._rulesDoneEvent)
+ if self._rulesPanel:
+ self._rulesPanel.exit()
+ self._rulesPanel.unload()
+ self._rulesPanel = None
+ return
+
+ def _handleRulesDone(self):
+ self.ignore(self._rulesDoneEvent)
+ self._rulesPanel.exit()
+ self._rulesPanel.unload()
+ self._rulesPanel = None
+ self.fsm.request('WaitServerStart')
+ self.d_setAvatarReady()
+ return
+
+ def d_setAvatarReady(self):
+ self.sendUpdate('setAvatarReady', [])
+
+ def enterWaitServerStart(self):
+ numToons = 1
+ interior = self.getInterior()
+ if interior:
+ numToons = len(interior.getToonIds())
+ if numToons > 1:
+ msg = TTL.MinigameWaitingForOtherPlayers
+ else:
+ msg = TTL.MinigamePleaseWait
+ self._waitingStartLabel['text'] = msg
+ self._waitingStartLabel.show()
+
+ def exitWaitServerStart(self):
+ self._waitingStartLabel.hide()
+
+ def setGameStart(self, timestamp):
+ self._startTime = globalClockDelta.networkToLocalTime(timestamp)
+ self.fsm.request('Game')
+
+ def getStartTime(self):
+ return self._startTime
+
+ def enterGame(self):
+ if SCHELLGAMES_DEV:
+ self.acceptOnce('escape', messenger.send, ['magicWord', ['~endgame']])
+
+ def exitGame(self):
+ if SCHELLGAMES_DEV:
+ self.ignore('escape')
+
+ def setGameFinish(self, timestamp):
+ self._finishTime = globalClockDelta.networkToLocalTime(timestamp)
+ self.fsm.request('Finish')
+
+ def getFinishTime(self):
+ return self._finishTime
+
+ def enterFinish(self):
+ pass
+
+ def exitFinish(self):
+ pass
+
+ def setToonSad(self, toonId):
+ pass
+
+ def setToonDisconnect(self, toonId):
+ pass
diff --git a/toontown/cogdominium/DistCogdoGameAI.py b/toontown/cogdominium/DistCogdoGameAI.py
new file mode 100644
index 0000000..aa5bcf1
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoGameAI.py
@@ -0,0 +1,270 @@
+import new
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from direct.distributed.ClockDelta import globalClockDelta
+from direct.distributed.DistributedObjectAI import DistributedObjectAI
+from direct.fsm import ClassicFSM, State
+from toontown.cogdominium import CogdoGameConsts
+from toontown.cogdominium.DistCogdoGameBase import DistCogdoGameBase
+from otp.ai.Barrier import Barrier
+
+class SadCallbackToken:
+ pass
+
+
+class DistCogdoGameAI(DistCogdoGameBase, DistributedObjectAI):
+ notify = directNotify.newCategory('DistCogdoGameAI')
+ EndlessCogdoGames = simbase.config.GetBool('endless-cogdo-games', 0)
+
+ def __init__(self, air, interior):
+ DistributedObjectAI.__init__(self, air)
+ self._interior = interior
+ self.loadFSM = ClassicFSM.ClassicFSM('DistCogdoGameAI.loaded', [
+ State.State('NotLoaded', self.enterNotLoaded, self.exitNotLoaded, [
+ 'Loaded']),
+ State.State('Loaded', self.enterLoaded, self.exitLoaded, [
+ 'NotLoaded'])], 'NotLoaded', 'NotLoaded')
+ self.loadFSM.enterInitialState()
+ self.fsm = ClassicFSM.ClassicFSM('DistCogdoGameAI', [
+ State.State('Visible', self.enterVisible, self.exitVisible, [
+ 'Intro']),
+ State.State('Intro', self.enterIntro, self.exitIntro, [
+ 'Game']),
+ State.State('Game', self.enterGame, self.exitGame, [
+ 'Finish']),
+ State.State('Finish', self.enterFinish, self.exitFinish, [
+ 'Off']),
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'Visible'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self.difficultyOverride = None
+ self.exteriorZoneOverride = None
+
+ def setExteriorZone(self, exteriorZone):
+ self.exteriorZone = exteriorZone
+
+ def logSuspiciousEvent(self, avId, msg):
+ self.air.writeServerEvent('suspicious', avId, msg)
+
+ def generate(self):
+ DistributedObjectAI.generate(self)
+ self._sadToken2callback = {}
+ self.notify.debug('difficulty: %s, safezoneId: %s' % (self.getDifficulty(), self.getSafezoneId()))
+
+ def getInteriorId(self):
+ return self._interior.doId
+
+ def getExteriorZone(self):
+ return self.exteriorZone
+
+ def getDroneCogDNA(self):
+ return self._interior.getDroneCogDNA()
+
+ def getToonIds(self):
+ toonIds = []
+ for toonId in self._interior.getToons()[0]:
+ if toonId:
+ toonIds.append(toonId)
+
+ return toonIds
+
+ def getNumPlayers(self):
+ return len(self.getToonIds())
+
+ def requestDelete(self):
+ self.fsm.requestFinalState()
+ self.loadFSM.requestFinalState()
+ self._sadToken2callback = None
+ DistributedObjectAI.requestDelete(self)
+
+ def delete(self):
+ self._interior = None
+ self.fsm = None
+ self.loadFSM = None
+ DistributedObjectAI.delete(self)
+
+ def makeVisible(self):
+ self.loadFSM.request('Loaded')
+ self.fsm.request('Visible')
+
+ def start(self):
+ self.fsm.request('Intro')
+
+ def markStartTime(self):
+ self._startTime = globalClock.getRealTime()
+
+ def getStartTime(self):
+ return self._startTime
+
+ def markFinishTime(self):
+ self._finishTime = globalClock.getRealTime()
+
+ def getFinishTime(self):
+ return self._finishTime
+
+ def setDifficultyOverrides(self, difficultyOverride, exteriorZoneOverride):
+ self.difficultyOverride = difficultyOverride
+ if self.difficultyOverride is not None:
+ self.difficultyOverride = CogdoGameConsts.QuantizeDifficultyOverride(difficultyOverride)
+
+ self.exteriorZoneOverride = exteriorZoneOverride
+
+ def getDifficultyOverrides(self):
+ response = [
+ self.difficultyOverride,
+ self.exteriorZoneOverride]
+ if response[0] is None:
+ response[0] = CogdoGameConsts.NoDifficultyOverride
+ else:
+ response[0] *= CogdoGameConsts.DifficultyOverrideMult
+ response[0] = int(response[0])
+ if response[1] is None:
+ response[1] = CogdoGameConsts.NoExteriorZoneOverride
+
+ return response
+
+ def getDifficulty(self):
+ if self.difficultyOverride is not None:
+ return self.difficultyOverride
+
+ if hasattr(self.air, 'cogdoGameDifficulty'):
+ return float(self.air.cogdoGameDifficulty)
+
+ return CogdoGameConsts.getDifficulty(self.getSafezoneId())
+
+ def getSafezoneId(self):
+ if self.exteriorZoneOverride is not None:
+ return self.exteriorZoneOverride
+
+ if hasattr(self.air, 'cogdoGameSafezoneId'):
+ return CogdoGameConsts.getSafezoneId(self.air.cogdoGameSafezoneId)
+
+ return CogdoGameConsts.getSafezoneId(self.exteriorZone)
+
+ def _validateSenderId(self, senderId):
+ if senderId in self.getToonIds():
+ return True
+
+ self._reportSuspiciousEvent(senderId, 'Not currently playing CogDo Game.')
+ return False
+
+ def _reportSuspiciousEvent(self, senderId, message):
+ self.logSuspiciousEvent(senderId, message)
+
+ def handleToonDisconnected(self, toonId):
+ self.notify.debug('handleToonDisconnected: %s' % toonId)
+ self.sendUpdate('setToonDisconnect', [
+ toonId])
+
+ def handleToonWentSad(self, toonId):
+ self.notify.debug('handleToonWentSad: %s' % toonId)
+ self.sendUpdate('setToonSad', [
+ toonId])
+ if self._sadToken2callback is not None:
+ callbacks = self._sadToken2callback.values()
+ for callback in callbacks:
+ callback(toonId)
+
+ def _registerSadCallback(self, callback):
+ token = SadCallbackToken()
+ self._sadToken2callback[token] = callback
+ return token
+
+ def _unregisterSadCallback(self, token):
+ self._sadToken2callback.pop(token)
+
+ def enterNotLoaded(self):
+ pass
+
+ def exitNotLoaded(self):
+ pass
+
+ def enterLoaded(self):
+ pass
+
+ def exitLoaded(self):
+ pass
+
+ def enterOff(self):
+ self.ignore('cogdoGameEnd')
+
+ def exitOff(self):
+ self._wasEnded = False
+ self.accept('cogdoGameEnd', self._handleGameEnd)
+
+ def enterVisible(self):
+ self.sendUpdate('setVisible', [])
+
+ def exitVisible(self):
+ pass
+
+ def enterIntro(self):
+ self.sendUpdate('setIntroStart', [])
+ self._introBarrier = Barrier('intro', self.uniqueName('intro'), self.getToonIds(), 1 << 20, doneFunc = self._handleIntroBarrierDone)
+ self._sadToken = self._registerSadCallback(self._handleSadToonDuringIntro)
+
+ def exitIntro(self):
+ self._unregisterSadCallback(self._sadToken)
+ self._sadToken = None
+ self._introBarrier.cleanup()
+ self._introBarrier = None
+
+ def _handleSadToonDuringIntro(self, toonId):
+ self._introBarrier.clear(toonId)
+
+ def setAvatarReady(self):
+ senderId = self.air.getAvatarIdFromSender()
+ if senderId not in self.getToonIds():
+ self.logSuspiciousEvent(senderId, 'CogdoGameAI.setAvatarReady: unknown avatar')
+ return
+
+ if hasattr(self, '_introBarrier') and self._introBarrier:
+ self._introBarrier.clear(senderId)
+
+ def _handleIntroBarrierDone(self, avIds):
+ self.fsm.request('Game')
+
+ def enterGame(self):
+ self.markStartTime()
+ self.sendUpdate('setGameStart', [
+ globalClockDelta.localToNetworkTime(self.getStartTime())])
+
+ def exitGame(self):
+ pass
+
+ def _handleGameFinished(self, overrideEndless = False):
+ if overrideEndless or not (self.EndlessCogdoGames):
+ self.fsm.request('Finish')
+
+ def _handleGameEnd(self):
+ self._wasEnded = True
+ if self.fsm.getCurrentState().getName() == 'Off':
+ self.fsm.request('Intro')
+
+ if self.fsm.getCurrentState().getName() == 'Intro':
+ self.fsm.request('Game')
+
+ self._handleGameFinished(overrideEndless = True)
+ self.announceGameDone()
+
+ def wasEnded(self):
+ return self._wasEnded
+
+ def enterFinish(self):
+ self.markFinishTime()
+ self.sendUpdate('setGameFinish', [
+ globalClockDelta.localToNetworkTime(self.getFinishTime())])
+
+ def exitFinish(self):
+ pass
+
+ def setScore(self, score):
+ self._interior._setGameScore(score)
+
+ def isDoorOpen(self):
+ return True
+
+ def isToonInDoor(self, toonId):
+ return True
+
+ def announceGameDone(self):
+ self._interior._gameDone()
diff --git a/toontown/cogdominium/DistCogdoGameBase.py b/toontown/cogdominium/DistCogdoGameBase.py
new file mode 100644
index 0000000..f67b81f
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoGameBase.py
@@ -0,0 +1,12 @@
+
+
+class DistCogdoGameBase:
+
+ def local2GameTime(self, timestamp):
+ return timestamp - self._startTime
+
+ def game2LocalTime(self, timestamp):
+ return timestamp + self._startTime
+
+ def getCurrentGameTime(self):
+ return self.local2GameTime(globalClock.getFrameTime())
diff --git a/toontown/cogdominium/DistCogdoLevelGame.py b/toontown/cogdominium/DistCogdoLevelGame.py
new file mode 100644
index 0000000..93cd1cb
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoLevelGame.py
@@ -0,0 +1,65 @@
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from otp.level.DistributedLevel import DistributedLevel
+from otp.level import LevelConstants
+from otp.level import EditorGlobals
+from toontown.cogdominium.DistCogdoGame import DistCogdoGame
+from toontown.cogdominium.CogdoLevelGameBase import CogdoLevelGameBase
+from toontown.cogdominium.CogdoEntityCreator import CogdoEntityCreator
+
+class DistCogdoLevelGame(CogdoLevelGameBase, DistCogdoGame, DistributedLevel):
+ notify = directNotify.newCategory('DistCogdoLevelGame')
+
+ def __init__(self, cr):
+ DistributedLevel.__init__(self, cr)
+ DistCogdoGame.__init__(self, cr)
+
+ def generate(self):
+ DistributedLevel.generate(self)
+ DistCogdoGame.generate(self)
+ if __dev__:
+ bboard.post(EditorGlobals.EditTargetPostName, self)
+
+ def announceGenerate(self):
+ DistributedLevel.announceGenerate(self)
+ DistCogdoGame.announceGenerate(self)
+ if __dev__:
+ self.startHandleEdits()
+
+ def createEntityCreator(self):
+ return CogdoEntityCreator(level=self)
+
+ def levelAnnounceGenerate(self):
+ self.notify.debug('levelAnnounceGenerate')
+ DistributedLevel.levelAnnounceGenerate(self)
+ spec = self.getLevelSpec()
+ if __dev__:
+ typeReg = self.getEntityTypeReg()
+ spec.setEntityTypeReg(typeReg)
+ DistributedLevel.initializeLevel(self, spec)
+
+ def privGotSpec(self, levelSpec):
+ if __dev__:
+ if not levelSpec.hasEntityTypeReg():
+ typeReg = self.getEntityTypeReg()
+ levelSpec.setEntityTypeReg(typeReg)
+ DistributedLevel.privGotSpec(self, levelSpec)
+
+ def initVisibility(self):
+ levelMgr = self.getEntity(LevelConstants.LevelMgrEntId)
+ levelMgr.geom.reparentTo(render)
+ DistributedLevel.initVisibility(self)
+
+ def placeLocalToon(self):
+ DistributedLevel.placeLocalToon(self, moveLocalAvatar=False)
+
+ def disable(self):
+ if __dev__:
+ self.stopHandleEdits()
+ DistCogdoGame.disable(self)
+ DistributedLevel.disable(self)
+
+ def delete(self):
+ DistCogdoGame.delete(self)
+ DistributedLevel.delete(self)
+ if __dev__:
+ bboard.removeIfEqual(EditorGlobals.EditTargetPostName, self)
diff --git a/toontown/cogdominium/DistCogdoLevelGameAI.py b/toontown/cogdominium/DistCogdoLevelGameAI.py
new file mode 100644
index 0000000..b12b2f9
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoLevelGameAI.py
@@ -0,0 +1,41 @@
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from otp.level.DistributedLevelAI import DistributedLevelAI
+from toontown.cogdominium.DistCogdoGameAI import DistCogdoGameAI
+from toontown.cogdominium.CogdoEntityCreatorAI import CogdoEntityCreatorAI
+from toontown.cogdominium.CogdoLevelGameBase import CogdoLevelGameBase
+
+class DistCogdoLevelGameAI(CogdoLevelGameBase, DistCogdoGameAI, DistributedLevelAI):
+ notify = directNotify.newCategory('DistCogdoLevelGameAI')
+
+ def __init__(self, air, interior):
+ DistCogdoGameAI.__init__(self, air, interior)
+ DistributedLevelAI.__init__(self, air, self.zoneId, 0, self.getToonIds())
+
+ def generate(self):
+ self.notify.info('loading spec')
+ spec = self.getLevelSpec()
+ if __dev__:
+ self.notify.info('creating entity type registry')
+ typeReg = self.getEntityTypeReg()
+ spec.setEntityTypeReg(typeReg)
+
+ DistributedLevelAI.generate(self, spec)
+ DistCogdoGameAI.generate(self)
+ if __dev__:
+ self.startHandleEdits()
+
+ def createEntityCreator(self):
+ return CogdoEntityCreatorAI(level = self)
+
+ def _levelControlsRequestDelete(self):
+ return False
+
+ def requestDelete(self):
+ DistCogdoGameAI.requestDelete(self)
+
+ def delete(self):
+ if __dev__:
+ self.stopHandleEdits()
+
+ DistCogdoGameAI.delete(self)
+ DistributedLevelAI.delete(self, deAllocZone = False)
diff --git a/toontown/cogdominium/DistCogdoMazeGame.py b/toontown/cogdominium/DistCogdoMazeGame.py
new file mode 100644
index 0000000..ddd2d34
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoMazeGame.py
@@ -0,0 +1,232 @@
+from direct.distributed.ClockDelta import globalClockDelta
+from toontown.toonbase import TTLocalizer
+from DistCogdoGame import DistCogdoGame
+from toontown.cogdominium.DistCogdoMazeGameBase import DistCogdoMazeGameBase
+from CogdoMazeGame import CogdoMazeGame
+from CogdoMaze import CogdoMazeFactory
+import CogdoMazeGameGlobals
+import CogdoMazeGameGlobals as Globals
+
+class DistCogdoMazeGame(DistCogdoGame, DistCogdoMazeGameBase):
+ notify = directNotify.newCategory('DistCogdoMazeGame')
+
+ def __init__(self, cr):
+ DistCogdoGame.__init__(self, cr)
+ self.game = CogdoMazeGame(self)
+ self._numSuits = (0, 0, 0)
+ if __debug__ and base.config.GetBool('schellgames-dev', True):
+ self.accept('onCodeReload', self.__sgOnCodeReload)
+
+ def delete(self):
+ del self.randomNumGen
+ del self.game
+ DistCogdoGame.delete(self)
+
+ def getTitle(self):
+ return TTLocalizer.CogdoMazeGameTitle
+
+ def getInstructions(self):
+ return TTLocalizer.CogdoMazeGameInstructions
+
+ def generate(self):
+ self.randomNumGen = self.createRandomNumGen()
+ DistCogdoGame.generate(self)
+
+ def placeEntranceElev(self, elev):
+ DistCogdoGame.placeEntranceElev(self, elev)
+ self.game.placeEntranceElevator(elev)
+
+ def _gameInProgress(self):
+ return self.fsm.getCurrentState().getName() == 'Game'
+
+ def enterLoaded(self):
+ DistCogdoGame.enterLoaded(self)
+ mazeFactory = self.createMazeFactory(self.createRandomNumGen())
+ bossCode = None
+ if self._numSuits[0] > 0:
+ bossCode = ''
+ for u in range(self._numSuits[0]):
+ bossCode += '%X' % self.randomNumGen.randint(0, 15)
+
+ self.game.load(mazeFactory, self._numSuits, bossCode)
+ return
+
+ def exitLoaded(self):
+ self.game.unload()
+ self.ignoreAll()
+ DistCogdoGame.exitLoaded(self)
+
+ def enterVisible(self):
+ DistCogdoGame.enterVisible(self)
+ self.game.initPlayers()
+ self.game.onstage()
+
+ def exitVisible(self):
+ DistCogdoGame.exitVisible(self)
+
+ def enterIntro(self):
+ DistCogdoGame.enterIntro(self, Globals.IntroDurationSeconds)
+ self.game.startIntro()
+
+ def exitIntro(self):
+ DistCogdoGame.exitIntro(self)
+ self.game.endIntro()
+ self.stashEntranceElevator()
+
+ def enterGame(self):
+ DistCogdoGame.enterGame(self)
+ self.game.start()
+
+ def exitGame(self):
+ DistCogdoGame.exitGame(self)
+ self.game.exit()
+
+ def enterFinish(self):
+ DistCogdoGame.enterFinish(self)
+ self.game.startFinish()
+
+ def exitFinish(self):
+ DistCogdoGame.exitFinish(self)
+ self.game.endFinish()
+ self.game.offstage()
+
+ def setNumSuits(self, numSuits):
+ self._numSuits = numSuits
+
+ def d_sendRequestAction(self, action, data):
+ self.sendUpdate('requestAction', [action, data])
+
+ def toonUsedGag(self, toonId, x, y, h, networkTime):
+ if not self._gameInProgress():
+ return
+ if not self.getToon(base.localAvatar.doId):
+ return
+ if toonId == localAvatar.doId:
+ return
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.toonUsedGag(toonId, x, y, h, elapsedTime)
+
+ def d_requestUseGag(self, x, y, h):
+ networkTime = globalClockDelta.localToNetworkTime(globalClock.getFrameTime())
+ self.sendUpdate('requestUseGag', [x,
+ y,
+ h,
+ networkTime])
+
+ def b_toonUsedGag(self, x, y, h):
+ self.d_requestUseGag(x, y, h)
+ self.game.toonUsedGag(base.localAvatar.doId, x, y, h)
+
+ def suitHitByGag(self, toonId, suitType, suitNum):
+ if not self._gameInProgress():
+ return
+ if not self.getToon(base.localAvatar.doId):
+ return
+ if toonId == localAvatar.doId:
+ return
+ self.game.suitHitByGag(toonId, suitType, suitNum)
+
+ def d_requestSuitHitByGag(self, suitType, suitNum):
+ self.sendUpdate('requestSuitHitByGag', [suitType, suitNum])
+
+ def b_suitHitByGag(self, suitType, suitNum):
+ self.d_requestSuitHitByGag(suitType, suitNum)
+ self.game.suitHitByGag(base.localAvatar.doId, suitType, suitNum)
+
+ def toonHitByGag(self, toonId, hitToon, networkTime):
+ if not self._gameInProgress():
+ return
+ if not self.getToon(base.localAvatar.doId):
+ return
+ if toonId == localAvatar.doId:
+ return
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.toonHitByGag(toonId, hitToon, elapsedTime)
+
+ def d_broadcastSendToonHitByGag(self, toonId):
+ pass
+
+ def b_toonHitByGag(self, toonId):
+ self.d_broadcastSendToonHitByGag(toonId)
+ self.game.toonHitByGag(base.localAvatar.doId, toonId)
+
+ def toonHitBySuit(self, toonId, suitType, suitNum, networkTime):
+ if not self._gameInProgress():
+ return
+ if not self.getToon(base.localAvatar.doId):
+ return
+ if toonId == localAvatar.doId:
+ return
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.toonHitBySuit(toonId, suitType, suitNum, elapsedTime)
+
+ def d_requestHitBySuit(self, suitType, suitNum):
+ networkTime = globalClockDelta.localToNetworkTime(globalClock.getFrameTime())
+ self.sendUpdate('requestHitBySuit', [suitType, suitNum, networkTime])
+
+ def b_toonHitBySuit(self, suitType, suitNum):
+ self.d_requestHitBySuit(suitType, suitNum)
+ self.game.toonHitBySuit(base.localAvatar.doId, suitType, suitNum)
+
+ def toonHitByDrop(self, toonId):
+ if not self._gameInProgress():
+ return
+ if not self.getToon(base.localAvatar.doId):
+ return
+ if toonId == localAvatar.doId:
+ return
+ self.game.toonHitByDrop(toonId)
+
+ def d_requestHitByDrop(self):
+ self.sendUpdate('requestHitByDrop', [])
+
+ def b_toonHitByDrop(self):
+ self.d_requestHitByDrop()
+ self.game.toonHitByDrop(base.localAvatar.doId)
+
+ def d_sendRequestPickUp(self, pickupNum):
+ self.sendUpdate('requestPickUp', [pickupNum])
+
+ def pickUp(self, toonId, pickupNum, networkTime):
+ if not self._gameInProgress():
+ return
+ if not self.getToon(base.localAvatar.doId):
+ return
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.pickUp(toonId, pickupNum, elapsedTime)
+
+ def d_sendRequestGag(self, waterCoolerIndex):
+ self.game.localPlayer.orthoWalk.sendCurrentPosition()
+ self.sendUpdate('requestGag', [waterCoolerIndex])
+
+ def hasGag(self, toonId, networkTime):
+ if not self._gameInProgress():
+ return
+ if not self.getToon(base.localAvatar.doId):
+ return
+ elapsedTime = globalClockDelta.localElapsedTime(networkTime)
+ self.game.hasGag(toonId, elapsedTime)
+
+ def doAction(self, action, data, networkTime):
+ if not self._gameInProgress():
+ return
+ if action == Globals.GameActions.RevealDoor:
+ self.game.toonRevealsDoor(data)
+ elif action == Globals.GameActions.EnterDoor:
+ self.game.toonEntersDoor(data)
+ elif action == Globals.GameActions.OpenDoor:
+ timeLeft = Globals.SecondsUntilGameEnds - globalClockDelta.localElapsedTime(networkTime)
+ self.game.openDoor(timeLeft)
+ elif action == Globals.GameActions.Countdown:
+ countdownTimeLeft = Globals.SecondsUntilTimeout
+ self.game.countdown(countdownTimeLeft)
+ elif action == Globals.GameActions.TimeAlert:
+ self.game.timeAlert()
+
+ def setToonSad(self, toonId):
+ DistCogdoGame.setToonSad(self, toonId)
+ self.game.handleToonWentSad(toonId)
+
+ def setToonDisconnect(self, toonId):
+ DistCogdoGame.setToonDisconnect(self, toonId)
+ self.game.handleToonDisconnected(toonId)
diff --git a/toontown/cogdominium/DistCogdoMazeGameAI.py b/toontown/cogdominium/DistCogdoMazeGameAI.py
new file mode 100644
index 0000000..0af9d21
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoMazeGameAI.py
@@ -0,0 +1,483 @@
+from pandac.PandaModules import Vec3, NodePath
+from direct.distributed.ClockDelta import globalClockDelta
+from otp.avatar.SpeedMonitor import SpeedMonitor
+from toontown.cogdominium.CogdoMaze import CogdoMazeFactory
+from toontown.cogdominium.DistCogdoMazeGameBase import DistCogdoMazeGameBase
+from DistCogdoGameAI import DistCogdoGameAI
+import CogdoMazeGameGlobals as Globals
+cogdoMazeTimeScoreRatio = 0.5
+cogdoMazePerfectTime = 90
+cogdoMazeMaxTime = 210
+cogdoMazePickupScoreRatio = 0.7
+
+class DistCogdoMazeGameAI(DistCogdoGameAI, DistCogdoMazeGameBase):
+ notify = directNotify.newCategory('DistCogdoMazeGameAI')
+ TimerExpiredTaskName = 'CMG_TimerExpiredTask'
+ TimeoutTimerTaskName = 'CMG_timeoutTimerTask'
+ CountdownTimerTaskName = 'CMG_countdownTimerTask'
+ AnnounceGameDoneTimerTaskName = 'CMG_AnnounceGameDoneTimerTask'
+ SkipCogdoGames = simbase.config.GetBool('skip-cogdo-game', 0)
+
+ def __init__(self, air, id):
+ DistCogdoGameAI.__init__(self, air, id)
+ self.doorIsOpen = False
+ self.toonsInDoor = []
+ self.pickups = []
+ self.pickupsDropped = 0
+ self.maxPickups = 0
+ self.numPickedUp = 0
+ self.suits = {}
+ self.lastRequestId = None
+ self.requestStartTime = globalClock.getFrameTime()
+ self.requestCount = None
+ self.jokeLastRequestId = None
+ self.jokeRequestStartTime = globalClock.getFrameTime()
+ self.jokeRequestCount = None
+ if __debug__ and simbase.config.GetBool('schellgames-dev', True):
+ self.accept('onCodeReload', self.__sgOnCodeReload)
+
+ def setExteriorZone(self, exteriorZone):
+ DistCogdoGameAI.setExteriorZone(self, exteriorZone)
+ self.difficulty = self.getDifficulty()
+ self.createSuits()
+
+ def createSuits(self):
+ serialNum = 0
+ self._numSuits = []
+ extraSuits = 0
+ for i in range(len(Globals.NumSuits)):
+ extraSuits = int(round(self.difficulty * Globals.SuitsModifier[i]))
+ self._numSuits.append(Globals.NumSuits[i] + extraSuits)
+
+ self.bosses = self._numSuits[0]
+ for i in range(self._numSuits[0]):
+ self.suits[serialNum] = Globals.SuitData[Globals.SuitTypes.Boss]['hp']
+ serialNum += 1
+
+ for i in range(self._numSuits[1]):
+ self.suits[serialNum] = Globals.SuitData[Globals.SuitTypes.FastMinion]['hp']
+ serialNum += 1
+
+ for i in range(self._numSuits[2]):
+ self.suits[serialNum] = Globals.SuitData[Globals.SuitTypes.SlowMinion]['hp']
+ serialNum += 1
+
+ self._totalSuits = serialNum
+ self.maxPickups = self._numSuits[0] * Globals.SuitData[0]['memos']
+ self.maxPickups += self._numSuits[1] * Globals.SuitData[1]['memos']
+ self.maxPickups += self._numSuits[2] * Globals.SuitData[2]['memos']
+
+ def generate(self):
+ DistCogdoGameAI.generate(self)
+ mazeFactory = self.createMazeFactory(self.createRandomNumGen())
+ waterCoolerList = []
+ mazeModel = mazeFactory._loadAndBuildMazeModel()
+ for waterCooler in mazeModel.findAllMatches('**/*waterCooler'):
+ waterCoolerList.append((waterCooler.getPos(mazeModel), waterCooler.getHpr(mazeModel)))
+
+ waterCoolerList.sort()
+ baseNp = NodePath('base')
+ rotNp = baseNp.attachNewNode('rot')
+ childNp = rotNp.attachNewNode('child')
+ childNp.setPos(*Globals.WaterCoolerTriggerOffset)
+ self._waterCoolerPosList = []
+ for (pos, hpr) in waterCoolerList:
+ rotNp.setHpr(hpr)
+ offset = childNp.getPos(baseNp)
+ self._waterCoolerPosList.append(pos + offset)
+
+ self._speedMonitor = SpeedMonitor('cogdoMazeGame-%s' % self.doId)
+ self._toonId2speedToken = {}
+
+ def delete(self):
+ self.ignoreAll()
+ self._speedMonitor.destroy()
+ DistCogdoGameAI.delete(self)
+
+ def isDoorOpen(self):
+ return self.doorIsOpen
+
+ def isToonInDoor(self, toonId):
+ return toonId in self.toonsInDoor
+
+ def areAllToonsInDoor(self):
+ return self.getNumPlayers() == len(self.toonsInDoor)
+
+ def getCurrentNetworkTime(self):
+ return globalClockDelta.localToNetworkTime(globalClock.getRealTime())
+
+ def suitHit(self, suitType, suitNum):
+ avId = simbase.air.getAvatarIdFromSender()
+ toon = simbase.air.doId2do.get(avId)
+ if not toon:
+ simbase.air.writeServerEvent('suspicious', avId, 'CogdoMazeGame.suitHit: toon not present?')
+ return False
+
+ result = True
+ if self.lastRequestId == avId:
+ self.requestCount += 1
+ now = globalClock.getFrameTime()
+ elapsed = now - self.requestStartTime
+ if elapsed > 10:
+ self.requestCount = 1
+ self.requestStartTime = now
+ else:
+ secondsPerGrab = elapsed / self.requestCount
+ if self.requestCount >= 3 and secondsPerGrab <= 0.4:
+ simbase.air.writeServerEvent('suspicious', avId, 'suitHit %s suits in %s seconds' % (self.requestCount, elapsed))
+ if simbase.config.GetBool('want-ban-cogdo-maze-suit-hit', False):
+ toon.ban('suitHit %s suits in %s seconds' % (self.requestCount, elapsed))
+
+ result = False
+
+ else:
+ self.lastRequestId = avId
+ self.requestCount = 1
+ self.requestStartTime = globalClock.getFrameTime()
+ if result:
+ self.suits[suitNum] -= 1
+ hp = self.suits[suitNum]
+ if hp <= 0:
+ self.suitDestroyed(suitType, suitNum)
+
+ return result
+
+ def suitDestroyed(self, suitType, suitNum):
+ if suitType == Globals.SuitTypes.Boss:
+ self.bosses -= 1
+ if self.bosses <= 0:
+ self.openDoor()
+
+ self.createPickups(suitType)
+ del self.suits[suitNum]
+
+ def createPickups(self, suitType):
+ for i in range(Globals.SuitData[suitType]['memos']):
+ self.pickups.append(self.pickupsDropped)
+ self.pickupsDropped += 1
+
+ def _removeToonFromGame(self, toonId):
+ if self.fsm.getCurrentState().getName() == 'Game':
+ if toonId not in self._toonId2speedToken:
+ simbase.air.writeServerEvent('avoid_crash', toonId, 'CogdoMazeGame._removeToonFromGame: toon not in _toonId2speedToken')
+ else:
+ token = self._toonId2speedToken.pop(toonId)
+ self._speedMonitor.removeNodepath(token)
+ if self.areAllToonsInDoor():
+ self._handleGameFinished()
+
+ def handleToonDisconnected(self, toonId):
+ DistCogdoGameAI.handleToonDisconnected(self, toonId)
+ self._removeToonFromGame(toonId)
+
+ def handleToonWentSad(self, toonId):
+ DistCogdoGameAI.handleToonWentSad(self, toonId)
+ self._removeToonFromGame(toonId)
+
+ def getNumSuits(self):
+ return list(self._numSuits)
+
+ def d_broadcastPickup(self, senderId, pickupNum, networkTime):
+ self.sendUpdate('pickUp', [
+ senderId,
+ pickupNum,
+ networkTime])
+
+ def requestAction(self, action, data):
+ senderId = self.air.getAvatarIdFromSender()
+ if not self._validateSenderId(senderId):
+ return False
+
+ if self.fsm.getCurrentState().getName() != 'Game':
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestAction outside of Game state')
+ return False
+
+ if action == Globals.GameActions.EnterDoor:
+ if not self.doorIsOpen:
+ self.logSuspiciousEvent(senderId, "CogdoMazeGameAI.requestAction(EnterDoor): door isn't open yet")
+ elif senderId not in self.toonsInDoor:
+ self.toonsInDoor.append(senderId)
+ self.d_broadcastDoAction(action, senderId)
+ if self.areAllToonsInDoor():
+ self._handleGameFinished()
+
+ else:
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestAction: toon already in door')
+ elif action == Globals.GameActions.RevealDoor:
+ self.d_broadcastDoAction(action, senderId)
+ else:
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestAction: invalid action %s' % action)
+
+ def d_broadcastDoAction(self, action, data = 0, networkTime = 0):
+ self.sendUpdate('doAction', [
+ action,
+ data,
+ networkTime])
+
+ def requestUseGag(self, x, y, h, networkTime):
+ senderId = self.air.getAvatarIdFromSender()
+ if not self._validateSenderId(senderId):
+ return False
+
+ if self.fsm.getCurrentState().getName() != 'Game':
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestUseGag outside of Game state')
+ return False
+
+ self.d_broadcastToonUsedGag(senderId, x, y, h, networkTime)
+
+ def d_broadcastToonUsedGag(self, toonId, x, y, h, networkTime):
+ self.sendUpdate('toonUsedGag', [
+ toonId,
+ x,
+ y,
+ h,
+ networkTime])
+
+ def requestSuitHitByGag(self, suitType, suitNum):
+ senderId = self.air.getAvatarIdFromSender()
+ if not self._validateSenderId(senderId):
+ return False
+
+ if self.fsm.getCurrentState().getName() != 'Game':
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestSuitHitByGag outside of Game state')
+ return False
+
+ if suitType not in Globals.SuitTypes:
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestSuitHitByGag: invalid suit type %s' % suitType)
+ return False
+
+ if suitNum not in self.suits.keys():
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestSuitHitByGag: invalid suit num %s' % suitNum)
+ return False
+
+ resultValid = self.suitHit(suitType, suitNum)
+ if resultValid:
+ self.d_broadcastSuitHitByGag(senderId, suitType, suitNum)
+
+ def d_broadcastSuitHitByGag(self, toonId, suitType, suitNum):
+ self.sendUpdate('suitHitByGag', [
+ toonId,
+ suitType,
+ suitNum])
+
+ def requestHitBySuit(self, suitType, suitNum, networkTime):
+ senderId = self.air.getAvatarIdFromSender()
+ if not self._validateSenderId(senderId):
+ return False
+
+ if self.fsm.getCurrentState().getName() != 'Game':
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestHitBySuit outside of Game state')
+ return False
+
+ if suitType not in Globals.SuitTypes:
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestHitBySuit: invalid suit type %s' % suitType)
+ return False
+
+ if suitNum not in self.suits.keys():
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestHitBySuit: invalid suit num %s' % suitNum)
+ return False
+
+ toon = self.air.doId2do[senderId]
+ self.d_broadcastToonHitBySuit(senderId, suitType, suitNum, networkTime)
+ damage = Globals.SuitData[suitType]['toonDamage']
+ damage += int(round(damage * Globals.DamageModifier * self.difficulty))
+ toon.takeDamage(damage, quietly = 0)
+
+ def d_broadcastToonHitBySuit(self, toonId, suitType, suitNum, networkTime):
+ self.sendUpdate('toonHitBySuit', [
+ toonId,
+ suitType,
+ suitNum,
+ networkTime])
+
+ def requestHitByDrop(self):
+ senderId = self.air.getAvatarIdFromSender()
+ if not self._validateSenderId(senderId):
+ return False
+
+ if self.fsm.getCurrentState().getName() != 'Game':
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestHitByDrop outside of Game state')
+ return False
+
+ toon = self.air.doId2do[senderId]
+ self.d_broadcastToonHitByDrop(senderId)
+ if Globals.DropDamage > 0:
+ damage = Globals.DropDamage
+ damage += int(round(damage * Globals.DamageModifier * self.difficulty))
+ toon.takeDamage(damage, quietly = 0)
+
+ def d_broadcastToonHitByDrop(self, toonId):
+ self.sendUpdate('toonHitByDrop', [
+ toonId])
+
+ def requestGag(self, waterCoolerIndex):
+ senderId = self.air.getAvatarIdFromSender()
+ if not self._validateSenderId(senderId):
+ return False
+
+ if self.fsm.getCurrentState().getName() != 'Game':
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestGag outside of Game state')
+ return False
+
+ if waterCoolerIndex >= len(self._waterCoolerPosList):
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestGag: invalid waterCoolerIndex')
+ return
+
+ wcPos = self._waterCoolerPosList[waterCoolerIndex]
+ toon = self.air.doId2do.get(senderId)
+ if not toon:
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestGag: toon not present')
+ return
+
+ distance = (toon.getPos() - wcPos).length()
+ threshold = (Globals.WaterCoolerTriggerRadius + Globals.PlayerCollisionRadius) * 1.05
+ if distance > threshold:
+ self._toonHackingRequestGag(senderId)
+ return
+
+ self.d_broadcastHasGag(senderId, self.getCurrentNetworkTime())
+
+ def d_broadcastHasGag(self, senderId, networkTime):
+ self.sendUpdate('hasGag', [
+ senderId,
+ networkTime])
+
+ def requestPickUp(self, pickupNum):
+ senderId = self.air.getAvatarIdFromSender()
+ if not self._validateSenderId(senderId):
+ return False
+
+ if self.fsm.getCurrentState().getName() != 'Game':
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestPickUp outside of Game state')
+ return False
+
+ if pickupNum not in self.pickups:
+ self.logSuspiciousEvent(senderId, 'CogdoMazeGameAI.requestPickUp: invalid pickupNum %s' % pickupNum)
+ return False
+
+ toon = simbase.air.doId2do.get(senderId)
+ if not toon:
+ simbase.air.writeServerEvent('suspicious', senderId, 'CogdoMazeGame.requestPickUp: toon not present?')
+ return False
+
+ result = True
+ if self.jokeLastRequestId == senderId:
+ self.jokeRequestCount += 1
+ now = globalClock.getFrameTime()
+ elapsed = now - self.jokeRequestStartTime
+ if elapsed > 10:
+ self.jokeRequestCount = 1
+ self.jokeRequestStartTime = now
+ else:
+ secondsPerGrab = elapsed / self.jokeRequestCount
+ if self.jokeRequestCount >= 4 and secondsPerGrab <= 0.03:
+ simbase.air.writeServerEvent('suspicious', senderId, 'requestPickup %s jokes in %s seconds' % (self.jokeRequestCount, elapsed))
+ if simbase.config.GetBool('want-ban-cogdo-maze-request-pickup', False):
+ toon.ban('requestPickup %s jokes in %s seconds' % (self.jokeRequestCount, elapsed))
+
+ result = False
+
+ else:
+ self.jokeLastRequestId = senderId
+ self.jokeRequestCount = 1
+ self.jokeRequestStartTime = globalClock.getFrameTime()
+ if result:
+ self.numPickedUp += 1
+ self.pickups.remove(pickupNum)
+ self.d_broadcastPickup(senderId, pickupNum, self.getCurrentNetworkTime())
+
+ def enterGame(self):
+ DistCogdoGameAI.enterGame(self)
+ endTime = Globals.SecondsUntilTimeout - (globalClock.getRealTime() - self.getStartTime())
+ self._countdownTimerTask = taskMgr.doMethodLater(endTime - Globals.SecondsForTimeAlert, self.handleCountdownTimer, self.taskName(DistCogdoMazeGameAI.CountdownTimerTaskName), [])
+ self._startGameTimer()
+ self._timeoutTimerTask = taskMgr.doMethodLater(endTime, self.handleEndGameTimerExpired, self.taskName(DistCogdoMazeGameAI.TimeoutTimerTaskName), [])
+ if self.SkipCogdoGames:
+ self.fsm.request('Finish')
+
+ for toonId in self.getToonIds():
+ toon = self.air.doId2do.get(toonId)
+ if toon:
+ token = self._speedMonitor.addNodepath(toon)
+ self._toonId2speedToken[toonId] = token
+ self._speedMonitor.setSpeedLimit(token, config.GetFloat('cogdo-maze-speed-limit', Globals.ToonRunSpeed * 1.1), Functor(self._toonOverSpeedLimit, toonId))
+
+ def _toonOverSpeedLimit(self, toonId, speed):
+ self._bootPlayerForHacking(toonId, 'speeding in cogdo maze game (%.2f feet/sec)' % speed, config.GetBool('want-ban-cogdo-maze-speeding', 0))
+
+ def _toonHackingRequestGag(self, toonId):
+ simbase.air.writeServerEvent('suspicious', toonId, 'CogdoMazeGame: toon caught hacking requestGag')
+ self._bootPlayerForHacking(toonId, 'hacking cogdo maze game requestGag', config.GetBool('want-ban-cogdo-maze-requestgag-hacking', 0))
+
+ def _bootPlayerForHacking(self, toonId, reason, wantBan):
+ toon = simbase.air.doId2do.get(toonId)
+ if not toon:
+ simbase.air.writeServerEvent('suspicious', toonId, 'CogdoMazeGame._bootPlayerForHacking(%s): toon not present' % (reason,))
+ return
+
+ if wantBan:
+ toon.ban(reason)
+
+ def _removeEndGameTimerTask(self):
+ if hasattr(self, '_gameTimerExpiredTask'):
+ taskMgr.remove(self._gameTimerExpiredTask)
+ del self._gameTimerExpiredTask
+
+ def _removeTimeoutTimerTask(self):
+ if hasattr(self, '_timeoutTimerTask'):
+ taskMgr.remove(self._timeoutTimerTask)
+ del self._timeoutTimerTask
+
+ def _removeCountdownTimerTask(self):
+ if hasattr(self, '_countdownTimerTask'):
+ taskMgr.remove(self._countdownTimerTask)
+ del self._countdownTimerTask
+
+ def _startGameTimer(self):
+ self.d_broadcastDoAction(Globals.GameActions.Countdown, networkTime = self.getCurrentNetworkTime())
+
+ def openDoor(self):
+ self._removeTimeoutTimerTask()
+ self._removeCountdownTimerTask()
+ self.doorIsOpen = True
+ self.d_broadcastDoAction(Globals.GameActions.OpenDoor, networkTime = self.getCurrentNetworkTime())
+ self._gameTimerExpiredTask = taskMgr.doMethodLater(Globals.SecondsUntilGameEnds, self.handleEndGameTimerExpired, self.taskName(DistCogdoMazeGameAI.TimerExpiredTaskName), [])
+
+ def handleCountdownTimer(self):
+ self.d_broadcastDoAction(Globals.GameActions.TimeAlert, networkTime = self.getCurrentNetworkTime())
+
+ def handleEndGameTimerExpired(self):
+ self._handleGameFinished()
+
+ def exitGame(self):
+ DistCogdoGameAI.exitGame(self)
+ for (toonId, token) in self._toonId2speedToken.iteritems():
+ self._speedMonitor.removeNodepath(token)
+
+ self._toonId2speedToken = {}
+ self._removeTimeoutTimerTask()
+ self._removeEndGameTimerTask()
+
+ def enterFinish(self):
+ DistCogdoGameAI.enterFinish(self)
+ if self.numPickedUp > self.maxPickups:
+ self.logSuspiciousEvent(0, 'CogdoMazeGameAI: collected more memos than possible: %s, players: %s' % (self.numPickedUp, self.getToonIds()))
+
+ time = globalClock.getRealTime() - self.getStartTime()
+ adjustedTime = min(max(time - cogdoMazePerfectTime, 0), cogdoMazeMaxTime)
+ timeScore = 1 - adjustedTime / cogdoMazeMaxTime
+ pickupScore = 0
+ if self.maxPickups:
+ pickupScore = float(self.numPickedUp) / self.maxPickups
+
+ weightedPickup = pickupScore * cogdoMazePickupScoreRatio
+ weightedTime = timeScore * cogdoMazeTimeScoreRatio
+ score = min(weightedPickup + weightedTime, 1.0)
+ self.air.writeServerEvent('CogdoMazeGame', self._interior.toons, 'Memos: %s/%s Weighted Memos: %s Time: %s Weighted Time: %s Score: %s' % (self.numPickedUp, self.maxPickups, weightedPickup, time, weightedTime, score))
+ self.setScore(score)
+ self._announceGameDoneTask = taskMgr.doMethodLater(Globals.FinishDurationSeconds, self.announceGameDone, self.taskName(DistCogdoMazeGameAI.AnnounceGameDoneTimerTaskName), [])
+
+ def exitFinish(self):
+ DistCogdoGameAI.exitFinish(self)
+ taskMgr.remove(self._announceGameDoneTask)
+ del self._announceGameDoneTask
diff --git a/toontown/cogdominium/DistCogdoMazeGameBase.py b/toontown/cogdominium/DistCogdoMazeGameBase.py
new file mode 100644
index 0000000..9b6294b
--- /dev/null
+++ b/toontown/cogdominium/DistCogdoMazeGameBase.py
@@ -0,0 +1,11 @@
+from direct.showbase.RandomNumGen import RandomNumGen
+from toontown.cogdominium.CogdoMaze import CogdoMazeFactory
+import CogdoMazeGameGlobals as Globals
+
+class DistCogdoMazeGameBase:
+
+ def createRandomNumGen(self):
+ return RandomNumGen(self.doId)
+
+ def createMazeFactory(self, randomNumGen):
+ return CogdoMazeFactory(randomNumGen, Globals.NumQuadrants[0], Globals.NumQuadrants[1])
diff --git a/toontown/cogdominium/DistributedCogdoBarrel.py b/toontown/cogdominium/DistributedCogdoBarrel.py
new file mode 100644
index 0000000..d105fd0
--- /dev/null
+++ b/toontown/cogdominium/DistributedCogdoBarrel.py
@@ -0,0 +1,124 @@
+from pandac.PandaModules import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObject
+from toontown.toonbase import ToontownGlobals, ToontownIntervals
+from toontown.cogdominium import CogdoBarrelRoomConsts
+
+class DistributedCogdoBarrel(DistributedObject.DistributedObject):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogdoBarrel')
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.index = None
+ self.state = None
+ self.model = None
+ self.collSphere = None
+ self.collNode = None
+ self.collNodePath = None
+ self.availableTex = None
+ self.usedTex = None
+ return
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+ self.bumpSound = base.loadSfx(CogdoBarrelRoomConsts.BarrelBumpSound)
+ self.grabSound = base.loadSfx(CogdoBarrelRoomConsts.BarrelGrabSound)
+
+ def __setModel(self):
+ self.model = loader.loadModel(CogdoBarrelRoomConsts.BarrelModel)
+ self.model.setScale(CogdoBarrelRoomConsts.BarrelModelScale)
+ self.model.setPos(self.__getProp('pos'))
+ self.model.setH(self.__getProp('heading'))
+ cogdoBarrelsNode = render.find('@@CogdoBarrels')
+ if not cogdoBarrelsNode or cogdoBarrelsNode.isEmpty():
+ cogdoBarrelsNode = render.attachNewNode('CogdoBarrels')
+ cogdoBarrelsNode.stash()
+ self.model.reparentTo(cogdoBarrelsNode)
+ self.availableTex = loader.loadTexture('phase_5/maps/tt_t_ara_cbr_Barrel_notUsed.jpg')
+ self.usedTex = loader.loadTexture('phase_5/maps/tt_t_ara_cbr_Barrel_Used.jpg')
+ self.model.setTexture(self.availableTex, 100)
+
+ def __addCollision(self):
+ if not self.collSphere:
+ ax, ay, az = (0, 0, 0.5)
+ bx, by, bz = (0, 0, 2.5)
+ radius = 1
+ self.collTube = CollisionTube(ax, ay, az, bx, by, bz, radius)
+ self.collTube.setTangible(1)
+ self.collSphere = CollisionSphere(*CogdoBarrelRoomConsts.BarrelCollParams)
+ self.collSphere.setTangible(0)
+ self.collNode = CollisionNode(self.uniqueName('barrelSphere'))
+ self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
+ self.collNode.addSolid(self.collSphere)
+ self.collNode.addSolid(self.collTube)
+ self.collNodePath = self.model.attachNewNode(self.collNode)
+ self.collNodePath.hide()
+
+ def announceGenerate(self):
+ DistributedObject.DistributedObject.announceGenerate(self)
+ self.__setModel()
+ self.__addCollision()
+
+ def disable(self):
+ DistributedObject.DistributedObject.disable(self)
+
+ def delete(self):
+ DistributedObject.DistributedObject.delete(self)
+ ToontownIntervals.cleanup(self.__pulseIvalName())
+ if self.model:
+ self.model.removeNode()
+ del self.model
+ self.model = None
+ self.ignore(self.uniqueName('enterbarrelSphere'))
+ return
+
+ def setIndex(self, index):
+ self.index = index
+
+ def __getProp(self, prop):
+ return CogdoBarrelRoomConsts.BarrelProps[self.index][prop]
+
+ def setState(self, state):
+ self.state = state
+ self.__updateState()
+
+ def __updateState(self):
+ if self.state == CogdoBarrelRoomConsts.StateAvailable:
+ if self.model:
+ self.model.unstash()
+ self.model.setTexture(self.availableTex, 100)
+ self.accept(self.uniqueName('enterbarrelSphere'), self.handleEnterSphere)
+ elif self.state == CogdoBarrelRoomConsts.StateUsed:
+ if self.model:
+ self.model.unstash()
+ self.model.setTexture(self.usedTex, 100)
+ self.ignore(self.uniqueName('enterbarrelSphere'))
+ elif self.state == CogdoBarrelRoomConsts.StateHidden or self.state == CogdoBarrelRoomConsts.StateCrushed:
+ if self.model:
+ self.model.stash()
+ self.ignore(self.uniqueName('enterbarrelSphere'))
+ else:
+ if self.model:
+ self.model.stash()
+ self.ignore(self.uniqueName('enterbarrelSphere'))
+
+ def handleEnterSphere(self, collEntry = None):
+ base.playSfx(self.bumpSound, volume=0.35, node=self.model, listener=camera)
+ self.d_requestGrab()
+
+ def d_requestGrab(self):
+ self.sendUpdate('requestGrab', [])
+
+ def setGrab(self, avId):
+ if avId == base.localAvatar.doId:
+ ToontownIntervals.start(ToontownIntervals.getPulseIval(self.model, self.__pulseIvalName(), 1.15, duration=0.2))
+ self.setState(CogdoBarrelRoomConsts.StateUsed)
+
+ def setReject(self):
+ pass
+
+ def __pulseIvalName(self):
+ return 'DistributedCogdoBarrelPulse%s' % self.doId
+
+ def __str__(self):
+ return 'Barrel %s' % self.index
diff --git a/toontown/cogdominium/DistributedCogdoBarrelAI.py b/toontown/cogdominium/DistributedCogdoBarrelAI.py
new file mode 100644
index 0000000..fa49209
--- /dev/null
+++ b/toontown/cogdominium/DistributedCogdoBarrelAI.py
@@ -0,0 +1,65 @@
+import random
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObjectAI
+from toontown.cogdominium import CogdoBarrelRoomConsts
+
+class DistributedCogdoBarrelAI(DistributedObjectAI.DistributedObjectAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogdoBarrelAI')
+
+ def __init__(self, air, index, collectedCallback):
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ self.grabbedBy = []
+ self.index = index
+ self.state = CogdoBarrelRoomConsts.StateHidden
+ self.interactive = False
+ self.collectedCallback = collectedCallback
+ self.laff = random.randint(*CogdoBarrelRoomConsts.ToonUp)
+
+ def generate(self):
+ DistributedObjectAI.DistributedObjectAI.generate(self)
+
+ def delete(self):
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def getIndex(self):
+ return self.index
+
+ def getState(self):
+ return self.state
+
+ def d_setState(self, state):
+ self.state = state
+ self.sendUpdate('setState', [
+ self.state])
+ if self.state == CogdoBarrelRoomConsts.StateAvailable:
+ self.grabbedBy = []
+
+ def requestGrab(self):
+ avId = self.air.getAvatarIdFromSender()
+ av = self.air.doId2do.get(avId)
+ if not av:
+ self.notify.warning('requestGrab found no avatar with id %s' % avId)
+ return
+
+ if self.__canGrab(av):
+ self.d_setGrab(avId)
+ else:
+ self.d_setReject()
+
+ def __canGrab(self, av):
+ return self.state == CogdoBarrelRoomConsts.StateAvailable and self.interactive and not av.isToonedUp() and av.doId not in self.grabbedBy
+
+ def d_setGrab(self, avId):
+ self.collectedCallback(self, avId)
+ self.grabbedBy.append(avId)
+ self.sendUpdate('setGrab', [
+ avId])
+ av = self.air.doId2do.get(avId)
+ if av:
+ av.toonUp(self.laff)
+
+ def d_setReject(self):
+ self.sendUpdate('setReject', [])
+
+ def __str__(self):
+ return 'Barrel %s' % self.index
diff --git a/toontown/cogdominium/DistributedCogdoBattleBldg.py b/toontown/cogdominium/DistributedCogdoBattleBldg.py
new file mode 100644
index 0000000..aaaa9af
--- /dev/null
+++ b/toontown/cogdominium/DistributedCogdoBattleBldg.py
@@ -0,0 +1,12 @@
+from direct.directnotify import DirectNotifyGlobal
+from toontown.toonbase import TTLocalizer
+from toontown.battle import DistributedBattleBldg
+
+class DistributedCogdoBattleBldg(DistributedBattleBldg.DistributedBattleBldg):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogdoBattleBldg')
+
+ def __init__(self, cr):
+ DistributedBattleBldg.DistributedBattleBldg.__init__(self, cr)
+
+ def getBossBattleTaunt(self):
+ return TTLocalizer.CogdoBattleBldgBossTaunt
diff --git a/toontown/cogdominium/DistributedCogdoBattleBldgAI.py b/toontown/cogdominium/DistributedCogdoBattleBldgAI.py
new file mode 100644
index 0000000..b2dca7a
--- /dev/null
+++ b/toontown/cogdominium/DistributedCogdoBattleBldgAI.py
@@ -0,0 +1,8 @@
+from direct.directnotify import DirectNotifyGlobal
+from toontown.battle import DistributedBattleBldgAI
+
+class DistributedCogdoBattleBldgAI(DistributedBattleBldgAI.DistributedBattleBldgAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogdoBattleBldgAI')
+
+ def __init__(self, air, zoneId, roundCallback = None, finishCallback = None, maxSuits = 4, bossBattle = 0):
+ DistributedBattleBldgAI.DistributedBattleBldgAI.__init__(self, air, zoneId, roundCallback, finishCallback, maxSuits, bossBattle)
diff --git a/toontown/cogdominium/DistributedCogdoElevatorExt.py b/toontown/cogdominium/DistributedCogdoElevatorExt.py
new file mode 100644
index 0000000..25f6533
--- /dev/null
+++ b/toontown/cogdominium/DistributedCogdoElevatorExt.py
@@ -0,0 +1,17 @@
+from toontown.building.DistributedElevatorExt import DistributedElevatorExt
+
+class DistributedCogdoElevatorExt(DistributedElevatorExt):
+
+ def setupElevator(self):
+ DistributedElevatorExt.setupElevator(self)
+ self.elevatorSphereNodePath.setY(-1.0)
+ self.elevatorSphereNodePath.setZ(1.5)
+
+ def getElevatorModel(self):
+ return self.bldg.getCogdoElevatorNodePath()
+
+ def getBldgDoorOrigin(self):
+ return self.bldg.getCogdoDoorOrigin()
+
+ def _getDoorsClosedInfo(self):
+ return ('cogdoInterior', 'cogdoInterior')
diff --git a/toontown/cogdominium/DistributedCogdoElevatorExtAI.py b/toontown/cogdominium/DistributedCogdoElevatorExtAI.py
new file mode 100644
index 0000000..7c108de
--- /dev/null
+++ b/toontown/cogdominium/DistributedCogdoElevatorExtAI.py
@@ -0,0 +1,8 @@
+from direct.directnotify import DirectNotifyGlobal
+from toontown.building.DistributedElevatorExtAI import DistributedElevatorExtAI
+
+class DistributedCogdoElevatorExtAI(DistributedElevatorExtAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogdoElevatorExtAI')
+
+ def _createInterior(self):
+ self.bldg.createCogdoInterior()
diff --git a/toontown/cogdominium/DistributedCogdoElevatorInt.py b/toontown/cogdominium/DistributedCogdoElevatorInt.py
new file mode 100644
index 0000000..6294013
--- /dev/null
+++ b/toontown/cogdominium/DistributedCogdoElevatorInt.py
@@ -0,0 +1,6 @@
+from toontown.building.DistributedElevatorInt import DistributedElevatorInt
+
+class DistributedCogdoElevatorInt(DistributedElevatorInt):
+
+ def _getDoorsClosedInfo(self):
+ return ('cogdoInterior', 'cogdoInterior')
diff --git a/toontown/cogdominium/DistributedCogdoElevatorIntAI.py b/toontown/cogdominium/DistributedCogdoElevatorIntAI.py
new file mode 100644
index 0000000..dd2b0ec
--- /dev/null
+++ b/toontown/cogdominium/DistributedCogdoElevatorIntAI.py
@@ -0,0 +1,4 @@
+from toontown.building.DistributedElevatorIntAI import DistributedElevatorIntAI
+
+class DistributedCogdoElevatorIntAI(DistributedElevatorIntAI):
+ pass
diff --git a/toontown/cogdominium/DistributedCogdoInterior.py b/toontown/cogdominium/DistributedCogdoInterior.py
new file mode 100644
index 0000000..ab79278
--- /dev/null
+++ b/toontown/cogdominium/DistributedCogdoInterior.py
@@ -0,0 +1,765 @@
+import random
+from direct.interval.IntervalGlobal import *
+from direct.distributed.ClockDelta import *
+from toontown.building.ElevatorConstants import *
+from toontown.toon import NPCToons
+from pandac.PandaModules import NodePath
+from toontown.building import ElevatorUtils
+from toontown.toonbase import ToontownGlobals
+from toontown.toonbase import ToontownBattleGlobals
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM, State
+from direct.distributed import DistributedObject
+from direct.fsm import State
+from direct.fsm.StatePush import StateVar, FunctionCall
+from toontown.battle import BattleBase
+from toontown.hood import ZoneUtil
+from toontown.cogdominium.CogdoLayout import CogdoLayout
+from toontown.cogdominium import CogdoGameConsts
+from toontown.cogdominium import CogdoBarrelRoom, CogdoBarrelRoomConsts
+from toontown.distributed import DelayDelete
+from toontown.toonbase import TTLocalizer
+from CogdoExecutiveSuiteMovies import CogdoExecutiveSuiteIntro
+from CogdoElevatorMovie import CogdoElevatorMovie
+PAINTING_DICT = {'s': 'tt_m_ara_crg_paintingMoverShaker',
+ 'l': 'tt_m_ara_crg_paintingLegalEagle',
+ 'm': 'tt_m_ara_crg_paintingMoverShaker',
+ 'c': 'tt_m_ara_crg_paintingMoverShaker'}
+
+class DistributedCogdoInterior(DistributedObject.DistributedObject):
+ id = 0
+ cageHeights = [11.36, 0.01]
+
+ def __init__(self, cr):
+ DistributedObject.DistributedObject.__init__(self, cr)
+ self.toons = []
+ self.activeIntervals = {}
+ self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.mp3')
+ self.closeSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_close.mp3')
+ self.suits = []
+ self.reserveSuits = []
+ self.joiningReserves = []
+ self.distBldgDoId = None
+ self._CogdoGameRepeat = config.GetBool('cogdo-game-repeat', 0)
+ self.currentFloor = -1
+ self.elevatorName = self.__uniqueName('elevator')
+ self.floorModel = None
+ self.elevatorOutOpen = 0
+ self.BottomFloor_SuitPositions = [Point3(0, 15, 0),
+ Point3(10, 20, 0),
+ Point3(-7, 24, 0),
+ Point3(-10, 0, 0)]
+ self.BottomFloor_SuitHs = [75,
+ 170,
+ -91,
+ -44]
+ self.Cubicle_SuitPositions = [Point3(0, 18, 0),
+ Point3(10, 12, 0),
+ Point3(-9, 11, 0),
+ Point3(-3, 13, 0)]
+ self.Cubicle_SuitHs = [170,
+ 56,
+ -52,
+ 10]
+ self.BossOffice_SuitPositions = [Point3(0, 15, 0),
+ Point3(10, 20, 0),
+ Point3(-10, 6, 0),
+ Point3(-17, 30, 0)]
+ self.BossOffice_SuitHs = [170,
+ 120,
+ 12,
+ 38]
+ self._wantBarrelRoom = config.GetBool('cogdo-want-barrel-room', 0)
+ self.barrelRoom = CogdoBarrelRoom.CogdoBarrelRoom()
+ self.brResults = [[], []]
+ self.barrelRoomIntroTrack = None
+ self.penthouseOutroTrack = None
+ self.penthouseOutroChatDoneTrack = None
+ self.penthouseIntroTrack = None
+ self.waitMusic = base.loadMusic('phase_7/audio/bgm/encntr_toon_winning_indoor.mid')
+ self.elevatorMusic = base.loadMusic('phase_7/audio/bgm/tt_elevator.mid')
+ self.fsm = ClassicFSM.ClassicFSM('DistributedCogdoInterior', [State.State('WaitForAllToonsInside', self.enterWaitForAllToonsInside, self.exitWaitForAllToonsInside, ['Elevator']),
+ State.State('Elevator', self.enterElevator, self.exitElevator, ['Game']),
+ State.State('Game', self.enterGame, self.exitGame, ['Resting', 'Failed', 'BattleIntro']),
+ State.State('BarrelRoomIntro', self.enterBarrelRoomIntro, self.exitBarrelRoomIntro, ['CollectBarrels', 'Off']),
+ State.State('CollectBarrels', self.enterCollectBarrels, self.exitCollectBarrels, ['BarrelRoomReward', 'Off']),
+ State.State('BarrelRoomReward', self.enterBarrelRoomReward, self.exitBarrelRoomReward, ['Battle',
+ 'ReservesJoining',
+ 'BattleIntro',
+ 'Off']),
+ State.State('BattleIntro', self.enterBattleIntro, self.exitBattleIntro, ['Battle', 'ReservesJoining', 'Off']),
+ State.State('Battle', self.enterBattle, self.exitBattle, ['Resting', 'Reward', 'ReservesJoining']),
+ State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, ['Battle']),
+ State.State('Resting', self.enterResting, self.exitResting, ['Elevator']),
+ State.State('Reward', self.enterReward, self.exitReward, ['Off']),
+ State.State('Failed', self.enterFailed, self.exitFailed, ['Off']),
+ State.State('Off', self.enterOff, self.exitOff, ['Elevator', 'WaitForAllToonsInside', 'Battle'])], 'Off', 'Off')
+ self.fsm.enterInitialState()
+ self._haveEntranceElevator = StateVar(False)
+ self._stashEntranceElevator = StateVar(False)
+ self._stashEntranceElevatorFC = FunctionCall(self._doStashEntranceElevator, self._haveEntranceElevator, self._stashEntranceElevator)
+ self._entranceElevCallbacks = []
+ self._doEntranceElevCallbacksFC = FunctionCall(self._doEntranceElevCallbacks, self._haveEntranceElevator)
+ self.cage = None
+ self.shopOwnerNpcId = None
+ self.shopOwnerNpc = None
+ self._movie = None
+ self.SOSToonName = None
+ self.FOType = None
+ return
+
+ def setShopOwnerNpcId(self, npcId):
+ self.shopOwnerNpcId = npcId
+
+ def setSOSNpcId(self, npcId):
+ self.SOSToonName = NPCToons.getNPCName(npcId)
+
+ def setFOType(self, typeId):
+ self.FOType = chr(typeId)
+
+ def __uniqueName(self, name):
+ DistributedCogdoInterior.id += 1
+ return name + '%d' % DistributedCogdoInterior.id
+
+ def generate(self):
+ DistributedObject.DistributedObject.generate(self)
+ self.announceGenerateName = self.uniqueName('generate')
+ self.accept(self.announceGenerateName, self.handleAnnounceGenerate)
+ self.elevatorModelIn = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_csa_elevatorB')
+ self.leftDoorIn = self.elevatorModelIn.find('**/left_door')
+ self.rightDoorIn = self.elevatorModelIn.find('**/right_door')
+ self.elevatorModelOut = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_csa_elevatorB')
+ self.leftDoorOut = self.elevatorModelOut.find('**/left_door')
+ self.rightDoorOut = self.elevatorModelOut.find('**/right_door')
+
+ def __makeShopOwnerNpc(self):
+ if self.shopOwnerNpc:
+ return
+ self.shopOwnerNpc = NPCToons.createLocalNPC(self.shopOwnerNpcId)
+ if not self.shopOwnerNpc:
+ self.notify.warning('No shopkeeper in this cogdominium, using FunnyFarm Sellbot FO NPCToons')
+ random.seed(self.doId)
+ shopkeeper = random.randint(7001, 7009)
+ self.shopOwnerNpc = NPCToons.createLocalNPC(shopkeeper)
+ self.shopOwnerNpc.addActive()
+ self.shopOwnerNpc.reparentTo(self.cage)
+ self.shopOwnerNpc.setPosHpr(0, -2, 0, 180, 0, 0)
+ self.shopOwnerNpc.loop('neutral')
+
+ def setElevatorLights(self, elevatorModel):
+ npc = elevatorModel.findAllMatches('**/floor_light_?;+s')
+ for i in range(npc.getNumPaths()):
+ np = npc.getPath(i)
+ floor = int(np.getName()[-1:]) - 1
+ if floor == self.currentFloor:
+ np.setColor(LIGHT_ON_COLOR)
+ elif floor < self.layout.getNumGameFloors():
+ if self.isBossFloor(self.currentFloor):
+ np.setColor(LIGHT_ON_COLOR)
+ else:
+ np.setColor(LIGHT_OFF_COLOR)
+ else:
+ np.hide()
+
+ def startAlertElevatorLightIval(self, elevatorModel):
+ light = elevatorModel.find('**/floor_light_%s' % (self.currentFloor + 1))
+ track = Sequence(Func(light.setColor, Vec4(1.0, 0.6, 0.6, 1.0)), Wait(0.9), Func(light.setColor, LIGHT_ON_COLOR), Wait(0.9))
+ self.activeIntervals['alertElevatorLight'] = track
+ track.loop()
+
+ def stopAlertElevatorLightIval(self, elevatorModel):
+ self.__finishInterval('alertElevatorLight')
+ self.setElevatorLights(elevatorModel)
+
+ def handleAnnounceGenerate(self, obj):
+ self.ignore(self.announceGenerateName)
+ self.cageDoorSfx = loader.loadSfx('phase_5/audio/sfx/CHQ_SOS_cage_door.mp3')
+ self.cageLowerSfx = loader.loadSfx('phase_5/audio/sfx/CHQ_SOS_cage_lower.mp3')
+ self.sendUpdate('setAvatarJoined', [])
+
+ def disable(self):
+ self.fsm.requestFinalState()
+ self.__cleanupIntervals()
+ self.ignoreAll()
+ self.__cleanup()
+ self.__cleanupShopOwnerNpc()
+ self.__cleanupPenthouseIntro()
+ DistributedObject.DistributedObject.disable(self)
+
+ def __cleanupShopOwnerNpc(self):
+ if self.shopOwnerNpc:
+ self.shopOwnerNpc.removeActive()
+ self.shopOwnerNpc.delete()
+ self.shopOwnerNpc = None
+ return
+
+ def __cleanupPenthouseIntro(self):
+ if hasattr(self, '_movie') and self._movie:
+ self._movie.unload()
+ self._movie = None
+ return
+
+ def delete(self):
+ self._stashEntranceElevatorFC.destroy()
+ self._doEntranceElevCallbacksFC.destroy()
+ self._haveEntranceElevator.destroy()
+ self._stashEntranceElevator.destroy()
+ self._entranceElevCallbacks = None
+ del self.waitMusic
+ del self.elevatorMusic
+ del self.openSfx
+ del self.closeSfx
+ del self.fsm
+ base.localAvatar.inventory.setBattleCreditMultiplier(1)
+ DistributedObject.DistributedObject.delete(self)
+ return
+
+ def isBossFloor(self, floorNum):
+ if self.layout.hasBossBattle():
+ if self.layout.getBossBattleFloor() == floorNum:
+ return True
+ return False
+
+ def __cleanup(self):
+ self.toons = []
+ self.suits = []
+ self.reserveSuits = []
+ self.joiningReserves = []
+ if self.elevatorModelIn != None:
+ self.elevatorModelIn.removeNode()
+ if self.elevatorModelOut != None:
+ self.elevatorModelOut.removeNode()
+ if self.floorModel != None:
+ self.floorModel.removeNode()
+ if self.cage != None:
+ self.cage = None
+ if self.barrelRoom != None:
+ self.barrelRoom.destroy()
+ self.barrelRoom = None
+ self.leftDoorIn = None
+ self.rightDoorIn = None
+ self.leftDoorOut = None
+ self.rightDoorOut = None
+ return
+
+ def __addToon(self, toon):
+ self.accept(toon.uniqueName('disable'), self.__handleUnexpectedExit, extraArgs=[toon])
+
+ def __handleUnexpectedExit(self, toon):
+ self.notify.warning('handleUnexpectedExit() - toon: %d' % toon.doId)
+ self.__removeToon(toon, unexpected=1)
+
+ def __removeToon(self, toon, unexpected = 0):
+ if self.toons.count(toon) == 1:
+ self.toons.remove(toon)
+ self.ignore(toon.uniqueName('disable'))
+
+ def __finishInterval(self, name):
+ if self.activeIntervals.has_key(name):
+ interval = self.activeIntervals[name]
+ if interval.isPlaying():
+ interval.finish()
+
+ def __cleanupIntervals(self):
+ for interval in self.activeIntervals.values():
+ interval.finish()
+
+ self.activeIntervals = {}
+
+ def __closeInElevator(self):
+ self.leftDoorIn.setPos(3.5, 0, 0)
+ self.rightDoorIn.setPos(-3.5, 0, 0)
+
+ def getZoneId(self):
+ return self.zoneId
+
+ def setZoneId(self, zoneId):
+ self.zoneId = zoneId
+
+ def getExtZoneId(self):
+ return self.extZoneId
+
+ def setExtZoneId(self, extZoneId):
+ self.extZoneId = extZoneId
+
+ def getDistBldgDoId(self):
+ return self.distBldgDoId
+
+ def setDistBldgDoId(self, distBldgDoId):
+ self.distBldgDoId = distBldgDoId
+
+ def setNumFloors(self, numFloors):
+ self.layout = CogdoLayout(numFloors)
+
+ def getToonIds(self):
+ toonIds = []
+ for toon in self.toons:
+ toonIds.append(toon.doId)
+
+ return toonIds
+
+ def setToons(self, toonIds, hack):
+ self.toonIds = toonIds
+ oldtoons = self.toons
+ self.toons = []
+ for toonId in toonIds:
+ if toonId != 0:
+ if self.cr.doId2do.has_key(toonId):
+ toon = self.cr.doId2do[toonId]
+ toon.stopSmooth()
+ self.toons.append(toon)
+ if oldtoons.count(toon) == 0:
+ self.__addToon(toon)
+ else:
+ self.notify.warning('setToons() - no toon: %d' % toonId)
+
+ for toon in oldtoons:
+ if self.toons.count(toon) == 0:
+ self.__removeToon(toon)
+
+ def setSuits(self, suitIds, reserveIds, values):
+ oldsuits = self.suits
+ self.suits = []
+ self.joiningReserves = []
+ for suitId in suitIds:
+ if self.cr.doId2do.has_key(suitId):
+ suit = self.cr.doId2do[suitId]
+ self.suits.append(suit)
+ suit.fsm.request('Battle')
+ suit.buildingSuit = 1
+ suit.reparentTo(render)
+ if oldsuits.count(suit) == 0:
+ self.joiningReserves.append(suit)
+ else:
+ self.notify.warning('setSuits() - no suit: %d' % suitId)
+
+ self.reserveSuits = []
+ for index in range(len(reserveIds)):
+ suitId = reserveIds[index]
+ if self.cr.doId2do.has_key(suitId):
+ suit = self.cr.doId2do[suitId]
+ self.reserveSuits.append((suit, values[index]))
+ else:
+ self.notify.warning('setSuits() - no suit: %d' % suitId)
+
+ if len(self.joiningReserves) > 0:
+ self.fsm.request('ReservesJoining')
+
+ def setState(self, state, timestamp):
+ self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])
+
+ def stashElevatorIn(self, stash = True):
+ self._stashEntranceElevator.set(stash)
+
+ def getEntranceElevator(self, callback):
+ if self._haveEntranceElevator.get():
+ callback(self.elevIn)
+ else:
+ self._entranceElevCallbacks.append(callback)
+
+ def _doEntranceElevCallbacks(self, haveElev):
+ if haveElev:
+ while len(self._entranceElevCallbacks):
+ cbs = self._entranceElevCallbacks[:]
+ self._entranceElevCallbacks = []
+ for callback in cbs:
+ callback(self.elevIn)
+
+ def _doStashEntranceElevator(self, haveElev, doStash):
+ if haveElev:
+ if doStash:
+ self.elevIn.stash()
+ else:
+ self.elevIn.unstash()
+
+ def d_elevatorDone(self):
+ self.sendUpdate('elevatorDone', [])
+
+ def d_reserveJoinDone(self):
+ self.sendUpdate('reserveJoinDone', [])
+
+ def enterOff(self, ts = 0):
+ messenger.send('sellbotFieldOfficeChanged', [False])
+ return None
+
+ def exitOff(self):
+ return None
+
+ def enterWaitForAllToonsInside(self, ts = 0):
+ base.transitions.fadeOut(0)
+ return None
+
+ def exitWaitForAllToonsInside(self):
+ return None
+
+ def enterGame(self, ts = 0):
+ base.cr.forbidCheesyEffects(1)
+
+ def exitGame(self):
+ base.cr.forbidCheesyEffects(0)
+
+ def __playElevator(self, ts, name, callback):
+ SuitHs = []
+ SuitPositions = []
+ if self.floorModel:
+ self.floorModel.removeNode()
+ self.floorModel = None
+ if self.cage:
+ self.cage = None
+ if self.currentFloor == 0:
+ SuitHs = self.BottomFloor_SuitHs
+ SuitPositions = self.BottomFloor_SuitPositions
+ if self.isBossFloor(self.currentFloor):
+ self.barrelRoom.unload()
+ self.floorModel = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_crg_penthouse')
+ self.cage = self.floorModel.find('**/cage')
+ pos = self.cage.getPos()
+ self.cagePos = []
+ for height in self.cageHeights:
+ self.cagePos.append(Point3(pos[0], pos[1], height))
+
+ self.cageDoor = self.floorModel.find('**/cage_door')
+ self.cageDoor.wrtReparentTo(self.cage)
+ if self.FOType:
+ paintingModelName = PAINTING_DICT.get(self.FOType)
+ for i in range(4):
+ paintingModel = loader.loadModel('phase_5/models/cogdominium/%s' % paintingModelName)
+ loc = self.floorModel.find('**/loc_painting%d' % (i + 1))
+ paintingModel.reparentTo(loc)
+
+ SuitHs = self.BossOffice_SuitHs
+ SuitPositions = self.BossOffice_SuitPositions
+ self.__makeShopOwnerNpc()
+ else:
+ if self._wantBarrelRoom:
+ self.barrelRoom.load()
+ self.barrelRoom.hide()
+ SuitHs = self.Cubicle_SuitHs
+ SuitPositions = self.Cubicle_SuitPositions
+ if self.floorModel:
+ self.floorModel.reparentTo(render)
+ if self.isBossFloor(self.currentFloor):
+ self.notify.debug('Load boss_suit_office')
+ elevIn = self.floorModel.find(CogdoGameConsts.PenthouseElevatorInPath).copyTo(render)
+ elevOut = self.floorModel.find(CogdoGameConsts.PenthouseElevatorOutPath)
+ frame = self.elevatorModelOut.find('**/frame')
+ if not frame.isEmpty():
+ frame.hide()
+ frame = self.elevatorModelIn.find('**/frame')
+ if not frame.isEmpty():
+ frame.hide()
+ self.elevatorModelOut.reparentTo(elevOut)
+ else:
+ elevIn = self.floorModel.find('**/elevator-in')
+ elevOut = self.floorModel.find('**/elevator-out')
+ elif self._wantBarrelRoom and self.barrelRoom.isLoaded():
+ elevIn = self.barrelRoom.dummyElevInNode
+ elevOut = self.barrelRoom.model.find(CogdoBarrelRoomConsts.BarrelRoomElevatorOutPath)
+ y = elevOut.getY(render)
+ elevOut = elevOut.copyTo(render)
+ elevOut.setY(render, y - 0.75)
+ else:
+ floorModel = loader.loadModel('phase_7/models/modules/boss_suit_office')
+ elevIn = floorModel.find('**/elevator-in').copyTo(render)
+ elevOut = floorModel.find('**/elevator-out').copyTo(render)
+ floorModel.removeNode()
+ self.elevIn = elevIn
+ self.elevOut = elevOut
+ self._haveEntranceElevator.set(True)
+ for index in range(len(self.suits)):
+ self.suits[index].setPos(SuitPositions[index])
+ if len(self.suits) > 2:
+ self.suits[index].setH(SuitHs[index])
+ else:
+ self.suits[index].setH(170)
+ self.suits[index].loop('neutral')
+
+ for toon in self.toons:
+ toon.reparentTo(self.elevatorModelIn)
+ index = self.toonIds.index(toon.doId)
+ toon.setPos(ElevatorPoints[index][0], ElevatorPoints[index][1], ElevatorPoints[index][2])
+ toon.setHpr(180, 0, 0)
+ toon.loop('neutral')
+
+ self.elevatorModelIn.reparentTo(elevIn)
+ self.leftDoorIn.setPos(3.5, 0, 0)
+ self.rightDoorIn.setPos(-3.5, 0, 0)
+ camera.reparentTo(self.elevatorModelIn)
+ camera.setH(180)
+ camera.setP(0)
+ camera.setPos(0, 14, 4)
+ base.playMusic(self.elevatorMusic, looping=1, volume=0.8)
+ track = Sequence(Func(base.transitions.noTransitions), ElevatorUtils.getRideElevatorInterval(ELEVATOR_NORMAL), ElevatorUtils.getOpenInterval(self, self.leftDoorIn, self.rightDoorIn, self.openSfx, None, type=ELEVATOR_NORMAL), Func(camera.wrtReparentTo, render))
+ for toon in self.toons:
+ track.append(Func(toon.wrtReparentTo, render))
+
+ track.append(Func(callback))
+ track.start(ts)
+ self.activeIntervals[name] = track
+ return
+
+ def enterElevator(self, ts = 0):
+ if not self._CogdoGameRepeat:
+ self.currentFloor += 1
+ self.cr.playGame.getPlace().currentFloor = self.currentFloor
+ self.setElevatorLights(self.elevatorModelIn)
+ self.setElevatorLights(self.elevatorModelOut)
+ if not self.isBossFloor(self.currentFloor):
+ self.elevatorModelOut.detachNode()
+ messenger.send('sellbotFieldOfficeChanged', [True])
+ else:
+ self._movie = CogdoElevatorMovie()
+ self._movie.load()
+ self._movie.play()
+ self.__playElevator(ts, self.elevatorName, self.__handleElevatorDone)
+ mult = ToontownBattleGlobals.getCreditMultiplier(self.currentFloor)
+ base.localAvatar.inventory.setBattleCreditMultiplier(mult)
+
+ def __handleElevatorDone(self):
+ self.d_elevatorDone()
+
+ def exitElevator(self):
+ self.elevatorMusic.stop()
+ if self._movie:
+ self._movie.end()
+ self.__cleanupPenthouseIntro()
+ self.__finishInterval(self.elevatorName)
+ return None
+
+ def __setupBarrelRoom(self):
+ base.cr.playGame.getPlace().fsm.request('stopped')
+ base.transitions.irisOut(0.0)
+ self.elevatorModelIn.detachNode()
+ self._showExitElevator()
+ self.barrelRoom.show()
+ self.barrelRoom.placeToonsAtEntrance(self.toons)
+
+ def barrelRoomIntroDone(self):
+ self.sendUpdate('toonBarrelRoomIntroDone', [])
+
+ def enterBarrelRoomIntro(self, ts = 0):
+ if not self.isBossFloor(self.currentFloor):
+ if self._wantBarrelRoom:
+ self.__setupBarrelRoom()
+ self.barrelRoomIntroTrack, trackName = self.barrelRoom.getIntroInterval()
+ self.barrelRoomIntroDoneEvent = trackName
+ self.accept(self.barrelRoomIntroDoneEvent, self.barrelRoomIntroDone)
+ self.activeIntervals[trackName] = self.barrelRoomIntroTrack
+ self.barrelRoomIntroTrack.start(ts)
+ else:
+ self._showExitElevator()
+
+ def exitBarrelRoomIntro(self):
+ if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
+ self.ignore(self.barrelRoomIntroDoneEvent)
+ if self.barrelRoomIntroTrack:
+ self.barrelRoomIntroTrack.finish()
+ DelayDelete.cleanupDelayDeletes(self.barrelRoomIntroTrack)
+ self.barrelRoomIntroTrack = None
+ return
+
+ def __handleLocalToonLeftBarrelRoom(self):
+ self.notify.info('Local toon teleported out of barrel room.')
+ self.sendUpdate('toonLeftBarrelRoom', [])
+ self.barrelRoom.deactivate()
+
+ def enterCollectBarrels(self, ts = 0):
+ if not self.isBossFloor(self.currentFloor):
+ if self._wantBarrelRoom:
+ self.acceptOnce('localToonLeft', self.__handleLocalToonLeftBarrelRoom)
+ self.barrelRoom.activate()
+ base.playMusic(self.waitMusic, looping=1, volume=0.7)
+
+ def exitCollectBarrels(self):
+ if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
+ self.ignore('localToonLeft')
+ self.barrelRoom.deactivate()
+ self.waitMusic.stop()
+
+ def __brRewardDone(self, task = None):
+ self.notify.info('Toon finished watching the barrel room reward.')
+ self.sendUpdate('toonBarrelRoomRewardDone', [])
+
+ def setBarrelRoomReward(self, avIds, laffs):
+ self.brResults = [avIds, laffs]
+ self.barrelRoom.setRewardResults(self.brResults)
+
+ def enterBarrelRoomReward(self, ts = 0):
+ if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
+ base.cr.playGame.getPlace().fsm.request('stopped')
+ self.startAlertElevatorLightIval(self.elevatorModelOut)
+ track, trackName = self.barrelRoom.showRewardUi(self.brResults, callback=self.__brRewardDone)
+ self.activeIntervals[trackName] = track
+ track.start()
+ self.barrelRoom.placeToonsNearBattle(self.toons)
+
+ def exitBarrelRoomReward(self):
+ if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
+ base.cr.playGame.getPlace().fsm.request('walk')
+ self.stopAlertElevatorLightIval(self.elevatorModelOut)
+ self.barrelRoom.hideRewardUi()
+
+ def enterBattleIntro(self, ts = 0):
+ self._movie = CogdoExecutiveSuiteIntro(self.shopOwnerNpc)
+ self._movie.load()
+ self._movie.play()
+
+ def exitBattleIntro(self):
+ self._movie.end()
+ self.__cleanupPenthouseIntro()
+
+ def __playCloseElevatorOut(self, name, delay = 0):
+ track = Sequence(Wait(delay + SUIT_LEAVE_ELEVATOR_TIME), Parallel(SoundInterval(self.closeSfx), LerpPosInterval(self.leftDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL), startPos=Point3(0, 0, 0), blendType='easeOut'), LerpPosInterval(self.rightDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL), startPos=Point3(0, 0, 0), blendType='easeOut')))
+ track.start()
+ self.activeIntervals[name] = track
+
+ def enterBattle(self, ts = 0):
+ if self._wantBarrelRoom and self.elevatorOutOpen == 1:
+ self.__playCloseElevatorOut(self.uniqueName('close-out-elevator'), delay=2)
+ camera.setPos(0, -15, 6)
+ camera.headsUp(self.elevatorModelOut)
+
+ def _showExitElevator(self):
+ self.elevatorModelOut.reparentTo(self.elevOut)
+ self.leftDoorOut.setPos(3.5, 0, 0)
+ self.rightDoorOut.setPos(-3.5, 0, 0)
+ if not self._wantBarrelRoom and self.elevatorOutOpen == 1:
+ self.__playCloseElevatorOut(self.uniqueName('close-out-elevator'))
+ camera.setPos(0, -15, 6)
+ camera.headsUp(self.elevatorModelOut)
+ return None
+
+ def exitBattle(self):
+ if self.elevatorOutOpen == 1:
+ self.__finishInterval(self.uniqueName('close-out-elevator'))
+ self.elevatorOutOpen = 0
+ return None
+
+ def __playReservesJoining(self, ts, name, callback):
+ index = 0
+ for suit in self.joiningReserves:
+ suit.reparentTo(render)
+ suit.setPos(self.elevatorModelOut, Point3(ElevatorPoints[index][0], ElevatorPoints[index][1], ElevatorPoints[index][2]))
+ index += 1
+ suit.setH(180)
+ suit.loop('neutral')
+
+ if len(self.suits) == len(self.joiningReserves):
+ camSequence = Sequence(Func(camera.wrtReparentTo, localAvatar), Func(camera.setPos, Point3(0, 5, 5)), Func(camera.headsUp, self.elevatorModelOut))
+ else:
+ camSequence = Sequence(Func(camera.wrtReparentTo, self.elevatorModelOut), Func(camera.setPos, Point3(0, -8, 2)), Func(camera.setHpr, Vec3(0, 10, 0)))
+ track = Sequence(camSequence, Parallel(SoundInterval(self.openSfx), LerpPosInterval(self.leftDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], Point3(0, 0, 0), startPos=ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL), blendType='easeOut'), LerpPosInterval(self.rightDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], Point3(0, 0, 0), startPos=ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL), blendType='easeOut')), Wait(SUIT_HOLD_ELEVATOR_TIME), Func(camera.wrtReparentTo, render), Func(callback))
+ track.start(ts)
+ self.activeIntervals[name] = track
+
+ def enterReservesJoining(self, ts = 0):
+ self.__playReservesJoining(ts, self.uniqueName('reserves-joining'), self.__handleReserveJoinDone)
+ return None
+
+ def __handleReserveJoinDone(self):
+ self.joiningReserves = []
+ self.elevatorOutOpen = 1
+ self.d_reserveJoinDone()
+
+ def exitReservesJoining(self):
+ self.__finishInterval(self.uniqueName('reserves-joining'))
+ return None
+
+ def enterResting(self, ts = 0):
+ self._showExitElevator()
+ self._setAvPosFDC = FrameDelayedCall('setAvPos', self._setAvPosToExit)
+ if self._wantBarrelRoom:
+ self.barrelRoom.showBattleAreaLight(True)
+ base.playMusic(self.waitMusic, looping=1, volume=0.7)
+ self.__closeInElevator()
+ self._haveEntranceElevator.set(False)
+ self._stashEntranceElevator.set(False)
+
+ def _setAvPosToExit(self):
+ base.localAvatar.setPos(self.elevOut, 0, -10, 0)
+ base.localAvatar.setHpr(self.elevOut, 0, 0, 0)
+ base.cr.playGame.getPlace().fsm.request('walk')
+
+ def exitResting(self):
+ self._setAvPosFDC.destroy()
+ self.waitMusic.stop()
+
+ def enterReward(self, ts = 0):
+ if self.isBossFloor(self.currentFloor):
+ self.penthouseOutroTrack = self.__outroPenthouse()
+ self.penthouseOutroTrack.start(ts)
+ else:
+ self.exitCogdoBuilding()
+ return None
+
+ def exitReward(self):
+ self.notify.debug('exitReward')
+ if self.penthouseOutroTrack:
+ self.penthouseOutroTrack.finish()
+ DelayDelete.cleanupDelayDeletes(self.penthouseOutroTrack)
+ self.penthouseOutroTrack = None
+ if not self.penthouseOutroChatDoneTrack:
+ self.notify.debug('exitReward: instanting outroPenthouseChatDone track')
+ self.__outroPenthouseChatDone()
+ self.penthouseOutroChatDoneTrack.finish()
+ self.penthouseOutroChatDoneTrack = None
+ return
+
+ def enterFailed(self, ts = 0):
+ self.exitCogdoBuilding()
+ return None
+
+ def exitFailed(self):
+ self.notify.debug('exitFailed()')
+ self.exitCogdoBuilding()
+ return None
+
+ def exitCogdoBuilding(self):
+ if base.localAvatar.hp < 0:
+ return
+ base.localAvatar.b_setParent(ToontownGlobals.SPHidden)
+ request = {'loader': ZoneUtil.getBranchLoaderName(self.extZoneId),
+ 'where': ZoneUtil.getToonWhereName(self.extZoneId),
+ 'how': 'elevatorIn',
+ 'hoodId': ZoneUtil.getHoodId(self.extZoneId),
+ 'zoneId': self.extZoneId,
+ 'shardId': None,
+ 'avId': -1,
+ 'bldgDoId': self.distBldgDoId}
+ messenger.send('DSIDoneEvent', [request])
+ return
+
+ def displayBadges(self):
+ numFloors = self.layout.getNumGameFloors()
+ if numFloors > 5 or numFloors < 3:
+ pass
+ else:
+ self.notify.warning('Invalid floor number for display badges.')
+ for player in range(len(self.toons)):
+ goldBadge = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_crg_goldTrophy')
+ goldBadge.setScale(1.2)
+ goldNode = render.find('**/gold_0' + str(player + 1))
+ goldBadge.reparentTo(goldNode)
+ for floor in range(numFloors):
+ silverBadge = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_crg_silverTrophy.bam')
+ silverBadge.setScale(1.2)
+ silverNode = render.find('**/silver_0' + str(floor * 4 + (player + 1)))
+ silverBadge.reparentTo(silverNode)
+
+ def __outroPenthouse(self):
+ avatar = base.localAvatar
+ trackName = '__outroPenthouse-%d' % avatar.doId
+ track = Parallel(name=trackName)
+ base.cr.playGame.getPlace().fsm.request('stopped')
+ speech = TTLocalizer.CogdoExecutiveSuiteToonThankYou % self.SOSToonName
+ track.append(Sequence(Func(camera.wrtReparentTo, localAvatar), Func(camera.setPos, 0, -9, 9), Func(camera.lookAt, Point3(5, 15, 0)), Parallel(self.cage.posInterval(0.75, self.cagePos[1], blendType='easeOut'), SoundInterval(self.cageLowerSfx, duration=0.5)), Parallel(self.cageDoor.hprInterval(0.5, VBase3(0, 90, 0), blendType='easeOut'), Sequence(SoundInterval(self.cageDoorSfx), duration=0)), Wait(0.25), Func(self.shopOwnerNpc.wrtReparentTo, render), Func(self.shopOwnerNpc.setScale, 1), Func(self.shopOwnerNpc.loop, 'walk'), Func(self.shopOwnerNpc.headsUp, Point3(0, 10, 0)), ParallelEndTogether(self.shopOwnerNpc.posInterval(1.5, Point3(0, 10, 0)), self.shopOwnerNpc.hprInterval(0.5, VBase3(180, 0, 0), blendType='easeInOut')), Func(self.shopOwnerNpc.setChatAbsolute, TTLocalizer.CagedToonYippee, CFSpeech), ActorInterval(self.shopOwnerNpc, 'jump'), Func(self.shopOwnerNpc.loop, 'neutral'), Func(self.shopOwnerNpc.headsUp, localAvatar), Func(self.shopOwnerNpc.setLocalPageChat, speech, 0), Func(camera.lookAt, self.shopOwnerNpc, Point3(0, 0, 2))))
+ self.activeIntervals[trackName] = track
+ self.accept('doneChatPage', self.__outroPenthouseChatDone)
+ return track
+
+ def __outroPenthouseChatDone(self, elapsed = None):
+ self.shopOwnerNpc.setChatAbsolute(TTLocalizer.CogdoExecutiveSuiteToonBye, CFSpeech)
+ self.ignore('doneChatPage')
+ track = Parallel(Sequence(ActorInterval(self.shopOwnerNpc, 'wave'), Func(self.shopOwnerNpc.loop, 'neutral')), Sequence(Wait(2.0), Func(self.exitCogdoBuilding), Func(base.camLens.setFov, ToontownGlobals.DefaultCameraFov)))
+ track.start()
+ self.penthouseOutroChatDoneTrack = track
diff --git a/toontown/cogdominium/DistributedCogdoInteriorAI.py b/toontown/cogdominium/DistributedCogdoInteriorAI.py
new file mode 100644
index 0000000..90575e5
--- /dev/null
+++ b/toontown/cogdominium/DistributedCogdoInteriorAI.py
@@ -0,0 +1,939 @@
+import copy
+import random
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed import DistributedObjectAI
+from direct.distributed.ClockDelta import *
+from direct.fsm import ClassicFSM, State
+from direct.task import Timer
+from toontown.toon import NPCToons
+from otp.ai.AIBaseGlobal import *
+from toontown.ai import ToonBarrier
+from toontown.battle import BattleBase
+from toontown.cogdominium import DistributedCogdoBattleBldgAI
+from toontown.building.ElevatorConstants import *
+from toontown.suit.SuitDNA import SuitDNA
+from toontown.toonbase.ToontownBattleGlobals import *
+from toontown.hood import ZoneUtil
+from toontown.minigame.MinigameGlobals import SafeZones
+from toontown.toon import NPCToons
+from DistributedCogdoElevatorIntAI import DistributedCogdoElevatorIntAI
+from toontown.cogdominium import CogdoBarrelRoomConsts
+from toontown.cogdominium import CogdoBarrelRoomAI
+from DistCogdoBoardroomGameAI import DistCogdoBoardroomGameAI
+from toontown.cogdominium.DistCogdoCraneGameAI import DistCogdoCraneGameAI
+from toontown.cogdominium.DistCogdoMazeGameAI import DistCogdoMazeGameAI
+from toontown.cogdominium.DistCogdoFlyingGameAI import DistCogdoFlyingGameAI
+from toontown.cogdominium import CogdoGameConsts
+CogdoGames = {
+ 'boardroom': DistCogdoBoardroomGameAI,
+ 'crane': DistCogdoCraneGameAI,
+ 'maze': DistCogdoMazeGameAI,
+ 'flying': DistCogdoFlyingGameAI}
+IntGames = set([
+ 'boardroom',
+ 'crane',
+ 'flying',
+ 'defense'])
+simbase.forcedCogdoGame = config.GetString('cogdo-game', '')
+GameRequests = {}
+
+class DistributedCogdoInteriorAI(DistributedObjectAI.DistributedObjectAI):
+
+ def __init__(self, air, elevator):
+ self.air = air
+ DistributedObjectAI.DistributedObjectAI.__init__(self, air)
+ (self.extZoneId, self.zoneId) = elevator.bldg.getExteriorAndInteriorZoneId()
+ self._numFloors = elevator.bldg.planner.numFloors
+ self.layout = elevator.bldg._cogdoLayout
+ self.avatarExitEvents = []
+ self.toons = []
+ self.toonSkillPtsGained = {}
+ self.toonExp = {}
+ self.toonOrigQuests = {}
+ self.toonItems = {}
+ self.toonOrigMerits = {}
+ self.toonMerits = {}
+ self.toonParts = {}
+ self.helpfulToons = []
+ self.shopOwnerNpcId = 0
+ npcDesc = NPCToons.zone2NpcDict.get(self.zoneId)
+ if npcDesc:
+ self.shopOwnerNpcId = npcDesc[0]
+
+ if not self.shopOwnerNpcId:
+ self.notify.warning('No npcs found for current cogdo building')
+
+ self.currentFloor = 0
+ self.bldg = elevator.bldg
+ self.elevator = elevator
+ self._game = None
+ self._CogdoGameRepeat = config.GetBool('cogdo-game-repeat', 0)
+ self.suits = []
+ self.activeSuits = []
+ self.reserveSuits = []
+ self.joinedReserves = []
+ self.suitsKilled = []
+ self.suitsKilledPerFloor = []
+ self.battle = None
+ self.timer = Timer.Timer()
+ self._wantBarrelRoom = config.GetBool('cogdo-want-barrel-room', 0)
+ self.barrelRoom = None
+ self.responses = {}
+ self.ignoreResponses = 0
+ self.ignoreElevatorDone = 0
+ self.ignoreReserveJoinDone = 0
+ self.toonIds = copy.copy(elevator.seats)
+ for toonId in self.toonIds:
+ if toonId != None:
+ self.__addToon(toonId)
+
+ self.savedByMap = {}
+ self.fsm = ClassicFSM.ClassicFSM('DistributedCogdoInteriorAI', [
+ State.State('WaitForAllToonsInside', self.enterWaitForAllToonsInside, self.exitWaitForAllToonsInside, [
+ 'Elevator']),
+ State.State('Elevator', self.enterElevator, self.exitElevator, [
+ 'Game']),
+ State.State('Game', self.enterGame, self.exitGame, [
+ 'Resting',
+ 'Failed',
+ 'BattleIntro',
+ 'Off']),
+ State.State('BarrelRoomIntro', self.enterBarrelRoomIntro, self.exitBarrelRoomIntro, [
+ 'CollectBarrels',
+ 'Off']),
+ State.State('CollectBarrels', self.enterCollectBarrels, self.exitCollectBarrels, [
+ 'BarrelRoomReward',
+ 'Off']),
+ State.State('BarrelRoomReward', self.enterBarrelRoomReward, self.exitBarrelRoomReward, [
+ 'Battle',
+ 'ReservesJoining',
+ 'BattleIntro',
+ 'Off']),
+ State.State('BattleIntro', self.enterBattleIntro, self.exitBattleIntro, [
+ 'Battle',
+ 'ReservesJoining',
+ 'Off']),
+ State.State('Battle', self.enterBattle, self.exitBattle, [
+ 'ReservesJoining',
+ 'BattleDone',
+ 'Off']),
+ State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, [
+ 'Battle',
+ 'Off']),
+ State.State('BattleDone', self.enterBattleDone, self.exitBattleDone, [
+ 'Resting',
+ 'Reward',
+ 'Off']),
+ State.State('Resting', self.enterResting, self.exitResting, [
+ 'Elevator',
+ 'Off']),
+ State.State('Reward', self.enterReward, self.exitReward, [
+ 'Off']),
+ State.State('Failed', self.enterFailed, self.exitFailed, [
+ 'Off']),
+ State.State('Off', self.enterOff, self.exitOff, [
+ 'WaitForAllToonsInside'])], 'Off', 'Off', onUndefTransition = ClassicFSM.ClassicFSM.ALLOW)
+ self.fsm.enterInitialState()
+ safeZone = ZoneUtil.getCanonicalHoodId(self.extZoneId)
+ difficulty = SafeZones.index(safeZone)
+ self.SOSCard = self.chooseSOSCard(difficulty)
+
+ def generateWithRequired(self, zoneId):
+ self._disCleanupTask = None
+ self._sadCleanupTask = None
+ DistributedObjectAI.DistributedObjectAI.generateWithRequired(self, zoneId)
+
+ def delete(self):
+ self.ignoreAll()
+ self.toons = []
+ self.toonIds = []
+ self.fsm.requestFinalState()
+ del self.fsm
+ del self.bldg
+ del self.elevator
+ self.timer.stop()
+ del self.timer
+ self._cogdoLayout = None
+ self.__cleanupFloorBattle()
+ if self.barrelRoom:
+ self.barrelRoom.destroy()
+ self.barrelRoom = None
+
+ taskName = self.taskName('deleteInterior')
+ taskMgr.remove(taskName)
+ if self._disCleanupTask:
+ taskMgr.remove(self._disCleanupTask)
+
+ if self._sadCleanupTask:
+ taskMgr.remove(self._sadCleanupTask)
+
+ DistributedObjectAI.DistributedObjectAI.delete(self)
+
+ def requestDelete(self):
+ if self._game:
+ self._game.requestDelete()
+
+ DistributedObjectAI.DistributedObjectAI.requestDelete(self)
+
+ def getShopOwnerNpcId(self):
+ return self.shopOwnerNpcId
+
+ def getSOSNpcId(self):
+ return self.SOSCard
+
+ def __handleUnexpectedExit(self, toonId):
+ self.notify.warning('toon: %d exited unexpectedly' % toonId)
+ self.__removeToon(toonId)
+ if self._game:
+ self._game.handleToonDisconnected(toonId)
+
+ if len(self.toons) == 0:
+ self.timer.stop()
+ if self.fsm.getCurrentState().getName() == 'Resting':
+ pass
+ elif self.battle == None:
+ self.fsm.requestFinalState()
+ self._disCleanupTask = taskMgr.doMethodLater(20, self._cleanupAfterLastToonWentDis, self.uniqueName('discleanup'))
+
+ def _cleanupAfterLastToonWentDis(self, task):
+ self._disCleanupTask = None
+ self.bldg.deleteCogdoInterior()
+ return task.done
+
+ def _handleToonWentSad(self, toonId):
+ self.notify.info('toon: %d went sad' % toonId)
+ self.__removeToon(toonId)
+ toon = self.air.getDo(toonId)
+ if toon:
+ self.ignore(toon.getGoneSadMessage())
+
+ if self._game:
+ self._game.handleToonWentSad(toonId)
+
+ if len(self.toons) == 0:
+ self.timer.stop()
+ if self.fsm.getCurrentState().getName() == 'Resting':
+ pass
+ elif self.battle == None:
+ self._sadCleanupTask = taskMgr.doMethodLater(20, self._cleanupAfterLastToonWentSad, self.uniqueName('sadcleanup'))
+
+ def _cleanupAfterLastToonWentSad(self, task):
+ self._sadCleanupTask = None
+ self.bldg.deleteCogdoInterior()
+ return task.done
+
+ def __addToon(self, toonId):
+ if not self.air.doId2do.has_key(toonId):
+ self.notify.warning('addToon() - no toon for doId: %d' % toonId)
+ return
+
+ event = self.air.getAvatarExitEvent(toonId)
+ self.avatarExitEvents.append(event)
+ self.accept(event, self.__handleUnexpectedExit, extraArgs = [
+ toonId])
+ self.toons.append(toonId)
+ self.responses[toonId] = 0
+
+ def __removeToon(self, toonId):
+ if self.toons.count(toonId):
+ self.toons.remove(toonId)
+
+ if self.toonIds.count(toonId):
+ self.toonIds[self.toonIds.index(toonId)] = None
+
+ if self.responses.has_key(toonId):
+ del self.responses[toonId]
+
+ event = self.air.getAvatarExitEvent(toonId)
+ if self.avatarExitEvents.count(event):
+ self.avatarExitEvents.remove(event)
+
+ self.ignore(event)
+
+ def __resetResponses(self):
+ self.responses = {}
+ for toon in self.toons:
+ self.responses[toon] = 0
+
+ self.ignoreResponses = 0
+
+ def __allToonsResponded(self):
+ for toon in self.toons:
+ if self.responses[toon] == 0:
+ return 0
+
+ self.ignoreResponses = 1
+ return 1
+
+ def getZoneId(self):
+ return self.zoneId
+
+ def getExtZoneId(self):
+ return self.extZoneId
+
+ def getDistBldgDoId(self):
+ return self.bldg.getDoId()
+
+ def getNumFloors(self):
+ return self._numFloors
+
+ def d_setToons(self):
+ self.sendUpdate('setToons', self.getToons())
+
+ def getToons(self):
+ sendIds = []
+ for toonId in self.toonIds:
+ if toonId == None:
+ sendIds.append(0)
+ else:
+ sendIds.append(toonId)
+
+ return [
+ sendIds,
+ 0]
+
+ def getDroneCogDNA(self):
+ dna = SuitDNA()
+ dna.newSuitRandom(level = 2)
+ return dna
+
+ def d_setSuits(self):
+ self.sendUpdate('setSuits', self.getSuits())
+
+ def getSuits(self):
+ suitIds = []
+ for suit in self.activeSuits:
+ suitIds.append(suit.doId)
+
+ reserveIds = []
+ values = []
+ for info in self.reserveSuits:
+ reserveIds.append(info[0].doId)
+ values.append(info[1])
+
+ return [
+ suitIds,
+ reserveIds,
+ values]
+
+ def b_setState(self, state):
+ self.d_setState(state)
+ self.setState(state)
+
+ def d_setState(self, state):
+ stime = globalClock.getRealTime() + BattleBase.SERVER_BUFFER_TIME
+ self.sendUpdate('setState', [
+ state,
+ globalClockDelta.localToNetworkTime(stime)])
+
+ def setState(self, state):
+ self.fsm.request(state)
+
+ def getState(self):
+ return [
+ self.fsm.getCurrentState().getName(),
+ globalClockDelta.getRealNetworkTime()]
+
+ def setAvatarJoined(self):
+ avId = self.air.getAvatarIdFromSender()
+ if self.toons.count(avId) == 0:
+ self.air.writeServerEvent('suspicious', avId, 'DistributedCogdoInteriorAI.setAvatarJoined from toon not in %s.' % self.toons)
+ self.notify.warning('setAvatarJoined() - av: %d not in list' % avId)
+ return
+
+ if self.fsm.getCurrentState().getName() != 'WaitForAllToonsInside':
+ self.air.writeServerEvent('suspicious', avId, 'CogdoInteriorAI.setAvatarJoined: not in wait state')
+ return
+
+ avatar = self.air.doId2do.get(avId)
+ if avatar == None:
+ self.air.writeServerEvent('suspicious', avId, 'CogdoInteriorAI.setAvatarJoined: avatar not present')
+ return
+ else:
+ self.savedByMap[avId] = (avatar.getName(), avatar.dna.asTuple())
+ if avId not in self.responses:
+ self.air.writeServerEvent('suspicious', avId, 'CogdoInteriorAI.setAvatarJoined: avId not in responses')
+ self.notify.warning('CogdoInteriorAI.setAvatarJoined: avId not in responses')
+ return
+
+ if self.responses[avId] > 0:
+ self.air.writeServerEvent('suspicious', avId, 'CogdoInteriorAI.setAvatarJoined: av already responded')
+ return
+
+ self.responses[avId] += 1
+ if self.__allToonsResponded():
+ self.fsm.request('Elevator')
+
+ def elevatorDone(self):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreResponses == 1:
+ return
+ elif self.toons.count(toonId) == 0:
+ self.air.writeServerEvent('suspicious', toonId, 'CogdoInteriorAI.elevatorDone: toon not in participant list')
+ self.notify.warning('elevatorDone() - toon not in toon list: %d' % toonId)
+ return
+
+ if self.fsm.getCurrentState().getName() != 'Elevator':
+ self.air.writeServerEvent('suspicious', toonId, 'CogdoInteriorAI.elevatorDone: not in Elevator state')
+ return
+
+ if toonId not in self.responses:
+ self.air.writeServerEvent('suspicious', toonId, 'CogdoInteriorAI.elevatorDone: toon not in responses')
+ self.notify.warning('CogdoInteriorAI.elevatorDone: avId not in responses')
+ return
+
+ self.responses[toonId] += 1
+ if self.__allToonsResponded() and self.ignoreElevatorDone == 0:
+ self.b_setState('Game')
+
+ def reserveJoinDone(self):
+ toonId = self.air.getAvatarIdFromSender()
+ if self.ignoreResponses == 1:
+ return
+ elif self.toons.count(toonId) == 0:
+ self.air.writeServerEvent('suspicious', toonId, 'CogdoInteriorAI.reserveJoinDone: toon not in participant list')
+ self.notify.warning('reserveJoinDone() - toon not in list: %d' % toonId)
+ return
+
+ if self.fsm.getCurrentState().getName() != 'ReservesJoining':
+ self.air.writeServerEvent('suspicious', toonId, 'CogdoInteriorAI.reserveJoinDone: not in ReservesJoining state')
+ return
+
+ if toonId not in self.responses:
+ self.air.writeServerEvent('suspicious', toonId, 'CogdoInteriorAI.reserveJoinDone: toon not in responses')
+ self.notify.warning('CogdoInteriorAI.reserveJoinDone: avId not in responses')
+ return
+
+ self.responses[toonId] += 1
+ if self.__allToonsResponded() and self.ignoreReserveJoinDone == 0:
+ self.b_setState('Battle')
+
+ def isBossFloor(self, floorNum):
+ if self.layout.hasBossBattle():
+ if self.layout.getBossBattleFloor() == floorNum:
+ return True
+
+ return False
+
+ def isTopFloor(self, floorNum):
+ return self.layout.getNumFloors() - 1 == floorNum
+
+ def enterOff(self):
+ return None
+
+ def exitOff(self):
+ return None
+
+ def enterWaitForAllToonsInside(self):
+ self.__resetResponses()
+ return None
+
+ def exitWaitForAllToonsInside(self):
+ self.__resetResponses()
+ return None
+
+ def enterElevator(self):
+ if self.isBossFloor(self.currentFloor):
+ self._populateFloorSuits()
+ else:
+ self.d_setToons()
+ self.__resetResponses()
+ if self._wantBarrelRoom:
+ if self.barrelRoom:
+ self.barrelRoom.reset()
+ else:
+ self.barrelRoom = CogdoBarrelRoomAI.CogdoBarrelRoomAI(self)
+
+ self._game = self._createGame()
+ if self._game:
+ self._game.makeVisible()
+
+ self.d_setState('Elevator')
+ self.timer.startCallback(BattleBase.ELEVATOR_T + ElevatorData[ELEVATOR_NORMAL]['openTime'] + BattleBase.SERVER_BUFFER_TIME, self.__serverElevatorDone)
+ return None
+
+ def _createGame(self):
+ game = None
+ if not self.isBossFloor(self.currentFloor):
+ for toonId in self.toonIds:
+ if toonId:
+ toon = self.air.getDo(toonId)
+ if toon:
+ self.accept(toon.getGoneSadMessage(), Functor(self._handleToonWentSad, toonId))
+
+ forcedGame = simbase.forcedCogdoGame
+ diff = None
+ pgId = None
+ for toonId in self.toonIds:
+ if toonId:
+ if toonId in GameRequests:
+ (forcedGame, keep, diff, pgId) = GameRequests[toonId]
+ if not keep:
+ GameRequests.pop(toonId)
+
+ if forcedGame:
+ gameCtor = CogdoGames[forcedGame]
+ else:
+ name = None
+ while name is None or name in IntGames:
+ name = random.choice(CogdoGames.keys())
+ gameCtor = CogdoGames[name]
+ game = gameCtor(self.air, self)
+ game.setExteriorZone(self.extZoneId)
+ game.setDifficultyOverrides(diff, pgId)
+ game.generateWithRequired(self.zoneId)
+
+ return game
+
+ def __serverElevatorDone(self):
+ self.ignoreElevatorDone = 1
+ self.b_setState('Game')
+
+ def exitElevator(self):
+ self.timer.stop()
+ self.__resetResponses()
+ return None
+
+ def enterGame(self):
+ self.notify.info('Entering Maze Game')
+ self._gameScore = None
+ self._rewardedLaff = {}
+ self._penaltyLaff = {}
+ self.d_setState('Game')
+ self.elevator.d_setFloor(self.currentFloor)
+ if self._game:
+ self._game.start()
+ else:
+ self._gameDone()
+
+ def _populateFloorSuits(self):
+ suitHandles = self.bldg.planner.genFloorSuits(self.currentFloor)
+ self.suits = suitHandles['activeSuits']
+ self.activeSuits = []
+ for suit in self.suits:
+ self.activeSuits.append(suit)
+
+ self.reserveSuits = suitHandles['reserveSuits']
+ self.d_setToons()
+ self.d_setSuits()
+
+ def _setGameScore(self, score):
+ self._gameScore = score
+
+ def getGameScore(self):
+ return self._gameScore
+
+ def _gameDone(self):
+ self.__calcLaff()
+ for (toonId, reward) in self._rewardedLaff.iteritems():
+ if reward:
+ av = self.air.doId2do.get(toonId)
+ av.toonUp(reward)
+
+ for (toonId, penalty) in self._penaltyLaff.iteritems():
+ if penalty:
+ av = self.air.doId2do.get(toonId)
+ if config.GetBool('want-cogdo-maze-no-sad', 1):
+ avHp = av.getHp()
+ if avHp < 1:
+ avHp = 1
+
+ if penalty >= avHp:
+ penalty = avHp - 1
+
+ if penalty:
+ av.takeDamage(penalty, quietly = 0)
+
+ if self._game and self._game.wasEnded():
+ self.b_setState('Resting')
+ elif self._game and self._game.isDoorOpen():
+ self.b_setState('Resting')
+ elif self._game and not self._game.isDoorOpen():
+ self.b_setState('Failed')
+ else:
+ self.b_setState('BattleIntro')
+
+ def exitGame(self):
+ self.sendUpdate('setSOSNpcId', [
+ self.SOSCard])
+ self.sendUpdate('setFOType', [
+ ord(self.bldg.track)])
+
+ def __calcLaff(self):
+ self.notify.debug('__calcLaff()')
+ self._rewardedLaff = {}
+ self._penaltyLaff = {}
+ if self._game:
+ score = self.getGameScore()
+ if score:
+ maxToAward = self._game.getDifficulty() * CogdoGameConsts.LaffRewardRange + CogdoGameConsts.LaffRewardMin
+ reward = score * maxToAward
+ for toonId in self.toons:
+ if self._game.isToonInDoor(toonId):
+ self._rewardedLaff[toonId] = reward
+
+ if self._rewardedLaff:
+ self.air.writeServerEvent('CogdoLaffReward', self._rewardedLaff.keys(), 'Awarded %s Laff for difficulty %s and score %s' % (reward, self._game.getDifficulty(), score))
+
+ penalty = self._game.getDifficulty() * CogdoGameConsts.LaffPenalty
+ for toonId in self.toons:
+ if not self._game.isToonInDoor(toonId):
+ self._penaltyLaff[toonId] = penalty
+
+ if self._penaltyLaff:
+ self.air.writeServerEvent('CogdoLaffPenalty', self._penaltyLaff.keys(), 'Penalized %s Laff for difficulty %s (did not reach exit)' % (penalty, self._game.getDifficulty()))
+
+ def toonBarrelRoomIntroDone(self):
+ avId = self.air.getAvatarIdFromSender()
+ if self.fsm.getCurrentState().getName() != 'BarrelRoomIntro':
+ self.air.writeServerEvent('suspicious', avId, 'CogdoInteriorAI.toonBarrelRoomIntroDone: not in BarrelRoomIntro state')
+ return
+
+ if avId not in self.responses:
+ self.air.writeServerEvent('suspicious', avId, 'CogdoInteriorAI.toonBarrelRoomIntroDone: unknown avId')
+ return
+
+ if hasattr(self, 'brBarrier'):
+ self.brBarrier.clear(avId)
+ else:
+ self.notify.warning('toonBarrelRoomIntroDone from %s in invalid state' % avId)
+
+ def __brIntroDone(self, clearedAvIds = []):
+ self.b_setState('CollectBarrels')
+
+ def enterBarrelRoomIntro(self):
+ if not self._wantBarrelRoom:
+ pass
+ if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
+ self.barrelRoom.setScore(1.0)
+ self.brBarrier = ToonBarrier.ToonBarrier('waitBrIntroDone', self.uniqueName('waitBrIntroDone'), self.toons, CogdoBarrelRoomConsts.BarrelRoomIntroTimeout, doneFunc = self.__brIntroDone)
+ else:
+ self.__brIntroDone()
+
+ def exitBarrelRoomIntro(self):
+ if hasattr(self, 'brBarrier'):
+ self.brBarrier.cleanup()
+ del self.brBarrier
+
+ def __endCollectBarrels(self):
+ if len(self.toons) > 0:
+ self.notify.info('%d toons in barrel room.' % len(self.toons))
+ self.b_setState('BarrelRoomReward')
+ else:
+ self.notify.warning('0 toons in barrel room.')
+ self.bldg.deleteCogdoInterior()
+
+ def toonLeftBarrelRoom(self):
+ avId = self.air.getAvatarIdFromSender()
+ self.notify.info('avId=%s left the barrel room via teleport' % avId)
+ if self.fsm.getCurrentState().getName() != 'CollectBarrels':
+ self.air.writeServerEvent('suspicious', avId, 'CogdoInteriorAI.toonLeftBarrelRoom called outside of CollectBarrels state')
+ return
+
+ if avId not in self.responses:
+ self.air.writeServerEvent('suspicious', avId, 'CogdoInteriorAI.toonLeftBarrelRoom: unknown avId')
+ return
+
+ self.__removeToon(avId)
+ self.notify.info('%d toon(s) remaining in barrel room.' % len(self.toons))
+ if len(self.toons) == 0:
+ self.notify.warning('0 toons in barrel room.')
+ self.bldg.deleteCogdoInterior()
+
+ def enterCollectBarrels(self):
+ if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
+ self.acceptOnce(self.barrelRoom.collectionDoneEvent, self.__endCollectBarrels)
+ self.barrelRoom.activate()
+ else:
+ self.__endCollectBarrels()
+
+ def exitCollectBarrels(self):
+ if self.barrelRoom:
+ self.ignore(self.barrelRoom.collectionDoneEvent)
+ self.barrelRoom.deactivate()
+
+ def toonBarrelRoomRewardDone(self):
+ avId = self.air.getAvatarIdFromSender()
+ if self.fsm.getCurrentState().getName() != 'BarrelRoomReward':
+ self.air.writeServerEvent('suspicious', avId, 'CogdoInteriorAI.toonBarrelRoomIntroDone: not in BarrelRoomReward state')
+ return
+
+ if avId not in self.responses:
+ self.air.writeServerEvent('suspicious', avId, 'CogdoInteriorAI.toonBarrelRoomRewardDone: unknown avId')
+ return
+
+ if hasattr(self, 'brBarrier'):
+ self.brBarrier.clear(avId)
+ else:
+ self.notify.warning('toonBarrelRoomRewardDone from %s in invalid state' % avId)
+
+ def __brRewardDone(self, clearedAvIds = []):
+ if len(self.toons) > 0:
+ if not self.isBossFloor(self.currentFloor):
+ self._populateFloorSuits()
+ self.b_setState('Battle')
+ else:
+ self.b_setState('BattleIntro')
+ else:
+ self.notify.warning('0 toons in barrel room.')
+ self.bldg.deleteCogdoInterior()
+
+ def enterBarrelRoomReward(self):
+ if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
+ self.sendUpdate('setBarrelRoomReward', self.barrelRoom.results)
+ self.brBarrier = ToonBarrier.ToonBarrier('waitBrRewardDone', self.uniqueName('waitBrRewardDone'), self.toons, CogdoBarrelRoomConsts.RewardUiTime + 5.0, doneFunc = self.__brRewardDone)
+ else:
+ self.__brRewardDone()
+
+ def exitBarrelRoomReward(self):
+ if hasattr(self, 'brBarrier'):
+ self.brBarrier.cleanup()
+ del self.brBarrier
+
+ def __createFloorBattle(self):
+ if self.isBossFloor(self.currentFloor):
+ self.notify.info('%d toon(s) in boss battle' % len(self.toons))
+ bossBattle = 1
+ else:
+ self.notify.info('%d toon(s) in barrel battle' % len(self.toons))
+ bossBattle = 0
+ self.battle = DistributedCogdoBattleBldgAI.DistributedCogdoBattleBldgAI(self.air, self.zoneId, self.__handleRoundDone, self.__handleBattleDone, bossBattle = bossBattle)
+ self.battle.suitsKilled = self.suitsKilled
+ self.battle.suitsKilledPerFloor = self.suitsKilledPerFloor
+ self.battle.battleCalc.toonSkillPtsGained = self.toonSkillPtsGained
+ self.battle.toonExp = self.toonExp
+ self.battle.toonOrigQuests = self.toonOrigQuests
+ self.battle.toonItems = self.toonItems
+ self.battle.toonOrigMerits = self.toonOrigMerits
+ self.battle.toonMerits = self.toonMerits
+ self.battle.toonParts = self.toonParts
+ self.battle.helpfulToons = self.helpfulToons
+ self.battle.setInitialMembers(self.toons, self.suits)
+ self.battle.generateWithRequired(self.zoneId)
+ mult = getCreditMultiplier(self.currentFloor)
+ if self.air.suitInvasionManager.getInvading():
+ mult *= getInvasionMultiplier()
+
+ self.battle.battleCalc.setSkillCreditMultiplier(mult)
+
+ def __cleanupFloorBattle(self):
+ for suit in self.suits:
+ self.notify.debug('cleaning up floor suit: %d' % suit.doId)
+ if suit.isDeleted():
+ self.notify.debug('whoops, suit %d is deleted.' % suit.doId)
+ else:
+ suit.requestDelete()
+
+ self.suits = []
+ self.reserveSuits = []
+ self.activeSuits = []
+ if self.battle != None:
+ self.battle.requestDelete()
+
+ self.battle = None
+
+ def __handleRoundDone(self, toonIds, totalHp, deadSuits):
+ totalMaxHp = 0
+ for suit in self.suits:
+ totalMaxHp += suit.maxHP
+
+ for suit in deadSuits:
+ self.activeSuits.remove(suit)
+
+ if len(self.reserveSuits) > 0 and len(self.activeSuits) < 4:
+ self.joinedReserves = []
+ hpPercent = 100 - (totalHp / totalMaxHp) * 100.0
+ for info in self.reserveSuits:
+ if info[1] <= hpPercent and len(self.activeSuits) < 4:
+ self.suits.append(info[0])
+ self.activeSuits.append(info[0])
+ self.joinedReserves.append(info)
+
+ for info in self.joinedReserves:
+ self.reserveSuits.remove(info)
+
+ if len(self.joinedReserves) > 0:
+ self.fsm.request('ReservesJoining')
+ self.d_setSuits()
+ return
+
+ if len(self.activeSuits) == 0:
+ self.fsm.request('BattleDone', [
+ toonIds])
+ else:
+ self.battle.resume()
+
+ def __handleBattleDone(self, zoneId, toonIds):
+ if len(toonIds) == 0:
+ self.notify.info('handleBattleDone() - last toon gone')
+ taskName = self.taskName('deleteInterior')
+ taskMgr.doMethodLater(10, self.__doDeleteInterior, taskName)
+ elif self.isTopFloor(self.currentFloor):
+ self.setState('Reward')
+ else:
+ self.b_setState('Resting')
+
+ def __doDeleteInterior(self, task):
+ self.bldg.deleteCogdoInterior()
+
+ def enterBattleIntro(self):
+ timeLeft = 7.0
+ self._battleIntroTaskName = self.taskName('BattleIntro')
+ taskMgr.doMethodLater(timeLeft, self._battleIntroDone, self._battleIntroTaskName)
+
+ def _battleIntroDone(self, task):
+ self.b_setState('Battle')
+ return task.done
+
+ def exitBattleIntro(self):
+ taskMgr.remove(self._battleIntroTaskName)
+ self._battleIntroTaskName = None
+
+ def enterBattle(self):
+ if self.battle == None:
+ self.__createFloorBattle()
+
+ def exitBattle(self):
+ return None
+
+ def enterReservesJoining(self):
+ self.__resetResponses()
+ self.timer.startCallback(ElevatorData[ELEVATOR_NORMAL]['openTime'] + SUIT_HOLD_ELEVATOR_TIME + BattleBase.SERVER_BUFFER_TIME, self.__serverReserveJoinDone)
+ return None
+
+ def __serverReserveJoinDone(self):
+ self.ignoreReserveJoinDone = 1
+ self.b_setState('Battle')
+
+ def exitReservesJoining(self):
+ self.timer.stop()
+ self.__resetResponses()
+ for info in self.joinedReserves:
+ self.battle.suitRequestJoin(info[0])
+
+ self.battle.resume()
+ self.joinedReserves = []
+ return None
+
+ def enterBattleDone(self, toonIds):
+ if len(toonIds) != len(self.toons):
+ deadToons = []
+ for toon in self.toons:
+ if toonIds.count(toon) == 0:
+ deadToons.append(toon)
+
+ for toon in deadToons:
+ self.__removeToon(toon)
+
+ self.d_setToons()
+ if len(self.toons) == 0:
+ self.bldg.deleteCogdoInterior()
+ elif self.isTopFloor(self.currentFloor):
+ self.notify.info('%d Toon(s) beat the executive suite building' % len(self.toons))
+ self.battle.resume(self.currentFloor, topFloor = 1)
+ else:
+ self.battle.resume(self.currentFloor, topFloor = 0)
+ return None
+
+ def exitBattleDone(self):
+ self.__cleanupFloorBattle()
+ self.d_setSuits()
+ return None
+
+ def __handleEnterElevator(self):
+ self.fsm.request('Elevator')
+
+ def enterResting(self):
+ if self._game:
+ self._game.requestDelete()
+ self._game = None
+ for toonId in self.toonIds:
+ if toonId:
+ toon = self.air.getDo(toonId)
+ if toon:
+ self.ignore(toon.getGoneSadMessage())
+
+ self.handleAllAboard()
+
+ def handleAllAboard(self, seats = None):
+ if seats == None:
+ seats = copy.copy(self.toonIds)
+
+ if not hasattr(self, 'fsm'):
+ return None
+
+ numOfEmptySeats = seats.count(None)
+ if numOfEmptySeats == 4:
+ self.bldg.deleteCogdoInterior()
+ return None
+ elif numOfEmptySeats >= 0 and numOfEmptySeats <= 3:
+ pass
+ else:
+ self.error('Bad number of empty seats: %s' % numOfEmptySeats)
+ for toon in self.toons:
+ if seats.count(toon) == 0:
+ self.__removeToon(toon)
+
+ self.toonIds = copy.copy(seats)
+ self.toons = []
+ for toonId in self.toonIds:
+ if toonId != None:
+ self.toons.append(toonId)
+
+ self.d_setToons()
+ if not self._CogdoGameRepeat:
+ self.currentFloor += 1
+
+ self.fsm.request('Elevator')
+
+ def exitResting(self):
+ return None
+
+ def enterReward(self):
+ victors = self.toonIds[:]
+ savedBy = []
+ for v in victors:
+ tuple = self.savedByMap.get(v)
+ if tuple:
+ savedBy.append([
+ v,
+ tuple[0],
+ tuple[1]])
+ toon = self.air.doId2do.get(v)
+ if toon:
+ self.notify.info('Reward State: toonId:%d laff:%d/%d get ready for the victors to come outside' % (toon.doId, toon.hp, toon.maxHp))
+ if not toon.attemptAddNPCFriend(self.SOSCard, numCalls = 1):
+ self.notify.info('%s.unable to add NPCFriend %s to %s.' % (self.doId, self.SOSCard, v))
+
+ self.bldg.fsm.request('waitForVictorsFromCogdo', [
+ victors,
+ savedBy])
+ self.d_setState('Reward')
+ return None
+
+ def exitReward(self):
+ return None
+
+ def enterFailed(self):
+ victors = self.toonIds[:]
+ savedBy = []
+ anyVictors = victors.count(None) < 4
+ if anyVictors:
+ self.bldg.fsm.request('waitForVictorsFromCogdo', [
+ victors,
+ savedBy])
+
+ def exitFailed(self):
+ return None
+
+ def chooseSOSCard(self, difficulty):
+ if difficulty < 0 or difficulty > 5:
+ return None
+
+ if difficulty <= 1:
+ card = random.choice(NPCToons.npcFriendsMinMaxStars(0, 1))
+ elif difficulty <= 3:
+ card = random.choice(NPCToons.npcFriendsMinMaxStars(1, 1))
+ else:
+ card = random.choice(NPCToons.npcFriendsMinMaxStars(2, 2))
+ return card
diff --git a/toontown/cogdominium/SuitPlannerCogdoInteriorAI.py b/toontown/cogdominium/SuitPlannerCogdoInteriorAI.py
new file mode 100644
index 0000000..b9ae13d
--- /dev/null
+++ b/toontown/cogdominium/SuitPlannerCogdoInteriorAI.py
@@ -0,0 +1,10 @@
+from toontown.building.SuitPlannerInteriorAI import SuitPlannerInteriorAI
+
+class SuitPlannerCogdoInteriorAI(SuitPlannerInteriorAI):
+
+ def __init__(self, cogdoLayout, bldgLevel, bldgTrack, zone):
+ self._cogdoLayout = cogdoLayout
+ SuitPlannerInteriorAI.__init__(self, self._cogdoLayout.getNumGameFloors(), bldgLevel, bldgTrack, zone, respectInvasions = 0)
+
+ def _genSuitInfos(self, numFloors, bldgLevel, bldgTrack):
+ SuitPlannerInteriorAI._genSuitInfos(self, self._cogdoLayout.getNumFloors(), bldgLevel, bldgTrack)
diff --git a/toontown/cogdominium/__init__.py b/toontown/cogdominium/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/toontown/coghq/ActiveCell.py b/toontown/coghq/ActiveCell.py
new file mode 100644
index 0000000..4462762
--- /dev/null
+++ b/toontown/coghq/ActiveCell.py
@@ -0,0 +1,42 @@
+from pandac.PandaModules import *
+from otp.level import BasicEntities
+from direct.directnotify import DirectNotifyGlobal
+
+class ActiveCell(BasicEntities.DistributedNodePathEntity):
+ notify = DirectNotifyGlobal.directNotify.newCategory('ActiveCell')
+
+ def __init__(self, cr):
+ BasicEntities.DistributedNodePathEntity.__init__(self, cr)
+ self.occupantId = -1
+ self.state = 0
+
+ def announceGenerate(self):
+ BasicEntities.DistributedNodePathEntity.announceGenerate(self)
+ self.loadModel()
+
+ def loadModel(self):
+ if 0 and __debug__:
+ grid = self.level.entities.get(self.gridId, None)
+ if grid:
+ pos = grid.getPos() + Vec3(self.col * grid.cellSize, self.row * grid.cellSize, 0)
+ model = loader.loadModel('phase_5/models/modules/suit_walls.bam')
+ model.setScale(grid.cellSize, 1, grid.cellSize)
+ model.setP(-90)
+ model.flattenMedium()
+ model.setZ(0.05)
+ model.setColorScale(1, 0, 0, 0.5)
+ model.copyTo(self)
+ self.setPos(pos)
+ return
+
+ def setState(self, state, objId):
+ self.state = state
+ self.occupantId = objId
+
+ if __dev__:
+
+ def attribChanged(self, *args):
+ model = self.find('*')
+ if not model.isEmpty():
+ model.removeNode()
+ self.loadModel()
diff --git a/toontown/coghq/ActiveCellAI.py b/toontown/coghq/ActiveCellAI.py
new file mode 100644
index 0000000..ef3fe27
--- /dev/null
+++ b/toontown/coghq/ActiveCellAI.py
@@ -0,0 +1,55 @@
+from otp.level import DistributedEntityAI
+from direct.directnotify import DirectNotifyGlobal
+
+class ActiveCellAI(DistributedEntityAI.DistributedEntityAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('ActiveCellAI')
+
+ def __init__(self, level, entId):
+ self.state = 0
+ self.grid = None
+ self.occupantIds = []
+ DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId)
+
+ def setGrid(gridId=self.gridId, self=self):
+ self.grid = self.level.entities.get(gridId, None)
+ if self.grid:
+ self.grid.addActiveCell(self)
+ return 1
+ return 0
+
+ if not setGrid():
+ self.accept(self.level.getEntityCreateEvent(self.gridId), setGrid)
+ return
+
+ def generate(self):
+ DistributedEntityAI.DistributedEntityAI.generate(self)
+
+ def delete(self):
+ self.notify.debug('delete')
+ self.ignoreAll()
+ DistributedEntityAI.DistributedEntityAI.delete(self)
+
+ def getState(self):
+ return self.state
+
+ def b_setState(self, state, objId=None):
+ self.setState(state, objId)
+ self.d_setState(state, objId)
+
+ def d_setState(self, state, objId=None):
+ if not objId:
+ objId = 0
+ self.sendUpdate('setState', [state, objId])
+
+ def setState(self, state, objId=None):
+ self.state = state
+ if state:
+ self.occupantIds.append(objId)
+ else:
+ try:
+ self.occupantIds.remove(objId)
+ except:
+ self.notify.warning("couldn't remove %s from active cell" % objId)
+
+ def getRowCol(self):
+ return [self.row, self.col]
diff --git a/toontown/coghq/BanquetTableBase.py b/toontown/coghq/BanquetTableBase.py
new file mode 100644
index 0000000..0659460
--- /dev/null
+++ b/toontown/coghq/BanquetTableBase.py
@@ -0,0 +1,9 @@
+
+
+class BanquetTableBase:
+ HUNGRY = 1
+ DEAD = 0
+ EATING = 2
+ ANGRY = 3
+ HIDDEN = 4
+ INACTIVE = 5
diff --git a/toontown/coghq/BarrelBase.py b/toontown/coghq/BarrelBase.py
new file mode 100644
index 0000000..3ac803c
--- /dev/null
+++ b/toontown/coghq/BarrelBase.py
@@ -0,0 +1,58 @@
+import random
+
+class BarrelBase:
+
+ def getRng(self):
+ return random.Random(self.entId * self.level.doId)
+
+ def getRewardPerGrab(self):
+ if not hasattr(self, '_reward'):
+ if self.rewardPerGrabMax > self.rewardPerGrab:
+ self._reward = self.getRng().randrange(self.rewardPerGrab, self.rewardPerGrabMax + 1)
+ else:
+ self._reward = self.rewardPerGrab
+ return self._reward
+
+ def getGagLevel(self):
+ if not hasattr(self, '_gagLevel'):
+ if self.gagLevelMax > self.gagLevel:
+ self._gagLevel = self.getRng().randrange(self.gagLevel, self.gagLevelMax + 1)
+ else:
+ self._gagLevel = self.gagLevel
+ return self._gagLevel
+
+ def getGagTrack(self):
+ if not hasattr(self, '_gagTrack'):
+ if self.gagTrack == 'random':
+ tracks = (0, 1, 2, 3, 4, 4, 5, 5, 6)
+ self._gagTrack = self.getRng().choice(tracks)
+ else:
+ self._gagTrack = self.gagTrack
+ return self._gagTrack
+
+ if __dev__:
+
+ def setRewardPerGrab(self, rewardPerGrab):
+ if hasattr(self, '_reward'):
+ del self._reward
+ self.rewardPerGrab = rewardPerGrab
+
+ def setRewardPerGrabMax(self, rewardPerGrabMax):
+ if hasattr(self, '_reward'):
+ del self._reward
+ self.rewardPerGrabMax = rewardPerGrabMax
+
+ def setGagLevel(self, gagLevel):
+ if hasattr(self, '_gagLevel'):
+ del self._gagLevel
+ self.gagLevel = gagLevel
+
+ def setGagLevelMax(self, gagLevelMax):
+ if hasattr(self, '_gagLevel'):
+ del self._gagLevel
+ self.gagLevelMax = gagLevelMax
+
+ def setGagTrack(self, gagTrack):
+ if hasattr(self, '_gagTrack'):
+ del self._gagTrack
+ self.gagTrack = gagTrack
diff --git a/toontown/coghq/BattleBlocker.py b/toontown/coghq/BattleBlocker.py
new file mode 100644
index 0000000..ae333c6
--- /dev/null
+++ b/toontown/coghq/BattleBlocker.py
@@ -0,0 +1,95 @@
+from pandac.PandaModules import *
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from otp.level import BasicEntities
+from toontown.toonbase import ToontownGlobals
+from direct.directnotify import DirectNotifyGlobal
+
+class BattleBlocker(BasicEntities.DistributedNodePathEntity):
+ notify = DirectNotifyGlobal.directNotify.newCategory('BattleBlocker')
+
+ def __init__(self, cr):
+ BasicEntities.DistributedNodePathEntity.__init__(self, cr)
+ self.suitIds = []
+ self.battleId = None
+ return
+
+ def setActive(self, active):
+ self.active = active
+
+ def announceGenerate(self):
+ BasicEntities.DistributedNodePathEntity.announceGenerate(self)
+ self.initCollisionGeom()
+
+ def disable(self):
+ self.ignoreAll()
+ self.unloadCollisionGeom()
+ BasicEntities.DistributedNodePathEntity.disable(self)
+
+ def destroy(self):
+ BasicEntities.DistributedNodePathEntity.destroy(self)
+
+ def setSuits(self, suitIds):
+ self.suitIds = suitIds
+
+ def setBattle(self, battleId):
+ self.battleId = battleId
+
+ def setBattleFinished(self):
+ self.ignoreAll()
+
+ def initCollisionGeom(self):
+ self.cSphere = CollisionSphere(0, 0, 0, self.radius)
+ self.cSphereNode = CollisionNode('battleBlocker-%s-%s' % (self.level.getLevelId(), self.entId))
+ self.cSphereNode.addSolid(self.cSphere)
+ self.cSphereNodePath = self.attachNewNode(self.cSphereNode)
+ self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask)
+ self.cSphere.setTangible(0)
+ self.enterEvent = 'enter' + self.cSphereNode.getName()
+ self.accept(self.enterEvent, self.__handleToonEnter)
+
+ def unloadCollisionGeom(self):
+ if hasattr(self, 'cSphereNodePath'):
+ self.ignore(self.enterEvent)
+ del self.cSphere
+ del self.cSphereNode
+ self.cSphereNodePath.removeNode()
+ del self.cSphereNodePath
+
+ def __handleToonEnter(self, collEntry):
+ self.notify.debug('__handleToonEnter, %s' % self.entId)
+ self.startBattle()
+
+ def startBattle(self):
+ if not self.active:
+ return
+ callback = None
+ if self.battleId != None and self.battleId in base.cr.doId2do:
+ battle = base.cr.doId2do.get(self.battleId)
+ if battle:
+ self.notify.debug('act like we collided with battle %d' % self.battleId)
+ callback = battle.handleBattleBlockerCollision
+ elif len(self.suitIds) > 0:
+ for suitId in self.suitIds:
+ suit = base.cr.doId2do.get(suitId)
+ if suit:
+ self.notify.debug('act like we collided with Suit %d ( in state %s )' % (suitId, suit.fsm.getCurrentState().getName()))
+ callback = suit.handleBattleBlockerCollision
+ break
+
+ self.showReaction(callback)
+ return
+
+ def showReaction(self, callback = None):
+ if not base.localAvatar.wantBattles:
+ return
+ track = Sequence()
+ if callback:
+ track.append(Func(callback))
+ track.start()
+
+ if __dev__:
+
+ def attribChanged(self, *args):
+ self.unloadCollisionGeom()
+ self.initCollisionGeom()
diff --git a/toontown/coghq/BattleBlockerAI.py b/toontown/coghq/BattleBlockerAI.py
new file mode 100644
index 0000000..027e616
--- /dev/null
+++ b/toontown/coghq/BattleBlockerAI.py
@@ -0,0 +1,80 @@
+from otp.level import DistributedEntityAI
+from direct.directnotify import DirectNotifyGlobal
+
+class BattleBlockerAI(DistributedEntityAI.DistributedEntityAI):
+ notify = DirectNotifyGlobal.directNotify.newCategory('BattleBlockerAI')
+
+ def __init__(self, level, entId):
+ DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId)
+ self.suitIds = []
+ self.active = 1
+
+ def destroy(self):
+ self.notify.debug('delete')
+ self.ignoreAll()
+ DistributedEntityAI.DistributedEntityAI.destroy(self)
+
+ def generate(self):
+ DistributedEntityAI.DistributedEntityAI.generate(self)
+ self.accept('plannerCreated-' + str(self.level.doId), self.registerBlocker)
+
+ def registerBlocker(self):
+ if hasattr(self.level, 'planner'):
+ self.level.planner.battleMgr.addBattleBlocker(self, self.cellId)
+
+ def deactivate(self):
+ if self.isDeleted():
+ return
+ self.active = 0
+ self.sendUpdate('setActive', [self.active])
+
+ def getActive(self):
+ return self.active
+
+ def addSuit(self, suit):
+ self.suitIds.append(suit.doId)
+ self.d_setSuits()
+
+ def removeSuit(self, suit):
+ try:
+ self.suitIds.remove(suit.doId)
+ self.d_setSuits()
+ except:
+ self.notify.debug("didn't have suitId %d" % suit.doId)
+
+ def d_setSuits(self):
+ self.sendUpdate('setSuits', [self.suitIds])
+
+ def b_setBattle(self, battleId):
+ self.battle = battleId
+ self.d_setBattle(battleId)
+
+ def d_setBattle(self, battleId):
+ self.sendUpdate('setBattle', [battleId])
+
+ def b_setBattleFinished(self):
+ self.deactivate()
+ self.setBattleFinished()
+ self.d_setBattleFinished()
+
+ def setBattleFinished(self):
+ self.notify.debug('setBattleFinished: %s' % self.entId)
+ messenger.send('battleBlockerFinished-' + str(self.entId))
+ messenger.send(self.getOutputEventName(), [1])
+
+ def d_setBattleFinished(self):
+ self.sendUpdate('setBattleFinished', [])
+
+ if __dev__:
+
+ def attribChanged(self, *args):
+ self.suitIds = []
+ suits = self.level.planner.battleCellId2suits.get(self.cellId)
+ if suits:
+ for suit in suits:
+ self.suitIds.append(suit.doId)
+
+ else:
+ self.notify.warning("Couldn't find battle cell id %d in battleCellId2suits" % self.cellId)
+ self.d_setSuits()
+ self.registerBlocker()
diff --git a/toontown/coghq/BattleExperienceAggregatorAI.py b/toontown/coghq/BattleExperienceAggregatorAI.py
new file mode 100644
index 0000000..bdbacae
--- /dev/null
+++ b/toontown/coghq/BattleExperienceAggregatorAI.py
@@ -0,0 +1,27 @@
+
+
+class BattleExperienceAggregatorAI:
+
+ def __init__(self):
+ self.suitsKilled = []
+ self.suitsKilledPerFloor = []
+ self.toonSkillPtsGained = {}
+ self.toonExp = {}
+ self.toonOrigQuests = {}
+ self.toonItems = {}
+ self.toonOrigMerits = {}
+ self.toonMerits = {}
+ self.toonParts = {}
+ self.helpfulToons = []
+
+ def attachToBattle(self, battle):
+ battle.suitsKilled = self.suitsKilled
+ battle.suitsKilledPerFloor = self.suitsKilledPerFloor
+ battle.battleCalc.toonSkillPtsGained = self.toonSkillPtsGained
+ battle.toonExp = self.toonExp
+ battle.toonOrigQuests = self.toonOrigQuests
+ battle.toonItems = self.toonItems
+ battle.toonOrigMerits = self.toonOrigMerits
+ battle.toonMerits = self.toonMerits
+ battle.toonParts = self.toonParts
+ battle.helpfulToons = self.helpfulToons
diff --git a/toontown/coghq/BossbotCogHQLoader.py b/toontown/coghq/BossbotCogHQLoader.py
new file mode 100644
index 0000000..9c6491b
--- /dev/null
+++ b/toontown/coghq/BossbotCogHQLoader.py
@@ -0,0 +1,135 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import StateData
+import CogHQLoader
+from toontown.toonbase import ToontownGlobals
+from direct.gui import DirectGui
+from toontown.toonbase import TTLocalizer
+from toontown.toon import Toon
+from direct.fsm import State
+from toontown.coghq import BossbotHQExterior
+from toontown.coghq import BossbotHQBossBattle
+from toontown.coghq import BossbotOfficeExterior
+from toontown.coghq import CountryClubInterior
+from pandac.PandaModules import DecalEffect, TextEncoder
+import random
+aspectSF = 0.7227
+
+class BossbotCogHQLoader(CogHQLoader.CogHQLoader):
+ notify = DirectNotifyGlobal.directNotify.newCategory('BossbotCogHQLoader')
+
+ def __init__(self, hood, parentFSMState, doneEvent):
+ CogHQLoader.CogHQLoader.__init__(self, hood, parentFSMState, doneEvent)
+ self.fsm.addState(State.State('countryClubInterior', self.enterCountryClubInterior, self.exitCountryClubInterior, ['quietZone', 'cogHQExterior']))
+ for stateName in ['start', 'cogHQExterior', 'quietZone']:
+ state = self.fsm.getStateNamed(stateName)
+ state.addTransition('countryClubInterior')
+
+ self.musicFile = random.choice(['phase_12/audio/bgm/Bossbot_Entry_v1.mid', 'phase_12/audio/bgm/Bossbot_Entry_v2.mid', 'phase_12/audio/bgm/Bossbot_Entry_v3.mid'])
+ self.cogHQExteriorModelPath = 'phase_12/models/bossbotHQ/CogGolfHub'
+ self.factoryExteriorModelPath = 'phase_11/models/lawbotHQ/LB_DA_Lobby'
+ self.cogHQLobbyModelPath = 'phase_12/models/bossbotHQ/CogGolfCourtyard'
+ self.geom = None
+ return
+
+ def load(self, zoneId):
+ CogHQLoader.CogHQLoader.load(self, zoneId)
+ Toon.loadBossbotHQAnims()
+
+ def unloadPlaceGeom(self):
+ if self.geom:
+ self.geom.removeNode()
+ self.geom = None
+ CogHQLoader.CogHQLoader.unloadPlaceGeom(self)
+ return
+
+ def loadPlaceGeom(self, zoneId):
+ self.notify.info('loadPlaceGeom: %s' % zoneId)
+ zoneId = zoneId - zoneId % 100
+ self.notify.debug('zoneId = %d ToontownGlobals.BossbotHQ=%d' % (zoneId, ToontownGlobals.BossbotHQ))
+ if zoneId == ToontownGlobals.BossbotHQ:
+ self.geom = loader.loadModel(self.cogHQExteriorModelPath)
+ gzLinkTunnel = self.geom.find('**/LinkTunnel1')
+ gzLinkTunnel.setName('linktunnel_gz_17000_DNARoot')
+ self.makeSigns()
+ top = self.geom.find('**/TunnelEntrance')
+ origin = top.find('**/tunnel_origin')
+ origin.setH(-33.33)
+ elif zoneId == ToontownGlobals.BossbotLobby:
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: COGHQ: Visit BossbotLobby')
+ self.notify.debug('cogHQLobbyModelPath = %s' % self.cogHQLobbyModelPath)
+ self.geom = loader.loadModel(self.cogHQLobbyModelPath)
+ else:
+ self.notify.warning('loadPlaceGeom: unclassified zone %s' % zoneId)
+ CogHQLoader.CogHQLoader.loadPlaceGeom(self, zoneId)
+
+ def makeSigns(self):
+
+ def makeSign(topStr, signStr, textId):
+ top = self.geom.find('**/' + topStr)
+ sign = top.find('**/' + signStr)
+ locator = top.find('**/sign_origin')
+ signText = DirectGui.OnscreenText(text=TextEncoder.upper(TTLocalizer.GlobalStreetNames[textId][-1]), font=ToontownGlobals.getSuitFont(), scale=TTLocalizer.BCHQLsignText, fg=(0, 0, 0, 1), parent=sign)
+ signText.setPosHpr(locator, 0, -0.1, -0.25, 0, 0, 0)
+ signText.setDepthWrite(0)
+
+ makeSign('Gate_2', 'Sign_6', 10700)
+ makeSign('TunnelEntrance', 'Sign_2', 1000)
+ makeSign('Gate_3', 'Sign_3', 10600)
+ makeSign('Gate_4', 'Sign_4', 10500)
+ makeSign('GateHouse', 'Sign_5', 10200)
+
+ def unload(self):
+ CogHQLoader.CogHQLoader.unload(self)
+ Toon.unloadSellbotHQAnims()
+
+ def enterStageInterior(self, requestStatus):
+ self.placeClass = StageInterior.StageInterior
+ self.stageId = requestStatus['stageId']
+ self.enterPlace(requestStatus)
+
+ def exitStageInterior(self):
+ self.exitPlace()
+ self.placeClass = None
+ return
+
+ def getExteriorPlaceClass(self):
+ self.notify.debug('getExteriorPlaceClass')
+ return BossbotHQExterior.BossbotHQExterior
+
+ def getBossPlaceClass(self):
+ self.notify.debug('getBossPlaceClass')
+ return BossbotHQBossBattle.BossbotHQBossBattle
+
+ def enterFactoryExterior(self, requestStatus):
+ self.placeClass = BossbotOfficeExterior.BossbotOfficeExterior
+ self.enterPlace(requestStatus)
+
+ def exitFactoryExterior(self):
+ taskMgr.remove('titleText')
+ self.hood.hideTitleText()
+ self.exitPlace()
+ self.placeClass = None
+ return
+
+ def enterCogHQBossBattle(self, requestStatus):
+ self.notify.debug('BossbotCogHQLoader.enterCogHQBossBattle')
+ CogHQLoader.CogHQLoader.enterCogHQBossBattle(self, requestStatus)
+ base.cr.forbidCheesyEffects(1)
+
+ def exitCogHQBossBattle(self):
+ self.notify.debug('BossbotCogHQLoader.exitCogHQBossBattle')
+ CogHQLoader.CogHQLoader.exitCogHQBossBattle(self)
+ base.cr.forbidCheesyEffects(0)
+
+ def enterCountryClubInterior(self, requestStatus):
+ self.placeClass = CountryClubInterior.CountryClubInterior
+ self.notify.info('enterCountryClubInterior, requestStatus=%s' % requestStatus)
+ self.countryClubId = requestStatus['countryClubId']
+ self.enterPlace(requestStatus)
+
+ def exitCountryClubInterior(self):
+ self.exitPlace()
+ self.placeClass = None
+ del self.countryClubId
+ return
diff --git a/toontown/coghq/BossbotCountryClubEntrance_Action00.py b/toontown/coghq/BossbotCountryClubEntrance_Action00.py
new file mode 100644
index 0000000..6e12640
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubEntrance_Action00.py
@@ -0,0 +1,43 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotEntranceRoom',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10000: {'type': 'entrancePoint',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 6, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1,
+ 'entranceId': 0,
+ 'radius': 15,
+ 'theta': 20},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00.py b/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00.py
new file mode 100644
index 0000000..0e3c75d
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00.py
@@ -0,0 +1,72 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotFairwayRoom_A',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': {'entType': 'door',
+ 'username': 'rurbino',
+ 'parentEntId': 110001,
+ 'entId': 110002},
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110200: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(70, 0, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'cellId': 0,
+ 'radius': 10},
+ 110202: {'type': 'door',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 110001,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1,
+ 'color': Vec4(1, 1, 1, 1),
+ 'isLock0Unlocked': 1,
+ 'isLock1Unlocked': 0,
+ 'isLock2Unlocked': 1,
+ 'isLock3Unlocked': 1,
+ 'isOpen': 0,
+ 'isOpenEvent': 0,
+ 'isVisBlocker': 0,
+ 'secondsOpen': 1,
+ 'unlock0Event': 0,
+ 'unlock1Event': 110200,
+ 'unlock2Event': 0,
+ 'unlock3Event': 0},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110001: {'type': 'nodepath',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(101.07, 0, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00_Cogs.py b/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00_Cogs.py
new file mode 100644
index 0000000..366237d
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00_Cogs.py
@@ -0,0 +1,44 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 110200
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0,
+ 'revives': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/BossbotCountryClubGreenRoom_Action00.py b/toontown/coghq/BossbotCountryClubGreenRoom_Action00.py
new file mode 100644
index 0000000..b7196a2
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubGreenRoom_Action00.py
@@ -0,0 +1,72 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotGreenRoom_A',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110301: {'type': 'door',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 110303,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1,
+ 'color': Vec4(1, 1, 1, 1),
+ 'isLock0Unlocked': 1,
+ 'isLock1Unlocked': 0,
+ 'isLock2Unlocked': 1,
+ 'isLock3Unlocked': 1,
+ 'isOpen': 0,
+ 'isOpenEvent': 0,
+ 'isVisBlocker': 0,
+ 'secondsOpen': 1,
+ 'unlock0Event': 0,
+ 'unlock1Event': 110302,
+ 'unlock2Event': 0,
+ 'unlock3Event': 0},
+ 110302: {'type': 'golfGreenGame',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1,
+ 'cellId': 0,
+ 'puzzleBase': 3,
+ 'puzzlePerPlayer': 1,
+ 'switchId': 0,
+ 'timeToPlay': 120},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110303: {'type': 'nodepath',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(40.9635, 2, 0),
+ 'hpr': Vec3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubGreenRoom_Action01.py b/toontown/coghq/BossbotCountryClubGreenRoom_Action01.py
new file mode 100644
index 0000000..ae1b8c3
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubGreenRoom_Action01.py
@@ -0,0 +1,72 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotGreenRoom_A',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110301: {'type': 'door',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 110303,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1,
+ 'color': Vec4(1, 1, 1, 1),
+ 'isLock0Unlocked': 1,
+ 'isLock1Unlocked': 0,
+ 'isLock2Unlocked': 1,
+ 'isLock3Unlocked': 1,
+ 'isOpen': 0,
+ 'isOpenEvent': 0,
+ 'isVisBlocker': 0,
+ 'secondsOpen': 1,
+ 'unlock0Event': 0,
+ 'unlock1Event': 110302,
+ 'unlock2Event': 0,
+ 'unlock3Event': 0},
+ 110302: {'type': 'golfGreenGame',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1,
+ 'puzzleBase': 3,
+ 'puzzlePerPlayer': 2,
+ 'timeToPlay': 140,
+ 'cellId': 0,
+ 'switchId': 0},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110303: {'type': 'nodepath',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(40.9635, 2, 0),
+ 'hpr': Vec3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubGreenRoom_Action02.py b/toontown/coghq/BossbotCountryClubGreenRoom_Action02.py
new file mode 100644
index 0000000..33e8581
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubGreenRoom_Action02.py
@@ -0,0 +1,72 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotGreenRoom_A',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110301: {'type': 'door',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 110303,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1,
+ 'color': Vec4(1, 1, 1, 1),
+ 'isLock0Unlocked': 1,
+ 'isLock1Unlocked': 0,
+ 'isLock2Unlocked': 1,
+ 'isLock3Unlocked': 1,
+ 'isOpen': 0,
+ 'isOpenEvent': 0,
+ 'isVisBlocker': 0,
+ 'secondsOpen': 1,
+ 'unlock0Event': 0,
+ 'unlock1Event': 110302,
+ 'unlock2Event': 0,
+ 'unlock3Event': 0},
+ 110302: {'type': 'golfGreenGame',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1,
+ 'puzzleBase': 3,
+ 'puzzlePerPlayer': 3,
+ 'timeToPlay': 180,
+ 'cellId': 0,
+ 'switchId': 0},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110303: {'type': 'nodepath',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(40.9635, 2, 0),
+ 'hpr': Vec3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubKartRoom_Battle00.py b/toontown/coghq/BossbotCountryClubKartRoom_Battle00.py
new file mode 100644
index 0000000..416b240
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubKartRoom_Battle00.py
@@ -0,0 +1,57 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotKartBoardingRm',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110400: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(4, 0, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'cellId': 0,
+ 'radius': 10},
+ 110000: {'type': 'elevatorMarker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(26.854, 0, 0),
+ 'hpr': Vec3(90, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'modelPath': 0},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110401: {'type': 'nodepath',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(101.07, 0, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubKartRoom_Battle00_Cogs.py b/toontown/coghq/BossbotCountryClubKartRoom_Battle00_Cogs.py
new file mode 100644
index 0000000..d5bb9db
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubKartRoom_Battle00_Cogs.py
@@ -0,0 +1,45 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 110400
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'revives': 1,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0,
+ 'revives': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle00.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle00.py
new file mode 100644
index 0000000..dffc77e
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle00.py
@@ -0,0 +1,77 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotMazex1_C',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110000: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-131.21, 84.92, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'cellId': 0,
+ 'radius': 10},
+ 110202: {'type': 'door',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 110001,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1,
+ 'color': Vec4(1, 1, 1, 1),
+ 'isLock0Unlocked': 1,
+ 'isLock1Unlocked': 0,
+ 'isLock2Unlocked': 1,
+ 'isLock3Unlocked': 1,
+ 'isOpen': 0,
+ 'isOpenEvent': 0,
+ 'isVisBlocker': 0,
+ 'secondsOpen': 1,
+ 'unlock0Event': 0,
+ 'unlock1Event': 110000,
+ 'unlock2Event': 0,
+ 'unlock3Event': 0},
+ 110002: {'type': 'maze',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-141.563, -78.8353, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'numSections': 1},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110001: {'type': 'nodepath',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-106.91, 82.6953, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle00_Cogs.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle00_Cogs.py
new file mode 100644
index 0000000..fd11c5d
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle00_Cogs.py
@@ -0,0 +1,44 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 110000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0,
+ 'revives': 1}]
+ReserveCogData = []
diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle01.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle01.py
new file mode 100644
index 0000000..4599fff
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle01.py
@@ -0,0 +1,77 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotMazex2_straight_C',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110000: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(40.29, 84.9249, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'cellId': 0,
+ 'radius': 10},
+ 110202: {'type': 'door',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 110001,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1,
+ 'color': Vec4(1, 1, 1, 1),
+ 'isLock0Unlocked': 1,
+ 'isLock1Unlocked': 0,
+ 'isLock2Unlocked': 1,
+ 'isLock3Unlocked': 1,
+ 'isOpen': 0,
+ 'isOpenEvent': 0,
+ 'isVisBlocker': 0,
+ 'secondsOpen': 1,
+ 'unlock0Event': 0,
+ 'unlock1Event': 110000,
+ 'unlock2Event': 0,
+ 'unlock3Event': 0},
+ 110002: {'type': 'maze',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-141.563, -78.8353, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'numSections': 2},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110001: {'type': 'nodepath',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(61.31, 82.0083, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle01_Cogs.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle01_Cogs.py
new file mode 100644
index 0000000..f9262dc
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle01_Cogs.py
@@ -0,0 +1,44 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 110000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0,
+ 'revives': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle02.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle02.py
new file mode 100644
index 0000000..c6e9092
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle02.py
@@ -0,0 +1,77 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotMazex4_C',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110000: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(42.8475, 84.9249, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'cellId': 0,
+ 'radius': 10},
+ 110202: {'type': 'door',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 110001,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1,
+ 'color': Vec4(1, 1, 1, 1),
+ 'isLock0Unlocked': 1,
+ 'isLock1Unlocked': 0,
+ 'isLock2Unlocked': 1,
+ 'isLock3Unlocked': 1,
+ 'isOpen': 0,
+ 'isOpenEvent': 0,
+ 'isVisBlocker': 0,
+ 'secondsOpen': 1,
+ 'unlock0Event': 0,
+ 'unlock1Event': 110000,
+ 'unlock2Event': 0,
+ 'unlock3Event': 0},
+ 110002: {'type': 'maze',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-141.563, -78.8353, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'numSections': 3},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110001: {'type': 'nodepath',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(60.0276, 82.0315, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle02_Cogs.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle02_Cogs.py
new file mode 100644
index 0000000..3ae0128
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle02_Cogs.py
@@ -0,0 +1,44 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 110000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0,
+ 'revives': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle03_Cogs.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle03_Cogs.py
new file mode 100644
index 0000000..a809961
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle03_Cogs.py
@@ -0,0 +1,44 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 110000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0,
+ 'revives': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00.py b/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00.py
new file mode 100644
index 0000000..6b0fd4a
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00.py
@@ -0,0 +1,49 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotPresidentsRm',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110400: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(4, 0, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'cellId': 0,
+ 'radius': 10},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110401: {'type': 'nodepath',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(101.07, 0, 0),
+ 'hpr': Point3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00_Cogs.py b/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00_Cogs.py
new file mode 100644
index 0000000..bc62540
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00_Cogs.py
@@ -0,0 +1,46 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 110400
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 1,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0,
+ 'revives': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0,
+ 'revives': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.BossbotCountryClubCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0,
+ 'revives': 1}]
+ReserveCogData = []
diff --git a/toontown/coghq/BossbotCountryClubTeeOffRoom_Action00.py b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action00.py
new file mode 100644
index 0000000..da3e405
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action00.py
@@ -0,0 +1,74 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotTeeOffRoom',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110100: {'type': 'door',
+ 'name': 'TeeOffExitDoor',
+ 'comment': '',
+ 'parentEntId': 110001,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Point3(0, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'color': Vec4(1, 1, 1, 1),
+ 'isLock0Unlocked': 1,
+ 'isLock1Unlocked': 0,
+ 'isLock2Unlocked': 1,
+ 'isLock3Unlocked': 1,
+ 'isOpen': 0,
+ 'isOpenEvent': 0,
+ 'isVisBlocker': 0,
+ 'secondsOpen': 1,
+ 'unlock0Event': 0,
+ 'unlock1Event': 110102,
+ 'unlock2Event': 0,
+ 'unlock3Event': 0},
+ 110102: {'type': 'moleField',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-38.6164, -26.2922, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'numSquaresX': 6,
+ 'numSquaresY': 6,
+ 'spacingX': 10.0,
+ 'spacingY': 10.0,
+ 'timeToPlay': 60,
+ 'molesBase': 4,
+ 'molesPerPlayer': 1},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110001: {'type': 'nodepath',
+ 'name': 'doorParent',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(60.2682, 0.55914, 0),
+ 'hpr': Vec3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubTeeOffRoom_Action01.py b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action01.py
new file mode 100644
index 0000000..485cbf9
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action01.py
@@ -0,0 +1,74 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotTeeOffRoom',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110100: {'type': 'door',
+ 'name': 'TeeOffExitDoor',
+ 'comment': '',
+ 'parentEntId': 110001,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Point3(0, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'color': Vec4(1, 1, 1, 1),
+ 'isLock0Unlocked': 1,
+ 'isLock1Unlocked': 0,
+ 'isLock2Unlocked': 1,
+ 'isLock3Unlocked': 1,
+ 'isOpen': 0,
+ 'isOpenEvent': 0,
+ 'isVisBlocker': 0,
+ 'secondsOpen': 1,
+ 'unlock0Event': 0,
+ 'unlock1Event': 110102,
+ 'unlock2Event': 0,
+ 'unlock3Event': 0},
+ 110102: {'type': 'moleField',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-38.6164, -26.2922, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'numSquaresX': 6,
+ 'numSquaresY': 6,
+ 'spacingX': 10.0,
+ 'spacingY': 10.0,
+ 'timeToPlay': 60,
+ 'molesBase': 4,
+ 'molesPerPlayer': 2},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110001: {'type': 'nodepath',
+ 'name': 'doorParent',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(60.2682, 0.55914, 0),
+ 'hpr': Vec3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotCountryClubTeeOffRoom_Action02.py b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action02.py
new file mode 100644
index 0000000..76c7b1c
--- /dev/null
+++ b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action02.py
@@ -0,0 +1,74 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_12/models/bossbotHQ/BossbotTeeOffRoom',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 110100: {'type': 'door',
+ 'name': 'TeeOffExitDoor',
+ 'comment': '',
+ 'parentEntId': 110001,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Point3(0, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'color': Vec4(1, 1, 1, 1),
+ 'isLock0Unlocked': 1,
+ 'isLock1Unlocked': 0,
+ 'isLock2Unlocked': 1,
+ 'isLock3Unlocked': 1,
+ 'isOpen': 0,
+ 'isOpenEvent': 0,
+ 'isVisBlocker': 0,
+ 'secondsOpen': 1,
+ 'unlock0Event': 0,
+ 'unlock1Event': 110102,
+ 'unlock2Event': 0,
+ 'unlock3Event': 0},
+ 110102: {'type': 'moleField',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-38.6164, -26.2922, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': Vec3(1, 1, 1),
+ 'numSquaresX': 6,
+ 'numSquaresY': 6,
+ 'spacingX': 10.0,
+ 'spacingY': 10.0,
+ 'timeToPlay': 60,
+ 'molesBase': 4,
+ 'molesPerPlayer': 3},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0, 0, 0),
+ 'hpr': Vec3(0, 0, 0),
+ 'scale': 1},
+ 110001: {'type': 'nodepath',
+ 'name': 'doorParent',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(60.2682, 0.55914, 0),
+ 'hpr': Vec3(270, 0, 0),
+ 'scale': Vec3(1, 1, 1)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/BossbotHQBossBattle.py b/toontown/coghq/BossbotHQBossBattle.py
new file mode 100644
index 0000000..17bf36f
--- /dev/null
+++ b/toontown/coghq/BossbotHQBossBattle.py
@@ -0,0 +1,34 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from toontown.suit import DistributedBossbotBoss
+from direct.directnotify import DirectNotifyGlobal
+from toontown.coghq import CogHQBossBattle
+
+class BossbotHQBossBattle(CogHQBossBattle.CogHQBossBattle):
+ notify = DirectNotifyGlobal.directNotify.newCategory('BossbotHQBossBattle')
+
+ def __init__(self, loader, parentFSM, doneEvent):
+ CogHQBossBattle.CogHQBossBattle.__init__(self, loader, parentFSM, doneEvent)
+ self.teleportInPosHpr = (88, -214, 0, 210, 0, 0)
+ for stateName in ['movie']:
+ state = self.fsm.getStateNamed(stateName)
+ state.addTransition('crane')
+
+ state = self.fsm.getStateNamed('finalBattle')
+ state.addTransition('finalBattle')
+
+ def load(self):
+ CogHQBossBattle.CogHQBossBattle.load(self)
+
+ def unload(self):
+ CogHQBossBattle.CogHQBossBattle.unload(self)
+
+ def enter(self, requestStatus):
+ CogHQBossBattle.CogHQBossBattle.enter(self, requestStatus, DistributedBossbotBoss.OneBossCog)
+
+ def exit(self):
+ CogHQBossBattle.CogHQBossBattle.exit(self)
+
+ def exitCrane(self):
+ CogHQBossBattle.CogHQBossBattle.exitCrane(self)
+ messenger.send('exitCrane')
diff --git a/toontown/coghq/BossbotHQExterior.py b/toontown/coghq/BossbotHQExterior.py
new file mode 100644
index 0000000..0c05641
--- /dev/null
+++ b/toontown/coghq/BossbotHQExterior.py
@@ -0,0 +1,58 @@
+from direct.directnotify import DirectNotifyGlobal
+from toontown.battle import BattlePlace
+from direct.fsm import ClassicFSM, State
+from direct.fsm import State
+from toontown.toonbase import ToontownGlobals
+from toontown.building import Elevator
+from pandac.PandaModules import *
+from toontown.coghq import CogHQExterior
+
+class BossbotHQExterior(CogHQExterior.CogHQExterior):
+ notify = DirectNotifyGlobal.directNotify.newCategory('BossbotHQExterior')
+
+ def __init__(self, loader, parentFSM, doneEvent):
+ CogHQExterior.CogHQExterior.__init__(self, loader, parentFSM, doneEvent)
+ self.elevatorDoneEvent = 'elevatorDone'
+ self.trains = None
+ self.fsm.addState(State.State('elevator', self.enterElevator, self.exitElevator, ['walk', 'stopped']))
+ state = self.fsm.getStateNamed('walk')
+ state.addTransition('elevator')
+ state = self.fsm.getStateNamed('stopped')
+ state.addTransition('elevator')
+ state = self.fsm.getStateNamed('stickerBook')
+ state.addTransition('elevator')
+ return
+
+ def enterElevator(self, distElevator, skipDFABoard = 0):
+ self.accept(self.elevatorDoneEvent, self.handleElevatorDone)
+ self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator)
+ if skipDFABoard:
+ self.elevator.skipDFABoard = 1
+ self.elevator.setReverseBoardingCamera(True)
+ self.elevator.load()
+ self.elevator.enter()
+
+ def exitElevator(self):
+ self.ignore(self.elevatorDoneEvent)
+ self.elevator.unload()
+ self.elevator.exit()
+ del self.elevator
+
+ def detectedElevatorCollision(self, distElevator):
+ self.fsm.request('elevator', [distElevator])
+
+ def handleElevatorDone(self, doneStatus):
+ self.notify.debug('handling elevator done event')
+ where = doneStatus['where']
+ if where == 'reject':
+ if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen():
+ pass
+ else:
+ self.fsm.request('walk')
+ elif where == 'exit':
+ self.fsm.request('walk')
+ elif where == 'countryClubInterior':
+ self.doneStatus = doneStatus
+ messenger.send(self.doneEvent)
+ else:
+ self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone')
diff --git a/toontown/coghq/BossbotOfficeExterior.py b/toontown/coghq/BossbotOfficeExterior.py
new file mode 100644
index 0000000..0f6a8a0
--- /dev/null
+++ b/toontown/coghq/BossbotOfficeExterior.py
@@ -0,0 +1,15 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import ClassicFSM
+from direct.fsm import State
+from toontown.toonbase import ToontownGlobals
+from toontown.building import Elevator
+from pandac.PandaModules import *
+import FactoryExterior
+
+class BossbotOfficeExterior(FactoryExterior.FactoryExterior):
+ notify = DirectNotifyGlobal.directNotify.newCategory('LawbotOfficeExterior')
+
+ def enterWalk(self, teleportIn = 0):
+ FactoryExterior.FactoryExterior.enterWalk(self, teleportIn)
+ self.ignore('teleportQuery')
+ base.localAvatar.setTeleportAvailable(0)
diff --git a/toontown/coghq/CashbotCogHQLoader.py b/toontown/coghq/CashbotCogHQLoader.py
new file mode 100644
index 0000000..c5fe58e
--- /dev/null
+++ b/toontown/coghq/CashbotCogHQLoader.py
@@ -0,0 +1,80 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.fsm import StateData
+import CogHQLoader, MintInterior
+from toontown.toonbase import ToontownGlobals
+from direct.gui import DirectGui
+from toontown.toonbase import TTLocalizer
+from toontown.toon import Toon
+from direct.fsm import State
+import CashbotHQExterior
+import CashbotHQBossBattle
+from pandac.PandaModules import DecalEffect
+
+class CashbotCogHQLoader(CogHQLoader.CogHQLoader):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CashbotCogHQLoader')
+
+ def __init__(self, hood, parentFSMState, doneEvent):
+ CogHQLoader.CogHQLoader.__init__(self, hood, parentFSMState, doneEvent)
+ self.fsm.addState(State.State('mintInterior', self.enterMintInterior, self.exitMintInterior, ['quietZone', 'cogHQExterior']))
+ for stateName in ['start', 'cogHQExterior', 'quietZone']:
+ state = self.fsm.getStateNamed(stateName)
+ state.addTransition('mintInterior')
+
+ self.musicFile = 'phase_9/audio/bgm/encntr_suit_HQ_nbrhood.mid'
+ self.cogHQExteriorModelPath = 'phase_10/models/cogHQ/CashBotShippingStation'
+ self.cogHQLobbyModelPath = 'phase_10/models/cogHQ/VaultLobby'
+ self.geom = None
+ return
+
+ def load(self, zoneId):
+ CogHQLoader.CogHQLoader.load(self, zoneId)
+ Toon.loadCashbotHQAnims()
+
+ def unloadPlaceGeom(self):
+ if self.geom:
+ self.geom.removeNode()
+ self.geom = None
+ CogHQLoader.CogHQLoader.unloadPlaceGeom(self)
+ return
+
+ def loadPlaceGeom(self, zoneId):
+ self.notify.info('loadPlaceGeom: %s' % zoneId)
+ zoneId = zoneId - zoneId % 100
+ if zoneId == ToontownGlobals.CashbotHQ:
+ self.geom = loader.loadModel(self.cogHQExteriorModelPath)
+ ddLinkTunnel = self.geom.find('**/LinkTunnel1')
+ ddLinkTunnel.setName('linktunnel_dl_9252_DNARoot')
+ locator = self.geom.find('**/sign_origin')
+ backgroundGeom = self.geom.find('**/EntranceFrameFront')
+ backgroundGeom.node().setEffect(DecalEffect.make())
+ signText = DirectGui.OnscreenText(text=TTLocalizer.DonaldsDreamland[-1], font=ToontownGlobals.getSuitFont(), scale=3, fg=(0.87, 0.87, 0.87, 1), mayChange=False, parent=backgroundGeom)
+ signText.setPosHpr(locator, 0, 0, 0, 0, 0, 0)
+ signText.setDepthWrite(0)
+ elif zoneId == ToontownGlobals.CashbotLobby:
+ if base.config.GetBool('want-qa-regression', 0):
+ self.notify.info('QA-REGRESSION: COGHQ: Visit CashbotLobby')
+ self.geom = loader.loadModel(self.cogHQLobbyModelPath)
+ else:
+ self.notify.warning('loadPlaceGeom: unclassified zone %s' % zoneId)
+ CogHQLoader.CogHQLoader.loadPlaceGeom(self, zoneId)
+
+ def unload(self):
+ CogHQLoader.CogHQLoader.unload(self)
+ Toon.unloadCashbotHQAnims()
+
+ def enterMintInterior(self, requestStatus):
+ self.placeClass = MintInterior.MintInterior
+ self.mintId = requestStatus['mintId']
+ self.enterPlace(requestStatus)
+
+ def exitMintInterior(self):
+ self.exitPlace()
+ self.placeClass = None
+ del self.mintId
+ return
+
+ def getExteriorPlaceClass(self):
+ return CashbotHQExterior.CashbotHQExterior
+
+ def getBossPlaceClass(self):
+ return CashbotHQBossBattle.CashbotHQBossBattle
diff --git a/toontown/coghq/CashbotHQBossBattle.py b/toontown/coghq/CashbotHQBossBattle.py
new file mode 100644
index 0000000..3eb29f5
--- /dev/null
+++ b/toontown/coghq/CashbotHQBossBattle.py
@@ -0,0 +1,28 @@
+from pandac.PandaModules import *
+from direct.interval.IntervalGlobal import *
+from toontown.suit import DistributedCashbotBoss
+from direct.directnotify import DirectNotifyGlobal
+from toontown.coghq import CogHQBossBattle
+
+class CashbotHQBossBattle(CogHQBossBattle.CogHQBossBattle):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CashbotHQBossBattle')
+
+ def __init__(self, loader, parentFSM, doneEvent):
+ CogHQBossBattle.CogHQBossBattle.__init__(self, loader, parentFSM, doneEvent)
+ self.teleportInPosHpr = (88, -214, 0, 210, 0, 0)
+
+ def load(self):
+ CogHQBossBattle.CogHQBossBattle.load(self)
+
+ def unload(self):
+ CogHQBossBattle.CogHQBossBattle.unload(self)
+
+ def enter(self, requestStatus):
+ CogHQBossBattle.CogHQBossBattle.enter(self, requestStatus, DistributedCashbotBoss.OneBossCog)
+
+ def exit(self):
+ CogHQBossBattle.CogHQBossBattle.exit(self)
+
+ def exitCrane(self):
+ CogHQBossBattle.CogHQBossBattle.exitCrane(self)
+ messenger.send('exitCrane')
diff --git a/toontown/coghq/CashbotHQExterior.py b/toontown/coghq/CashbotHQExterior.py
new file mode 100644
index 0000000..aa985af
--- /dev/null
+++ b/toontown/coghq/CashbotHQExterior.py
@@ -0,0 +1,93 @@
+from direct.directnotify import DirectNotifyGlobal
+from direct.interval.IntervalGlobal import *
+from direct.fsm import State
+from pandac.PandaModules import *
+from toontown.building import Elevator
+from toontown.coghq import CogHQExterior
+from toontown.safezone import Train
+
+class CashbotHQExterior(CogHQExterior.CogHQExterior):
+ notify = DirectNotifyGlobal.directNotify.newCategory('CashbotHQExterior')
+ TrackZ = -67
+ TrainTracks = [{'start': Point3(-1000, -54.45, TrackZ),
+ 'end': Point3(2200, -54.45, TrackZ)},
+ {'start': Point3(1800, -133.45, TrackZ),
+ 'end': Point3(-1200, -133.45, TrackZ)},
+ {'start': Point3(-1000, -212.45, TrackZ),
+ 'end': Point3(2200, -212.45, TrackZ)},
+ {'start': Point3(1800, -291.45, TrackZ),
+ 'end': Point3(-1200, -291.45, TrackZ)}]
+
+ def __init__(self, loader, parentFSM, doneEvent):
+ CogHQExterior.CogHQExterior.__init__(self, loader, parentFSM, doneEvent)
+ self.elevatorDoneEvent = 'elevatorDone'
+ self.trains = None
+ self.fsm.addState(State.State('elevator', self.enterElevator, self.exitElevator, ['walk', 'stopped']))
+ state = self.fsm.getStateNamed('walk')
+ state.addTransition('elevator')
+ state = self.fsm.getStateNamed('stopped')
+ state.addTransition('elevator')
+ state = self.fsm.getStateNamed('stickerBook')
+ state.addTransition('elevator')
+ state = self.fsm.getStateNamed('squished')
+ state.addTransition('elevator')
+ return
+
+ def load(self):
+ CogHQExterior.CogHQExterior.load(self)
+ if not self.trains:
+ self.trains = []
+ for track in self.TrainTracks:
+ train = Train.Train(track['start'], track['end'], self.TrainTracks.index(track), len(self.TrainTracks))
+ self.trains.append(train)
+
+ def unload(self):
+ CogHQExterior.CogHQExterior.unload(self)
+ for train in self.trains:
+ train.delete()
+
+ self.trains = None
+ return
+
+ def enter(self, requestStatus):
+ CogHQExterior.CogHQExterior.enter(self, requestStatus)
+ for train in self.trains:
+ train.show()
+
+ def exit(self):
+ CogHQExterior.CogHQExterior.exit(self)
+ for train in self.trains:
+ train.hide()
+
+ def enterElevator(self, distElevator, skipDFABoard = 0):
+ self.accept(self.elevatorDoneEvent, self.handleElevatorDone)
+ self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator)
+ if skipDFABoard:
+ self.elevator.skipDFABoard = 1
+ self.elevator.load()
+ self.elevator.enter()
+
+ def exitElevator(self):
+ self.ignore(self.elevatorDoneEvent)
+ self.elevator.unload()
+ self.elevator.exit()
+ del self.elevator
+
+ def detectedElevatorCollision(self, distElevator):
+ self.fsm.request('elevator', [distElevator])
+
+ def handleElevatorDone(self, doneStatus):
+ self.notify.debug('handling elevator done event')
+ where = doneStatus['where']
+ if where == 'reject':
+ if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen():
+ pass
+ else:
+ self.fsm.request('walk')
+ elif where == 'exit':
+ self.fsm.request('walk')
+ elif where == 'mintInterior':
+ self.doneStatus = doneStatus
+ messenger.send(self.doneEvent)
+ else:
+ self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone')
diff --git a/toontown/coghq/CashbotMintBoilerRoom_Action00.py b/toontown/coghq/CashbotMintBoilerRoom_Action00.py
new file mode 100644
index 0000000..34605eb
--- /dev/null
+++ b/toontown/coghq/CashbotMintBoilerRoom_Action00.py
@@ -0,0 +1,622 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE08a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10055: {'type': 'attribModifier',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'attribName': 'modelPath',
+ 'recursive': 1,
+ 'typeName': 'model',
+ 'value': ''},
+ 10045: {'type': 'gagBarrel',
+ 'name': 'gag',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(1.36976861954, 0.773027420044, 0.0),
+ 'hpr': Vec3(51.1066703796, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'gagLevel': 5,
+ 'gagLevelMax': 0,
+ 'gagTrack': 'random',
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 0},
+ 10047: {'type': 'gagBarrel',
+ 'name': 'gag',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.137291625142, 2.83575630188, 0.0),
+ 'hpr': Vec3(-210.47303772, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'gagLevel': 5,
+ 'gagLevelMax': 0,
+ 'gagTrack': 'random',
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 0},
+ 10054: {'type': 'gagBarrel',
+ 'name': 'gag',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-2.34864091873, 2.16795802116, 0.0),
+ 'hpr': Vec3(-141.715744019, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'gagLevel': 5,
+ 'gagLevelMax': 0,
+ 'gagTrack': 'random',
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 0},
+ 10020: {'type': 'gear',
+ 'name': 'upper',
+ 'comment': '',
+ 'parentEntId': 10044,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.60000002384),
+ 'degreesPerSec': -5.0,
+ 'gearScale': 24.3,
+ 'modelType': 'mint',
+ 'orientation': 'horizontal',
+ 'phaseShift': 0},
+ 10004: {'type': 'healBarrel',
+ 'name': 'heal',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.0, -0.748414576054, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 6,
+ 'rewardPerGrabMax': 8},
+ 10005: {'type': 'healBarrel',
+ 'name': 'heal',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-2.20195555687, -0.384303599596, 0.0),
+ 'hpr': Vec3(-64.4312591553, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 6,
+ 'rewardPerGrabMax': 8},
+ 10037: {'type': 'healBarrel',
+ 'name': 'atTheEnd',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(64.282081604, 42.8509597778, 0.0),
+ 'hpr': Vec3(274.906707764, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 0},
+ 10010: {'type': 'mintShelf',
+ 'name': 'shelf0',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12700},
+ 10021: {'type': 'mintShelf',
+ 'name': 'copy of shelf0',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(-13.4654359818, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10022: {'type': 'mintShelf',
+ 'name': 'copy of shelf0 (2)',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(-26.8826961517, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10025: {'type': 'mintShelf',
+ 'name': 'shelf0',
+ 'comment': '',
+ 'parentEntId': 10024,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10026: {'type': 'mintShelf',
+ 'name': 'copy of shelf0',
+ 'comment': '',
+ 'parentEntId': 10024,
+ 'pos': Point3(-13.4654359818, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10027: {'type': 'mintShelf',
+ 'name': 'copy of shelf0 (2)',
+ 'comment': '',
+ 'parentEntId': 10024,
+ 'pos': Point3(-26.8826961517, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10000: {'type': 'model',
+ 'name': 'crate',
+ 'comment': '',
+ 'parentEntId': 10009,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10007: {'type': 'model',
+ 'name': 'upper',
+ 'comment': '',
+ 'parentEntId': 10059,
+ 'pos': Point3(0.0, 2.0, 5.5),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10008: {'type': 'model',
+ 'name': 'crate',
+ 'comment': '',
+ 'parentEntId': 10009,
+ 'pos': Point3(0.0, -5.79679441452, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10012: {'type': 'model',
+ 'name': 'copy of crate',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.0, -5.79679441452, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10013: {'type': 'model',
+ 'name': 'copy of crate (2)',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10014: {'type': 'model',
+ 'name': 'copy of crate (2)',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(-5.65285158157, -11.6494598389, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10015: {'type': 'model',
+ 'name': 'copy of crate (2)',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(-5.80570077896, -5.79679441452, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10016: {'type': 'model',
+ 'name': 'copy of crate (3)',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(-3.93829965591, -17.6477527618, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10018: {'type': 'model',
+ 'name': 'copy of upper',
+ 'comment': '',
+ 'parentEntId': 10059,
+ 'pos': Point3(0.0, -3.83362102509, 5.5),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10019: {'type': 'model',
+ 'name': 'copy of upper (2)',
+ 'comment': '',
+ 'parentEntId': 10059,
+ 'pos': Point3(0.0, -9.69304847717, 5.5),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10030: {'type': 'model',
+ 'name': 'lastCrateStack',
+ 'comment': '',
+ 'parentEntId': 10029,
+ 'pos': Point3(47.9848709106, 27.71052742, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10031: {'type': 'model',
+ 'name': 'upper',
+ 'comment': '',
+ 'parentEntId': 10030,
+ 'pos': Point3(0.0, 0.0, 5.5),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10033: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(-41.8699073792, -36.9582328796, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'},
+ 10034: {'type': 'model',
+ 'name': 'crateStack',
+ 'comment': '',
+ 'parentEntId': 10029,
+ 'pos': Point3(47.9848709106, -3.09666919708, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10035: {'type': 'model',
+ 'name': 'upper',
+ 'comment': '',
+ 'parentEntId': 10034,
+ 'pos': Point3(0.0, 0.0, 5.5),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10036: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(0.0, -41.4516029358, 30.2685108185),
+ 'hpr': Vec3(180.0, 0.0, 180.0),
+ 'scale': Vec3(0.850346446037, 0.850346446037, 0.850346446037),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'},
+ 10041: {'type': 'model',
+ 'name': 'crateStack',
+ 'comment': '',
+ 'parentEntId': 10040,
+ 'pos': Point3(36.5904769897, -31.6758518219, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10042: {'type': 'model',
+ 'name': 'upper',
+ 'comment': '',
+ 'parentEntId': 10041,
+ 'pos': Point3(0.0, 0.0, 5.5),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10043: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(19.5017147064, 84.0786056519, 10.0058736801),
+ 'hpr': Vec3(171.253845215, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/boiler_B1.bam'},
+ 10048: {'type': 'model',
+ 'name': 'crate',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'pos': Point3(0.0, 0.0, 8.25758934021),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.29999995232, 1.29999995232, 1.64999997616),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10050: {'type': 'model',
+ 'name': 'support',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/gears_C2.bam'},
+ 10052: {'type': 'model',
+ 'name': 'crate',
+ 'comment': '',
+ 'parentEntId': 10051,
+ 'pos': Point3(0.0, 0.0, 8.25758934021),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.29999995232, 1.29999995232, 1.64999997616),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10053: {'type': 'model',
+ 'name': 'support',
+ 'comment': '',
+ 'parentEntId': 10051,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/gears_C2.bam'},
+ 10056: {'type': 'model',
+ 'name': 'collision',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-0.62570387125, 0.824797034264, 0.0),
+ 'hpr': Vec3(318.366455078, 0.0, 0.0),
+ 'scale': Vec3(0.644617915154, 0.639999985695, 1.28725671768),
+ 'collisionsOnly': 1,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'},
+ 10057: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10044,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(11.0614013672, 11.0614013672, 11.0614013672),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/RoundShadow.bam'},
+ 10058: {'type': 'model',
+ 'name': 'shelf',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(62.9968643188, 21.712474823, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/shelf_A1.bam'},
+ 10062: {'type': 'model',
+ 'name': 'copy of upper',
+ 'comment': '',
+ 'parentEntId': 10061,
+ 'pos': Point3(0.0, -3.83362102509, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10063: {'type': 'model',
+ 'name': 'copy of upper (2)',
+ 'comment': '',
+ 'parentEntId': 10061,
+ 'pos': Point3(0.0, -9.69304847717, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10064: {'type': 'model',
+ 'name': 'upper',
+ 'comment': '',
+ 'parentEntId': 10061,
+ 'pos': Point3(0.0, 2.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10001: {'type': 'nodepath',
+ 'name': 'crates',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.29999995232, 1.29999995232, 1.64892423153)},
+ 10002: {'type': 'nodepath',
+ 'name': 'rewardBarrels',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-0.719733536243, 56.9690589905, 10.0021047592),
+ 'hpr': Vec3(61.6992454529, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10003: {'type': 'nodepath',
+ 'name': 'upperWall',
+ 'comment': 'TODO: replace with lines of shelves',
+ 'parentEntId': 0,
+ 'pos': Point3(-20.3202514648, 52.6549415588, 9.9087305069),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.11429846287, 1.11429846287, 1.11429846287)},
+ 10009: {'type': 'nodepath',
+ 'name': 'toGear0',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(-26.5593318939, 31.8559513092, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10011: {'type': 'nodepath',
+ 'name': 'toGear1',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(-25.88397789, 13.6748971939, 0.0),
+ 'hpr': Vec3(41.6335411072, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10023: {'type': 'nodepath',
+ 'name': 'leftWall',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10024: {'type': 'nodepath',
+ 'name': 'rightWall',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(-26.7111759186, 6.85981559753, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10028: {'type': 'nodepath',
+ 'name': 'lowerPuzzle',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0500000007451),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10029: {'type': 'nodepath',
+ 'name': 'entranceWall',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10032: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10038: {'type': 'nodepath',
+ 'name': 'archStompers',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10040: {'type': 'nodepath',
+ 'name': 'backWall',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10044: {'type': 'nodepath',
+ 'name': 'gear',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(11.8500003815, -11.3800001144, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10046: {'type': 'nodepath',
+ 'name': 'supportedCrateBackWall',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(34.904460907, -34.058883667, -1.51686680317),
+ 'hpr': Vec3(63.4349479675, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10051: {'type': 'nodepath',
+ 'name': 'supportedCrateEntrance',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(50.5076904298, 7.75915336609, 0.35789707303),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10059: {'type': 'nodepath',
+ 'name': 'largeStack',
+ 'comment': '',
+ 'parentEntId': 10029,
+ 'pos': Point3(47.9799995422, -16.9799995422, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10061: {'type': 'nodepath',
+ 'name': 'lower',
+ 'comment': '',
+ 'parentEntId': 10059,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10049: {'type': 'stomper',
+ 'name': 'second',
+ 'comment': '',
+ 'parentEntId': 10038,
+ 'pos': Point3(62.3684997559, -19.4456634521, 18.1217155457),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'animateShadow': 1,
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Point3(3.79999995232, 4.30000019073, 3.79999995232),
+ 'modelPath': 0,
+ 'motion': 3,
+ 'period': 3.0,
+ 'phaseShift': 0.34,
+ 'range': 7.0,
+ 'removeCamBarrierCollisions': 0,
+ 'removeHeadFloor': 1,
+ 'shaftScale': Point3(1.71000003815, 2.78999996185, 1.71000003815),
+ 'soundLen': 0,
+ 'soundOn': 1,
+ 'soundPath': 1,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 1,
+ 'zOffset': 0}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintBoilerRoom_Battle00.py b/toontown/coghq/CashbotMintBoilerRoom_Battle00.py
new file mode 100644
index 0000000..06a5dab
--- /dev/null
+++ b/toontown/coghq/CashbotMintBoilerRoom_Battle00.py
@@ -0,0 +1,448 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE08a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10001: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(-1.02925205231, 87.0907745361, 11.8959827423),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'cellId': 0,
+ 'radius': 10.0},
+ 10006: {'type': 'battleBlocker',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(-60.9065246582, -3.26905798912, 0.117109239101),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'cellId': 1,
+ 'radius': 15.0},
+ 10047: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10013,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 0.20000000298, 1.0),
+ 'cellId': 2,
+ 'radius': 20.0},
+ 10041: {'type': 'gagBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10033,
+ 'pos': Point3(5.40611028671, 0.0, 0.0),
+ 'hpr': Vec3(199.440032959, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'gagLevel': 5,
+ 'gagLevelMax': 0,
+ 'gagTrack': 'random',
+ 'rewardPerGrab': 4,
+ 'rewardPerGrabMax': 6},
+ 10034: {'type': 'healBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10033,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(163.300750732, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 7,
+ 'rewardPerGrabMax': 9},
+ 10015: {'type': 'mintProductPallet',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10014,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10016: {'type': 'mintProductPallet',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10014,
+ 'pos': Point3(0.0, 13.6865262985, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10017: {'type': 'mintProductPallet',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10014,
+ 'pos': Point3(0.0, 27.3799991608, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10018: {'type': 'mintProductPallet',
+ 'name': 'copy of (3)',
+ 'comment': '',
+ 'parentEntId': 10014,
+ 'pos': Point3(0.0, 41.0699996948, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10019: {'type': 'mintProductPallet',
+ 'name': 'copy of (4)',
+ 'comment': '',
+ 'parentEntId': 10014,
+ 'pos': Point3(0.0, 54.7599983215, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10020: {'type': 'mintProductPallet',
+ 'name': 'copy of (5)',
+ 'comment': '',
+ 'parentEntId': 10014,
+ 'pos': Point3(0.0, 68.4499969482, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10022: {'type': 'mintProductPallet',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10021,
+ 'pos': Point3(0.0, 11.766998291, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10025: {'type': 'mintProductPallet',
+ 'name': 'copy of (4)',
+ 'comment': '',
+ 'parentEntId': 10045,
+ 'pos': Point3(0.0, 54.7599983215, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10026: {'type': 'mintProductPallet',
+ 'name': 'copy of (5)',
+ 'comment': '',
+ 'parentEntId': 10045,
+ 'pos': Point3(0.0, 68.4499969482, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10036: {'type': 'mintProductPallet',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10035,
+ 'pos': Point3(0.0, 13.6865262985, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10037: {'type': 'mintProductPallet',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10035,
+ 'pos': Point3(0.0, 27.3799991608, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10038: {'type': 'mintProductPallet',
+ 'name': 'copy of (3)',
+ 'comment': '',
+ 'parentEntId': 10035,
+ 'pos': Point3(0.0, 41.0699996948, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10043: {'type': 'mintProductPallet',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(-36.662399292, -39.0314712524, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10044: {'type': 'mintProductPallet',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10021,
+ 'pos': Point3(0.0, 25.4739685059, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10004: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10021,
+ 'pos': Point3(0.0, -1.09804749489, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(2.0, 2.0, 2.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'strong',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10009: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(-3.9962117672, 0.695078849792, 0.0113303475082),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.20000004768, 1.20000004768, 1.20000004768),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'},
+ 10010: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(48.0530014038, -0.531660735607, -0.327078670263),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'},
+ 10012: {'type': 'model',
+ 'name': 'rightCrates',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(36.0373382568, 71.3546981812, 9.99835586548),
+ 'hpr': Vec3(315.0, 0.0, 0.0),
+ 'scale': Vec3(1.5, 1.5, 1.5),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'},
+ 10024: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(-3.7328555584, 27.1218452454, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(2.0, 2.0, 2.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10027: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(-11.9349050522, 38.9528312683, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(2.0, 2.0, 2.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10029: {'type': 'model',
+ 'name': 'crate',
+ 'comment': '',
+ 'parentEntId': 10035,
+ 'pos': Point3(0.0, 0.863602340221, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(2.0, 2.0, 2.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10030: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10031: {'type': 'model',
+ 'name': 'copy of crate',
+ 'comment': '',
+ 'parentEntId': 10029,
+ 'pos': Point3(0.0, 0.0, 5.46999979019),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10032: {'type': 'model',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(0.0, -5.92218112946, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10039: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(-9.23663234711, 0.821143984795, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.5, 1.5, 1.5),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'},
+ 10042: {'type': 'model',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(3.0, -11.8400001526, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10048: {'type': 'model',
+ 'name': 'cratesAgainstWall',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(-37.0983123779, 70.2133865356, 10.0),
+ 'hpr': Vec3(225.0, 0.0, 0.0),
+ 'scale': Vec3(1.5, 1.5, 1.5),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.0, 66.1200027466, 10.1833248138),
+ 'hpr': Point3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10002: {'type': 'nodepath',
+ 'name': 'battle',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': 1},
+ 10003: {'type': 'nodepath',
+ 'name': 'cogs2',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(-53.9246749878, -22.7616195679, 0.0),
+ 'hpr': Point3(45.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10005: {'type': 'nodepath',
+ 'name': 'battle',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10007: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10008: {'type': 'nodepath',
+ 'name': 'topWall',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 48.0299987793, 10.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10011: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10013: {'type': 'nodepath',
+ 'name': 'frontCogs',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(25.3957309723, -12.3005743027, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10014: {'type': 'nodepath',
+ 'name': 'frontPalletWall',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(45.5494384766, 38.2237281799, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10021: {'type': 'nodepath',
+ 'name': 'middlePalletWallLeft',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'pos': Point3(6.0, -37.9928665161, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10023: {'type': 'nodepath',
+ 'name': 'crateIsland',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(-23.1813278198, 7.08758449554, 0.00999999977648),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(2.0, 2.0, 2.0)},
+ 10028: {'type': 'nodepath',
+ 'name': 'rewardCulDeSac',
+ 'comment': '',
+ 'parentEntId': 10045,
+ 'pos': Point3(-8.26172065735, 38.377407074, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10033: {'type': 'nodepath',
+ 'name': 'barrels',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(-4.75077962875, 34.1425209045, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10035: {'type': 'nodepath',
+ 'name': 'backPalletWall',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(-47.6501731873, 40.006893158, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10040: {'type': 'nodepath',
+ 'name': 'centerCogs',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(-23.9375743866, 28.353269577, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10045: {'type': 'nodepath',
+ 'name': 'middlePalletWallRight',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'pos': Point3(17.4200000763, -38.2999992371, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10046: {'type': 'nodepath',
+ 'name': 'middlePalletWall',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintBoilerRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintBoilerRoom_Battle00_Cogs.py
new file mode 100644
index 0000000..991eeca
--- /dev/null
+++ b/toontown/coghq/CashbotMintBoilerRoom_Battle00_Cogs.py
@@ -0,0 +1,165 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+LowerCogParent = 10003
+BattleParent = 10002
+LowerBattleParent = 10005
+FrontCogParent = 10013
+CenterCogParent = 10040
+BattleCellId = 0
+LowerBattleCellId = 1
+FrontBattleCellId = 2
+CenterBattleCellId = 3
+BattleCells = {BattleCellId: {'parentEntId': BattleParent,
+ 'pos': Point3(0, 0, 0)},
+ LowerBattleCellId: {'parentEntId': LowerBattleParent,
+ 'pos': Point3(0, 0, 0)},
+ FrontBattleCellId: {'parentEntId': FrontCogParent,
+ 'pos': Point3(0, 0, 0)},
+ CenterBattleCellId: {'parentEntId': CenterCogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': LowerCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': LowerBattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': LowerCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': LowerBattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': LowerCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': LowerBattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': LowerCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': LowerBattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CenterCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': CenterBattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CenterCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': CenterBattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CenterCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': CenterBattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CenterCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': CenterBattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintBoilerRoom_Battle01.py b/toontown/coghq/CashbotMintBoilerRoom_Battle01.py
new file mode 100644
index 0000000..28e2ddc
--- /dev/null
+++ b/toontown/coghq/CashbotMintBoilerRoom_Battle01.py
@@ -0,0 +1,401 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE08a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10006: {'type': 'gagBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10005,
+ 'pos': Point3(-23.8955783844, -29.8914642334, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'gagLevel': 5,
+ 'gagLevelMax': 0,
+ 'gagTrack': 'random',
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 7},
+ 10007: {'type': 'healBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10005,
+ 'pos': Point3(-7.71000003815, 6.03817367554, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 8},
+ 10001: {'type': 'locator',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'searchPath': '**/EXIT'},
+ 10014: {'type': 'mintProduct',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10013,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10015: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10013,
+ 'pos': Point3(9.73605537415, 0.935430526733, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10016: {'type': 'mintProduct',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10013,
+ 'pos': Point3(-11.0564117432, 0.213024124503, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10028: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10027,
+ 'pos': Point3(12.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10029: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10030: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10027,
+ 'pos': Point3(-12.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10031: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10030,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10033: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10034: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10033,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10035: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(24.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10036: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10035,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10037: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(-24.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10038: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10037,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10040: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10039,
+ 'pos': Point3(12.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10041: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10040,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10044: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10039,
+ 'pos': Point3(-12.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10045: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10044,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10046: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10039,
+ 'pos': Point3(-36.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10047: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10049: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10048,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10050: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10048,
+ 'pos': Point3(24.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10051: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10048,
+ 'pos': Point3(-24.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10052: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10049,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10053: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10050,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10054: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10051,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10056: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10055,
+ 'pos': Point3(12.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10057: {'type': 'mintShelf',
+ 'name': 'shelfPair',
+ 'comment': '',
+ 'parentEntId': 10055,
+ 'pos': Point3(-12.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10058: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10056,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10059: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10057,
+ 'pos': Point3(0.167918920517, 6.80000019073, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10002: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'},
+ 10011: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(34.3037414551, 6.2506942749, 0.0),
+ 'hpr': Vec3(306.869903564, 0.0, 0.0),
+ 'scale': Vec3(1.22879016399, 1.22879016399, 1.22879016399),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/boiler_A2.bam'},
+ 10012: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(-37.5963821411, 0.68013381958, 0.0),
+ 'hpr': Vec3(45.0, 0.0, 0.0),
+ 'scale': Vec3(0.761251866817, 0.761251866817, 0.761251866817),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 68.932258606, 9.97146701813),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10003: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10004: {'type': 'nodepath',
+ 'name': 'lower',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10005: {'type': 'nodepath',
+ 'name': 'barrels',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10008: {'type': 'nodepath',
+ 'name': 'upperLevel',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 65.4967575073, 9.99451065063),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10013: {'type': 'nodepath',
+ 'name': 'product',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(0.0, 17.8199996948, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10023: {'type': 'nodepath',
+ 'name': 'shelves',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(0.0, 1.89410364628, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10027: {'type': 'nodepath',
+ 'name': 'row1',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(0.0, -32.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10032: {'type': 'nodepath',
+ 'name': 'row2',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(0.0, -14.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10039: {'type': 'nodepath',
+ 'name': 'row3',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(0.0, 4.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10048: {'type': 'nodepath',
+ 'name': 'row4',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(0.0, 22.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10055: {'type': 'nodepath',
+ 'name': 'row5',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(0.0, 40.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintBoilerRoom_Battle01_Cogs.py b/toontown/coghq/CashbotMintBoilerRoom_Battle01_Cogs.py
new file mode 100644
index 0000000..61883ec
--- /dev/null
+++ b/toontown/coghq/CashbotMintBoilerRoom_Battle01_Cogs.py
@@ -0,0 +1,43 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 1,
+ 'level': ToontownGlobals.CashbotMintBossLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintControlRoom_Battle00.py b/toontown/coghq/CashbotMintControlRoom_Battle00.py
new file mode 100644
index 0000000..7624c34
--- /dev/null
+++ b/toontown/coghq/CashbotMintControlRoom_Battle00.py
@@ -0,0 +1,137 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE31a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10013: {'type': 'mintProduct',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10012,
+ 'pos': Point3(0.0, 16.5455417633, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10014: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10012,
+ 'pos': Point3(9.33731842041, 15.9028654099, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10015: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10012,
+ 'pos': Point3(-9.30014419556, 11.6405067444, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10003: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(19.5716362, 16.3833560944, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10004: {'type': 'mintShelf',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(19.5716362, 2.93304467201, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10005: {'type': 'mintShelf',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(19.5716362, -10.4780406952, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10006: {'type': 'mintShelf',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-19.5699996948, 16.3833560944, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10007: {'type': 'mintShelf',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-19.5699996948, 2.93304467201, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10008: {'type': 'mintShelf',
+ 'name': 'copy of (3)',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-19.5699996948, -10.4780406952, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10001: {'type': 'model',
+ 'name': 'vaultDoor',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 22.9976291656, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10002: {'type': 'nodepath',
+ 'name': 'shelves',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10011: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10012: {'type': 'nodepath',
+ 'name': 'product',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintControlRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintControlRoom_Battle00_Cogs.py
new file mode 100644
index 0000000..61883ec
--- /dev/null
+++ b/toontown/coghq/CashbotMintControlRoom_Battle00_Cogs.py
@@ -0,0 +1,43 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 1,
+ 'level': ToontownGlobals.CashbotMintBossLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintDuctRoom_Action00.py b/toontown/coghq/CashbotMintDuctRoom_Action00.py
new file mode 100644
index 0000000..ffddc61
--- /dev/null
+++ b/toontown/coghq/CashbotMintDuctRoom_Action00.py
@@ -0,0 +1,250 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE15a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10015: {'type': 'healBarrel',
+ 'name': 'heal',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(16.9929084778, 7.15916633606, 0.0),
+ 'hpr': Vec3(107.078933716, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 6,
+ 'rewardPerGrabMax': 0},
+ 10004: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(41.5774269104, -16.0394973755, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10005: {'type': 'mintShelf',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(41.5774269104, 15.5885248184, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10009: {'type': 'model',
+ 'name': 'crateColl0',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(-21.0479602814, -8.71147918701, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.6654573679, 4.67459440231, 4.99637460709),
+ 'collisionsOnly': 1,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'},
+ 10013: {'type': 'model',
+ 'name': 'crate0',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(-21.0, 0.735621452332, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10016: {'type': 'model',
+ 'name': 'copy of crate0',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(-21.0, -8.74976444244, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10017: {'type': 'model',
+ 'name': 'copy of crate0 (2)',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(-21.0, -18.1307086945, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10019: {'type': 'model',
+ 'name': 'copy of crate0',
+ 'comment': '',
+ 'parentEntId': 10018,
+ 'pos': Point3(-21.0, -8.74976444244, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10020: {'type': 'model',
+ 'name': 'crateColl0',
+ 'comment': '',
+ 'parentEntId': 10018,
+ 'pos': Point3(-21.0479602814, -8.71147918701, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.69406318665, 4.75488471985, 5.08219003677),
+ 'collisionsOnly': 1,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'},
+ 10021: {'type': 'model',
+ 'name': 'crate0',
+ 'comment': '',
+ 'parentEntId': 10018,
+ 'pos': Point3(-21.0, 0.735621452332, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10022: {'type': 'model',
+ 'name': 'copy of crate0 (2)',
+ 'comment': '',
+ 'parentEntId': 10018,
+ 'pos': Point3(-21.0, -18.1307086945, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10024: {'type': 'model',
+ 'name': 'hider',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(17.0452461243, -0.882949709892, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.54636645317, 1.54636645317, 1.54636645317),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10003: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10010: {'type': 'nodepath',
+ 'name': 'crates0',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(1.2899544239, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10018: {'type': 'nodepath',
+ 'name': 'crates1',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-13.2792396545, 0.0, 0.0),
+ 'hpr': Vec3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10023: {'type': 'nodepath',
+ 'name': 'heal',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10000: {'type': 'stomper',
+ 'name': 'stomper0',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-23.0840358734, 13.8124275208, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(8.0, 8.0, 8.0),
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Vec3(1.0, 1.0, 1.0),
+ 'modelPath': 0,
+ 'motion': 3,
+ 'period': 3.0,
+ 'phaseShift': 0.0,
+ 'range': 1.6,
+ 'shaftScale': Point3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 1,
+ 'soundPath': 0,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 1,
+ 'zOffset': 0},
+ 10001: {'type': 'stomper',
+ 'name': 'stomper1',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-5.92516326904, -0.618411839008, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(8.0, 8.0, 8.0),
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Vec3(1.0, 1.0, 1.0),
+ 'modelPath': 0,
+ 'motion': 3,
+ 'period': 3.0,
+ 'phaseShift': 0.33,
+ 'range': 1.6,
+ 'shaftScale': Point3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 1,
+ 'soundPath': 0,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 1,
+ 'zOffset': 0},
+ 10002: {'type': 'stomper',
+ 'name': 'stomper2',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(11.6394100189, -14.1471977234, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(8.0, 8.0, 8.0),
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Vec3(1.0, 1.0, 1.0),
+ 'modelPath': 0,
+ 'motion': 3,
+ 'period': 3.0,
+ 'phaseShift': 0.66,
+ 'range': 1.6,
+ 'shaftScale': Point3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 1,
+ 'soundPath': 0,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 1,
+ 'zOffset': 0}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintDuctRoom_Battle00.py b/toontown/coghq/CashbotMintDuctRoom_Battle00.py
new file mode 100644
index 0000000..646fcc5
--- /dev/null
+++ b/toontown/coghq/CashbotMintDuctRoom_Battle00.py
@@ -0,0 +1,136 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE15a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10001: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(44.257774353, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'cellId': 0,
+ 'radius': 10.0},
+ 10003: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(39.3964080811, 22.2593421936, 16.0659122467),
+ 'hpr': Vec3(270.0, 0.0, 90.0),
+ 'scale': Vec3(0.560864269733, 0.560864269733, 0.560864269733),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModel',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'},
+ 10005: {'type': 'model',
+ 'name': 'farLeft',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(41.8226966858, 16.5434036255, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(2.28777551651, 2.28777551651, 2.28777551651),
+ 'collisionsOnly': 1,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'},
+ 10006: {'type': 'model',
+ 'name': 'farRight',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(41.8226966858, -16.5400009155, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(2.28777551651, 2.28777551651, 2.28777551651),
+ 'collisionsOnly': 1,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'},
+ 10007: {'type': 'model',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(39.3192749023, -22.8234348297, 13.6092739105),
+ 'hpr': Vec3(270.0, 0.0, 270.0),
+ 'scale': Point3(0.560000002384, 0.560864269733, 0.560864269733),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModel',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'},
+ 10008: {'type': 'model',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-39.3800811768, -22.8855381012, 16.0659122467),
+ 'hpr': Point3(90.0, 0.0, 90.0),
+ 'scale': Vec3(0.560864269733, 0.560864269733, 0.560864269733),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModel',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'},
+ 10009: {'type': 'model',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-39.3062858582, 22.1807098389, 13.6092739105),
+ 'hpr': Point3(90.0, 0.0, 270.0),
+ 'scale': Point3(0.560000002384, 0.560864269733, 0.560864269733),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModel',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'},
+ 10010: {'type': 'model',
+ 'name': 'nearLeft',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-41.8199996948, 16.5434036255, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(2.28777551651, 2.28777551651, 2.28777551651),
+ 'collisionsOnly': 1,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'},
+ 10011: {'type': 'model',
+ 'name': 'nearRight',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-41.8199996948, -16.5400009155, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(2.28777551651, 2.28777551651, 2.28777551651),
+ 'collisionsOnly': 1,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10004: {'type': 'nodepath',
+ 'name': 'collisions',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintDuctRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintDuctRoom_Battle00_Cogs.py
new file mode 100644
index 0000000..017caac
--- /dev/null
+++ b/toontown/coghq/CashbotMintDuctRoom_Battle00_Cogs.py
@@ -0,0 +1,43 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintDuctRoom_Battle01.py b/toontown/coghq/CashbotMintDuctRoom_Battle01.py
new file mode 100644
index 0000000..127dbb6
--- /dev/null
+++ b/toontown/coghq/CashbotMintDuctRoom_Battle01.py
@@ -0,0 +1,139 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE15a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10001: {'type': 'locator',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'searchPath': '**/EXIT'},
+ 10009: {'type': 'mintProduct',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(37.843006134, 8.74360656738, 0.0),
+ 'hpr': Point3(-90.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10010: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(36.8768577576, -10.2692861557, 0.0),
+ 'hpr': Point3(-90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10011: {'type': 'mintProduct',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(26.2384986877, -16.2085189819, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10012: {'type': 'mintProduct',
+ 'name': 'copy of (3)',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(38.4032859802, -3.16089892387, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10002: {'type': 'model',
+ 'name': 'vaultDoor',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'},
+ 10004: {'type': 'model',
+ 'name': 'backRight',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(42.1358146667, -20.2375278473, 0.275439172983),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_A.bam'},
+ 10005: {'type': 'model',
+ 'name': 'backLeft',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(38.4724159241, 18.6007175446, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.44939172268, 1.44939172268, 1.44939172268),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'},
+ 10006: {'type': 'model',
+ 'name': 'frontRight',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(-42.3298530579, -20.0590000153, 0.0695294439793),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.40563333035, 1.40563333035, 1.40563333035),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_D.bam'},
+ 10007: {'type': 'model',
+ 'name': 'frontLeft',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(-42.3731651306, 18.05443573, 0.0688875243068),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_G1.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10003: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10008: {'type': 'nodepath',
+ 'name': 'product',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintDuctRoom_Battle01_Cogs.py b/toontown/coghq/CashbotMintDuctRoom_Battle01_Cogs.py
new file mode 100644
index 0000000..61883ec
--- /dev/null
+++ b/toontown/coghq/CashbotMintDuctRoom_Battle01_Cogs.py
@@ -0,0 +1,43 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 1,
+ 'level': ToontownGlobals.CashbotMintBossLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintEntrance_Action00.py b/toontown/coghq/CashbotMintEntrance_Action00.py
new file mode 100644
index 0000000..32c8f8d
--- /dev/null
+++ b/toontown/coghq/CashbotMintEntrance_Action00.py
@@ -0,0 +1,96 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE03a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10000: {'type': 'entrancePoint',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'entranceId': 0,
+ 'radius': 15,
+ 'theta': 20},
+ 10001: {'type': 'mintProduct',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-11.4890069962, 20.1173057556, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10003: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-20.4286708832, 12.2706327438, 0.0),
+ 'hpr': Vec3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10007: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-19.2144012451, 20.1173057556, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12700},
+ 10006: {'type': 'model',
+ 'name': 'crateStack',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(10.5386743546, 18.1184597015, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_G1.bam'},
+ 10008: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(13.8522205353, -20.3127307892, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10004: {'type': 'nodepath',
+ 'name': 'product',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintGearRoom_Action00.py b/toontown/coghq/CashbotMintGearRoom_Action00.py
new file mode 100644
index 0000000..3d7ee8c
--- /dev/null
+++ b/toontown/coghq/CashbotMintGearRoom_Action00.py
@@ -0,0 +1,233 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE07a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10007: {'type': 'attribModifier',
+ 'name': 'goonStrength',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'attribName': 'strength',
+ 'recursive': 1,
+ 'typeName': 'goon',
+ 'value': '10'},
+ 10002: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4.0},
+ 10004: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4},
+ 10006: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10005,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4},
+ 10009: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4},
+ 10011: {'type': 'healBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10012,
+ 'pos': Point3(2.15899157524, 2.29615116119, 5.45938539505),
+ 'hpr': Vec3(331.109100342, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 8,
+ 'rewardPerGrabMax': 0},
+ 10012: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(20.9361133575, 13.8672618866, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.920000016689, 0.920000016689, 0.920000016689),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'},
+ 10013: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(57.0218696594, 5.15023899078, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(0.660517215729, 0.660517215729, 0.660517215729),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'},
+ 10015: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(-25.9598789215, 59.4411621094, 9.73551368713),
+ 'hpr': Vec3(274.089996338, 0.0, 0.0),
+ 'scale': Vec3(1.53790044785, 1.53790044785, 1.53790044785),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'},
+ 10016: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(33.3394889832, -18.3643035889, 0.0),
+ 'hpr': Vec3(180.0, 0.0, 0.0),
+ 'scale': Vec3(0.660000026226, 0.660000026226, 0.660000026226),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'},
+ 10017: {'type': 'model',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10018,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(169.699996948, 0.0, 0.0),
+ 'scale': Vec3(0.902469694614, 0.902469694614, 0.902469694614),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_D4.bam'},
+ 10020: {'type': 'model',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10018,
+ 'pos': Point3(-12.071434021, 0.0, 0.0),
+ 'hpr': Vec3(288.434936523, 0.0, 0.0),
+ 'scale': Vec3(0.902469694614, 0.902469694614, 0.902469694614),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_D4.bam'},
+ 10022: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10021,
+ 'pos': Point3(-5.97179174423, -60.3133621216, 0.0),
+ 'hpr': Vec3(180.0, 0.0, 0.0),
+ 'scale': Vec3(0.869391143322, 0.869391143322, 0.869391143322),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10010: {'type': 'nodepath',
+ 'name': 'healPuzzle',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(43.1796302795, 0.0, 0.0),
+ 'hpr': Point3(-90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10018: {'type': 'nodepath',
+ 'name': 'rightVertPipes',
+ 'comment': '',
+ 'parentEntId': 10021,
+ 'pos': Point3(-16.4536571503, -45.3981781006, -8.39999961853),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(0.649999976158, 0.649999976158, 1.55999994278)},
+ 10021: {'type': 'nodepath',
+ 'name': 'rightPipes',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10001: {'type': 'path',
+ 'name': 'nearPace',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-59.7391967773, 0.0, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'pathIndex': 3,
+ 'pathScale': 1.0},
+ 10003: {'type': 'path',
+ 'name': 'bowtie',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-40.0336875916, 0.0, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'pathIndex': 2,
+ 'pathScale': 1.0},
+ 10005: {'type': 'path',
+ 'name': 'bridgePace',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-8.80618190765, -1.5122487545, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'pathIndex': 3,
+ 'pathScale': 1.0},
+ 10008: {'type': 'path',
+ 'name': 'farPace',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(7.5265827179, 7.56240034103, 0.0),
+ 'hpr': Vec3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'pathIndex': 3,
+ 'pathScale': 1.0}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintGearRoom_Battle00.py b/toontown/coghq/CashbotMintGearRoom_Battle00.py
new file mode 100644
index 0000000..ae8b14f
--- /dev/null
+++ b/toontown/coghq/CashbotMintGearRoom_Battle00.py
@@ -0,0 +1,79 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE07a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10001: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-27.3600006104, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'cellId': 0,
+ 'radius': 10.0},
+ 10002: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(57.0218696594, 3.79224324226, 0.0),
+ 'hpr': Vec3(111.037513733, 0.0, 0.0),
+ 'scale': Vec3(1.72596073151, 1.72596073151, 1.72596073151),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/boiler_B1.bam'},
+ 10004: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(-7.67323350906, -61.4041023254, 0.207314386964),
+ 'hpr': Vec3(169.695159912, 0.0, 0.0),
+ 'scale': Vec3(1.9143627882, 1.9143627882, 1.9143627882),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/boiler_A2.bam'},
+ 10005: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(-25.9598789215, 44.8260116577, 9.73551368713),
+ 'hpr': Vec3(94.0856170654, 0.0, 0.0),
+ 'scale': Vec3(1.53790044785, 1.53790044785, 1.53790044785),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-52.7907714844, 0.0, 0.0),
+ 'hpr': Point3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10003: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintGearRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintGearRoom_Battle00_Cogs.py
new file mode 100644
index 0000000..218ebaa
--- /dev/null
+++ b/toontown/coghq/CashbotMintGearRoom_Battle00_Cogs.py
@@ -0,0 +1,43 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintGearRoom_Battle01.py b/toontown/coghq/CashbotMintGearRoom_Battle01.py
new file mode 100644
index 0000000..005f1cb
--- /dev/null
+++ b/toontown/coghq/CashbotMintGearRoom_Battle01.py
@@ -0,0 +1,194 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE07a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10015: {'type': 'gagBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10009,
+ 'pos': Point3(0.35536468029, 1.03268241882, 0.0),
+ 'hpr': Point3(99.4599990845, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'gagLevel': 5,
+ 'gagLevelMax': 0,
+ 'gagTrack': 'random',
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 10},
+ 10007: {'type': 'gear',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(2.0, -65.0800018311, 11.0900001526),
+ 'hpr': Vec3(0.0, 90.0, 0.0),
+ 'scale': Point3(10.0, 10.0, 10.0),
+ 'degreesPerSec': 10.0,
+ 'gearScale': 1,
+ 'modelType': 'mint',
+ 'orientation': 'horizontal',
+ 'phaseShift': 0},
+ 10008: {'type': 'gear',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(-15.0, -65.0800018311, 15.0),
+ 'hpr': Vec3(0.0, 90.0, 0.0),
+ 'scale': Point3(10.0, 10.0, 10.0),
+ 'degreesPerSec': -10.0,
+ 'gearScale': 1,
+ 'modelType': 'mint',
+ 'orientation': 'horizontal',
+ 'phaseShift': 0.26},
+ 10016: {'type': 'healBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10009,
+ 'pos': Point3(2.0886592865, -5.19625711441, 0.0),
+ 'hpr': Vec3(80.5376815796, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 7,
+ 'rewardPerGrabMax': 10},
+ 10001: {'type': 'locator',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'searchPath': '**/EXIT'},
+ 10012: {'type': 'mintProduct',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10013: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(33.1602783203, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10014: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.0, -26.4398918152, 0.0),
+ 'hpr': Vec3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10002: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'},
+ 10004: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(28.4207668304, 0.886719465256, 0.0890568122268),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.42410159111, 1.42410159111, 1.42410159111),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_D.bam'},
+ 10005: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(1.02179563046, -5.71805810928, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(0.912700533867, 0.912700533867, 0.912700533867),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_G1.bam'},
+ 10006: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(28.0896816254, 6.65470600128, 0.0692733898759),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'},
+ 10010: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(-30.3416347504, -52.2014503479, 4.94085407257),
+ 'hpr': Vec3(180.0, 270.0, 270.0),
+ 'scale': Vec3(0.496759712696, 0.496759712696, 0.496759712696),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'},
+ 10017: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(-47.7702331543, -15.7725839615, 0.0),
+ 'hpr': Vec3(180.0, 0.0, 0.0),
+ 'scale': Vec3(0.75, 0.75, 0.75),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-16.2589473724, 49.1446685791, 10.2881116867),
+ 'hpr': Vec3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10003: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10009: {'type': 'nodepath',
+ 'name': 'barrels',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(39.311466217, 1.05693364143, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10011: {'type': 'nodepath',
+ 'name': 'product',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(-24.7985076904, 59.8468132019, 10.0710220337),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintGearRoom_Battle01_Cogs.py b/toontown/coghq/CashbotMintGearRoom_Battle01_Cogs.py
new file mode 100644
index 0000000..61883ec
--- /dev/null
+++ b/toontown/coghq/CashbotMintGearRoom_Battle01_Cogs.py
@@ -0,0 +1,43 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 1,
+ 'level': ToontownGlobals.CashbotMintBossLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Action00.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Action00.py
new file mode 100644
index 0000000..3a740f5
--- /dev/null
+++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Action00.py
@@ -0,0 +1,203 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE18a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10009: {'type': 'attribModifier',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'attribName': 'modelPath',
+ 'recursive': 1,
+ 'typeName': 'model',
+ 'value': ''},
+ 10017: {'type': 'attribModifier',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'attribName': 'scale',
+ 'recursive': 1,
+ 'typeName': 'model',
+ 'value': 'Vec3(.955,1,1)'},
+ 10015: {'type': 'crate',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10014,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'scale': 0.92,
+ 'crushCellId': None,
+ 'gridId': 10014,
+ 'modelType': 1,
+ 'pushable': 1},
+ 10014: {'type': 'grid',
+ 'name': 'crateGrid',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(-6.73230838776, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'cellSize': 3.0,
+ 'numCol': 4,
+ 'numRow': 2},
+ 10005: {'type': 'healBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(19.0611743927, -20.78266716, 0.0),
+ 'hpr': Vec3(160.016891479, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 8,
+ 'rewardPerGrabMax': 0},
+ 10001: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(-7.89672088623, 21.0129165649, 0.0),
+ 'hpr': Vec3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'},
+ 10002: {'type': 'model',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(-17.8739471436, 16.2802295685, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'},
+ 10006: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(20.9172992706, 20.2094459534, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'},
+ 10007: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(-18.3651504517, -19.2698841095, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'},
+ 10018: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.954999983311, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10019: {'type': 'model',
+ 'name': 'copy of middle',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(-5.72357320786, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.954999983311, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10020: {'type': 'model',
+ 'name': 'copy of middle',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(5.71999979019, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.954999983311, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10021: {'type': 'model',
+ 'name': 'copy of middle',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(11.4399995804, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.954999983311, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10003: {'type': 'nodepath',
+ 'name': 'cratePuzzle',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10008: {'type': 'nodepath',
+ 'name': 'wall',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(13.4399995804, 6.57999992371, 0.0),
+ 'hpr': Point3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.95812249184, 1.5, 1.79999995232)},
+ 10016: {'type': 'stomper',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10014,
+ 'pos': Point3(-4.04936361313, 3.45528435707, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'crushCellId': None,
+ 'damage': 6,
+ 'headScale': Point3(4.0, 3.0, 4.0),
+ 'modelPath': 0,
+ 'motion': 3,
+ 'period': 5.0,
+ 'phaseShift': 0.0,
+ 'range': 15.0,
+ 'shaftScale': Point3(0.75, 10.0, 0.75),
+ 'soundLen': 0,
+ 'soundOn': 1,
+ 'soundPath': 1,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 1,
+ 'zOffset': 0}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Action01.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Action01.py
new file mode 100644
index 0000000..bb66a24
--- /dev/null
+++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Action01.py
@@ -0,0 +1,249 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE18a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10000: {'type': 'attribModifier',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'attribName': 'modelPath',
+ 'recursive': 1,
+ 'typeName': 'model',
+ 'value': ''},
+ 10001: {'type': 'attribModifier',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'attribName': 'scale',
+ 'recursive': 1,
+ 'typeName': 'model',
+ 'value': 'Vec3(.955,1,1)'},
+ 10019: {'type': 'attribModifier',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10015,
+ 'attribName': 'modelPath',
+ 'recursive': 1,
+ 'typeName': 'model',
+ 'value': ''},
+ 10006: {'type': 'gear',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'degreesPerSec': -4.0,
+ 'gearScale': 14.193780914463838,
+ 'modelType': 'mint',
+ 'orientation': 'horizontal',
+ 'phaseShift': 0},
+ 10007: {'type': 'gear',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 4.28999996185),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'degreesPerSec': 4.0,
+ 'gearScale': 14.193780914463838,
+ 'modelType': 'mint',
+ 'orientation': 'horizontal',
+ 'phaseShift': 0},
+ 10009: {'type': 'gear',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 8.57999992371),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'degreesPerSec': -4.0,
+ 'gearScale': 14.193780914463838,
+ 'modelType': 'mint',
+ 'orientation': 'horizontal',
+ 'phaseShift': 0.055},
+ 10014: {'type': 'gear',
+ 'name': 'copy of (3)',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 12.8699998856),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'degreesPerSec': 4.0,
+ 'gearScale': 14.193780914463838,
+ 'modelType': 'mint',
+ 'orientation': 'horizontal',
+ 'phaseShift': 0.06},
+ 10018: {'type': 'healBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10017,
+ 'pos': Point3(-2.03643107414, 2.34967470169, 5.46433734894),
+ 'hpr': Vec3(34.1522636414, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 0},
+ 10002: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(6.5, 6.5, 6.5),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/RoundShadow.bam'},
+ 10005: {'type': 'model',
+ 'name': 'doorwayCrate',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(27.0090961456, 0.850000023842, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10008: {'type': 'model',
+ 'name': 'shaft',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 7.25891637802),
+ 'hpr': Vec3(0.0, 0.0, 180.0),
+ 'scale': Vec3(5.35842609406, 5.35842609406, 5.35842609406),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModel',
+ 'modelPath': 'phase_10/models/cashbotHQ/MintGearPost.bam'},
+ 10010: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.954999983311, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10011: {'type': 'model',
+ 'name': 'copy of middle',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-5.72357320786, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.954999983311, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10012: {'type': 'model',
+ 'name': 'copy of middle',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(5.71999979019, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.954999983311, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10013: {'type': 'model',
+ 'name': 'copy of middle',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(11.4399995804, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.954999983311, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10015: {'type': 'model',
+ 'name': 'crateStack',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-18.0376968384, 20.2023410797, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10016: {'type': 'model',
+ 'name': 'upper',
+ 'comment': '',
+ 'parentEntId': 10015,
+ 'pos': Point3(0.0, 0.0, 5.42841148376),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10017: {'type': 'model',
+ 'name': 'copy of upper',
+ 'comment': '',
+ 'parentEntId': 10016,
+ 'pos': Point3(0.0, 0.0, 5.43412637711),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10021: {'type': 'model',
+ 'name': 'crateStack',
+ 'comment': '',
+ 'parentEntId': 10020,
+ 'pos': Point3(21.064825058, 20.1899757385, 9.87216758728),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'},
+ 10003: {'type': 'nodepath',
+ 'name': 'gears',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-3.18650078773, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10004: {'type': 'nodepath',
+ 'name': 'wall',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(19.5468139648, 6.37875938416, 0.0),
+ 'hpr': Point3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.95812249184, 1.5, 1.79999995232)},
+ 10020: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00.py
new file mode 100644
index 0000000..0e1be57
--- /dev/null
+++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00.py
@@ -0,0 +1,89 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE18a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10004: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(23.908908844, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'cellId': 0,
+ 'radius': 10},
+ 10002: {'type': 'model',
+ 'name': 'crates',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(17.3283443451, 20.1608715057, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'},
+ 10003: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(-14.04317379, 20.9443073273, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'},
+ 10006: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(-3.16324114799, -0.608929097652, 5.57751512527),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10001: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10005: {'type': 'nodepath',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(-90.0, 0.0, 0.0),
+ 'scale': 1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00_Cogs.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00_Cogs.py
new file mode 100644
index 0000000..fe40eb3
--- /dev/null
+++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00_Cogs.py
@@ -0,0 +1,44 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+BattleParent = 10005
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': BattleParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintSkelecogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01.py
new file mode 100644
index 0000000..4586be5
--- /dev/null
+++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01.py
@@ -0,0 +1,156 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE18a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10004: {'type': 'locator',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'searchPath': '**/EXIT1'},
+ 10013: {'type': 'mintProduct',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10012,
+ 'pos': Point3(-3.94549632072, 18.2319583893, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10014: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10012,
+ 'pos': Point3(3.85402703285, 18.2319583893, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10015: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10012,
+ 'pos': Point3(-18.5567684174, 14.1500225067, 6.5729341507),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10001: {'type': 'model',
+ 'name': 'vaultDoor',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'},
+ 10003: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(13.2311220169, 20.3564720154, 0.305192321539),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.21849691868, 1.21849691868, 1.21849691868),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_A.bam'},
+ 10007: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-17.5481491089, 20.8210849762, 0.00756931304932),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.30483365059, 1.30483365059, 1.30483365059),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'},
+ 10008: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(-1.55398654938, -4.84950685501, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.913593888283, 0.913593888283, 0.913593888283),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'},
+ 10009: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-19.0412902832, -18.4314842224, 0.00867449026555),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_G1.bam'},
+ 10010: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(18.6662273407, -13.083732605, 0.00570194004104),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10002: {'type': 'nodepath',
+ 'name': 'crates',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10005: {'type': 'nodepath',
+ 'name': 'battle',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(-90.0, 0.0, 0.0),
+ 'scale': 1},
+ 10011: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(-90.0, 0.0, 0.0),
+ 'scale': 1},
+ 10012: {'type': 'nodepath',
+ 'name': 'product',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01_Cogs.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01_Cogs.py
new file mode 100644
index 0000000..505d6de
--- /dev/null
+++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01_Cogs.py
@@ -0,0 +1,44 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+BattleParent = 10005
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': BattleParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 1,
+ 'level': ToontownGlobals.CashbotMintBossLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintLavaRoom_Action00.py b/toontown/coghq/CashbotMintLavaRoom_Action00.py
new file mode 100644
index 0000000..4e4060f
--- /dev/null
+++ b/toontown/coghq/CashbotMintLavaRoom_Action00.py
@@ -0,0 +1,108 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE19a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10005: {'type': 'attribModifier',
+ 'name': 'sinkDuration',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'attribName': 'sinkDuration',
+ 'recursive': 1,
+ 'typeName': 'sinkingPlatform',
+ 'value': '2.0'},
+ 10006: {'type': 'attribModifier',
+ 'name': 'riseDuration',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'attribName': 'riseDuration',
+ 'recursive': 1,
+ 'typeName': 'sinkingPlatform',
+ 'value': '2.0'},
+ 10004: {'type': 'healBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-52.5099983215, -3.81641983986, 4.99661874771),
+ 'hpr': Vec3(100.165977478, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 8,
+ 'rewardPerGrabMax': 0},
+ 10008: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(-24.928899765, -4.86700963974, -1.70696532726),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModel',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A5.bam'},
+ 10002: {'type': 'nodepath',
+ 'name': 'sinkingPlatforms',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(2.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.864239275455, 0.864239275455, 0.864239275455)},
+ 10007: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10000: {'type': 'sinkingPlatform',
+ 'name': 'plat1',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'pauseBeforeRise': 1,
+ 'riseDuration': 2.0,
+ 'sinkDuration': 2.0,
+ 'verticalRange': 3.0},
+ 10001: {'type': 'sinkingPlatform',
+ 'name': 'plat0',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(20.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'pauseBeforeRise': 1,
+ 'riseDuration': 2.0,
+ 'sinkDuration': 2.0,
+ 'verticalRange': 3.0},
+ 10003: {'type': 'sinkingPlatform',
+ 'name': 'plat2',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-20.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'pauseBeforeRise': 1,
+ 'riseDuration': 2.0,
+ 'sinkDuration': 2.0,
+ 'verticalRange': 3.0}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintLobby_Action00.py b/toontown/coghq/CashbotMintLobby_Action00.py
new file mode 100644
index 0000000..09a9f4c
--- /dev/null
+++ b/toontown/coghq/CashbotMintLobby_Action00.py
@@ -0,0 +1,1796 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE04a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10005: {'type': 'attribModifier',
+ 'name': 'stomperPeriod',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'attribName': 'period',
+ 'recursive': 1,
+ 'typeName': 'stomper',
+ 'value': '2.2'},
+ 10015: {'type': 'attribModifier',
+ 'name': 'stomperShaftScale',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'attribName': 'shaftScale',
+ 'recursive': 1,
+ 'typeName': 'stomper',
+ 'value': 'Vec3(1,5,1)'},
+ 10067: {'type': 'attribModifier',
+ 'name': 'stomperDamage',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'attribName': 'damage',
+ 'recursive': 1,
+ 'typeName': 'stomper',
+ 'value': '8'},
+ 10130: {'type': 'attribModifier',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10153,
+ 'attribName': 'modelPath',
+ 'recursive': 1,
+ 'typeName': 'model',
+ 'value': "'phase_10/models/cogHQ/CBMetalCrate2.bam'"},
+ 10145: {'type': 'attribModifier',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'attribName': 'modelPath',
+ 'recursive': 1,
+ 'typeName': 'model',
+ 'value': "'phase_10/models/cogHQ/CBMetalCrate2.bam'"},
+ 10173: {'type': 'attribModifier',
+ 'name': 'copy of stomperShaftScale',
+ 'comment': '',
+ 'parentEntId': 10154,
+ 'attribName': 'shaftScale',
+ 'recursive': 1,
+ 'typeName': 'stomper',
+ 'value': 'Vec3(1,5,1)'},
+ 10169: {'type': 'gagBarrel',
+ 'name': 'gag',
+ 'comment': '',
+ 'parentEntId': 10170,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(1.0, 0.0, 0.0),
+ 'scale': 1,
+ 'gagLevel': 5,
+ 'gagLevelMax': 0,
+ 'gagTrack': 'random',
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 6},
+ 10171: {'type': 'gagBarrel',
+ 'name': 'gagLeft',
+ 'comment': '',
+ 'parentEntId': 10170,
+ 'pos': Point3(-3.0, 0.0, 0.0),
+ 'hpr': Vec3(-6.0, 0.0, 0.0),
+ 'scale': 1,
+ 'gagLevel': 5,
+ 'gagLevelMax': 0,
+ 'gagTrack': 'random',
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 6},
+ 10172: {'type': 'gagBarrel',
+ 'name': 'gagRight',
+ 'comment': '',
+ 'parentEntId': 10170,
+ 'pos': Point3(3.0, 0.0, 0.0),
+ 'hpr': Vec3(9.0, 0.0, 0.0),
+ 'scale': 1,
+ 'gagLevel': 5,
+ 'gagLevelMax': 0,
+ 'gagTrack': 'random',
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 6},
+ 10177: {'type': 'healBarrel',
+ 'name': 'healLeft',
+ 'comment': '',
+ 'parentEntId': 10170,
+ 'pos': Point3(-1.05319225788, 0.0, 4.12134313583),
+ 'hpr': Vec3(20.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 6},
+ 10178: {'type': 'healBarrel',
+ 'name': 'healRight',
+ 'comment': '',
+ 'parentEntId': 10170,
+ 'pos': Point3(2.16605138779, 0.0, 4.12134313583),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 5,
+ 'rewardPerGrabMax': 6},
+ 10181: {'type': 'healBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-2.92873573303, 110.585220337, 5.00378036499),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 10,
+ 'rewardPerGrabMax': 0},
+ 10023: {'type': 'mintShelf',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10017,
+ 'pos': Point3(-11.0, 0.0, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10026: {'type': 'mintShelf',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10017,
+ 'pos': Point3(11.0, 0.0, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10060: {'type': 'mintShelf',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10059,
+ 'pos': Point3(-11.0, 0.0, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10061: {'type': 'mintShelf',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10059,
+ 'pos': Point3(11.0, 0.0, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10064: {'type': 'mintShelf',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10065,
+ 'pos': Point3(-11.0, 0.0, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10066: {'type': 'mintShelf',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10065,
+ 'pos': Point3(11.0, 0.0, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10068: {'type': 'mintShelf',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10062,
+ 'pos': Point3(18.0, -1.0, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Point3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10069: {'type': 'mintShelf',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10062,
+ 'pos': Point3(-18.0, -1.0, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Point3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10093: {'type': 'mintShelf',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10092,
+ 'pos': Point3(-11.0, 0.0, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10094: {'type': 'mintShelf',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10092,
+ 'pos': Point3(11.0, 0.0, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10096: {'type': 'mintShelf',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10095,
+ 'pos': Point3(-11.0, 0.0, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10097: {'type': 'mintShelf',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10095,
+ 'pos': Point3(11.0, 0.0, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10099: {'type': 'mintShelf',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10098,
+ 'pos': Point3(-11.0, 0.0, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10100: {'type': 'mintShelf',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10098,
+ 'pos': Point3(11.0, 0.0, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10103: {'type': 'mintShelf',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10101,
+ 'pos': Point3(11.0, 0.0, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10159: {'type': 'mintShelf',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10157,
+ 'pos': Point3(11.0, 0.0, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10161: {'type': 'mintShelf',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10160,
+ 'pos': Point3(-11.0, 0.0, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10162: {'type': 'mintShelf',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10160,
+ 'pos': Point3(11.0, 0.0, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10164: {'type': 'mintShelf',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10163,
+ 'pos': Point3(-11.0, 0.0, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10165: {'type': 'mintShelf',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10163,
+ 'pos': Point3(11.0, 0.0, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'mintId': 12500},
+ 10001: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10002: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10003: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10008: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10006,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10009: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10006,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10011: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10012: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10013: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10014: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10022,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10024: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10020,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10025: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10020,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10027: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10021,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10028: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10021,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10029: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10022,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10030: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10022,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10036: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10033,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10037: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10033,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10038: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10033,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10039: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10034,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10040: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10034,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10041: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10034,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10042: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10035,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10043: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10035,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10048: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10044,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10049: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10044,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10050: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10045,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10051: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10045,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10052: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10047,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10053: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10047,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10076: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10075,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10077: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10075,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10078: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10075,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10079: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10047,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10084: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10081,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10085: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10081,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10086: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10081,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10087: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10082,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10088: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10082,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10089: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10082,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10090: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10083,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10091: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10083,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10102: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10072,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10108: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10105,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10109: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10105,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10110: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10072,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10111: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10106,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10112: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10106,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10113: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10072,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10114: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10107,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10115: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10107,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10116: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10107,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10121: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10118,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10122: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10118,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10123: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10118,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10124: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10119,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10125: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10119,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10126: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10119,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10127: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10120,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10128: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10120,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10129: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10073,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10131: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10073,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10132: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10074,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10133: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10074,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10134: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10074,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10139: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10136,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10140: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10136,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10141: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10136,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10143: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10137,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10146: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10137,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10147: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10138,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10148: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10138,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10149: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10144,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10150: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10144,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10151: {'type': 'model',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10144,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10152: {'type': 'model',
+ 'name': 'middle',
+ 'comment': '',
+ 'parentEntId': 10138,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'},
+ 10158: {'type': 'model',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10157,
+ 'pos': Point3(-11.0, 0.0, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/shelf_A1.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'crateField',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, -51.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10004: {'type': 'nodepath',
+ 'name': 'row0',
+ 'comment': '',
+ 'parentEntId': 10018,
+ 'pos': Point3(0.0, -6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10006: {'type': 'nodepath',
+ 'name': 'row1',
+ 'comment': '',
+ 'parentEntId': 10018,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10007: {'type': 'nodepath',
+ 'name': 'crates',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 0.800000011921)},
+ 10010: {'type': 'nodepath',
+ 'name': 'row2',
+ 'comment': '',
+ 'parentEntId': 10018,
+ 'pos': Point3(0.0, 6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10017: {'type': 'nodepath',
+ 'name': 'wall5',
+ 'comment': '',
+ 'parentEntId': 10063,
+ 'pos': Point3(0.0, 90.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10018: {'type': 'nodepath',
+ 'name': 'crateSquare0',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10019: {'type': 'nodepath',
+ 'name': 'crateSquare1',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 18.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10020: {'type': 'nodepath',
+ 'name': 'row2',
+ 'comment': '',
+ 'parentEntId': 10019,
+ 'pos': Point3(0.0, 6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10021: {'type': 'nodepath',
+ 'name': 'row0',
+ 'comment': '',
+ 'parentEntId': 10019,
+ 'pos': Point3(0.0, -6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10022: {'type': 'nodepath',
+ 'name': 'row1',
+ 'comment': '',
+ 'parentEntId': 10019,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10031: {'type': 'nodepath',
+ 'name': 'crateSquare3',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 54.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10032: {'type': 'nodepath',
+ 'name': 'crateSquare2',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 36.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10033: {'type': 'nodepath',
+ 'name': 'row2',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(0.0, 6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10034: {'type': 'nodepath',
+ 'name': 'row0',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(0.0, -6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10035: {'type': 'nodepath',
+ 'name': 'row1',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10044: {'type': 'nodepath',
+ 'name': 'row2',
+ 'comment': '',
+ 'parentEntId': 10031,
+ 'pos': Point3(0.0, 6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10045: {'type': 'nodepath',
+ 'name': 'row0',
+ 'comment': '',
+ 'parentEntId': 10031,
+ 'pos': Point3(0.0, -6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10046: {'type': 'nodepath',
+ 'name': 'stompers',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(-1.0, 0.0, 4.40000009537),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10047: {'type': 'nodepath',
+ 'name': 'row1',
+ 'comment': '',
+ 'parentEntId': 10031,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10059: {'type': 'nodepath',
+ 'name': 'wall6',
+ 'comment': '',
+ 'parentEntId': 10063,
+ 'pos': Point3(0.0, 108.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10062: {'type': 'nodepath',
+ 'name': 'wall7',
+ 'comment': '',
+ 'parentEntId': 10063,
+ 'pos': Point3(0.0, 124.5, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.39999997616, 1.0)},
+ 10063: {'type': 'nodepath',
+ 'name': 'walls',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(0.0, -0.019999999553, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10065: {'type': 'nodepath',
+ 'name': 'wall0',
+ 'comment': '',
+ 'parentEntId': 10063,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10070: {'type': 'nodepath',
+ 'name': 'leftBranch',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(-17.8978881836, 72.0, 0.0),
+ 'hpr': Vec3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10071: {'type': 'nodepath',
+ 'name': 'crateSquare0',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10153,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10072: {'type': 'nodepath',
+ 'name': 'row2',
+ 'comment': '',
+ 'parentEntId': 10071,
+ 'pos': Point3(0.0, 6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10073: {'type': 'nodepath',
+ 'name': 'row0',
+ 'comment': '',
+ 'parentEntId': 10071,
+ 'pos': Point3(0.0, -6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10074: {'type': 'nodepath',
+ 'name': 'row1',
+ 'comment': '',
+ 'parentEntId': 10071,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10075: {'type': 'nodepath',
+ 'name': 'frontCrateRow',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, -12.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.844285488129, 1.0, 1.0)},
+ 10080: {'type': 'nodepath',
+ 'name': 'crateSquare4',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 72.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10081: {'type': 'nodepath',
+ 'name': 'row2',
+ 'comment': '',
+ 'parentEntId': 10080,
+ 'pos': Point3(0.0, 6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10082: {'type': 'nodepath',
+ 'name': 'row0',
+ 'comment': '',
+ 'parentEntId': 10080,
+ 'pos': Point3(0.0, -6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10083: {'type': 'nodepath',
+ 'name': 'row1',
+ 'comment': '',
+ 'parentEntId': 10080,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10092: {'type': 'nodepath',
+ 'name': 'wall1',
+ 'comment': '',
+ 'parentEntId': 10063,
+ 'pos': Point3(0.0, 18.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10095: {'type': 'nodepath',
+ 'name': 'wall2',
+ 'comment': '',
+ 'parentEntId': 10063,
+ 'pos': Point3(0.0, 36.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10098: {'type': 'nodepath',
+ 'name': 'wall3',
+ 'comment': '',
+ 'parentEntId': 10063,
+ 'pos': Point3(0.0, 54.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10101: {'type': 'nodepath',
+ 'name': 'wall4',
+ 'comment': '',
+ 'parentEntId': 10063,
+ 'pos': Point3(0.0, 72.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10104: {'type': 'nodepath',
+ 'name': 'crateSquare5',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 90.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10105: {'type': 'nodepath',
+ 'name': 'row2',
+ 'comment': '',
+ 'parentEntId': 10104,
+ 'pos': Point3(0.0, 6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10106: {'type': 'nodepath',
+ 'name': 'row0',
+ 'comment': '',
+ 'parentEntId': 10104,
+ 'pos': Point3(0.0, -6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10107: {'type': 'nodepath',
+ 'name': 'row1',
+ 'comment': '',
+ 'parentEntId': 10104,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10117: {'type': 'nodepath',
+ 'name': 'crateSquare6',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 108.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10118: {'type': 'nodepath',
+ 'name': 'row2',
+ 'comment': '',
+ 'parentEntId': 10117,
+ 'pos': Point3(0.0, 6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10119: {'type': 'nodepath',
+ 'name': 'row0',
+ 'comment': '',
+ 'parentEntId': 10117,
+ 'pos': Point3(0.0, -6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10120: {'type': 'nodepath',
+ 'name': 'row1',
+ 'comment': '',
+ 'parentEntId': 10117,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10135: {'type': 'nodepath',
+ 'name': 'crateSquare1',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10153,
+ 'pos': Point3(0.0, 18.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10136: {'type': 'nodepath',
+ 'name': 'row2',
+ 'comment': '',
+ 'parentEntId': 10135,
+ 'pos': Point3(0.0, 6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10137: {'type': 'nodepath',
+ 'name': 'row0',
+ 'comment': '',
+ 'parentEntId': 10135,
+ 'pos': Point3(0.0, -6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10138: {'type': 'nodepath',
+ 'name': 'row1',
+ 'comment': '',
+ 'parentEntId': 10135,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10142: {'type': 'nodepath',
+ 'name': 'crateSquare7',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 126.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10144: {'type': 'nodepath',
+ 'name': 'row0',
+ 'comment': '',
+ 'parentEntId': 10142,
+ 'pos': Point3(0.0, -6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10153: {'type': 'nodepath',
+ 'name': 'crates',
+ 'comment': '',
+ 'parentEntId': 10070,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 0.800000011921)},
+ 10154: {'type': 'nodepath',
+ 'name': 'stompers',
+ 'comment': '',
+ 'parentEntId': 10070,
+ 'pos': Point3(-1.0, 0.0, 4.40000009537),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10156: {'type': 'nodepath',
+ 'name': 'walls',
+ 'comment': '',
+ 'parentEntId': 10070,
+ 'pos': Point3(0.0, 6.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10157: {'type': 'nodepath',
+ 'name': 'wall0',
+ 'comment': '',
+ 'parentEntId': 10156,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10160: {'type': 'nodepath',
+ 'name': 'wall1',
+ 'comment': '',
+ 'parentEntId': 10156,
+ 'pos': Point3(0.0, 18.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10163: {'type': 'nodepath',
+ 'name': 'wall2',
+ 'comment': '',
+ 'parentEntId': 10156,
+ 'pos': Point3(0.0, 36.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10168: {'type': 'nodepath',
+ 'name': 'safeArea',
+ 'comment': '',
+ 'parentEntId': 10070,
+ 'pos': Point3(0.0, 40.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10170: {'type': 'nodepath',
+ 'name': 'barrels',
+ 'comment': '',
+ 'parentEntId': 10168,
+ 'pos': Point3(0.0, 9.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10016: {'type': 'stomper',
+ 'name': 'stomper6',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10046,
+ 'pos': Point3(1.0, 108.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'animateShadow': 1,
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Point3(9.0, 9.0, 9.0),
+ 'modelPath': 0,
+ 'motion': 2,
+ 'period': 2.2,
+ 'phaseShift': 0.0,
+ 'range': 13.0,
+ 'removeCamBarrierCollisions': 1,
+ 'removeHeadFloor': 1,
+ 'shaftScale': Vec3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 1,
+ 'soundPath': 2,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 0,
+ 'zOffset': 0},
+ 10054: {'type': 'stomper',
+ 'name': 'stomper1',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'pos': Point3(1.0, 18.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'animateShadow': 1,
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Point3(9.0, 9.0, 9.0),
+ 'modelPath': 0,
+ 'motion': 2,
+ 'period': 2.2,
+ 'phaseShift': 0.0,
+ 'range': 13.0,
+ 'removeCamBarrierCollisions': 1,
+ 'removeHeadFloor': 1,
+ 'shaftScale': Vec3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 0,
+ 'soundPath': 2,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 0,
+ 'zOffset': 0},
+ 10055: {'type': 'stomper',
+ 'name': 'stomper0',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10046,
+ 'pos': Point3(1.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'animateShadow': 1,
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Point3(9.0, 9.0, 9.0),
+ 'modelPath': 0,
+ 'motion': 2,
+ 'period': 2.2,
+ 'phaseShift': 0.0,
+ 'range': 13.0,
+ 'removeCamBarrierCollisions': 1,
+ 'removeHeadFloor': 1,
+ 'shaftScale': Vec3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 1,
+ 'soundPath': 2,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 0,
+ 'zOffset': 0},
+ 10056: {'type': 'stomper',
+ 'name': 'stomper2',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'pos': Point3(1.0, 36.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'animateShadow': 1,
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Point3(9.0, 9.0, 9.0),
+ 'modelPath': 0,
+ 'motion': 2,
+ 'period': 2.2,
+ 'phaseShift': 0.0,
+ 'range': 13.0,
+ 'removeCamBarrierCollisions': 1,
+ 'removeHeadFloor': 1,
+ 'shaftScale': Vec3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 0,
+ 'soundPath': 0,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 0,
+ 'zOffset': 0},
+ 10057: {'type': 'stomper',
+ 'name': 'stomper3',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'pos': Point3(1.0, 54.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'animateShadow': 1,
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Point3(9.0, 9.0, 9.0),
+ 'modelPath': 0,
+ 'motion': 2,
+ 'period': 2.2,
+ 'phaseShift': 0.0,
+ 'range': 13.0,
+ 'removeCamBarrierCollisions': 1,
+ 'removeHeadFloor': 1,
+ 'shaftScale': Vec3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 0,
+ 'soundPath': 2,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 0,
+ 'zOffset': 0},
+ 10058: {'type': 'stomper',
+ 'name': 'stomper4',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'pos': Point3(1.0, 72.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'animateShadow': 1,
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Point3(9.0, 9.0, 9.0),
+ 'modelPath': 0,
+ 'motion': 2,
+ 'period': 2.2,
+ 'phaseShift': 0.0,
+ 'range': 13.0,
+ 'removeCamBarrierCollisions': 1,
+ 'removeHeadFloor': 1,
+ 'shaftScale': Vec3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 0,
+ 'soundPath': 2,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 0,
+ 'zOffset': 0},
+ 10155: {'type': 'stomper',
+ 'name': 'stomper5',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'pos': Point3(1.0, 90.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'animateShadow': 1,
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Point3(9.0, 9.0, 9.0),
+ 'modelPath': 0,
+ 'motion': 2,
+ 'period': 2.2,
+ 'phaseShift': 0.0,
+ 'range': 13.0,
+ 'removeCamBarrierCollisions': 1,
+ 'removeHeadFloor': 1,
+ 'shaftScale': Vec3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 0,
+ 'soundPath': 0,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 0,
+ 'zOffset': 0},
+ 10166: {'type': 'stomper',
+ 'name': 'stomper0',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10154,
+ 'pos': Point3(1.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'animateShadow': 1,
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Point3(9.0, 9.0, 9.0),
+ 'modelPath': 0,
+ 'motion': 2,
+ 'period': 2.2,
+ 'phaseShift': 0.0,
+ 'range': 13.0,
+ 'removeCamBarrierCollisions': 1,
+ 'removeHeadFloor': 1,
+ 'shaftScale': Vec3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 0,
+ 'soundPath': 0,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 0,
+ 'zOffset': 0},
+ 10167: {'type': 'stomper',
+ 'name': 'stomper1',
+ 'comment': 'Y=N*18',
+ 'parentEntId': 10154,
+ 'pos': Point3(1.0, 18.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'animateShadow': 1,
+ 'crushCellId': None,
+ 'damage': 8,
+ 'headScale': Point3(9.0, 9.0, 9.0),
+ 'modelPath': 0,
+ 'motion': 2,
+ 'period': 2.2,
+ 'phaseShift': 0.0,
+ 'range': 13.0,
+ 'removeCamBarrierCollisions': 1,
+ 'removeHeadFloor': 1,
+ 'shaftScale': Vec3(1.0, 5.0, 1.0),
+ 'soundLen': 0,
+ 'soundOn': 0,
+ 'soundPath': 0,
+ 'style': 'vertical',
+ 'switchId': 0,
+ 'wantShadow': 1,
+ 'wantSmoke': 0,
+ 'zOffset': 0}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintLobby_Battle00.py b/toontown/coghq/CashbotMintLobby_Battle00.py
new file mode 100644
index 0000000..bda6056
--- /dev/null
+++ b/toontown/coghq/CashbotMintLobby_Battle00.py
@@ -0,0 +1,429 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE04a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10001: {'type': 'battleBlocker',
+ 'name': 'exitBlocker',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 76.2264404297, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'cellId': 0,
+ 'radius': 10.0},
+ 10021: {'type': 'battleBlocker',
+ 'name': 'middleBlocker',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(9.79564476013, 7.17855405807, 0.0),
+ 'hpr': Vec3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.61347305775, 0.225867271423, 1.99822974205),
+ 'cellId': 1,
+ 'radius': 10.0},
+ 10061: {'type': 'battleBlocker',
+ 'name': 'frontBlocker',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-45.6075019836, -22.7538051605, 0.0),
+ 'hpr': Vec3(45.0, 0.0, 0.0),
+ 'scale': Vec3(1.61347305775, 0.225867271423, 1.99822974205),
+ 'cellId': 2,
+ 'radius': 10.0},
+ 10025: {'type': 'mintProductPallet',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10024,
+ 'pos': Point3(0.0, 7.96000003815, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10031: {'type': 'mintProductPallet',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10024,
+ 'pos': Point3(-32.7900009155, -5.48999977112, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10032: {'type': 'mintProductPallet',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(-25.0, 9.05000019073, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10033: {'type': 'mintProductPallet',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(26.8400001526, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10003: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-60.8400001526, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10004: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10005: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-47.4124145508, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10006: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10005,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10007: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-33.9436340332, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10008: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10009: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-20.4898967743, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10010: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10009,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10011: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(60.7196426392, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10012: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10013: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(33.2060928345, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10014: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10013,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10015: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(19.7813663483, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10016: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10015,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10017: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(-7.05515527725, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10018: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10017,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10019: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(6.35370635986, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10020: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10019,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10042: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10041,
+ 'pos': Point3(-7.05515527725, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10043: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10041,
+ 'pos': Point3(-60.8400001526, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10044: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10041,
+ 'pos': Point3(6.35370635986, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10045: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10041,
+ 'pos': Point3(-47.4124145508, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10046: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10041,
+ 'pos': Point3(-33.9436340332, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10047: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10041,
+ 'pos': Point3(-20.4898967743, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10048: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10041,
+ 'pos': Point3(60.7196426392, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10049: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10041,
+ 'pos': Point3(33.2060928345, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10050: {'type': 'mintShelf',
+ 'name': 'bookshelf',
+ 'comment': '',
+ 'parentEntId': 10041,
+ 'pos': Point3(19.7813663483, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10051: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10042,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10052: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10043,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10053: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10044,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10054: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10045,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10055: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10046,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10056: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10047,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10057: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10048,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10058: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10049,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10059: {'type': 'mintShelf',
+ 'name': 'copy of bookshelf',
+ 'comment': '',
+ 'parentEntId': 10050,
+ 'pos': Point3(0.180000007153, 6.85808324814, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 58.7970542908, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10002: {'type': 'nodepath',
+ 'name': 'backWall',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 22.0885009766, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10022: {'type': 'nodepath',
+ 'name': 'middleCogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 7.5760216713, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Point3(1.0, 1.0, 1.0)},
+ 10023: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10024: {'type': 'nodepath',
+ 'name': 'frontMoney',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(22.4126205444, -39.3388214111, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10028: {'type': 'nodepath',
+ 'name': 'backMoney',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(0.0, 48.498249054, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10041: {'type': 'nodepath',
+ 'name': 'backWall',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, -6.69597911835, 0.0),
+ 'hpr': Vec3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10060: {'type': 'nodepath',
+ 'name': 'frontCogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-28.8658733368, -31.173248291, 0.0),
+ 'hpr': Vec3(51.3401908875, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintLobby_Battle00_Cogs.py b/toontown/coghq/CashbotMintLobby_Battle00_Cogs.py
new file mode 100644
index 0000000..768dd1f
--- /dev/null
+++ b/toontown/coghq/CashbotMintLobby_Battle00_Cogs.py
@@ -0,0 +1,123 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+MidCogParent = 10022
+FrontCogParent = 10060
+BattleCellId = 0
+MidBattleCellId = 1
+FrontBattleCellId = 2
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)},
+ MidBattleCellId: {'parentEntId': MidCogParent,
+ 'pos': Point3(0, 0, 0)},
+ FrontBattleCellId: {'parentEntId': FrontCogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': MidCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': MidBattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': MidCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': MidBattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': MidCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': MidBattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': MidCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': MidBattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintLobby_Battle01.py b/toontown/coghq/CashbotMintLobby_Battle01.py
new file mode 100644
index 0000000..ae383de
--- /dev/null
+++ b/toontown/coghq/CashbotMintLobby_Battle01.py
@@ -0,0 +1,221 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE04a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10001: {'type': 'locator',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'searchPath': '**/DGG.EXIT'},
+ 10014: {'type': 'mintProduct',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10013,
+ 'pos': Point3(-7.80045461655, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10015: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10013,
+ 'pos': Point3(15.7111005783, -2.0225315094, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10016: {'type': 'mintProduct',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10013,
+ 'pos': Point3(6.66144704819, -1.54405653477, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10017: {'type': 'mintProductPallet',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10020,
+ 'pos': Point3(34.8923339844, 32.3863983154, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10021: {'type': 'mintProductPallet',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10020,
+ 'pos': Point3(-34.8899993896, 32.3863983154, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12600},
+ 10022: {'type': 'mintProductPallet',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10020,
+ 'pos': Point3(34.8923339844, -23.3355064392, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12600},
+ 10023: {'type': 'mintProductPallet',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10020,
+ 'pos': Point3(-34.8899993896, -23.3355064392, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12600},
+ 10002: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'},
+ 10005: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(26.7393283844, 70.7373962402, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'},
+ 10006: {'type': 'model',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(63.8051605225, 33.5718955994, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'},
+ 10007: {'type': 'model',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(63.7982444763, -19.1328792572, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'},
+ 10008: {'type': 'model',
+ 'name': 'copy of (3)',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(26.769536972, -56.2981185913, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'},
+ 10009: {'type': 'model',
+ 'name': 'copy of (4)',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-26.0181350708, -56.2504425049, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'},
+ 10010: {'type': 'model',
+ 'name': 'copy of (5)',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-63.1796150208, -19.1894207001, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'},
+ 10011: {'type': 'model',
+ 'name': 'copy of (6)',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-63.194984436, 33.6143226624, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'},
+ 10012: {'type': 'model',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-26.161315918, 70.7677078247, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10003: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10004: {'type': 'nodepath',
+ 'name': 'posts',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10013: {'type': 'nodepath',
+ 'name': 'productByVault',
+ 'comment': '',
+ 'parentEntId': 10020,
+ 'pos': Point3(0.0, 67.5628051758, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10020: {'type': 'nodepath',
+ 'name': 'product',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintLobby_Battle01_Cogs.py b/toontown/coghq/CashbotMintLobby_Battle01_Cogs.py
new file mode 100644
index 0000000..61883ec
--- /dev/null
+++ b/toontown/coghq/CashbotMintLobby_Battle01_Cogs.py
@@ -0,0 +1,43 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 1,
+ 'level': ToontownGlobals.CashbotMintBossLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintOilRoom_Battle00.py b/toontown/coghq/CashbotMintOilRoom_Battle00.py
new file mode 100644
index 0000000..8166237
--- /dev/null
+++ b/toontown/coghq/CashbotMintOilRoom_Battle00.py
@@ -0,0 +1,43 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE22a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10001: {'type': 'model',
+ 'name': 'vaultDoor',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(7.2503657341, -35.8064537048, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(180.0, 0.0, 0.0),
+ 'scale': 1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintOilRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintOilRoom_Battle00_Cogs.py
new file mode 100644
index 0000000..61883ec
--- /dev/null
+++ b/toontown/coghq/CashbotMintOilRoom_Battle00_Cogs.py
@@ -0,0 +1,43 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 1,
+ 'level': ToontownGlobals.CashbotMintBossLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintPaintMixerReward_Battle00.py b/toontown/coghq/CashbotMintPaintMixerReward_Battle00.py
new file mode 100644
index 0000000..1d01190
--- /dev/null
+++ b/toontown/coghq/CashbotMintPaintMixerReward_Battle00.py
@@ -0,0 +1,172 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE11a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10024: {'type': 'mintProduct',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(-4.0706076622, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10003: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12600},
+ 10005: {'type': 'mintShelf',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10006,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12600},
+ 10007: {'type': 'mintShelf',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10006,
+ 'pos': Point3(13.4306573868, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12600},
+ 10009: {'type': 'mintShelf',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(-13.4300003052, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12600},
+ 10013: {'type': 'mintShelf',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(-13.4300003052, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12600},
+ 10014: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12600},
+ 10015: {'type': 'mintShelf',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10012,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12600},
+ 10016: {'type': 'mintShelf',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10012,
+ 'pos': Point3(13.4306573868, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12600},
+ 10001: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 22.2368240356, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10002: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10004: {'type': 'nodepath',
+ 'name': 'backWall',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.0, 19.0782051086, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10006: {'type': 'nodepath',
+ 'name': 'rightShelves',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(17.2106304169, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10008: {'type': 'nodepath',
+ 'name': 'leftShelves',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-17.4771251678, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10010: {'type': 'nodepath',
+ 'name': 'frontWall',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.0, -19.1254119873, 0.0),
+ 'hpr': Point3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10011: {'type': 'nodepath',
+ 'name': 'leftShelves',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(-17.4771251678, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10012: {'type': 'nodepath',
+ 'name': 'rightShelves',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(17.2106304169, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10023: {'type': 'nodepath',
+ 'name': 'byVaultDoor',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.0, 16.0373382568, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintPaintMixerReward_Battle00_Cogs.py b/toontown/coghq/CashbotMintPaintMixerReward_Battle00_Cogs.py
new file mode 100644
index 0000000..61883ec
--- /dev/null
+++ b/toontown/coghq/CashbotMintPaintMixerReward_Battle00_Cogs.py
@@ -0,0 +1,43 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+BattleCellId = 0
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 1,
+ 'level': ToontownGlobals.CashbotMintBossLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 1},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintPaintMixer_Action00.py b/toontown/coghq/CashbotMintPaintMixer_Action00.py
new file mode 100644
index 0000000..8f13bbc
--- /dev/null
+++ b/toontown/coghq/CashbotMintPaintMixer_Action00.py
@@ -0,0 +1,99 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE10a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10009: {'type': 'healBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(63.9741363525, -10.9343223572, 9.97696113586),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 8,
+ 'rewardPerGrabMax': 0},
+ 10010: {'type': 'healBarrel',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10009,
+ 'pos': Point3(0.0, 0.0, 4.13999986649),
+ 'hpr': Vec3(349.358764648, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 8,
+ 'rewardPerGrabMax': 0},
+ 10000: {'type': 'nodepath',
+ 'name': 'mixers',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-19.2397289276, 0.0, 5.53999996185),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.758001744747, 0.758001744747, 0.758001744747)},
+ 10004: {'type': 'paintMixer',
+ 'name': 'mixer0',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(0.0, 10.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'floorName': 'PaintMixerFloorCollision',
+ 'modelPath': 'phase_9/models/cogHQ/PaintMixer',
+ 'modelScale': Vec3(1.0, 1.0, 1.0),
+ 'motion': 'easeInOut',
+ 'offset': Point3(20.0, 20.0, 0.0),
+ 'period': 8.0,
+ 'phaseShift': 0.0,
+ 'shaftScale': 1,
+ 'waitPercent': 0.1},
+ 10005: {'type': 'paintMixer',
+ 'name': 'mixer1',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(29.0, 10.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'floorName': 'PaintMixerFloorCollision',
+ 'modelPath': 'phase_9/models/cogHQ/PaintMixer',
+ 'modelScale': Vec3(1.0, 1.0, 1.0),
+ 'motion': 'easeInOut',
+ 'offset': Point3(0.0, -20.0, 0.0),
+ 'period': 8.0,
+ 'phaseShift': 0.5,
+ 'shaftScale': 1,
+ 'waitPercent': 0.1},
+ 10006: {'type': 'paintMixer',
+ 'name': 'mixer2',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(58.0, -8.94072246552, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'floorName': 'PaintMixerFloorCollision',
+ 'modelPath': 'phase_9/models/cogHQ/PaintMixer',
+ 'modelScale': Vec3(1.0, 1.0, 1.0),
+ 'motion': 'easeInOut',
+ 'offset': Point3(-20.0, -20.0, 0.0),
+ 'period': 8.0,
+ 'phaseShift': 0.5,
+ 'shaftScale': 1,
+ 'waitPercent': 0.1}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintPipeRoom_Action00.py b/toontown/coghq/CashbotMintPipeRoom_Action00.py
new file mode 100644
index 0000000..4bef84e
--- /dev/null
+++ b/toontown/coghq/CashbotMintPipeRoom_Action00.py
@@ -0,0 +1,585 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE13a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10025: {'type': 'attribModifier',
+ 'name': 'strength',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'attribName': 'strength',
+ 'recursive': 1,
+ 'typeName': 'goon',
+ 'value': '10'},
+ 10001: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10000,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4},
+ 10005: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10003,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4},
+ 10006: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4},
+ 10014: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10013,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4},
+ 10016: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10015,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4},
+ 10018: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10017,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4},
+ 10021: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10020,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4},
+ 10024: {'type': 'goon',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10023,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1.5,
+ 'attackRadius': 15,
+ 'crushCellId': None,
+ 'goonType': 'pg',
+ 'gridId': None,
+ 'hFov': 70,
+ 'strength': 10,
+ 'velocity': 4},
+ 10035: {'type': 'healBarrel',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10037,
+ 'pos': Point3(-56.3795814514, 0.0, 0.0),
+ 'hpr': Vec3(106.821411133, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 7,
+ 'rewardPerGrabMax': 8},
+ 10036: {'type': 'healBarrel',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10037,
+ 'pos': Point3(15.3852472305, 21.0357513428, 0.0),
+ 'hpr': Vec3(52.4314079285, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'rewardPerGrab': 7,
+ 'rewardPerGrabMax': 8},
+ 10029: {'type': 'model',
+ 'name': 'rightPillar',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(0.0, -22.3441867828, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10030: {'type': 'model',
+ 'name': 'leftPillar',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(0.0, 21.9451503754, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10033: {'type': 'model',
+ 'name': 'backPillar',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(41.4432792664, 0.0, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10034: {'type': 'model',
+ 'name': 'frontPillar',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(-41.0848464966, 0.0, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10039: {'type': 'model',
+ 'name': 'rightPillar',
+ 'comment': '',
+ 'parentEntId': 10038,
+ 'pos': Point3(0.0, -66.8615875244, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10040: {'type': 'model',
+ 'name': 'leftPillar',
+ 'comment': '',
+ 'parentEntId': 10038,
+ 'pos': Point3(0.0, 67.0966033936, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10042: {'type': 'model',
+ 'name': 'frontRightPillar',
+ 'comment': '',
+ 'parentEntId': 10043,
+ 'pos': Point3(0.0, -22.5711078644, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10044: {'type': 'model',
+ 'name': 'frontLeftPillar',
+ 'comment': '',
+ 'parentEntId': 10043,
+ 'pos': Point3(0.0, 22.1686630249, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10046: {'type': 'model',
+ 'name': 'frontRightPillar',
+ 'comment': '',
+ 'parentEntId': 10045,
+ 'pos': Point3(0.0, -22.5711078644, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10047: {'type': 'model',
+ 'name': 'frontLeftPillar',
+ 'comment': '',
+ 'parentEntId': 10045,
+ 'pos': Point3(0.0, 22.1686630249, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10049: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10048,
+ 'pos': Point3(0.949898421764, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.29060935974, 1.29060935974, 1.29060935974),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'},
+ 10050: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10048,
+ 'pos': Point3(-13.1818971634, -7.17138242722, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'},
+ 10051: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10048,
+ 'pos': Point3(0.968334257603, -13.3785037994, 0.0),
+ 'hpr': Vec3(180.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'},
+ 10053: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10052,
+ 'pos': Point3(0.606362164021, -12.1353359222, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_G1.bam'},
+ 10054: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10052,
+ 'pos': Point3(7.85215950012, 20.0426883698, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.16659212112, 1.16659212112, 1.16659212112),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'},
+ 10055: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10052,
+ 'pos': Point3(13.5166940689, -0.819138884544, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.51914477348, 1.51914477348, 1.51914477348),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_D.bam'},
+ 10056: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10052,
+ 'pos': Point3(10.8745326996, 4.61703014374, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'},
+ 10057: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10052,
+ 'pos': Point3(31.8470001221, -14.5645837784, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.1728117466, 1.1728117466, 1.1728117466),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_A.bam'},
+ 10058: {'type': 'model',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10052,
+ 'pos': Point3(31.9369258881, 14.3037395477, 0.0),
+ 'hpr': Point3(90.0, 0.0, 0.0),
+ 'scale': Vec3(1.1728117466, 1.1728117466, 1.1728117466),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/crates_A.bam'},
+ 10002: {'type': 'nodepath',
+ 'name': 'goons',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Point3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10007: {'type': 'nodepath',
+ 'name': 'rightElbow',
+ 'comment': '',
+ 'parentEntId': 10027,
+ 'pos': Point3(43.3083152771, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10008: {'type': 'nodepath',
+ 'name': 'leftElbow',
+ 'comment': '',
+ 'parentEntId': 10026,
+ 'pos': Point3(-38.578956604, -2.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10009: {'type': 'nodepath',
+ 'name': 'nearRight',
+ 'comment': '',
+ 'parentEntId': 10027,
+ 'pos': Point3(25.6041526794, -41.3753585815, 0.0),
+ 'hpr': Vec3(320.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10010: {'type': 'nodepath',
+ 'name': 'nearLeft',
+ 'comment': '',
+ 'parentEntId': 10026,
+ 'pos': Point3(-25.6000003815, -41.3800010681, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10011: {'type': 'nodepath',
+ 'name': 'farRight',
+ 'comment': '',
+ 'parentEntId': 10027,
+ 'pos': Point3(25.6000003815, 41.3800010681, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10012: {'type': 'nodepath',
+ 'name': 'farLeft',
+ 'comment': '',
+ 'parentEntId': 10026,
+ 'pos': Point3(-25.6000003815, 41.3800010681, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10019: {'type': 'nodepath',
+ 'name': 'entrance',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.0, -82.5020980835, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10022: {'type': 'nodepath',
+ 'name': 'exit',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.0, 88.4478759766, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10026: {'type': 'nodepath',
+ 'name': 'left',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10027: {'type': 'nodepath',
+ 'name': 'right',
+ 'comment': '',
+ 'parentEntId': 10002,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10028: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, -1.80477809906, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10031: {'type': 'nodepath',
+ 'name': 'pillars',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10032: {'type': 'nodepath',
+ 'name': 'centerPillars',
+ 'comment': '',
+ 'parentEntId': 10031,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10037: {'type': 'nodepath',
+ 'name': 'barrels',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(102.779998779, -1.24000000954, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10038: {'type': 'nodepath',
+ 'name': 'outerPillars',
+ 'comment': '',
+ 'parentEntId': 10031,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10043: {'type': 'nodepath',
+ 'name': 'frontPillars',
+ 'comment': '',
+ 'parentEntId': 10031,
+ 'pos': Point3(-89.9665527344, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10045: {'type': 'nodepath',
+ 'name': 'backPillars',
+ 'comment': '',
+ 'parentEntId': 10031,
+ 'pos': Point3(89.9700012207, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10048: {'type': 'nodepath',
+ 'name': 'frontProps',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(-100.412567139, -10.8835134506, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.66847121716, 1.66847121716, 1.66847121716)},
+ 10052: {'type': 'nodepath',
+ 'name': 'backProps',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(100.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10000: {'type': 'path',
+ 'name': 'triangle',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'pathIndex': 1,
+ 'pathScale': 1.5},
+ 10003: {'type': 'path',
+ 'name': 'square',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'pathIndex': 0,
+ 'pathScale': 1.0},
+ 10004: {'type': 'path',
+ 'name': 'bowtie',
+ 'comment': '',
+ 'parentEntId': 10009,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'pathIndex': 2,
+ 'pathScale': 1.0},
+ 10013: {'type': 'path',
+ 'name': 'square',
+ 'comment': '',
+ 'parentEntId': 10010,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'pathIndex': 0,
+ 'pathScale': 1.0},
+ 10015: {'type': 'path',
+ 'name': 'square',
+ 'comment': '',
+ 'parentEntId': 10011,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'pathIndex': 0,
+ 'pathScale': 1.0},
+ 10017: {'type': 'path',
+ 'name': 'square',
+ 'comment': '',
+ 'parentEntId': 10012,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'pathIndex': 0,
+ 'pathScale': 1.0},
+ 10020: {'type': 'path',
+ 'name': 'pace',
+ 'comment': '',
+ 'parentEntId': 10019,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'pathIndex': 3,
+ 'pathScale': 1.0},
+ 10023: {'type': 'path',
+ 'name': 'pace',
+ 'comment': '',
+ 'parentEntId': 10022,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'pathIndex': 3,
+ 'pathScale': 1.0}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintPipeRoom_Battle00.py b/toontown/coghq/CashbotMintPipeRoom_Battle00.py
new file mode 100644
index 0000000..da9120c
--- /dev/null
+++ b/toontown/coghq/CashbotMintPipeRoom_Battle00.py
@@ -0,0 +1,119 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE13a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10001: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(135.90512085, -2.06722736359, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'cellId': 0,
+ 'radius': 10.0},
+ 10003: {'type': 'battleBlocker',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-86.7350921631, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(0.378204703331, 2.94127488136, 2.80858683586),
+ 'cellId': 1,
+ 'radius': 10},
+ 10005: {'type': 'model',
+ 'name': 'farLeft',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(45.6502952576, 31.8647651672, 0.228899106383),
+ 'hpr': Vec3(333.434936523, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'},
+ 10006: {'type': 'model',
+ 'name': 'farRight',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(50.0559577942, -32.7609710693, 0.228899106383),
+ 'hpr': Vec3(206.565048218, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'},
+ 10008: {'type': 'model',
+ 'name': 'nearLeft',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-47.9696311951, 31.8647651672, 0.228899106383),
+ 'hpr': Vec3(26.5650520325, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'},
+ 10009: {'type': 'model',
+ 'name': 'nearRight',
+ 'comment': '',
+ 'parentEntId': 10004,
+ 'pos': Point3(-43.6387481689, -36.8883552551, 0.228899106383),
+ 'hpr': Vec3(154.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'},
+ 10000: {'type': 'nodepath',
+ 'name': 'cogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(102.420700073, 0.0, 0.0),
+ 'hpr': Point3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10002: {'type': 'nodepath',
+ 'name': 'frontCogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(-94.4453353882, 0.0, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10004: {'type': 'nodepath',
+ 'name': 'props',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1},
+ 10007: {'type': 'nodepath',
+ 'name': 'leftCogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, 40.9584655762, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)},
+ 10010: {'type': 'nodepath',
+ 'name': 'rightCogs',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'pos': Point3(0.0, -40.9599990845, 0.0),
+ 'hpr': Vec3(270.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0)}}
+Scenario0 = {}
+levelSpec = {'globalEntities': GlobalEntities,
+ 'scenarios': [Scenario0]}
diff --git a/toontown/coghq/CashbotMintPipeRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintPipeRoom_Battle00_Cogs.py
new file mode 100644
index 0000000..2797752
--- /dev/null
+++ b/toontown/coghq/CashbotMintPipeRoom_Battle00_Cogs.py
@@ -0,0 +1,163 @@
+from SpecImports import *
+from toontown.toonbase import ToontownGlobals
+CogParent = 10000
+FrontCogParent = 10002
+LeftCogParent = 10007
+RightCogParent = 10010
+BattleCellId = 0
+FrontBattleCellId = 1
+LeftBattleCellId = 2
+RightBattleCellId = 3
+BattleCells = {BattleCellId: {'parentEntId': CogParent,
+ 'pos': Point3(0, 0, 0)},
+ FrontBattleCellId: {'parentEntId': FrontCogParent,
+ 'pos': Point3(0, 0, 0)},
+ LeftBattleCellId: {'parentEntId': LeftCogParent,
+ 'pos': Point3(0, 0, 0)},
+ RightBattleCellId: {'parentEntId': RightCogParent,
+ 'pos': Point3(0, 0, 0)}}
+CogData = [{'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': CogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': BattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': FrontCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': FrontBattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': LeftCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': LeftBattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': LeftCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': LeftBattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': LeftCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': LeftBattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': LeftCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': LeftBattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': RightCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel + 1,
+ 'battleCell': RightBattleCellId,
+ 'pos': Point3(-6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': RightCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': RightBattleCellId,
+ 'pos': Point3(-2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': RightCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': RightBattleCellId,
+ 'pos': Point3(2, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0},
+ {'parentEntId': RightCogParent,
+ 'boss': 0,
+ 'level': ToontownGlobals.CashbotMintCogLevel,
+ 'battleCell': RightBattleCellId,
+ 'pos': Point3(6, 0, 0),
+ 'h': 180,
+ 'behavior': 'stand',
+ 'path': None,
+ 'skeleton': 0}]
+ReserveCogData = []
diff --git a/toontown/coghq/CashbotMintPipeRoom_Battle01.py b/toontown/coghq/CashbotMintPipeRoom_Battle01.py
new file mode 100644
index 0000000..cb50187
--- /dev/null
+++ b/toontown/coghq/CashbotMintPipeRoom_Battle01.py
@@ -0,0 +1,364 @@
+from toontown.coghq.SpecImports import *
+GlobalEntities = {1000: {'type': 'levelMgr',
+ 'name': 'LevelMgr',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'cogLevel': 0,
+ 'farPlaneDistance': 1500,
+ 'modelFilename': 'phase_10/models/cashbotHQ/ZONE13a',
+ 'wantDoors': 1},
+ 1001: {'type': 'editMgr',
+ 'name': 'EditMgr',
+ 'parentEntId': 0,
+ 'insertEntity': None,
+ 'removeEntity': None,
+ 'requestNewEntity': None,
+ 'requestSave': None},
+ 0: {'type': 'zone',
+ 'name': 'UberZone',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'scale': 1,
+ 'description': '',
+ 'visibility': []},
+ 10001: {'type': 'locator',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 0,
+ 'searchPath': '**/EXIT'},
+ 10020: {'type': 'mintProduct',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10019,
+ 'pos': Point3(6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12600},
+ 10021: {'type': 'mintProduct',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10019,
+ 'pos': Point3(13.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12600},
+ 10022: {'type': 'mintProduct',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10019,
+ 'pos': Point3(-6.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12600},
+ 10023: {'type': 'mintProduct',
+ 'name': 'copy of (3)',
+ 'comment': '',
+ 'parentEntId': 10019,
+ 'pos': Point3(-13.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12600},
+ 10025: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10027,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12600},
+ 10029: {'type': 'mintShelf',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10027,
+ 'pos': Point3(13.4300003052, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12600},
+ 10030: {'type': 'mintShelf',
+ 'name': 'copy of ',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12600},
+ 10031: {'type': 'mintShelf',
+ 'name': 'copy of (2)',
+ 'comment': '',
+ 'parentEntId': 10028,
+ 'pos': Point3(-13.4300003052, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12600},
+ 10033: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10034: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10032,
+ 'pos': Point3(29.7916145325, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10036: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10035,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'mintId': 12500},
+ 10037: {'type': 'mintShelf',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10035,
+ 'pos': Point3(-37.7522773743, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'mintId': 12500},
+ 10002: {'type': 'model',
+ 'name': '',
+ 'comment': '',
+ 'parentEntId': 10001,
+ 'pos': Point3(0.0, 0.0, 0.0),
+ 'hpr': Vec3(0.0, 0.0, 0.0),
+ 'scale': 1,
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'},
+ 10009: {'type': 'model',
+ 'name': 'backPillar',
+ 'comment': '',
+ 'parentEntId': 10005,
+ 'pos': Point3(41.4432792664, 0.0, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10010: {'type': 'model',
+ 'name': 'frontPillar',
+ 'comment': '',
+ 'parentEntId': 10005,
+ 'pos': Point3(-41.0848464966, 0.0, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10011: {'type': 'model',
+ 'name': 'rightPillar',
+ 'comment': '',
+ 'parentEntId': 10005,
+ 'pos': Point3(0.0, -22.3441867828, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10012: {'type': 'model',
+ 'name': 'leftPillar',
+ 'comment': '',
+ 'parentEntId': 10005,
+ 'pos': Point3(0.0, 21.9451503754, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10013: {'type': 'model',
+ 'name': 'frontRightPillar',
+ 'comment': '',
+ 'parentEntId': 10006,
+ 'pos': Point3(0.0, -22.5711078644, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10014: {'type': 'model',
+ 'name': 'frontLeftPillar',
+ 'comment': '',
+ 'parentEntId': 10006,
+ 'pos': Point3(0.0, 22.1686630249, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10015: {'type': 'model',
+ 'name': 'frontRightPillar',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, -22.5711078644, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10016: {'type': 'model',
+ 'name': 'frontLeftPillar',
+ 'comment': '',
+ 'parentEntId': 10007,
+ 'pos': Point3(0.0, 22.1686630249, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10017: {'type': 'model',
+ 'name': 'leftPillar',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(0.0, 67.0966033936, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10018: {'type': 'model',
+ 'name': 'rightPillar',
+ 'comment': '',
+ 'parentEntId': 10008,
+ 'pos': Point3(0.0, -66.8615875244, 0.0),
+ 'hpr': Point3(0.0, 0.0, 0.0),
+ 'scale': Vec3(1.0, 1.0, 1.0),
+ 'collisionsOnly': 0,
+ 'flattenType': 'light',
+ 'loadType': 'loadModelCopy',
+ 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'},
+ 10039: {'type': 'model',
+ 'name': '